wined3d: Pass around the context instead of gl_info.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob953d7013ebc3288f00134bea804449965bc3fead
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_update_draw_binding(struct wined3d_surface *surface)
105 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
106 surface->draw_binding = SFLAG_INDRAWABLE;
107 else
108 surface->draw_binding = SFLAG_INTEXTURE;
111 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
113 TRACE("surface %p, container %p.\n", surface, container);
115 if (!container && type != WINED3D_CONTAINER_NONE)
116 ERR("Setting NULL container of type %#x.\n", type);
118 if (type == WINED3D_CONTAINER_SWAPCHAIN)
120 surface->get_drawable_size = get_drawable_size_swapchain;
122 else
124 switch (wined3d_settings.offscreen_rendering_mode)
126 case ORM_FBO:
127 surface->get_drawable_size = get_drawable_size_fbo;
128 break;
130 case ORM_BACKBUFFER:
131 surface->get_drawable_size = get_drawable_size_backbuffer;
132 break;
134 default:
135 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
136 return;
140 surface->container.type = type;
141 surface->container.u.base = container;
142 surface_update_draw_binding(surface);
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* GL locking and context activation is done by the caller */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, const RECT *src_rect,
299 const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
301 struct blt_info info;
303 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
305 glEnable(info.bind_target);
306 checkGLcall("glEnable(bind_target)");
308 /* Bind the texture */
309 glBindTexture(info.bind_target, src_surface->texture_name);
310 checkGLcall("glBindTexture");
312 /* Filtering for StretchRect */
313 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
314 wined3d_gl_mag_filter(magLookup, Filter));
315 checkGLcall("glTexParameteri");
316 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
318 checkGLcall("glTexParameteri");
319 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
322 checkGLcall("glTexEnvi");
324 /* Draw a quad */
325 glBegin(GL_TRIANGLE_STRIP);
326 glTexCoord3fv(info.coords[0]);
327 glVertex2i(dst_rect->left, dst_rect->top);
329 glTexCoord3fv(info.coords[1]);
330 glVertex2i(dst_rect->right, dst_rect->top);
332 glTexCoord3fv(info.coords[2]);
333 glVertex2i(dst_rect->left, dst_rect->bottom);
335 glTexCoord3fv(info.coords[3]);
336 glVertex2i(dst_rect->right, dst_rect->bottom);
337 glEnd();
339 /* Unbind the texture */
340 glBindTexture(info.bind_target, 0);
341 checkGLcall("glBindTexture(info->bind_target, 0)");
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
347 struct wined3d_texture *texture = src_surface->container.u.texture;
348 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
349 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
350 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
354 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
356 const struct wined3d_format *format = surface->resource.format;
357 SYSTEM_INFO sysInfo;
358 BITMAPINFO *b_info;
359 int extraline = 0;
360 DWORD *masks;
361 UINT usage;
362 HDC dc;
364 TRACE("surface %p.\n", surface);
366 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
368 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
369 return WINED3DERR_INVALIDCALL;
372 switch (format->byte_count)
374 case 2:
375 case 4:
376 /* Allocate extra space to store the RGB bit masks. */
377 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
378 break;
380 case 3:
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
382 break;
384 default:
385 /* Allocate extra space for a palette. */
386 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
387 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
388 break;
391 if (!b_info)
392 return E_OUTOFMEMORY;
394 /* Some applications access the surface in via DWORDs, and do not take
395 * the necessary care at the end of the surface. So we need at least
396 * 4 extra bytes at the end of the surface. Check against the page size,
397 * if the last page used for the surface has at least 4 spare bytes we're
398 * safe, otherwise add an extra line to the DIB section. */
399 GetSystemInfo(&sysInfo);
400 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
402 extraline = 1;
403 TRACE("Adding an extra line to the DIB section.\n");
406 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
407 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
408 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
409 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
410 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
411 * wined3d_surface_get_pitch(surface);
412 b_info->bmiHeader.biPlanes = 1;
413 b_info->bmiHeader.biBitCount = format->byte_count * 8;
415 b_info->bmiHeader.biXPelsPerMeter = 0;
416 b_info->bmiHeader.biYPelsPerMeter = 0;
417 b_info->bmiHeader.biClrUsed = 0;
418 b_info->bmiHeader.biClrImportant = 0;
420 /* Get the bit masks */
421 masks = (DWORD *)b_info->bmiColors;
422 switch (surface->resource.format->id)
424 case WINED3DFMT_B8G8R8_UNORM:
425 usage = DIB_RGB_COLORS;
426 b_info->bmiHeader.biCompression = BI_RGB;
427 break;
429 case WINED3DFMT_B5G5R5X1_UNORM:
430 case WINED3DFMT_B5G5R5A1_UNORM:
431 case WINED3DFMT_B4G4R4A4_UNORM:
432 case WINED3DFMT_B4G4R4X4_UNORM:
433 case WINED3DFMT_B2G3R3_UNORM:
434 case WINED3DFMT_B2G3R3A8_UNORM:
435 case WINED3DFMT_R10G10B10A2_UNORM:
436 case WINED3DFMT_R8G8B8A8_UNORM:
437 case WINED3DFMT_R8G8B8X8_UNORM:
438 case WINED3DFMT_B10G10R10A2_UNORM:
439 case WINED3DFMT_B5G6R5_UNORM:
440 case WINED3DFMT_R16G16B16A16_UNORM:
441 usage = 0;
442 b_info->bmiHeader.biCompression = BI_BITFIELDS;
443 masks[0] = format->red_mask;
444 masks[1] = format->green_mask;
445 masks[2] = format->blue_mask;
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 usage = 0;
452 break;
455 if (!(dc = GetDC(0)))
457 HeapFree(GetProcessHeap(), 0, b_info);
458 return HRESULT_FROM_WIN32(GetLastError());
461 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
462 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
463 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
464 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
465 ReleaseDC(0, dc);
467 if (!surface->dib.DIBsection)
469 ERR("Failed to create DIB section.\n");
470 HeapFree(GetProcessHeap(), 0, b_info);
471 return HRESULT_FROM_WIN32(GetLastError());
474 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
475 /* Copy the existing surface to the dib section. */
476 if (surface->resource.allocatedMemory)
478 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
479 surface->resource.height * wined3d_surface_get_pitch(surface));
481 else
483 /* This is to make maps read the GL texture although memory is allocated. */
484 surface->flags &= ~SFLAG_INSYSMEM;
486 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
488 HeapFree(GetProcessHeap(), 0, b_info);
490 /* Now allocate a DC. */
491 surface->hDC = CreateCompatibleDC(0);
492 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
493 TRACE("Using wined3d palette %p.\n", surface->palette);
494 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
496 surface->flags |= SFLAG_DIBSECTION;
498 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
499 surface->resource.heapMemory = NULL;
501 return WINED3D_OK;
504 static void surface_prepare_system_memory(struct wined3d_surface *surface)
506 struct wined3d_device *device = surface->resource.device;
507 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
509 TRACE("surface %p.\n", surface);
511 /* Performance optimization: Count how often a surface is locked, if it is
512 * locked regularly do not throw away the system memory copy. This avoids
513 * the need to download the surface from OpenGL all the time. The surface
514 * is still downloaded if the OpenGL texture is changed. */
515 if (!(surface->flags & SFLAG_DYNLOCK))
517 if (++surface->lockCount > MAXLOCKCOUNT)
519 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
520 surface->flags |= SFLAG_DYNLOCK;
524 /* Create a PBO for dynamically locked surfaces but don't do it for
525 * converted or NPOT surfaces. Also don't create a PBO for systemmem
526 * surfaces. */
527 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
528 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
529 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
531 struct wined3d_context *context;
532 GLenum error;
534 context = context_acquire(device, NULL);
535 ENTER_GL();
537 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
538 error = glGetError();
539 if (!surface->pbo || error != GL_NO_ERROR)
540 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
542 TRACE("Binding PBO %u.\n", surface->pbo);
544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
545 checkGLcall("glBindBufferARB");
547 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
548 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
549 checkGLcall("glBufferDataARB");
551 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
552 checkGLcall("glBindBufferARB");
554 /* We don't need the system memory anymore and we can't even use it for PBOs. */
555 if (!(surface->flags & SFLAG_CLIENT))
557 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
558 surface->resource.heapMemory = NULL;
560 surface->resource.allocatedMemory = NULL;
561 surface->flags |= SFLAG_PBO;
562 LEAVE_GL();
563 context_release(context);
565 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
567 /* Whatever surface we have, make sure that there is memory allocated
568 * for the downloaded copy, or a PBO to map. */
569 if (!surface->resource.heapMemory)
570 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
572 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
573 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
575 if (surface->flags & SFLAG_INSYSMEM)
576 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
580 static void surface_evict_sysmem(struct wined3d_surface *surface)
582 if (surface->flags & SFLAG_DONOTFREE)
583 return;
585 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
586 surface->resource.allocatedMemory = NULL;
587 surface->resource.heapMemory = NULL;
588 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
591 /* Context activation is done by the caller. */
592 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
593 struct wined3d_context *context, BOOL srgb)
595 struct wined3d_device *device = surface->resource.device;
596 DWORD active_sampler;
597 GLint active_texture;
599 /* We don't need a specific texture unit, but after binding the texture
600 * the current unit is dirty. Read the unit back instead of switching to
601 * 0, this avoids messing around with the state manager's GL states. The
602 * current texture unit should always be a valid one.
604 * To be more specific, this is tricky because we can implicitly be
605 * called from sampler() in state.c. This means we can't touch anything
606 * other than whatever happens to be the currently active texture, or we
607 * would risk marking already applied sampler states dirty again.
609 * TODO: Track the current active texture per GL context instead of using
610 * glGet(). */
612 ENTER_GL();
613 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
614 LEAVE_GL();
615 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
617 if (active_sampler != WINED3D_UNMAPPED_STAGE)
618 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
619 surface_bind(surface, context, srgb);
622 static void surface_force_reload(struct wined3d_surface *surface)
624 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
627 static void surface_release_client_storage(struct wined3d_surface *surface)
629 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
631 ENTER_GL();
632 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
633 if (surface->texture_name)
635 surface_bind_and_dirtify(surface, context, FALSE);
636 glTexImage2D(surface->texture_target, surface->texture_level,
637 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
639 if (surface->texture_name_srgb)
641 surface_bind_and_dirtify(surface, context, TRUE);
642 glTexImage2D(surface->texture_target, surface->texture_level,
643 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
645 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
646 LEAVE_GL();
648 context_release(context);
650 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
651 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
652 surface_force_reload(surface);
655 static HRESULT surface_private_setup(struct wined3d_surface *surface)
657 /* TODO: Check against the maximum texture sizes supported by the video card. */
658 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
659 unsigned int pow2Width, pow2Height;
661 TRACE("surface %p.\n", surface);
663 surface->texture_name = 0;
664 surface->texture_target = GL_TEXTURE_2D;
666 /* Non-power2 support */
667 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
669 pow2Width = surface->resource.width;
670 pow2Height = surface->resource.height;
672 else
674 /* Find the nearest pow2 match */
675 pow2Width = pow2Height = 1;
676 while (pow2Width < surface->resource.width)
677 pow2Width <<= 1;
678 while (pow2Height < surface->resource.height)
679 pow2Height <<= 1;
681 surface->pow2Width = pow2Width;
682 surface->pow2Height = pow2Height;
684 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
686 /* TODO: Add support for non power two compressed textures. */
687 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
689 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
690 surface, surface->resource.width, surface->resource.height);
691 return WINED3DERR_NOTAVAILABLE;
695 if (pow2Width != surface->resource.width
696 || pow2Height != surface->resource.height)
698 surface->flags |= SFLAG_NONPOW2;
701 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
702 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
704 /* One of three options:
705 * 1: Do the same as we do with NPOT and scale the texture, (any
706 * texture ops would require the texture to be scaled which is
707 * potentially slow)
708 * 2: Set the texture to the maximum size (bad idea).
709 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
710 * 4: Create the surface, but allow it to be used only for DirectDraw
711 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
712 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
713 * the render target. */
714 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
716 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
717 return WINED3DERR_NOTAVAILABLE;
720 /* We should never use this surface in combination with OpenGL! */
721 TRACE("Creating an oversized surface: %ux%u.\n",
722 surface->pow2Width, surface->pow2Height);
724 else
726 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
727 * and EXT_PALETTED_TEXTURE is used in combination with texture
728 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
729 * EXT_PALETTED_TEXTURE doesn't work in combination with
730 * ARB_TEXTURE_RECTANGLE. */
731 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
732 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
733 && gl_info->supported[EXT_PALETTED_TEXTURE]
734 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
736 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
737 surface->pow2Width = surface->resource.width;
738 surface->pow2Height = surface->resource.height;
739 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
743 switch (wined3d_settings.offscreen_rendering_mode)
745 case ORM_FBO:
746 surface->get_drawable_size = get_drawable_size_fbo;
747 break;
749 case ORM_BACKBUFFER:
750 surface->get_drawable_size = get_drawable_size_backbuffer;
751 break;
753 default:
754 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
755 return WINED3DERR_INVALIDCALL;
758 surface->flags |= SFLAG_INSYSMEM;
760 return WINED3D_OK;
763 static void surface_realize_palette(struct wined3d_surface *surface)
765 struct wined3d_palette *palette = surface->palette;
767 TRACE("surface %p.\n", surface);
769 if (!palette) return;
771 if (surface->resource.format->id == WINED3DFMT_P8_UINT
772 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
774 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
776 /* Make sure the texture is up to date. This call doesn't do
777 * anything if the texture is already up to date. */
778 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
780 /* We want to force a palette refresh, so mark the drawable as not being up to date */
781 if (!surface_is_offscreen(surface))
782 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
784 else
786 if (!(surface->flags & SFLAG_INSYSMEM))
788 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
789 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
791 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
795 if (surface->flags & SFLAG_DIBSECTION)
797 RGBQUAD col[256];
798 unsigned int i;
800 TRACE("Updating the DC's palette.\n");
802 for (i = 0; i < 256; ++i)
804 col[i].rgbRed = palette->palents[i].peRed;
805 col[i].rgbGreen = palette->palents[i].peGreen;
806 col[i].rgbBlue = palette->palents[i].peBlue;
807 col[i].rgbReserved = 0;
809 SetDIBColorTable(surface->hDC, 0, 256, col);
812 /* Propagate the changes to the drawable when we have a palette. */
813 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
814 surface_load_location(surface, surface->draw_binding, NULL);
817 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
819 HRESULT hr;
821 /* If there's no destination surface there is nothing to do. */
822 if (!surface->overlay_dest)
823 return WINED3D_OK;
825 /* Blt calls ModifyLocation on the dest surface, which in turn calls
826 * DrawOverlay to update the overlay. Prevent an endless recursion. */
827 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
828 return WINED3D_OK;
830 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
831 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
832 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
833 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
835 return hr;
838 static void surface_preload(struct wined3d_surface *surface)
840 TRACE("surface %p.\n", surface);
842 surface_internal_preload(surface, SRGB_ANY);
845 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
847 struct wined3d_device *device = surface->resource.device;
848 const RECT *pass_rect = rect;
850 TRACE("surface %p, rect %s, flags %#x.\n",
851 surface, wine_dbgstr_rect(rect), flags);
853 if (flags & WINED3DLOCK_DISCARD)
855 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
856 surface_prepare_system_memory(surface);
857 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
859 else
861 /* surface_load_location() does not check if the rectangle specifies
862 * the full surface. Most callers don't need that, so do it here. */
863 if (rect && !rect->top && !rect->left
864 && rect->right == surface->resource.width
865 && rect->bottom == surface->resource.height)
866 pass_rect = NULL;
868 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
869 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
870 || surface == device->fb.render_targets[0])))
871 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
874 if (surface->flags & SFLAG_PBO)
876 const struct wined3d_gl_info *gl_info;
877 struct wined3d_context *context;
879 context = context_acquire(device, NULL);
880 gl_info = context->gl_info;
882 ENTER_GL();
883 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
884 checkGLcall("glBindBufferARB");
886 /* This shouldn't happen but could occur if some other function
887 * didn't handle the PBO properly. */
888 if (surface->resource.allocatedMemory)
889 ERR("The surface already has PBO memory allocated.\n");
891 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
892 checkGLcall("glMapBufferARB");
894 /* Make sure the PBO isn't set anymore in order not to break non-PBO
895 * calls. */
896 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
897 checkGLcall("glBindBufferARB");
899 LEAVE_GL();
900 context_release(context);
903 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
905 if (!rect)
906 surface_add_dirty_rect(surface, NULL);
907 else
909 WINED3DBOX b;
911 b.Left = rect->left;
912 b.Top = rect->top;
913 b.Right = rect->right;
914 b.Bottom = rect->bottom;
915 b.Front = 0;
916 b.Back = 1;
917 surface_add_dirty_rect(surface, &b);
922 static void surface_unmap(struct wined3d_surface *surface)
924 struct wined3d_device *device = surface->resource.device;
925 BOOL fullsurface;
927 TRACE("surface %p.\n", surface);
929 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
931 if (surface->flags & SFLAG_PBO)
933 const struct wined3d_gl_info *gl_info;
934 struct wined3d_context *context;
936 TRACE("Freeing PBO memory.\n");
938 context = context_acquire(device, NULL);
939 gl_info = context->gl_info;
941 ENTER_GL();
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
943 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
944 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
945 checkGLcall("glUnmapBufferARB");
946 LEAVE_GL();
947 context_release(context);
949 surface->resource.allocatedMemory = NULL;
952 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
954 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
956 TRACE("Not dirtified, nothing to do.\n");
957 goto done;
960 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
961 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
963 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
965 static BOOL warned = FALSE;
966 if (!warned)
968 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
969 warned = TRUE;
971 goto done;
974 if (!surface->dirtyRect.left && !surface->dirtyRect.top
975 && surface->dirtyRect.right == surface->resource.width
976 && surface->dirtyRect.bottom == surface->resource.height)
978 fullsurface = TRUE;
980 else
982 /* TODO: Proper partial rectangle tracking. */
983 fullsurface = FALSE;
984 surface->flags |= SFLAG_INSYSMEM;
987 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
989 /* Partial rectangle tracking is not commonly implemented, it is only
990 * done for render targets. INSYSMEM was set before to tell
991 * surface_load_location() where to read the rectangle from.
992 * Indrawable is set because all modifications from the partial
993 * sysmem copy are written back to the drawable, thus the surface is
994 * merged again in the drawable. The sysmem copy is not fully up to
995 * date because only a subrectangle was read in Map(). */
996 if (!fullsurface)
998 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
999 surface_evict_sysmem(surface);
1002 surface->dirtyRect.left = surface->resource.width;
1003 surface->dirtyRect.top = surface->resource.height;
1004 surface->dirtyRect.right = 0;
1005 surface->dirtyRect.bottom = 0;
1007 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1009 FIXME("Depth / stencil buffer locking is not implemented.\n");
1012 done:
1013 /* Overlays have to be redrawn manually after changes with the GL implementation */
1014 if (surface->overlay_dest)
1015 surface->surface_ops->surface_draw_overlay(surface);
1018 static HRESULT surface_getdc(struct wined3d_surface *surface)
1020 WINED3DLOCKED_RECT lock;
1021 HRESULT hr;
1023 TRACE("surface %p.\n", surface);
1025 /* Create a DIB section if there isn't a dc yet. */
1026 if (!surface->hDC)
1028 if (surface->flags & SFLAG_CLIENT)
1030 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1031 surface_release_client_storage(surface);
1033 hr = surface_create_dib_section(surface);
1034 if (FAILED(hr))
1035 return WINED3DERR_INVALIDCALL;
1037 /* Use the DIB section from now on if we are not using a PBO. */
1038 if (!(surface->flags & SFLAG_PBO))
1039 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1042 /* Map the surface. */
1043 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1044 if (FAILED(hr))
1045 ERR("Map failed, hr %#x.\n", hr);
1047 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1048 * activates the allocatedMemory. */
1049 if (surface->flags & SFLAG_PBO)
1050 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1052 return hr;
1055 static HRESULT surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1057 TRACE("surface %p, override %p.\n", surface, override);
1059 /* Flipping is only supported on render targets and overlays. */
1060 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1062 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1063 return WINEDDERR_NOTFLIPPABLE;
1066 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1068 flip_surface(surface, override);
1070 /* Update the overlay if it is visible */
1071 if (surface->overlay_dest)
1072 return surface->surface_ops->surface_draw_overlay(surface);
1073 else
1074 return WINED3D_OK;
1077 return WINED3D_OK;
1080 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1082 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1083 return FALSE;
1084 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1085 return FALSE;
1086 return TRUE;
1089 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1090 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1092 const struct wined3d_gl_info *gl_info;
1093 struct wined3d_context *context;
1094 DWORD src_mask, dst_mask;
1095 GLbitfield gl_mask;
1097 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1098 device, src_surface, wine_dbgstr_rect(src_rect),
1099 dst_surface, wine_dbgstr_rect(dst_rect));
1101 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1102 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1104 if (src_mask != dst_mask)
1106 ERR("Incompatible formats %s and %s.\n",
1107 debug_d3dformat(src_surface->resource.format->id),
1108 debug_d3dformat(dst_surface->resource.format->id));
1109 return;
1112 if (!src_mask)
1114 ERR("Not a depth / stencil format: %s.\n",
1115 debug_d3dformat(src_surface->resource.format->id));
1116 return;
1119 gl_mask = 0;
1120 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1121 gl_mask |= GL_DEPTH_BUFFER_BIT;
1122 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1123 gl_mask |= GL_STENCIL_BUFFER_BIT;
1125 /* Make sure the locations are up-to-date. Loading the destination
1126 * surface isn't required if the entire surface is overwritten. */
1127 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1128 if (!surface_is_full_rect(dst_surface, dst_rect))
1129 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1131 context = context_acquire(device, NULL);
1132 if (!context->valid)
1134 context_release(context);
1135 WARN("Invalid context, skipping blit.\n");
1136 return;
1139 gl_info = context->gl_info;
1141 ENTER_GL();
1143 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1144 glReadBuffer(GL_NONE);
1145 checkGLcall("glReadBuffer()");
1146 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1148 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1149 context_set_draw_buffer(context, GL_NONE);
1150 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1152 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1154 glDepthMask(GL_TRUE);
1155 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1157 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1159 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1161 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1162 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1164 glStencilMask(~0U);
1165 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1168 glDisable(GL_SCISSOR_TEST);
1169 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1171 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1172 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1173 checkGLcall("glBlitFramebuffer()");
1175 LEAVE_GL();
1177 if (wined3d_settings.strict_draw_ordering)
1178 wglFlush(); /* Flush to ensure ordering across contexts. */
1180 context_release(context);
1183 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1184 * Depth / stencil is not supported. */
1185 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1186 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1187 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1189 const struct wined3d_gl_info *gl_info;
1190 struct wined3d_context *context;
1191 RECT src_rect, dst_rect;
1192 GLenum gl_filter;
1193 GLenum buffer;
1195 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1196 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1197 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1198 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1199 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1201 src_rect = *src_rect_in;
1202 dst_rect = *dst_rect_in;
1204 switch (filter)
1206 case WINED3DTEXF_LINEAR:
1207 gl_filter = GL_LINEAR;
1208 break;
1210 default:
1211 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1212 case WINED3DTEXF_NONE:
1213 case WINED3DTEXF_POINT:
1214 gl_filter = GL_NEAREST;
1215 break;
1218 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
1219 src_location = SFLAG_INTEXTURE;
1220 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
1221 dst_location = SFLAG_INTEXTURE;
1223 /* Make sure the locations are up-to-date. Loading the destination
1224 * surface isn't required if the entire surface is overwritten. (And is
1225 * in fact harmful if we're being called by surface_load_location() with
1226 * the purpose of loading the destination surface.) */
1227 surface_load_location(src_surface, src_location, NULL);
1228 if (!surface_is_full_rect(dst_surface, &dst_rect))
1229 surface_load_location(dst_surface, dst_location, NULL);
1231 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1232 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1233 else context = context_acquire(device, NULL);
1235 if (!context->valid)
1237 context_release(context);
1238 WARN("Invalid context, skipping blit.\n");
1239 return;
1242 gl_info = context->gl_info;
1244 if (src_location == SFLAG_INDRAWABLE)
1246 TRACE("Source surface %p is onscreen.\n", src_surface);
1247 buffer = surface_get_gl_buffer(src_surface);
1248 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1250 else
1252 TRACE("Source surface %p is offscreen.\n", src_surface);
1253 buffer = GL_COLOR_ATTACHMENT0;
1256 ENTER_GL();
1257 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1258 glReadBuffer(buffer);
1259 checkGLcall("glReadBuffer()");
1260 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1261 LEAVE_GL();
1263 if (dst_location == SFLAG_INDRAWABLE)
1265 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1266 buffer = surface_get_gl_buffer(dst_surface);
1267 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1269 else
1271 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1272 buffer = GL_COLOR_ATTACHMENT0;
1275 ENTER_GL();
1276 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1277 context_set_draw_buffer(context, buffer);
1278 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1279 context_invalidate_state(context, STATE_FRAMEBUFFER);
1281 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1282 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1283 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1284 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1285 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1287 glDisable(GL_SCISSOR_TEST);
1288 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1290 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1291 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1292 checkGLcall("glBlitFramebuffer()");
1294 LEAVE_GL();
1296 if (wined3d_settings.strict_draw_ordering
1297 || (dst_location == SFLAG_INDRAWABLE
1298 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1299 wglFlush();
1301 context_release(context);
1304 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1305 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1306 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1308 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1309 return FALSE;
1311 /* Source and/or destination need to be on the GL side */
1312 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1313 return FALSE;
1315 switch (blit_op)
1317 case WINED3D_BLIT_OP_COLOR_BLIT:
1318 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1319 return FALSE;
1320 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1321 return FALSE;
1322 break;
1324 case WINED3D_BLIT_OP_DEPTH_BLIT:
1325 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1326 return FALSE;
1327 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1328 return FALSE;
1329 break;
1331 default:
1332 return FALSE;
1335 if (!(src_format->id == dst_format->id
1336 || (is_identity_fixup(src_format->color_fixup)
1337 && is_identity_fixup(dst_format->color_fixup))))
1338 return FALSE;
1340 return TRUE;
1343 /* This function checks if the primary render target uses the 8bit paletted format. */
1344 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1346 if (device->fb.render_targets && device->fb.render_targets[0])
1348 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1349 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1350 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1351 return TRUE;
1353 return FALSE;
1356 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1357 DWORD color, WINED3DCOLORVALUE *float_color)
1359 const struct wined3d_format *format = surface->resource.format;
1360 const struct wined3d_device *device = surface->resource.device;
1362 switch (format->id)
1364 case WINED3DFMT_P8_UINT:
1365 if (surface->palette)
1367 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1368 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1369 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1371 else
1373 float_color->r = 0.0f;
1374 float_color->g = 0.0f;
1375 float_color->b = 0.0f;
1377 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1378 break;
1380 case WINED3DFMT_B5G6R5_UNORM:
1381 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1382 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1383 float_color->b = (color & 0x1f) / 31.0f;
1384 float_color->a = 1.0f;
1385 break;
1387 case WINED3DFMT_B8G8R8_UNORM:
1388 case WINED3DFMT_B8G8R8X8_UNORM:
1389 float_color->r = D3DCOLOR_R(color);
1390 float_color->g = D3DCOLOR_G(color);
1391 float_color->b = D3DCOLOR_B(color);
1392 float_color->a = 1.0f;
1393 break;
1395 case WINED3DFMT_B8G8R8A8_UNORM:
1396 float_color->r = D3DCOLOR_R(color);
1397 float_color->g = D3DCOLOR_G(color);
1398 float_color->b = D3DCOLOR_B(color);
1399 float_color->a = D3DCOLOR_A(color);
1400 break;
1402 default:
1403 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1404 return FALSE;
1407 return TRUE;
1410 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1412 const struct wined3d_format *format = surface->resource.format;
1414 switch (format->id)
1416 case WINED3DFMT_S1_UINT_D15_UNORM:
1417 *float_depth = depth / (float)0x00007fff;
1418 break;
1420 case WINED3DFMT_D16_UNORM:
1421 *float_depth = depth / (float)0x0000ffff;
1422 break;
1424 case WINED3DFMT_D24_UNORM_S8_UINT:
1425 case WINED3DFMT_X8D24_UNORM:
1426 *float_depth = depth / (float)0x00ffffff;
1427 break;
1429 case WINED3DFMT_D32_UNORM:
1430 *float_depth = depth / (float)0xffffffff;
1431 break;
1433 default:
1434 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1435 return FALSE;
1438 return TRUE;
1441 /* Do not call while under the GL lock. */
1442 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1444 const struct wined3d_resource *resource = &surface->resource;
1445 struct wined3d_device *device = resource->device;
1446 const struct blit_shader *blitter;
1448 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1449 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1450 if (!blitter)
1452 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1453 return WINED3DERR_INVALIDCALL;
1456 return blitter->depth_fill(device, surface, rect, depth);
1459 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1460 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1462 struct wined3d_device *device = src_surface->resource.device;
1464 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1465 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1466 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1467 return WINED3DERR_INVALIDCALL;
1469 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1471 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1472 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1473 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1475 return WINED3D_OK;
1478 /* Do not call while under the GL lock. */
1479 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1480 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1481 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1483 const struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1484 struct wined3d_device *device = dst_surface->resource.device;
1485 DWORD src_ds_flags, dst_ds_flags;
1486 RECT src_rect, dst_rect;
1488 static const DWORD simple_blit = WINEDDBLT_ASYNC
1489 | WINEDDBLT_COLORFILL
1490 | WINEDDBLT_WAIT
1491 | WINEDDBLT_DEPTHFILL
1492 | WINEDDBLT_DONOTWAIT;
1494 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1495 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1496 flags, fx, debug_d3dtexturefiltertype(filter));
1497 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1499 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1501 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1502 return WINEDDERR_SURFACEBUSY;
1505 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1507 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1508 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1509 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1510 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1511 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1513 /* The destination rect can be out of bounds on the condition
1514 * that a clipper is set for the surface. */
1515 if (dst_surface->clipper)
1516 FIXME("Blit clipping not implemented.\n");
1517 else
1518 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1519 return WINEDDERR_INVALIDRECT;
1522 if (src_surface)
1524 surface_get_rect(src_surface, src_rect_in, &src_rect);
1526 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1527 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1528 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1529 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1530 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1532 WARN("Application gave us bad source rectangle for Blt.\n");
1533 return WINEDDERR_INVALIDRECT;
1536 else
1538 memset(&src_rect, 0, sizeof(src_rect));
1541 if (!fx || !(fx->dwDDFX))
1542 flags &= ~WINEDDBLT_DDFX;
1544 if (flags & WINEDDBLT_WAIT)
1545 flags &= ~WINEDDBLT_WAIT;
1547 if (flags & WINEDDBLT_ASYNC)
1549 static unsigned int once;
1551 if (!once++)
1552 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1553 flags &= ~WINEDDBLT_ASYNC;
1556 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1557 if (flags & WINEDDBLT_DONOTWAIT)
1559 static unsigned int once;
1561 if (!once++)
1562 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1563 flags &= ~WINEDDBLT_DONOTWAIT;
1566 if (!device->d3d_initialized)
1568 WARN("D3D not initialized, using fallback.\n");
1569 goto cpu;
1572 if (flags & ~simple_blit)
1574 WARN("Using fallback for complex blit (%#x).\n", flags);
1575 goto fallback;
1578 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1579 src_swapchain = src_surface->container.u.swapchain;
1580 else
1581 src_swapchain = NULL;
1583 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1584 dst_swapchain = dst_surface->container.u.swapchain;
1585 else
1586 dst_swapchain = NULL;
1588 /* This isn't strictly needed. FBO blits for example could deal with
1589 * cross-swapchain blits by first downloading the source to a texture
1590 * before switching to the destination context. We just have this here to
1591 * not have to deal with the issue, since cross-swapchain blits should be
1592 * rare. */
1593 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1595 FIXME("Using fallback for cross-swapchain blit.\n");
1596 goto fallback;
1599 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1600 if (src_surface)
1601 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1602 else
1603 src_ds_flags = 0;
1605 if (src_ds_flags || dst_ds_flags)
1607 if (flags & WINEDDBLT_DEPTHFILL)
1609 float depth;
1611 TRACE("Depth fill.\n");
1613 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1614 return WINED3DERR_INVALIDCALL;
1616 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1617 return WINED3D_OK;
1619 else
1621 /* Accessing depth / stencil surfaces is supposed to fail while in
1622 * a scene, except for fills, which seem to work. */
1623 if (device->inScene)
1625 WARN("Rejecting depth / stencil access while in scene.\n");
1626 return WINED3DERR_INVALIDCALL;
1629 if (src_ds_flags != dst_ds_flags)
1631 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1632 return WINED3DERR_INVALIDCALL;
1635 if (src_rect.top || src_rect.left
1636 || src_rect.bottom != src_surface->resource.height
1637 || src_rect.right != src_surface->resource.width)
1639 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1640 wine_dbgstr_rect(&src_rect));
1641 return WINED3DERR_INVALIDCALL;
1644 if (dst_rect.top || dst_rect.left
1645 || dst_rect.bottom != dst_surface->resource.height
1646 || dst_rect.right != dst_surface->resource.width)
1648 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1649 wine_dbgstr_rect(&src_rect));
1650 return WINED3DERR_INVALIDCALL;
1653 if (src_surface->resource.height != dst_surface->resource.height
1654 || src_surface->resource.width != dst_surface->resource.width)
1656 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1657 return WINED3DERR_INVALIDCALL;
1660 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1661 return WINED3D_OK;
1664 else
1666 if (flags & WINEDDBLT_COLORFILL)
1668 WINED3DCOLORVALUE color;
1670 TRACE("Color fill.\n");
1672 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1673 goto fallback;
1675 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1676 return WINED3D_OK;
1678 else
1680 TRACE("Color blit.\n");
1682 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1683 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1684 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1686 TRACE("Using FBO blit.\n");
1688 surface_blt_fbo(device, filter,
1689 src_surface, src_surface->draw_binding, &src_rect,
1690 dst_surface, dst_surface->draw_binding, &dst_rect);
1691 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1692 return WINED3D_OK;
1695 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1696 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1697 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1699 TRACE("Using arbfp blit.\n");
1701 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1702 return WINED3D_OK;
1707 fallback:
1709 /* Special cases for render targets. */
1710 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1711 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1713 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1714 src_surface, &src_rect, flags, fx, filter)))
1715 return WINED3D_OK;
1718 cpu:
1720 /* For the rest call the X11 surface implementation. For render targets
1721 * this should be implemented OpenGL accelerated in BltOverride, other
1722 * blits are rather rare. */
1723 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1726 /* Do not call while under the GL lock. */
1727 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1728 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1730 RECT src_rect, dst_rect;
1731 DWORD flags = 0;
1733 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1734 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1736 surface_get_rect(src_surface, src_rect_in, &src_rect);
1738 dst_rect.left = dst_x;
1739 dst_rect.top = dst_y;
1740 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1741 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1743 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1744 flags |= WINEDDBLT_KEYSRC;
1745 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1746 flags |= WINEDDBLT_KEYDEST;
1747 if (trans & WINEDDBLTFAST_WAIT)
1748 flags |= WINEDDBLT_WAIT;
1749 if (trans & WINEDDBLTFAST_DONOTWAIT)
1750 flags |= WINEDDBLT_DONOTWAIT;
1752 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1755 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1757 TRACE("surface %p, mem %p.\n", surface, mem);
1759 if (mem && mem != surface->resource.allocatedMemory)
1761 void *release = NULL;
1763 /* Do I have to copy the old surface content? */
1764 if (surface->flags & SFLAG_DIBSECTION)
1766 SelectObject(surface->hDC, surface->dib.holdbitmap);
1767 DeleteDC(surface->hDC);
1768 /* Release the DIB section. */
1769 DeleteObject(surface->dib.DIBsection);
1770 surface->dib.bitmap_data = NULL;
1771 surface->resource.allocatedMemory = NULL;
1772 surface->hDC = NULL;
1773 surface->flags &= ~SFLAG_DIBSECTION;
1775 else if (!(surface->flags & SFLAG_USERPTR))
1777 release = surface->resource.heapMemory;
1778 surface->resource.heapMemory = NULL;
1780 surface->resource.allocatedMemory = mem;
1781 surface->flags |= SFLAG_USERPTR;
1783 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1784 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1786 /* For client textures OpenGL has to be notified. */
1787 if (surface->flags & SFLAG_CLIENT)
1788 surface_release_client_storage(surface);
1790 /* Now free the old memory if any. */
1791 HeapFree(GetProcessHeap(), 0, release);
1793 else if (surface->flags & SFLAG_USERPTR)
1795 /* HeapMemory should be NULL already. */
1796 if (surface->resource.heapMemory)
1797 ERR("User pointer surface has heap memory allocated.\n");
1799 if (!mem)
1801 surface->resource.allocatedMemory = NULL;
1802 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1804 if (surface->flags & SFLAG_CLIENT)
1805 surface_release_client_storage(surface);
1807 surface_prepare_system_memory(surface);
1810 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1813 return WINED3D_OK;
1816 /* Context activation is done by the caller. */
1817 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1819 if (!surface->resource.heapMemory)
1821 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1822 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1823 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1826 ENTER_GL();
1827 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1828 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1829 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1830 surface->resource.size, surface->resource.allocatedMemory));
1831 checkGLcall("glGetBufferSubDataARB");
1832 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1833 checkGLcall("glDeleteBuffersARB");
1834 LEAVE_GL();
1836 surface->pbo = 0;
1837 surface->flags &= ~SFLAG_PBO;
1840 /* Do not call while under the GL lock. */
1841 static void surface_unload(struct wined3d_resource *resource)
1843 struct wined3d_surface *surface = surface_from_resource(resource);
1844 struct wined3d_renderbuffer_entry *entry, *entry2;
1845 struct wined3d_device *device = resource->device;
1846 const struct wined3d_gl_info *gl_info;
1847 struct wined3d_context *context;
1849 TRACE("surface %p.\n", surface);
1851 if (resource->pool == WINED3DPOOL_DEFAULT)
1853 /* Default pool resources are supposed to be destroyed before Reset is called.
1854 * Implicit resources stay however. So this means we have an implicit render target
1855 * or depth stencil. The content may be destroyed, but we still have to tear down
1856 * opengl resources, so we cannot leave early.
1858 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1859 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1860 * or the depth stencil into an FBO the texture or render buffer will be removed
1861 * and all flags get lost
1863 surface_init_sysmem(surface);
1865 else
1867 /* Load the surface into system memory */
1868 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1869 surface_modify_location(surface, surface->draw_binding, FALSE);
1871 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1872 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1873 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1875 context = context_acquire(device, NULL);
1876 gl_info = context->gl_info;
1878 /* Destroy PBOs, but load them into real sysmem before */
1879 if (surface->flags & SFLAG_PBO)
1880 surface_remove_pbo(surface, gl_info);
1882 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1883 * all application-created targets the application has to release the surface
1884 * before calling _Reset
1886 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1888 ENTER_GL();
1889 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1890 LEAVE_GL();
1891 list_remove(&entry->entry);
1892 HeapFree(GetProcessHeap(), 0, entry);
1894 list_init(&surface->renderbuffers);
1895 surface->current_renderbuffer = NULL;
1897 /* If we're in a texture, the texture name belongs to the texture.
1898 * Otherwise, destroy it. */
1899 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1901 ENTER_GL();
1902 glDeleteTextures(1, &surface->texture_name);
1903 surface->texture_name = 0;
1904 glDeleteTextures(1, &surface->texture_name_srgb);
1905 surface->texture_name_srgb = 0;
1906 LEAVE_GL();
1909 context_release(context);
1911 resource_unload(resource);
1914 static const struct wined3d_resource_ops surface_resource_ops =
1916 surface_unload,
1919 static const struct wined3d_surface_ops surface_ops =
1921 surface_private_setup,
1922 surface_cleanup,
1923 surface_realize_palette,
1924 surface_draw_overlay,
1925 surface_preload,
1926 surface_map,
1927 surface_unmap,
1928 surface_getdc,
1929 surface_flip,
1930 surface_set_mem,
1933 /*****************************************************************************
1934 * Initializes the GDI surface, aka creates the DIB section we render to
1935 * The DIB section creation is done by calling GetDC, which will create the
1936 * section and releasing the dc to allow the app to use it. The dib section
1937 * will stay until the surface is released
1939 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1940 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1941 * avoid confusion in the shared surface code.
1943 * Returns:
1944 * WINED3D_OK on success
1945 * The return values of called methods on failure
1947 *****************************************************************************/
1948 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1950 HRESULT hr;
1952 TRACE("surface %p.\n", surface);
1954 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1956 ERR("Overlays not yet supported by GDI surfaces.\n");
1957 return WINED3DERR_INVALIDCALL;
1960 /* Sysmem textures have memory already allocated - release it,
1961 * this avoids an unnecessary memcpy. */
1962 hr = surface_create_dib_section(surface);
1963 if (SUCCEEDED(hr))
1965 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1966 surface->resource.heapMemory = NULL;
1967 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1970 /* We don't mind the nonpow2 stuff in GDI. */
1971 surface->pow2Width = surface->resource.width;
1972 surface->pow2Height = surface->resource.height;
1974 return WINED3D_OK;
1977 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1979 TRACE("surface %p.\n", surface);
1981 if (surface->flags & SFLAG_DIBSECTION)
1983 /* Release the DC. */
1984 SelectObject(surface->hDC, surface->dib.holdbitmap);
1985 DeleteDC(surface->hDC);
1986 /* Release the DIB section. */
1987 DeleteObject(surface->dib.DIBsection);
1988 surface->dib.bitmap_data = NULL;
1989 surface->resource.allocatedMemory = NULL;
1992 if (surface->flags & SFLAG_USERPTR)
1993 wined3d_surface_set_mem(surface, NULL);
1994 if (surface->overlay_dest)
1995 list_remove(&surface->overlay_entry);
1997 HeapFree(GetProcessHeap(), 0, surface->palette9);
1999 resource_cleanup(&surface->resource);
2002 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
2004 struct wined3d_palette *palette = surface->palette;
2006 TRACE("surface %p.\n", surface);
2008 if (!palette) return;
2010 if (surface->flags & SFLAG_DIBSECTION)
2012 RGBQUAD col[256];
2013 unsigned int i;
2015 TRACE("Updating the DC's palette.\n");
2017 for (i = 0; i < 256; ++i)
2019 col[i].rgbRed = palette->palents[i].peRed;
2020 col[i].rgbGreen = palette->palents[i].peGreen;
2021 col[i].rgbBlue = palette->palents[i].peBlue;
2022 col[i].rgbReserved = 0;
2024 SetDIBColorTable(surface->hDC, 0, 256, col);
2027 /* Update the image because of the palette change. Some games like e.g.
2028 * Red Alert call SetEntries a lot to implement fading. */
2029 /* Tell the swapchain to update the screen. */
2030 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2032 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2033 if (surface == swapchain->front_buffer)
2035 x11_copy_to_screen(swapchain, NULL);
2040 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2042 FIXME("GDI surfaces can't draw overlays yet.\n");
2043 return E_FAIL;
2046 static void gdi_surface_preload(struct wined3d_surface *surface)
2048 TRACE("surface %p.\n", surface);
2050 ERR("Preloading GDI surfaces is not supported.\n");
2053 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2055 TRACE("surface %p, rect %s, flags %#x.\n",
2056 surface, wine_dbgstr_rect(rect), flags);
2058 if (!surface->resource.allocatedMemory)
2060 /* This happens on gdi surfaces if the application set a user pointer
2061 * and resets it. Recreate the DIB section. */
2062 surface_create_dib_section(surface);
2063 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2067 static void gdi_surface_unmap(struct wined3d_surface *surface)
2069 TRACE("surface %p.\n", surface);
2071 /* Tell the swapchain to update the screen. */
2072 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2074 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2075 if (surface == swapchain->front_buffer)
2077 x11_copy_to_screen(swapchain, &surface->lockedRect);
2081 memset(&surface->lockedRect, 0, sizeof(RECT));
2084 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2086 WINED3DLOCKED_RECT lock;
2087 HRESULT hr;
2089 TRACE("surface %p.\n", surface);
2091 /* Should have a DIB section already. */
2092 if (!(surface->flags & SFLAG_DIBSECTION))
2094 WARN("DC not supported on this surface\n");
2095 return WINED3DERR_INVALIDCALL;
2098 /* Map the surface. */
2099 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2100 if (FAILED(hr))
2101 ERR("Map failed, hr %#x.\n", hr);
2103 return hr;
2106 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
2108 TRACE("surface %p, override %p.\n", surface, override);
2110 return WINED3D_OK;
2113 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
2115 TRACE("surface %p, mem %p.\n", surface, mem);
2117 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2118 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2120 ERR("Not supported on render targets.\n");
2121 return WINED3DERR_INVALIDCALL;
2124 if (mem && mem != surface->resource.allocatedMemory)
2126 void *release = NULL;
2128 /* Do I have to copy the old surface content? */
2129 if (surface->flags & SFLAG_DIBSECTION)
2131 SelectObject(surface->hDC, surface->dib.holdbitmap);
2132 DeleteDC(surface->hDC);
2133 /* Release the DIB section. */
2134 DeleteObject(surface->dib.DIBsection);
2135 surface->dib.bitmap_data = NULL;
2136 surface->resource.allocatedMemory = NULL;
2137 surface->hDC = NULL;
2138 surface->flags &= ~SFLAG_DIBSECTION;
2140 else if (!(surface->flags & SFLAG_USERPTR))
2142 release = surface->resource.allocatedMemory;
2144 surface->resource.allocatedMemory = mem;
2145 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2147 /* Now free the old memory, if any. */
2148 HeapFree(GetProcessHeap(), 0, release);
2150 else if (surface->flags & SFLAG_USERPTR)
2152 /* Map() and GetDC() will re-create the dib section and allocated memory. */
2153 surface->resource.allocatedMemory = NULL;
2154 surface->flags &= ~SFLAG_USERPTR;
2157 return WINED3D_OK;
2160 static const struct wined3d_surface_ops gdi_surface_ops =
2162 gdi_surface_private_setup,
2163 surface_gdi_cleanup,
2164 gdi_surface_realize_palette,
2165 gdi_surface_draw_overlay,
2166 gdi_surface_preload,
2167 gdi_surface_map,
2168 gdi_surface_unmap,
2169 gdi_surface_getdc,
2170 gdi_surface_flip,
2171 gdi_surface_set_mem,
2174 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2176 GLuint *name;
2177 DWORD flag;
2179 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2181 if(srgb)
2183 name = &surface->texture_name_srgb;
2184 flag = SFLAG_INSRGBTEX;
2186 else
2188 name = &surface->texture_name;
2189 flag = SFLAG_INTEXTURE;
2192 if (!*name && new_name)
2194 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2195 * surface has no texture name yet. See if we can get rid of this. */
2196 if (surface->flags & flag)
2197 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2198 surface_modify_location(surface, flag, FALSE);
2201 *name = new_name;
2202 surface_force_reload(surface);
2205 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2207 TRACE("surface %p, target %#x.\n", surface, target);
2209 if (surface->texture_target != target)
2211 if (target == GL_TEXTURE_RECTANGLE_ARB)
2213 surface->flags &= ~SFLAG_NORMCOORD;
2215 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2217 surface->flags |= SFLAG_NORMCOORD;
2220 surface->texture_target = target;
2221 surface_force_reload(surface);
2224 /* Context activation is done by the caller. */
2225 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2227 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2229 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2231 struct wined3d_texture *texture = surface->container.u.texture;
2233 TRACE("Passing to container (%p).\n", texture);
2234 texture->texture_ops->texture_bind(texture, context, srgb);
2236 else
2238 if (surface->texture_level)
2240 ERR("Standalone surface %p is non-zero texture level %u.\n",
2241 surface, surface->texture_level);
2244 if (srgb)
2245 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2247 ENTER_GL();
2249 if (!surface->texture_name)
2251 glGenTextures(1, &surface->texture_name);
2252 checkGLcall("glGenTextures");
2254 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2256 glBindTexture(surface->texture_target, surface->texture_name);
2257 checkGLcall("glBindTexture");
2258 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2259 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2260 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2261 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2262 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2263 checkGLcall("glTexParameteri");
2265 else
2267 glBindTexture(surface->texture_target, surface->texture_name);
2268 checkGLcall("glBindTexture");
2271 LEAVE_GL();
2275 /* This call just downloads data, the caller is responsible for binding the
2276 * correct texture. */
2277 /* Context activation is done by the caller. */
2278 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2280 const struct wined3d_format *format = surface->resource.format;
2282 /* Only support read back of converted P8 surfaces. */
2283 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2285 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2286 return;
2289 ENTER_GL();
2291 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2293 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2294 surface, surface->texture_level, format->glFormat, format->glType,
2295 surface->resource.allocatedMemory);
2297 if (surface->flags & SFLAG_PBO)
2299 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2300 checkGLcall("glBindBufferARB");
2301 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2302 checkGLcall("glGetCompressedTexImageARB");
2303 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2304 checkGLcall("glBindBufferARB");
2306 else
2308 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2309 surface->texture_level, surface->resource.allocatedMemory));
2310 checkGLcall("glGetCompressedTexImageARB");
2313 LEAVE_GL();
2315 else
2317 void *mem;
2318 GLenum gl_format = format->glFormat;
2319 GLenum gl_type = format->glType;
2320 int src_pitch = 0;
2321 int dst_pitch = 0;
2323 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2324 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2326 gl_format = GL_ALPHA;
2327 gl_type = GL_UNSIGNED_BYTE;
2330 if (surface->flags & SFLAG_NONPOW2)
2332 unsigned char alignment = surface->resource.device->surface_alignment;
2333 src_pitch = format->byte_count * surface->pow2Width;
2334 dst_pitch = wined3d_surface_get_pitch(surface);
2335 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2336 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2338 else
2340 mem = surface->resource.allocatedMemory;
2343 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2344 surface, surface->texture_level, gl_format, gl_type, mem);
2346 if (surface->flags & SFLAG_PBO)
2348 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2349 checkGLcall("glBindBufferARB");
2351 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2352 checkGLcall("glGetTexImage");
2354 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2355 checkGLcall("glBindBufferARB");
2357 else
2359 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2360 checkGLcall("glGetTexImage");
2362 LEAVE_GL();
2364 if (surface->flags & SFLAG_NONPOW2)
2366 const BYTE *src_data;
2367 BYTE *dst_data;
2368 UINT y;
2370 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2371 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2372 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2374 * We're doing this...
2376 * instead of boxing the texture :
2377 * |<-texture width ->| -->pow2width| /\
2378 * |111111111111111111| | |
2379 * |222 Texture 222222| boxed empty | texture height
2380 * |3333 Data 33333333| | |
2381 * |444444444444444444| | \/
2382 * ----------------------------------- |
2383 * | boxed empty | boxed empty | pow2height
2384 * | | | \/
2385 * -----------------------------------
2388 * we're repacking the data to the expected texture width
2390 * |<-texture width ->| -->pow2width| /\
2391 * |111111111111111111222222222222222| |
2392 * |222333333333333333333444444444444| texture height
2393 * |444444 | |
2394 * | | \/
2395 * | | |
2396 * | empty | pow2height
2397 * | | \/
2398 * -----------------------------------
2400 * == is the same as
2402 * |<-texture width ->| /\
2403 * |111111111111111111|
2404 * |222222222222222222|texture height
2405 * |333333333333333333|
2406 * |444444444444444444| \/
2407 * --------------------
2409 * this also means that any references to allocatedMemory should work with the data as if were a
2410 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2412 * internally the texture is still stored in a boxed format so any references to textureName will
2413 * get a boxed texture with width pow2width and not a texture of width resource.width.
2415 * Performance should not be an issue, because applications normally do not lock the surfaces when
2416 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2417 * and doesn't have to be re-read. */
2418 src_data = mem;
2419 dst_data = surface->resource.allocatedMemory;
2420 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2421 for (y = 1; y < surface->resource.height; ++y)
2423 /* skip the first row */
2424 src_data += src_pitch;
2425 dst_data += dst_pitch;
2426 memcpy(dst_data, src_data, dst_pitch);
2429 HeapFree(GetProcessHeap(), 0, mem);
2433 /* Surface has now been downloaded */
2434 surface->flags |= SFLAG_INSYSMEM;
2437 /* This call just uploads data, the caller is responsible for binding the
2438 * correct texture. */
2439 /* Context activation is done by the caller. */
2440 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2441 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2442 BOOL srgb, const struct wined3d_bo_address *data)
2444 UINT update_w = src_rect->right - src_rect->left;
2445 UINT update_h = src_rect->bottom - src_rect->top;
2447 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2448 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2449 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2451 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2452 update_h *= format->heightscale;
2454 ENTER_GL();
2456 if (data->buffer_object)
2458 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2459 checkGLcall("glBindBufferARB");
2462 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2464 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2465 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2466 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2467 const BYTE *addr = data->addr;
2468 GLenum internal;
2470 addr += (src_rect->top / format->block_height) * src_pitch;
2471 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2473 if (srgb)
2474 internal = format->glGammaInternal;
2475 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2476 internal = format->rtInternal;
2477 else
2478 internal = format->glInternal;
2480 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2481 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2482 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2484 if (row_length == src_pitch)
2486 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2487 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2489 else
2491 UINT row, y;
2493 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2494 * can't use the unpack row length like below. */
2495 for (row = 0, y = dst_point->y; row < row_count; ++row)
2497 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2498 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2499 y += format->block_height;
2500 addr += src_pitch;
2503 checkGLcall("glCompressedTexSubImage2DARB");
2505 else
2507 const BYTE *addr = data->addr;
2509 addr += src_rect->top * src_w * format->byte_count;
2510 addr += src_rect->left * format->byte_count;
2512 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2513 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2514 update_w, update_h, format->glFormat, format->glType, addr);
2516 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2517 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2518 update_w, update_h, format->glFormat, format->glType, addr);
2519 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2520 checkGLcall("glTexSubImage2D");
2523 if (data->buffer_object)
2525 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2526 checkGLcall("glBindBufferARB");
2529 LEAVE_GL();
2531 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2533 struct wined3d_device *device = surface->resource.device;
2534 unsigned int i;
2536 for (i = 0; i < device->context_count; ++i)
2538 context_surface_update(device->contexts[i], surface);
2543 /* This call just allocates the texture, the caller is responsible for binding
2544 * the correct texture. */
2545 /* Context activation is done by the caller. */
2546 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2547 const struct wined3d_format *format, BOOL srgb)
2549 BOOL enable_client_storage = FALSE;
2550 GLsizei width = surface->pow2Width;
2551 GLsizei height = surface->pow2Height;
2552 const BYTE *mem = NULL;
2553 GLenum internal;
2555 if (srgb)
2557 internal = format->glGammaInternal;
2559 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2561 internal = format->rtInternal;
2563 else
2565 internal = format->glInternal;
2568 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2570 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",
2571 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2572 internal, width, height, format->glFormat, format->glType);
2574 ENTER_GL();
2576 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2578 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2579 || !surface->resource.allocatedMemory)
2581 /* In some cases we want to disable client storage.
2582 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2583 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2584 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2585 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2587 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2588 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2589 surface->flags &= ~SFLAG_CLIENT;
2590 enable_client_storage = TRUE;
2592 else
2594 surface->flags |= SFLAG_CLIENT;
2596 /* Point OpenGL to our allocated texture memory. Do not use
2597 * resource.allocatedMemory here because it might point into a
2598 * PBO. Instead use heapMemory, but get the alignment right. */
2599 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2600 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2604 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2606 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2607 internal, width, height, 0, surface->resource.size, mem));
2608 checkGLcall("glCompressedTexImage2DARB");
2610 else
2612 glTexImage2D(surface->texture_target, surface->texture_level,
2613 internal, width, height, 0, format->glFormat, format->glType, mem);
2614 checkGLcall("glTexImage2D");
2617 if(enable_client_storage) {
2618 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2619 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2621 LEAVE_GL();
2624 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2625 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2626 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2627 /* GL locking is done by the caller */
2628 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2630 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2631 struct wined3d_renderbuffer_entry *entry;
2632 GLuint renderbuffer = 0;
2633 unsigned int src_width, src_height;
2634 unsigned int width, height;
2636 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2638 width = rt->pow2Width;
2639 height = rt->pow2Height;
2641 else
2643 width = surface->pow2Width;
2644 height = surface->pow2Height;
2647 src_width = surface->pow2Width;
2648 src_height = surface->pow2Height;
2650 /* A depth stencil smaller than the render target is not valid */
2651 if (width > src_width || height > src_height) return;
2653 /* Remove any renderbuffer set if the sizes match */
2654 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2655 || (width == src_width && height == src_height))
2657 surface->current_renderbuffer = NULL;
2658 return;
2661 /* Look if we've already got a renderbuffer of the correct dimensions */
2662 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2664 if (entry->width == width && entry->height == height)
2666 renderbuffer = entry->id;
2667 surface->current_renderbuffer = entry;
2668 break;
2672 if (!renderbuffer)
2674 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2675 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2676 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2677 surface->resource.format->glInternal, width, height);
2679 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2680 entry->width = width;
2681 entry->height = height;
2682 entry->id = renderbuffer;
2683 list_add_head(&surface->renderbuffers, &entry->entry);
2685 surface->current_renderbuffer = entry;
2688 checkGLcall("set_compatible_renderbuffer");
2691 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2693 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2695 TRACE("surface %p.\n", surface);
2697 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2699 ERR("Surface %p is not on a swapchain.\n", surface);
2700 return GL_NONE;
2703 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2705 if (swapchain->render_to_fbo)
2707 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2708 return GL_COLOR_ATTACHMENT0;
2710 TRACE("Returning GL_BACK\n");
2711 return GL_BACK;
2713 else if (surface == swapchain->front_buffer)
2715 TRACE("Returning GL_FRONT\n");
2716 return GL_FRONT;
2719 FIXME("Higher back buffer, returning GL_BACK\n");
2720 return GL_BACK;
2723 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2724 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2726 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2728 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2729 /* No partial locking for textures yet. */
2730 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2732 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2733 if (dirty_rect)
2735 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2736 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2737 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2738 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2740 else
2742 surface->dirtyRect.left = 0;
2743 surface->dirtyRect.top = 0;
2744 surface->dirtyRect.right = surface->resource.width;
2745 surface->dirtyRect.bottom = surface->resource.height;
2748 /* if the container is a texture then mark it dirty. */
2749 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2751 TRACE("Passing to container.\n");
2752 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2756 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2758 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2759 BOOL ck_changed;
2761 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2763 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2765 ERR("Not supported on scratch surfaces.\n");
2766 return WINED3DERR_INVALIDCALL;
2769 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2771 /* Reload if either the texture and sysmem have different ideas about the
2772 * color key, or the actual key values changed. */
2773 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2774 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2775 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2777 TRACE("Reloading because of color keying\n");
2778 /* To perform the color key conversion we need a sysmem copy of
2779 * the surface. Make sure we have it. */
2781 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2782 /* Make sure the texture is reloaded because of the color key change,
2783 * this kills performance though :( */
2784 /* TODO: This is not necessarily needed with hw palettized texture support. */
2785 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2786 /* Switching color keying on / off may change the internal format. */
2787 if (ck_changed)
2788 surface_force_reload(surface);
2790 else if (!(surface->flags & flag))
2792 TRACE("Reloading because surface is dirty.\n");
2794 else
2796 TRACE("surface is already in texture\n");
2797 return WINED3D_OK;
2800 /* No partial locking for textures yet. */
2801 surface_load_location(surface, flag, NULL);
2802 surface_evict_sysmem(surface);
2804 return WINED3D_OK;
2807 /* See also float_16_to_32() in wined3d_private.h */
2808 static inline unsigned short float_32_to_16(const float *in)
2810 int exp = 0;
2811 float tmp = fabsf(*in);
2812 unsigned int mantissa;
2813 unsigned short ret;
2815 /* Deal with special numbers */
2816 if (*in == 0.0f)
2817 return 0x0000;
2818 if (isnan(*in))
2819 return 0x7c01;
2820 if (isinf(*in))
2821 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2823 if (tmp < powf(2, 10))
2827 tmp = tmp * 2.0f;
2828 exp--;
2829 } while (tmp < powf(2, 10));
2831 else if (tmp >= powf(2, 11))
2835 tmp /= 2.0f;
2836 exp++;
2837 } while (tmp >= powf(2, 11));
2840 mantissa = (unsigned int)tmp;
2841 if (tmp - mantissa >= 0.5f)
2842 ++mantissa; /* Round to nearest, away from zero. */
2844 exp += 10; /* Normalize the mantissa. */
2845 exp += 15; /* Exponent is encoded with excess 15. */
2847 if (exp > 30) /* too big */
2849 ret = 0x7c00; /* INF */
2851 else if (exp <= 0)
2853 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2854 while (exp <= 0)
2856 mantissa = mantissa >> 1;
2857 ++exp;
2859 ret = mantissa & 0x3ff;
2861 else
2863 ret = (exp << 10) | (mantissa & 0x3ff);
2866 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2867 return ret;
2870 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2872 ULONG refcount;
2874 TRACE("Surface %p, container %p of type %#x.\n",
2875 surface, surface->container.u.base, surface->container.type);
2877 switch (surface->container.type)
2879 case WINED3D_CONTAINER_TEXTURE:
2880 return wined3d_texture_incref(surface->container.u.texture);
2882 case WINED3D_CONTAINER_SWAPCHAIN:
2883 return wined3d_swapchain_incref(surface->container.u.swapchain);
2885 default:
2886 ERR("Unhandled container type %#x.\n", surface->container.type);
2887 case WINED3D_CONTAINER_NONE:
2888 break;
2891 refcount = InterlockedIncrement(&surface->resource.ref);
2892 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2894 return refcount;
2897 /* Do not call while under the GL lock. */
2898 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2900 ULONG refcount;
2902 TRACE("Surface %p, container %p of type %#x.\n",
2903 surface, surface->container.u.base, surface->container.type);
2905 switch (surface->container.type)
2907 case WINED3D_CONTAINER_TEXTURE:
2908 return wined3d_texture_decref(surface->container.u.texture);
2910 case WINED3D_CONTAINER_SWAPCHAIN:
2911 return wined3d_swapchain_decref(surface->container.u.swapchain);
2913 default:
2914 ERR("Unhandled container type %#x.\n", surface->container.type);
2915 case WINED3D_CONTAINER_NONE:
2916 break;
2919 refcount = InterlockedDecrement(&surface->resource.ref);
2920 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2922 if (!refcount)
2924 surface->surface_ops->surface_cleanup(surface);
2925 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2927 TRACE("Destroyed surface %p.\n", surface);
2928 HeapFree(GetProcessHeap(), 0, surface);
2931 return refcount;
2934 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2936 return resource_set_priority(&surface->resource, priority);
2939 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2941 return resource_get_priority(&surface->resource);
2944 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2946 TRACE("surface %p.\n", surface);
2948 surface->surface_ops->surface_preload(surface);
2951 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2953 TRACE("surface %p.\n", surface);
2955 return surface->resource.parent;
2958 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2960 TRACE("surface %p.\n", surface);
2962 return &surface->resource;
2965 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2967 TRACE("surface %p, flags %#x.\n", surface, flags);
2969 switch (flags)
2971 case WINEDDGBS_CANBLT:
2972 case WINEDDGBS_ISBLTDONE:
2973 return WINED3D_OK;
2975 default:
2976 return WINED3DERR_INVALIDCALL;
2980 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2982 TRACE("surface %p, flags %#x.\n", surface, flags);
2984 /* XXX: DDERR_INVALIDSURFACETYPE */
2986 switch (flags)
2988 case WINEDDGFS_CANFLIP:
2989 case WINEDDGFS_ISFLIPDONE:
2990 return WINED3D_OK;
2992 default:
2993 return WINED3DERR_INVALIDCALL;
2997 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2999 TRACE("surface %p.\n", surface);
3001 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
3002 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3005 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
3007 TRACE("surface %p.\n", surface);
3009 /* So far we don't lose anything :) */
3010 surface->flags &= ~SFLAG_LOST;
3011 return WINED3D_OK;
3014 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3016 TRACE("surface %p, palette %p.\n", surface, palette);
3018 if (surface->palette == palette)
3020 TRACE("Nop palette change.\n");
3021 return WINED3D_OK;
3024 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3025 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3027 surface->palette = palette;
3029 if (palette)
3031 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3032 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3034 surface->surface_ops->surface_realize_palette(surface);
3037 return WINED3D_OK;
3040 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3041 DWORD flags, const WINEDDCOLORKEY *color_key)
3043 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3045 if (flags & WINEDDCKEY_COLORSPACE)
3047 FIXME(" colorkey value not supported (%08x) !\n", flags);
3048 return WINED3DERR_INVALIDCALL;
3051 /* Dirtify the surface, but only if a key was changed. */
3052 if (color_key)
3054 switch (flags & ~WINEDDCKEY_COLORSPACE)
3056 case WINEDDCKEY_DESTBLT:
3057 surface->DestBltCKey = *color_key;
3058 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3059 break;
3061 case WINEDDCKEY_DESTOVERLAY:
3062 surface->DestOverlayCKey = *color_key;
3063 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3064 break;
3066 case WINEDDCKEY_SRCOVERLAY:
3067 surface->SrcOverlayCKey = *color_key;
3068 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3069 break;
3071 case WINEDDCKEY_SRCBLT:
3072 surface->SrcBltCKey = *color_key;
3073 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3074 break;
3077 else
3079 switch (flags & ~WINEDDCKEY_COLORSPACE)
3081 case WINEDDCKEY_DESTBLT:
3082 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3083 break;
3085 case WINEDDCKEY_DESTOVERLAY:
3086 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3087 break;
3089 case WINEDDCKEY_SRCOVERLAY:
3090 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3091 break;
3093 case WINEDDCKEY_SRCBLT:
3094 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3095 break;
3099 return WINED3D_OK;
3102 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3104 TRACE("surface %p.\n", surface);
3106 return surface->palette;
3109 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3111 const struct wined3d_format *format = surface->resource.format;
3112 DWORD pitch;
3114 TRACE("surface %p.\n", surface);
3116 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3118 /* Since compressed formats are block based, pitch means the amount of
3119 * bytes to the next row of block rather than the next row of pixels. */
3120 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3121 pitch = row_block_count * format->block_byte_count;
3123 else
3125 unsigned char alignment = surface->resource.device->surface_alignment;
3126 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3127 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3130 TRACE("Returning %u.\n", pitch);
3132 return pitch;
3135 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3137 TRACE("surface %p, mem %p.\n", surface, mem);
3139 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3141 WARN("Surface is locked or the DC is in use.\n");
3142 return WINED3DERR_INVALIDCALL;
3145 return surface->surface_ops->surface_set_mem(surface, mem);
3148 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3150 LONG w, h;
3152 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3154 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3156 WARN("Not an overlay surface.\n");
3157 return WINEDDERR_NOTAOVERLAYSURFACE;
3160 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3161 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3162 surface->overlay_destrect.left = x;
3163 surface->overlay_destrect.top = y;
3164 surface->overlay_destrect.right = x + w;
3165 surface->overlay_destrect.bottom = y + h;
3167 surface->surface_ops->surface_draw_overlay(surface);
3169 return WINED3D_OK;
3172 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3174 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3176 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3178 TRACE("Not an overlay surface.\n");
3179 return WINEDDERR_NOTAOVERLAYSURFACE;
3182 if (!surface->overlay_dest)
3184 TRACE("Overlay not visible.\n");
3185 *x = 0;
3186 *y = 0;
3187 return WINEDDERR_OVERLAYNOTVISIBLE;
3190 *x = surface->overlay_destrect.left;
3191 *y = surface->overlay_destrect.top;
3193 TRACE("Returning position %d, %d.\n", *x, *y);
3195 return WINED3D_OK;
3198 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3199 DWORD flags, struct wined3d_surface *ref)
3201 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3203 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3205 TRACE("Not an overlay surface.\n");
3206 return WINEDDERR_NOTAOVERLAYSURFACE;
3209 return WINED3D_OK;
3212 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3213 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3215 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3216 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3218 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3220 WARN("Not an overlay surface.\n");
3221 return WINEDDERR_NOTAOVERLAYSURFACE;
3223 else if (!dst_surface)
3225 WARN("Dest surface is NULL.\n");
3226 return WINED3DERR_INVALIDCALL;
3229 if (src_rect)
3231 surface->overlay_srcrect = *src_rect;
3233 else
3235 surface->overlay_srcrect.left = 0;
3236 surface->overlay_srcrect.top = 0;
3237 surface->overlay_srcrect.right = surface->resource.width;
3238 surface->overlay_srcrect.bottom = surface->resource.height;
3241 if (dst_rect)
3243 surface->overlay_destrect = *dst_rect;
3245 else
3247 surface->overlay_destrect.left = 0;
3248 surface->overlay_destrect.top = 0;
3249 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3250 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3253 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3255 list_remove(&surface->overlay_entry);
3258 if (flags & WINEDDOVER_SHOW)
3260 if (surface->overlay_dest != dst_surface)
3262 surface->overlay_dest = dst_surface;
3263 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3266 else if (flags & WINEDDOVER_HIDE)
3268 /* tests show that the rectangles are erased on hide */
3269 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3270 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3271 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3272 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3273 surface->overlay_dest = NULL;
3276 surface->surface_ops->surface_draw_overlay(surface);
3278 return WINED3D_OK;
3281 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3283 TRACE("surface %p, clipper %p.\n", surface, clipper);
3285 surface->clipper = clipper;
3287 return WINED3D_OK;
3290 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3292 TRACE("surface %p.\n", surface);
3294 return surface->clipper;
3297 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3299 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3301 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3303 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3305 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3306 return WINED3DERR_INVALIDCALL;
3309 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3310 surface->pow2Width, surface->pow2Height);
3311 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3312 surface->resource.format = format;
3314 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3315 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3316 format->glFormat, format->glInternal, format->glType);
3318 return WINED3D_OK;
3321 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3322 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3324 unsigned short *dst_s;
3325 const float *src_f;
3326 unsigned int x, y;
3328 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3330 for (y = 0; y < h; ++y)
3332 src_f = (const float *)(src + y * pitch_in);
3333 dst_s = (unsigned short *) (dst + y * pitch_out);
3334 for (x = 0; x < w; ++x)
3336 dst_s[x] = float_32_to_16(src_f + x);
3341 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3342 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3344 static const unsigned char convert_5to8[] =
3346 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3347 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3348 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3349 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3351 static const unsigned char convert_6to8[] =
3353 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3354 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3355 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3356 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3357 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3358 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3359 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3360 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3362 unsigned int x, y;
3364 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3366 for (y = 0; y < h; ++y)
3368 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3369 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3370 for (x = 0; x < w; ++x)
3372 WORD pixel = src_line[x];
3373 dst_line[x] = 0xff000000
3374 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3375 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3376 | convert_5to8[(pixel & 0x001f)];
3381 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3382 * in both cases we're just setting the X / Alpha channel to 0xff. */
3383 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3384 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3386 unsigned int x, y;
3388 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3390 for (y = 0; y < h; ++y)
3392 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3393 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3395 for (x = 0; x < w; ++x)
3397 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3402 static inline BYTE cliptobyte(int x)
3404 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3407 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3408 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3410 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3411 unsigned int x, y;
3413 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3415 for (y = 0; y < h; ++y)
3417 const BYTE *src_line = src + y * pitch_in;
3418 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3419 for (x = 0; x < w; ++x)
3421 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3422 * C = Y - 16; D = U - 128; E = V - 128;
3423 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3424 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3425 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3426 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3427 * U and V are shared between the pixels. */
3428 if (!(x & 1)) /* For every even pixel, read new U and V. */
3430 d = (int) src_line[1] - 128;
3431 e = (int) src_line[3] - 128;
3432 r2 = 409 * e + 128;
3433 g2 = - 100 * d - 208 * e + 128;
3434 b2 = 516 * d + 128;
3436 c2 = 298 * ((int) src_line[0] - 16);
3437 dst_line[x] = 0xff000000
3438 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3439 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3440 | cliptobyte((c2 + b2) >> 8); /* blue */
3441 /* Scale RGB values to 0..255 range,
3442 * then clip them if still not in range (may be negative),
3443 * then shift them within DWORD if necessary. */
3444 src_line += 2;
3449 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3450 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3452 unsigned int x, y;
3453 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3455 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3457 for (y = 0; y < h; ++y)
3459 const BYTE *src_line = src + y * pitch_in;
3460 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3461 for (x = 0; x < w; ++x)
3463 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3464 * C = Y - 16; D = U - 128; E = V - 128;
3465 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3466 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3467 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3468 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3469 * U and V are shared between the pixels. */
3470 if (!(x & 1)) /* For every even pixel, read new U and V. */
3472 d = (int) src_line[1] - 128;
3473 e = (int) src_line[3] - 128;
3474 r2 = 409 * e + 128;
3475 g2 = - 100 * d - 208 * e + 128;
3476 b2 = 516 * d + 128;
3478 c2 = 298 * ((int) src_line[0] - 16);
3479 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3480 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3481 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3482 /* Scale RGB values to 0..255 range,
3483 * then clip them if still not in range (may be negative),
3484 * then shift them within DWORD if necessary. */
3485 src_line += 2;
3490 struct d3dfmt_convertor_desc
3492 enum wined3d_format_id from, to;
3493 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3496 static const struct d3dfmt_convertor_desc convertors[] =
3498 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3499 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3500 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3501 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3502 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3503 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3506 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3507 enum wined3d_format_id to)
3509 unsigned int i;
3511 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3513 if (convertors[i].from == from && convertors[i].to == to)
3514 return &convertors[i];
3517 return NULL;
3520 /*****************************************************************************
3521 * surface_convert_format
3523 * Creates a duplicate of a surface in a different format. Is used by Blt to
3524 * blit between surfaces with different formats.
3526 * Parameters
3527 * source: Source surface
3528 * fmt: Requested destination format
3530 *****************************************************************************/
3531 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3533 const struct d3dfmt_convertor_desc *conv;
3534 WINED3DLOCKED_RECT lock_src, lock_dst;
3535 struct wined3d_surface *ret = NULL;
3536 HRESULT hr;
3538 conv = find_convertor(source->resource.format->id, to_fmt);
3539 if (!conv)
3541 FIXME("Cannot find a conversion function from format %s to %s.\n",
3542 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3543 return NULL;
3546 wined3d_surface_create(source->resource.device, source->resource.width,
3547 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3548 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3549 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3550 if (!ret)
3552 ERR("Failed to create a destination surface for conversion.\n");
3553 return NULL;
3556 memset(&lock_src, 0, sizeof(lock_src));
3557 memset(&lock_dst, 0, sizeof(lock_dst));
3559 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3560 if (FAILED(hr))
3562 ERR("Failed to lock the source surface.\n");
3563 wined3d_surface_decref(ret);
3564 return NULL;
3566 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3567 if (FAILED(hr))
3569 ERR("Failed to lock the destination surface.\n");
3570 wined3d_surface_unmap(source);
3571 wined3d_surface_decref(ret);
3572 return NULL;
3575 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3576 source->resource.width, source->resource.height);
3578 wined3d_surface_unmap(ret);
3579 wined3d_surface_unmap(source);
3581 return ret;
3584 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3585 unsigned int bpp, UINT pitch, DWORD color)
3587 BYTE *first;
3588 int x, y;
3590 /* Do first row */
3592 #define COLORFILL_ROW(type) \
3593 do { \
3594 type *d = (type *)buf; \
3595 for (x = 0; x < width; ++x) \
3596 d[x] = (type)color; \
3597 } while(0)
3599 switch (bpp)
3601 case 1:
3602 COLORFILL_ROW(BYTE);
3603 break;
3605 case 2:
3606 COLORFILL_ROW(WORD);
3607 break;
3609 case 3:
3611 BYTE *d = buf;
3612 for (x = 0; x < width; ++x, d += 3)
3614 d[0] = (color ) & 0xFF;
3615 d[1] = (color >> 8) & 0xFF;
3616 d[2] = (color >> 16) & 0xFF;
3618 break;
3620 case 4:
3621 COLORFILL_ROW(DWORD);
3622 break;
3624 default:
3625 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3626 return WINED3DERR_NOTAVAILABLE;
3629 #undef COLORFILL_ROW
3631 /* Now copy first row. */
3632 first = buf;
3633 for (y = 1; y < height; ++y)
3635 buf += pitch;
3636 memcpy(buf, first, width * bpp);
3639 return WINED3D_OK;
3642 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3644 TRACE("surface %p.\n", surface);
3646 if (!(surface->flags & SFLAG_LOCKED))
3648 WARN("Trying to unmap unmapped surface.\n");
3649 return WINEDDERR_NOTLOCKED;
3651 surface->flags &= ~SFLAG_LOCKED;
3653 surface->surface_ops->surface_unmap(surface);
3655 return WINED3D_OK;
3658 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3659 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3661 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3662 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3664 if (surface->flags & SFLAG_LOCKED)
3666 WARN("Surface is already mapped.\n");
3667 return WINED3DERR_INVALIDCALL;
3669 surface->flags |= SFLAG_LOCKED;
3671 if (!(surface->flags & SFLAG_LOCKABLE))
3672 WARN("Trying to lock unlockable surface.\n");
3674 surface->surface_ops->surface_map(surface, rect, flags);
3676 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3678 if (!rect)
3680 locked_rect->pBits = surface->resource.allocatedMemory;
3681 surface->lockedRect.left = 0;
3682 surface->lockedRect.top = 0;
3683 surface->lockedRect.right = surface->resource.width;
3684 surface->lockedRect.bottom = surface->resource.height;
3686 else
3688 const struct wined3d_format *format = surface->resource.format;
3690 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3692 /* Compressed textures are block based, so calculate the offset of
3693 * the block that contains the top-left pixel of the locked rectangle. */
3694 locked_rect->pBits = surface->resource.allocatedMemory
3695 + ((rect->top / format->block_height) * locked_rect->Pitch)
3696 + ((rect->left / format->block_width) * format->block_byte_count);
3698 else
3700 locked_rect->pBits = surface->resource.allocatedMemory
3701 + (locked_rect->Pitch * rect->top)
3702 + (rect->left * format->byte_count);
3704 surface->lockedRect.left = rect->left;
3705 surface->lockedRect.top = rect->top;
3706 surface->lockedRect.right = rect->right;
3707 surface->lockedRect.bottom = rect->bottom;
3710 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3711 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3713 return WINED3D_OK;
3716 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3718 HRESULT hr;
3720 TRACE("surface %p, dc %p.\n", surface, dc);
3722 if (surface->flags & SFLAG_USERPTR)
3724 ERR("Not supported on surfaces with application-provided memory.\n");
3725 return WINEDDERR_NODC;
3728 /* Give more detailed info for ddraw. */
3729 if (surface->flags & SFLAG_DCINUSE)
3730 return WINEDDERR_DCALREADYCREATED;
3732 /* Can't GetDC if the surface is locked. */
3733 if (surface->flags & SFLAG_LOCKED)
3734 return WINED3DERR_INVALIDCALL;
3736 hr = surface->surface_ops->surface_getdc(surface);
3737 if (FAILED(hr))
3738 return hr;
3740 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3741 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3743 /* GetDC on palettized formats is unsupported in D3D9, and the method
3744 * is missing in D3D8, so this should only be used for DX <=7
3745 * surfaces (with non-device palettes). */
3746 const PALETTEENTRY *pal = NULL;
3748 if (surface->palette)
3750 pal = surface->palette->palents;
3752 else
3754 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3755 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3757 if (dds_primary && dds_primary->palette)
3758 pal = dds_primary->palette->palents;
3761 if (pal)
3763 RGBQUAD col[256];
3764 unsigned int i;
3766 for (i = 0; i < 256; ++i)
3768 col[i].rgbRed = pal[i].peRed;
3769 col[i].rgbGreen = pal[i].peGreen;
3770 col[i].rgbBlue = pal[i].peBlue;
3771 col[i].rgbReserved = 0;
3773 SetDIBColorTable(surface->hDC, 0, 256, col);
3777 surface->flags |= SFLAG_DCINUSE;
3779 *dc = surface->hDC;
3780 TRACE("Returning dc %p.\n", *dc);
3782 return WINED3D_OK;
3785 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3787 TRACE("surface %p, dc %p.\n", surface, dc);
3789 if (!(surface->flags & SFLAG_DCINUSE))
3790 return WINEDDERR_NODC;
3792 if (surface->hDC != dc)
3794 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3795 dc, surface->hDC);
3796 return WINEDDERR_NODC;
3799 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3801 /* Copy the contents of the DIB over to the PBO. */
3802 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3805 /* We locked first, so unlock now. */
3806 wined3d_surface_unmap(surface);
3808 surface->flags &= ~SFLAG_DCINUSE;
3810 return WINED3D_OK;
3813 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3815 struct wined3d_swapchain *swapchain;
3816 HRESULT hr;
3818 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3820 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3822 ERR("Flipped surface is not on a swapchain.\n");
3823 return WINEDDERR_NOTFLIPPABLE;
3825 swapchain = surface->container.u.swapchain;
3827 hr = surface->surface_ops->surface_flip(surface, override);
3828 if (FAILED(hr))
3829 return hr;
3831 /* Just overwrite the swapchain presentation interval. This is ok because
3832 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3833 * specify the presentation interval. */
3834 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3835 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3836 else if (flags & WINEDDFLIP_NOVSYNC)
3837 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3838 else if (flags & WINEDDFLIP_INTERVAL2)
3839 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3840 else if (flags & WINEDDFLIP_INTERVAL3)
3841 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3842 else
3843 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3845 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3848 /* Do not call while under the GL lock. */
3849 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3851 struct wined3d_device *device = surface->resource.device;
3853 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3855 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3857 struct wined3d_texture *texture = surface->container.u.texture;
3859 TRACE("Passing to container (%p).\n", texture);
3860 texture->texture_ops->texture_preload(texture, srgb);
3862 else
3864 struct wined3d_context *context;
3866 TRACE("(%p) : About to load surface\n", surface);
3868 /* TODO: Use already acquired context when possible. */
3869 context = context_acquire(device, NULL);
3871 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3872 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3874 if (palette9_changed(surface))
3876 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3877 /* TODO: This is not necessarily needed with hw palettized texture support */
3878 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3879 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3880 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3884 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3886 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3888 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3889 GLclampf tmp;
3890 tmp = 0.9f;
3891 ENTER_GL();
3892 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3893 LEAVE_GL();
3896 context_release(context);
3900 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3902 if (!surface->resource.allocatedMemory)
3904 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3905 surface->resource.size + RESOURCE_ALIGNMENT);
3906 if (!surface->resource.heapMemory)
3908 ERR("Out of memory\n");
3909 return FALSE;
3911 surface->resource.allocatedMemory =
3912 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3914 else
3916 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3919 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3921 return TRUE;
3924 /* Read the framebuffer back into the surface */
3925 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3927 struct wined3d_device *device = surface->resource.device;
3928 const struct wined3d_gl_info *gl_info;
3929 struct wined3d_context *context;
3930 BYTE *mem;
3931 GLint fmt;
3932 GLint type;
3933 BYTE *row, *top, *bottom;
3934 int i;
3935 BOOL bpp;
3936 RECT local_rect;
3937 BOOL srcIsUpsideDown;
3938 GLint rowLen = 0;
3939 GLint skipPix = 0;
3940 GLint skipRow = 0;
3942 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3943 static BOOL warned = FALSE;
3944 if(!warned) {
3945 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3946 warned = TRUE;
3948 return;
3951 context = context_acquire(device, surface);
3952 context_apply_blit_state(context, device);
3953 gl_info = context->gl_info;
3955 ENTER_GL();
3957 /* Select the correct read buffer, and give some debug output.
3958 * There is no need to keep track of the current read buffer or reset it, every part of the code
3959 * that reads sets the read buffer as desired.
3961 if (surface_is_offscreen(surface))
3963 /* Mapping the primary render target which is not on a swapchain.
3964 * Read from the back buffer. */
3965 TRACE("Mapping offscreen render target.\n");
3966 glReadBuffer(device->offscreenBuffer);
3967 srcIsUpsideDown = TRUE;
3969 else
3971 /* Onscreen surfaces are always part of a swapchain */
3972 GLenum buffer = surface_get_gl_buffer(surface);
3973 TRACE("Mapping %#x buffer.\n", buffer);
3974 glReadBuffer(buffer);
3975 checkGLcall("glReadBuffer");
3976 srcIsUpsideDown = FALSE;
3979 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3980 if (!rect)
3982 local_rect.left = 0;
3983 local_rect.top = 0;
3984 local_rect.right = surface->resource.width;
3985 local_rect.bottom = surface->resource.height;
3987 else
3989 local_rect = *rect;
3991 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3993 switch (surface->resource.format->id)
3995 case WINED3DFMT_P8_UINT:
3997 if (primary_render_target_is_p8(device))
3999 /* In case of P8 render targets the index is stored in the alpha component */
4000 fmt = GL_ALPHA;
4001 type = GL_UNSIGNED_BYTE;
4002 mem = dest;
4003 bpp = surface->resource.format->byte_count;
4005 else
4007 /* GL can't return palettized data, so read ARGB pixels into a
4008 * separate block of memory and convert them into palettized format
4009 * in software. Slow, but if the app means to use palettized render
4010 * targets and locks it...
4012 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4013 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4014 * for the color channels when palettizing the colors.
4016 fmt = GL_RGB;
4017 type = GL_UNSIGNED_BYTE;
4018 pitch *= 3;
4019 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4020 if (!mem)
4022 ERR("Out of memory\n");
4023 LEAVE_GL();
4024 return;
4026 bpp = surface->resource.format->byte_count * 3;
4029 break;
4031 default:
4032 mem = dest;
4033 fmt = surface->resource.format->glFormat;
4034 type = surface->resource.format->glType;
4035 bpp = surface->resource.format->byte_count;
4038 if (surface->flags & SFLAG_PBO)
4040 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4041 checkGLcall("glBindBufferARB");
4042 if (mem)
4044 ERR("mem not null for pbo -- unexpected\n");
4045 mem = NULL;
4049 /* Save old pixel store pack state */
4050 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4051 checkGLcall("glGetIntegerv");
4052 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4053 checkGLcall("glGetIntegerv");
4054 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4055 checkGLcall("glGetIntegerv");
4057 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4058 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4059 checkGLcall("glPixelStorei");
4060 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4061 checkGLcall("glPixelStorei");
4062 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4063 checkGLcall("glPixelStorei");
4065 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4066 local_rect.right - local_rect.left,
4067 local_rect.bottom - local_rect.top,
4068 fmt, type, mem);
4069 checkGLcall("glReadPixels");
4071 /* Reset previous pixel store pack state */
4072 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4073 checkGLcall("glPixelStorei");
4074 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4075 checkGLcall("glPixelStorei");
4076 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4077 checkGLcall("glPixelStorei");
4079 if (surface->flags & SFLAG_PBO)
4081 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4082 checkGLcall("glBindBufferARB");
4084 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4085 * to get a pointer to it and perform the flipping in software. This is a lot
4086 * faster than calling glReadPixels for each line. In case we want more speed
4087 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4088 if (!srcIsUpsideDown)
4090 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4091 checkGLcall("glBindBufferARB");
4093 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4094 checkGLcall("glMapBufferARB");
4098 /* TODO: Merge this with the palettization loop below for P8 targets */
4099 if(!srcIsUpsideDown) {
4100 UINT len, off;
4101 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4102 Flip the lines in software */
4103 len = (local_rect.right - local_rect.left) * bpp;
4104 off = local_rect.left * bpp;
4106 row = HeapAlloc(GetProcessHeap(), 0, len);
4107 if(!row) {
4108 ERR("Out of memory\n");
4109 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4110 HeapFree(GetProcessHeap(), 0, mem);
4111 LEAVE_GL();
4112 return;
4115 top = mem + pitch * local_rect.top;
4116 bottom = mem + pitch * (local_rect.bottom - 1);
4117 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4118 memcpy(row, top + off, len);
4119 memcpy(top + off, bottom + off, len);
4120 memcpy(bottom + off, row, len);
4121 top += pitch;
4122 bottom -= pitch;
4124 HeapFree(GetProcessHeap(), 0, row);
4126 /* Unmap the temp PBO buffer */
4127 if (surface->flags & SFLAG_PBO)
4129 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4130 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4134 LEAVE_GL();
4135 context_release(context);
4137 /* For P8 textures we need to perform an inverse palette lookup. This is
4138 * done by searching for a palette index which matches the RGB value.
4139 * Note this isn't guaranteed to work when there are multiple entries for
4140 * the same color but we have no choice. In case of P8 render targets,
4141 * the index is stored in the alpha component so no conversion is needed. */
4142 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4144 const PALETTEENTRY *pal = NULL;
4145 DWORD width = pitch / 3;
4146 int x, y, c;
4148 if (surface->palette)
4150 pal = surface->palette->palents;
4152 else
4154 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4155 HeapFree(GetProcessHeap(), 0, mem);
4156 return;
4159 for(y = local_rect.top; y < local_rect.bottom; y++) {
4160 for(x = local_rect.left; x < local_rect.right; x++) {
4161 /* start lines pixels */
4162 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4163 const BYTE *green = blue + 1;
4164 const BYTE *red = green + 1;
4166 for(c = 0; c < 256; c++) {
4167 if(*red == pal[c].peRed &&
4168 *green == pal[c].peGreen &&
4169 *blue == pal[c].peBlue)
4171 *((BYTE *) dest + y * width + x) = c;
4172 break;
4177 HeapFree(GetProcessHeap(), 0, mem);
4181 /* Read the framebuffer contents into a texture */
4182 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4184 struct wined3d_device *device = surface->resource.device;
4185 const struct wined3d_gl_info *gl_info;
4186 struct wined3d_context *context;
4188 if (!surface_is_offscreen(surface))
4190 /* We would need to flip onscreen surfaces, but there's no efficient
4191 * way to do that here. It makes more sense for the caller to
4192 * explicitly go through sysmem. */
4193 ERR("Not supported for onscreen targets.\n");
4194 return;
4197 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4198 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4199 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4201 context = context_acquire(device, surface);
4202 gl_info = context->gl_info;
4203 device_invalidate_state(device, STATE_FRAMEBUFFER);
4205 surface_prepare_texture(surface, context, srgb);
4206 surface_bind_and_dirtify(surface, context, srgb);
4208 TRACE("Reading back offscreen render target %p.\n", surface);
4210 ENTER_GL();
4212 glReadBuffer(device->offscreenBuffer);
4213 checkGLcall("glReadBuffer");
4215 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4216 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4217 checkGLcall("glCopyTexSubImage2D");
4219 LEAVE_GL();
4221 context_release(context);
4224 /* Context activation is done by the caller. */
4225 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4226 struct wined3d_context *context, BOOL srgb)
4228 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4229 CONVERT_TYPES convert;
4230 struct wined3d_format format;
4232 if (surface->flags & alloc_flag) return;
4234 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4235 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4236 else surface->flags &= ~SFLAG_CONVERTED;
4238 surface_bind_and_dirtify(surface, context, srgb);
4239 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4240 surface->flags |= alloc_flag;
4243 /* Context activation is done by the caller. */
4244 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4246 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4248 struct wined3d_texture *texture = surface->container.u.texture;
4249 UINT sub_count = texture->level_count * texture->layer_count;
4250 UINT i;
4252 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4254 for (i = 0; i < sub_count; ++i)
4256 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4257 surface_prepare_texture_internal(s, context, srgb);
4260 return;
4263 surface_prepare_texture_internal(surface, context, srgb);
4266 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4267 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4269 struct wined3d_device *device = surface->resource.device;
4270 UINT pitch = wined3d_surface_get_pitch(surface);
4271 const struct wined3d_gl_info *gl_info;
4272 struct wined3d_context *context;
4273 RECT local_rect;
4274 UINT w, h;
4276 surface_get_rect(surface, rect, &local_rect);
4278 mem += local_rect.top * pitch + local_rect.left * bpp;
4279 w = local_rect.right - local_rect.left;
4280 h = local_rect.bottom - local_rect.top;
4282 /* Activate the correct context for the render target */
4283 context = context_acquire(device, surface);
4284 context_apply_blit_state(context, device);
4285 gl_info = context->gl_info;
4287 ENTER_GL();
4289 if (!surface_is_offscreen(surface))
4291 GLenum buffer = surface_get_gl_buffer(surface);
4292 TRACE("Unlocking %#x buffer.\n", buffer);
4293 context_set_draw_buffer(context, buffer);
4295 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4296 glPixelZoom(1.0f, -1.0f);
4298 else
4300 /* Primary offscreen render target */
4301 TRACE("Offscreen render target.\n");
4302 context_set_draw_buffer(context, device->offscreenBuffer);
4304 glPixelZoom(1.0f, 1.0f);
4307 glRasterPos3i(local_rect.left, local_rect.top, 1);
4308 checkGLcall("glRasterPos3i");
4310 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4311 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4313 if (surface->flags & SFLAG_PBO)
4315 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4316 checkGLcall("glBindBufferARB");
4319 glDrawPixels(w, h, fmt, type, mem);
4320 checkGLcall("glDrawPixels");
4322 if (surface->flags & SFLAG_PBO)
4324 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4325 checkGLcall("glBindBufferARB");
4328 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4329 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4331 LEAVE_GL();
4333 if (wined3d_settings.strict_draw_ordering
4334 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4335 && surface->container.u.swapchain->front_buffer == surface))
4336 wglFlush();
4338 context_release(context);
4341 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4342 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4344 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4345 const struct wined3d_device *device = surface->resource.device;
4346 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4347 BOOL blit_supported = FALSE;
4349 /* Copy the default values from the surface. Below we might perform fixups */
4350 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4351 *format = *surface->resource.format;
4352 *convert = NO_CONVERSION;
4354 /* Ok, now look if we have to do any conversion */
4355 switch (surface->resource.format->id)
4357 case WINED3DFMT_P8_UINT:
4358 /* Below the call to blit_supported is disabled for Wine 1.2
4359 * because the function isn't operating correctly yet. At the
4360 * moment 8-bit blits are handled in software and if certain GL
4361 * extensions are around, surface conversion is performed at
4362 * upload time. The blit_supported call recognizes it as a
4363 * destination fixup. This type of upload 'fixup' and 8-bit to
4364 * 8-bit blits need to be handled by the blit_shader.
4365 * TODO: get rid of this #if 0. */
4366 #if 0
4367 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4368 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4369 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4370 #endif
4371 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4373 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4374 * texturing. Further also use conversion in case of color keying.
4375 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4376 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4377 * conflicts with this.
4379 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4380 || colorkey_active || !use_texturing)
4382 format->glFormat = GL_RGBA;
4383 format->glInternal = GL_RGBA;
4384 format->glType = GL_UNSIGNED_BYTE;
4385 format->conv_byte_count = 4;
4386 if (colorkey_active)
4387 *convert = CONVERT_PALETTED_CK;
4388 else
4389 *convert = CONVERT_PALETTED;
4391 break;
4393 case WINED3DFMT_B2G3R3_UNORM:
4394 /* **********************
4395 GL_UNSIGNED_BYTE_3_3_2
4396 ********************** */
4397 if (colorkey_active) {
4398 /* This texture format will never be used.. So do not care about color keying
4399 up until the point in time it will be needed :-) */
4400 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4402 break;
4404 case WINED3DFMT_B5G6R5_UNORM:
4405 if (colorkey_active)
4407 *convert = CONVERT_CK_565;
4408 format->glFormat = GL_RGBA;
4409 format->glInternal = GL_RGB5_A1;
4410 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4411 format->conv_byte_count = 2;
4413 break;
4415 case WINED3DFMT_B5G5R5X1_UNORM:
4416 if (colorkey_active)
4418 *convert = CONVERT_CK_5551;
4419 format->glFormat = GL_BGRA;
4420 format->glInternal = GL_RGB5_A1;
4421 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4422 format->conv_byte_count = 2;
4424 break;
4426 case WINED3DFMT_B8G8R8_UNORM:
4427 if (colorkey_active)
4429 *convert = CONVERT_CK_RGB24;
4430 format->glFormat = GL_RGBA;
4431 format->glInternal = GL_RGBA8;
4432 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4433 format->conv_byte_count = 4;
4435 break;
4437 case WINED3DFMT_B8G8R8X8_UNORM:
4438 if (colorkey_active)
4440 *convert = CONVERT_RGB32_888;
4441 format->glFormat = GL_RGBA;
4442 format->glInternal = GL_RGBA8;
4443 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4444 format->conv_byte_count = 4;
4446 break;
4448 default:
4449 break;
4452 return WINED3D_OK;
4455 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4457 const struct wined3d_device *device = surface->resource.device;
4458 const struct wined3d_palette *pal = surface->palette;
4459 BOOL index_in_alpha = FALSE;
4460 unsigned int i;
4462 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4463 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4464 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4465 * duplicate entries. Store the color key in the unused alpha component to speed the
4466 * download up and to make conversion unneeded. */
4467 index_in_alpha = primary_render_target_is_p8(device);
4469 if (!pal)
4471 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4472 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4474 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4475 if (index_in_alpha)
4477 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4478 * there's no palette at this time. */
4479 for (i = 0; i < 256; i++) table[i][3] = i;
4482 else
4484 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4485 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4486 * capability flag is present (wine does advertise this capability) */
4487 for (i = 0; i < 256; ++i)
4489 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4490 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4491 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4492 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4496 else
4498 TRACE("Using surface palette %p\n", pal);
4499 /* Get the surface's palette */
4500 for (i = 0; i < 256; ++i)
4502 table[i][0] = pal->palents[i].peRed;
4503 table[i][1] = pal->palents[i].peGreen;
4504 table[i][2] = pal->palents[i].peBlue;
4506 /* When index_in_alpha is set the palette index is stored in the
4507 * alpha component. In case of a readback we can then read
4508 * GL_ALPHA. Color keying is handled in BltOverride using a
4509 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4510 * color key itself is passed to glAlphaFunc in other cases the
4511 * alpha component of pixels that should be masked away is set to 0. */
4512 if (index_in_alpha)
4514 table[i][3] = i;
4516 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4517 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4519 table[i][3] = 0x00;
4521 else if (pal->flags & WINEDDPCAPS_ALPHA)
4523 table[i][3] = pal->palents[i].peFlags;
4525 else
4527 table[i][3] = 0xFF;
4533 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4534 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4536 const BYTE *source;
4537 BYTE *dest;
4538 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4540 switch (convert) {
4541 case NO_CONVERSION:
4543 memcpy(dst, src, pitch * height);
4544 break;
4546 case CONVERT_PALETTED:
4547 case CONVERT_PALETTED_CK:
4549 BYTE table[256][4];
4550 unsigned int x, y;
4552 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4554 for (y = 0; y < height; y++)
4556 source = src + pitch * y;
4557 dest = dst + outpitch * y;
4558 /* This is an 1 bpp format, using the width here is fine */
4559 for (x = 0; x < width; x++) {
4560 BYTE color = *source++;
4561 *dest++ = table[color][0];
4562 *dest++ = table[color][1];
4563 *dest++ = table[color][2];
4564 *dest++ = table[color][3];
4568 break;
4570 case CONVERT_CK_565:
4572 /* Converting the 565 format in 5551 packed to emulate color-keying.
4574 Note : in all these conversion, it would be best to average the averaging
4575 pixels to get the color of the pixel that will be color-keyed to
4576 prevent 'color bleeding'. This will be done later on if ever it is
4577 too visible.
4579 Note2: Nvidia documents say that their driver does not support alpha + color keying
4580 on the same surface and disables color keying in such a case
4582 unsigned int x, y;
4583 const WORD *Source;
4584 WORD *Dest;
4586 TRACE("Color keyed 565\n");
4588 for (y = 0; y < height; y++) {
4589 Source = (const WORD *)(src + y * pitch);
4590 Dest = (WORD *) (dst + y * outpitch);
4591 for (x = 0; x < width; x++ ) {
4592 WORD color = *Source++;
4593 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4594 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4595 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4596 *Dest |= 0x0001;
4597 Dest++;
4601 break;
4603 case CONVERT_CK_5551:
4605 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4606 unsigned int x, y;
4607 const WORD *Source;
4608 WORD *Dest;
4609 TRACE("Color keyed 5551\n");
4610 for (y = 0; y < height; y++) {
4611 Source = (const WORD *)(src + y * pitch);
4612 Dest = (WORD *) (dst + y * outpitch);
4613 for (x = 0; x < width; x++ ) {
4614 WORD color = *Source++;
4615 *Dest = color;
4616 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4617 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4618 *Dest |= (1 << 15);
4619 else
4620 *Dest &= ~(1 << 15);
4621 Dest++;
4625 break;
4627 case CONVERT_CK_RGB24:
4629 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4630 unsigned int x, y;
4631 for (y = 0; y < height; y++)
4633 source = src + pitch * y;
4634 dest = dst + outpitch * y;
4635 for (x = 0; x < width; x++) {
4636 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4637 DWORD dstcolor = color << 8;
4638 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4639 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4640 dstcolor |= 0xff;
4641 *(DWORD*)dest = dstcolor;
4642 source += 3;
4643 dest += 4;
4647 break;
4649 case CONVERT_RGB32_888:
4651 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4652 unsigned int x, y;
4653 for (y = 0; y < height; y++)
4655 source = src + pitch * y;
4656 dest = dst + outpitch * y;
4657 for (x = 0; x < width; x++) {
4658 DWORD color = 0xffffff & *(const DWORD*)source;
4659 DWORD dstcolor = color << 8;
4660 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4661 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4662 dstcolor |= 0xff;
4663 *(DWORD*)dest = dstcolor;
4664 source += 4;
4665 dest += 4;
4669 break;
4671 default:
4672 ERR("Unsupported conversion type %#x.\n", convert);
4674 return WINED3D_OK;
4677 BOOL palette9_changed(struct wined3d_surface *surface)
4679 struct wined3d_device *device = surface->resource.device;
4681 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4682 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4684 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4685 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4687 return FALSE;
4690 if (surface->palette9)
4692 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4694 return FALSE;
4697 else
4699 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4701 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4703 return TRUE;
4706 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4708 /* Flip the surface contents */
4709 /* Flip the DC */
4711 HDC tmp;
4712 tmp = front->hDC;
4713 front->hDC = back->hDC;
4714 back->hDC = tmp;
4717 /* Flip the DIBsection */
4719 HBITMAP tmp;
4720 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4721 tmp = front->dib.DIBsection;
4722 front->dib.DIBsection = back->dib.DIBsection;
4723 back->dib.DIBsection = tmp;
4725 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4726 else front->flags &= ~SFLAG_DIBSECTION;
4727 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4728 else back->flags &= ~SFLAG_DIBSECTION;
4731 /* Flip the surface data */
4733 void* tmp;
4735 tmp = front->dib.bitmap_data;
4736 front->dib.bitmap_data = back->dib.bitmap_data;
4737 back->dib.bitmap_data = tmp;
4739 tmp = front->resource.allocatedMemory;
4740 front->resource.allocatedMemory = back->resource.allocatedMemory;
4741 back->resource.allocatedMemory = tmp;
4743 tmp = front->resource.heapMemory;
4744 front->resource.heapMemory = back->resource.heapMemory;
4745 back->resource.heapMemory = tmp;
4748 /* Flip the PBO */
4750 GLuint tmp_pbo = front->pbo;
4751 front->pbo = back->pbo;
4752 back->pbo = tmp_pbo;
4755 /* client_memory should not be different, but just in case */
4757 BOOL tmp;
4758 tmp = front->dib.client_memory;
4759 front->dib.client_memory = back->dib.client_memory;
4760 back->dib.client_memory = tmp;
4763 /* Flip the opengl texture */
4765 GLuint tmp;
4767 tmp = back->texture_name;
4768 back->texture_name = front->texture_name;
4769 front->texture_name = tmp;
4771 tmp = back->texture_name_srgb;
4772 back->texture_name_srgb = front->texture_name_srgb;
4773 front->texture_name_srgb = tmp;
4775 resource_unload(&back->resource);
4776 resource_unload(&front->resource);
4780 DWORD tmp_flags = back->flags;
4781 back->flags = front->flags;
4782 front->flags = tmp_flags;
4786 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4787 * pixel copy calls. */
4788 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4789 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4791 struct wined3d_device *device = dst_surface->resource.device;
4792 float xrel, yrel;
4793 UINT row;
4794 struct wined3d_context *context;
4795 BOOL upsidedown = FALSE;
4796 RECT dst_rect = *dst_rect_in;
4798 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4799 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4801 if(dst_rect.top > dst_rect.bottom) {
4802 UINT tmp = dst_rect.bottom;
4803 dst_rect.bottom = dst_rect.top;
4804 dst_rect.top = tmp;
4805 upsidedown = TRUE;
4808 context = context_acquire(device, src_surface);
4809 context_apply_blit_state(context, device);
4810 surface_internal_preload(dst_surface, SRGB_RGB);
4811 ENTER_GL();
4813 /* Bind the target texture */
4814 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4815 checkGLcall("glBindTexture");
4816 if (surface_is_offscreen(src_surface))
4818 TRACE("Reading from an offscreen target\n");
4819 upsidedown = !upsidedown;
4820 glReadBuffer(device->offscreenBuffer);
4822 else
4824 glReadBuffer(surface_get_gl_buffer(src_surface));
4826 checkGLcall("glReadBuffer");
4828 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4829 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4831 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4833 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4835 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4836 ERR("Texture filtering not supported in direct blit\n");
4839 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4840 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4842 ERR("Texture filtering not supported in direct blit\n");
4845 if (upsidedown
4846 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4847 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4849 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4851 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4852 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4853 src_rect->left, src_surface->resource.height - src_rect->bottom,
4854 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4856 else
4858 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4859 /* I have to process this row by row to swap the image,
4860 * otherwise it would be upside down, so stretching in y direction
4861 * doesn't cost extra time
4863 * However, stretching in x direction can be avoided if not necessary
4865 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4866 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4868 /* Well, that stuff works, but it's very slow.
4869 * find a better way instead
4871 UINT col;
4873 for (col = dst_rect.left; col < dst_rect.right; ++col)
4875 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4876 dst_rect.left + col /* x offset */, row /* y offset */,
4877 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4880 else
4882 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4883 dst_rect.left /* x offset */, row /* y offset */,
4884 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4888 checkGLcall("glCopyTexSubImage2D");
4890 LEAVE_GL();
4891 context_release(context);
4893 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4894 * path is never entered
4896 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4899 /* Uses the hardware to stretch and flip the image */
4900 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4901 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4903 struct wined3d_device *device = dst_surface->resource.device;
4904 struct wined3d_swapchain *src_swapchain = NULL;
4905 GLuint src, backup = 0;
4906 float left, right, top, bottom; /* Texture coordinates */
4907 UINT fbwidth = src_surface->resource.width;
4908 UINT fbheight = src_surface->resource.height;
4909 struct wined3d_context *context;
4910 GLenum drawBuffer = GL_BACK;
4911 GLenum texture_target;
4912 BOOL noBackBufferBackup;
4913 BOOL src_offscreen;
4914 BOOL upsidedown = FALSE;
4915 RECT dst_rect = *dst_rect_in;
4917 TRACE("Using hwstretch blit\n");
4918 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4919 context = context_acquire(device, src_surface);
4920 context_apply_blit_state(context, device);
4921 surface_internal_preload(dst_surface, SRGB_RGB);
4923 src_offscreen = surface_is_offscreen(src_surface);
4924 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4925 if (!noBackBufferBackup && !src_surface->texture_name)
4927 /* Get it a description */
4928 surface_internal_preload(src_surface, SRGB_RGB);
4930 ENTER_GL();
4932 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4933 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4935 if (context->aux_buffers >= 2)
4937 /* Got more than one aux buffer? Use the 2nd aux buffer */
4938 drawBuffer = GL_AUX1;
4940 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4942 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4943 drawBuffer = GL_AUX0;
4946 if(noBackBufferBackup) {
4947 glGenTextures(1, &backup);
4948 checkGLcall("glGenTextures");
4949 glBindTexture(GL_TEXTURE_2D, backup);
4950 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4951 texture_target = GL_TEXTURE_2D;
4952 } else {
4953 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4954 * we are reading from the back buffer, the backup can be used as source texture
4956 texture_target = src_surface->texture_target;
4957 glBindTexture(texture_target, src_surface->texture_name);
4958 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4959 glEnable(texture_target);
4960 checkGLcall("glEnable(texture_target)");
4962 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4963 src_surface->flags &= ~SFLAG_INTEXTURE;
4966 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4967 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4969 if(dst_rect.top > dst_rect.bottom) {
4970 UINT tmp = dst_rect.bottom;
4971 dst_rect.bottom = dst_rect.top;
4972 dst_rect.top = tmp;
4973 upsidedown = TRUE;
4976 if (src_offscreen)
4978 TRACE("Reading from an offscreen target\n");
4979 upsidedown = !upsidedown;
4980 glReadBuffer(device->offscreenBuffer);
4982 else
4984 glReadBuffer(surface_get_gl_buffer(src_surface));
4987 /* TODO: Only back up the part that will be overwritten */
4988 glCopyTexSubImage2D(texture_target, 0,
4989 0, 0 /* read offsets */,
4990 0, 0,
4991 fbwidth,
4992 fbheight);
4994 checkGLcall("glCopyTexSubImage2D");
4996 /* No issue with overriding these - the sampler is dirty due to blit usage */
4997 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4998 wined3d_gl_mag_filter(magLookup, Filter));
4999 checkGLcall("glTexParameteri");
5000 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5001 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5002 checkGLcall("glTexParameteri");
5004 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5005 src_swapchain = src_surface->container.u.swapchain;
5006 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5008 src = backup ? backup : src_surface->texture_name;
5010 else
5012 glReadBuffer(GL_FRONT);
5013 checkGLcall("glReadBuffer(GL_FRONT)");
5015 glGenTextures(1, &src);
5016 checkGLcall("glGenTextures(1, &src)");
5017 glBindTexture(GL_TEXTURE_2D, src);
5018 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
5020 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5021 * out for power of 2 sizes
5023 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5024 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5025 checkGLcall("glTexImage2D");
5026 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5027 0, 0 /* read offsets */,
5028 0, 0,
5029 fbwidth,
5030 fbheight);
5032 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5033 checkGLcall("glTexParameteri");
5034 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5035 checkGLcall("glTexParameteri");
5037 glReadBuffer(GL_BACK);
5038 checkGLcall("glReadBuffer(GL_BACK)");
5040 if(texture_target != GL_TEXTURE_2D) {
5041 glDisable(texture_target);
5042 glEnable(GL_TEXTURE_2D);
5043 texture_target = GL_TEXTURE_2D;
5046 checkGLcall("glEnd and previous");
5048 left = src_rect->left;
5049 right = src_rect->right;
5051 if (!upsidedown)
5053 top = src_surface->resource.height - src_rect->top;
5054 bottom = src_surface->resource.height - src_rect->bottom;
5056 else
5058 top = src_surface->resource.height - src_rect->bottom;
5059 bottom = src_surface->resource.height - src_rect->top;
5062 if (src_surface->flags & SFLAG_NORMCOORD)
5064 left /= src_surface->pow2Width;
5065 right /= src_surface->pow2Width;
5066 top /= src_surface->pow2Height;
5067 bottom /= src_surface->pow2Height;
5070 /* draw the source texture stretched and upside down. The correct surface is bound already */
5071 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5072 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5074 context_set_draw_buffer(context, drawBuffer);
5075 glReadBuffer(drawBuffer);
5077 glBegin(GL_QUADS);
5078 /* bottom left */
5079 glTexCoord2f(left, bottom);
5080 glVertex2i(0, 0);
5082 /* top left */
5083 glTexCoord2f(left, top);
5084 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5086 /* top right */
5087 glTexCoord2f(right, top);
5088 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5090 /* bottom right */
5091 glTexCoord2f(right, bottom);
5092 glVertex2i(dst_rect.right - dst_rect.left, 0);
5093 glEnd();
5094 checkGLcall("glEnd and previous");
5096 if (texture_target != dst_surface->texture_target)
5098 glDisable(texture_target);
5099 glEnable(dst_surface->texture_target);
5100 texture_target = dst_surface->texture_target;
5103 /* Now read the stretched and upside down image into the destination texture */
5104 glBindTexture(texture_target, dst_surface->texture_name);
5105 checkGLcall("glBindTexture");
5106 glCopyTexSubImage2D(texture_target,
5108 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5109 0, 0, /* We blitted the image to the origin */
5110 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5111 checkGLcall("glCopyTexSubImage2D");
5113 if(drawBuffer == GL_BACK) {
5114 /* Write the back buffer backup back */
5115 if(backup) {
5116 if(texture_target != GL_TEXTURE_2D) {
5117 glDisable(texture_target);
5118 glEnable(GL_TEXTURE_2D);
5119 texture_target = GL_TEXTURE_2D;
5121 glBindTexture(GL_TEXTURE_2D, backup);
5122 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5124 else
5126 if (texture_target != src_surface->texture_target)
5128 glDisable(texture_target);
5129 glEnable(src_surface->texture_target);
5130 texture_target = src_surface->texture_target;
5132 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5133 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5136 glBegin(GL_QUADS);
5137 /* top left */
5138 glTexCoord2f(0.0f, 0.0f);
5139 glVertex2i(0, fbheight);
5141 /* bottom left */
5142 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5143 glVertex2i(0, 0);
5145 /* bottom right */
5146 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5147 (float)fbheight / (float)src_surface->pow2Height);
5148 glVertex2i(fbwidth, 0);
5150 /* top right */
5151 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5152 glVertex2i(fbwidth, fbheight);
5153 glEnd();
5155 glDisable(texture_target);
5156 checkGLcall("glDisable(texture_target)");
5158 /* Cleanup */
5159 if (src != src_surface->texture_name && src != backup)
5161 glDeleteTextures(1, &src);
5162 checkGLcall("glDeleteTextures(1, &src)");
5164 if(backup) {
5165 glDeleteTextures(1, &backup);
5166 checkGLcall("glDeleteTextures(1, &backup)");
5169 LEAVE_GL();
5171 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5173 context_release(context);
5175 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5176 * path is never entered
5178 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5181 /* Front buffer coordinates are always full screen coordinates, but our GL
5182 * drawable is limited to the window's client area. The sysmem and texture
5183 * copies do have the full screen size. Note that GL has a bottom-left
5184 * origin, while D3D has a top-left origin. */
5185 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5187 UINT drawable_height;
5189 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5190 && surface == surface->container.u.swapchain->front_buffer)
5192 POINT offset = {0, 0};
5193 RECT windowsize;
5195 ScreenToClient(window, &offset);
5196 OffsetRect(rect, offset.x, offset.y);
5198 GetClientRect(window, &windowsize);
5199 drawable_height = windowsize.bottom - windowsize.top;
5201 else
5203 drawable_height = surface->resource.height;
5206 rect->top = drawable_height - rect->top;
5207 rect->bottom = drawable_height - rect->bottom;
5210 static void surface_blt_to_drawable(struct wined3d_device *device,
5211 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5212 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5213 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5215 struct wined3d_context *context;
5216 RECT src_rect, dst_rect;
5218 src_rect = *src_rect_in;
5219 dst_rect = *dst_rect_in;
5221 /* Make sure the surface is up-to-date. This should probably use
5222 * surface_load_location() and worry about the destination surface too,
5223 * unless we're overwriting it completely. */
5224 surface_internal_preload(src_surface, SRGB_RGB);
5226 /* Activate the destination context, set it up for blitting */
5227 context = context_acquire(device, dst_surface);
5228 context_apply_blit_state(context, device);
5230 if (!surface_is_offscreen(dst_surface))
5231 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5233 device->blitter->set_shader(device->blit_priv, context, src_surface);
5235 ENTER_GL();
5237 if (color_key)
5239 glEnable(GL_ALPHA_TEST);
5240 checkGLcall("glEnable(GL_ALPHA_TEST)");
5242 /* When the primary render target uses P8, the alpha component
5243 * contains the palette index. Which means that the colorkey is one of
5244 * the palette entries. In other cases pixels that should be masked
5245 * away have alpha set to 0. */
5246 if (primary_render_target_is_p8(device))
5247 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5248 else
5249 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5250 checkGLcall("glAlphaFunc");
5252 else
5254 glDisable(GL_ALPHA_TEST);
5255 checkGLcall("glDisable(GL_ALPHA_TEST)");
5258 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5260 if (color_key)
5262 glDisable(GL_ALPHA_TEST);
5263 checkGLcall("glDisable(GL_ALPHA_TEST)");
5266 LEAVE_GL();
5268 /* Leave the opengl state valid for blitting */
5269 device->blitter->unset_shader(context->gl_info);
5271 if (wined3d_settings.strict_draw_ordering
5272 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5273 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5274 wglFlush(); /* Flush to ensure ordering across contexts. */
5276 context_release(context);
5279 /* Do not call while under the GL lock. */
5280 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5282 struct wined3d_device *device = s->resource.device;
5283 const struct blit_shader *blitter;
5285 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5286 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5287 if (!blitter)
5289 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5290 return WINED3DERR_INVALIDCALL;
5293 return blitter->color_fill(device, s, rect, color);
5296 /* Do not call while under the GL lock. */
5297 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5298 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5299 WINED3DTEXTUREFILTERTYPE Filter)
5301 struct wined3d_device *device = dst_surface->resource.device;
5302 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5303 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5305 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5306 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5307 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5309 /* Get the swapchain. One of the surfaces has to be a primary surface */
5310 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5312 WARN("Destination is in sysmem, rejecting gl blt\n");
5313 return WINED3DERR_INVALIDCALL;
5316 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5317 dstSwapchain = dst_surface->container.u.swapchain;
5319 if (src_surface)
5321 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5323 WARN("Src is in sysmem, rejecting gl blt\n");
5324 return WINED3DERR_INVALIDCALL;
5327 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5328 srcSwapchain = src_surface->container.u.swapchain;
5331 /* Early sort out of cases where no render target is used */
5332 if (!dstSwapchain && !srcSwapchain
5333 && src_surface != device->fb.render_targets[0]
5334 && dst_surface != device->fb.render_targets[0])
5336 TRACE("No surface is render target, not using hardware blit.\n");
5337 return WINED3DERR_INVALIDCALL;
5340 /* No destination color keying supported */
5341 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5343 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5344 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5345 return WINED3DERR_INVALIDCALL;
5348 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5349 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5350 && dst_surface == dstSwapchain->front_buffer
5351 && src_surface == dstSwapchain->back_buffers[0])
5353 /* Half-Life does a Blt from the back buffer to the front buffer,
5354 * Full surface size, no flags... Use present instead
5356 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5359 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5360 for (;;)
5362 TRACE("Looking if a Present can be done...\n");
5363 /* Source Rectangle must be full surface */
5364 if (src_rect->left || src_rect->top
5365 || src_rect->right != src_surface->resource.width
5366 || src_rect->bottom != src_surface->resource.height)
5368 TRACE("No, Source rectangle doesn't match\n");
5369 break;
5372 /* No stretching may occur */
5373 if (src_rect->right != dst_rect->right - dst_rect->left
5374 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5376 TRACE("No, stretching is done\n");
5377 break;
5380 /* Destination must be full surface or match the clipping rectangle */
5381 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5383 RECT cliprect;
5384 POINT pos[2];
5385 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5386 pos[0].x = dst_rect->left;
5387 pos[0].y = dst_rect->top;
5388 pos[1].x = dst_rect->right;
5389 pos[1].y = dst_rect->bottom;
5390 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5392 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5393 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5395 TRACE("No, dest rectangle doesn't match(clipper)\n");
5396 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5397 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5398 break;
5401 else if (dst_rect->left || dst_rect->top
5402 || dst_rect->right != dst_surface->resource.width
5403 || dst_rect->bottom != dst_surface->resource.height)
5405 TRACE("No, dest rectangle doesn't match(surface size)\n");
5406 break;
5409 TRACE("Yes\n");
5411 /* These flags are unimportant for the flag check, remove them */
5412 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5414 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5416 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5417 * take very long, while a flip is fast.
5418 * This applies to Half-Life, which does such Blts every time it finished
5419 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5420 * menu. This is also used by all apps when they do windowed rendering
5422 * The problem is that flipping is not really the same as copying. After a
5423 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5424 * untouched. Therefore it's necessary to override the swap effect
5425 * and to set it back after the flip.
5427 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5428 * testcases.
5431 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5432 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5434 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5435 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5437 dstSwapchain->presentParms.SwapEffect = orig_swap;
5439 return WINED3D_OK;
5441 break;
5444 TRACE("Unsupported blit between buffers on the same swapchain\n");
5445 return WINED3DERR_INVALIDCALL;
5446 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5447 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5448 return WINED3DERR_INVALIDCALL;
5449 } else if(dstSwapchain && srcSwapchain) {
5450 FIXME("Implement hardware blit between two different swapchains\n");
5451 return WINED3DERR_INVALIDCALL;
5453 else if (dstSwapchain)
5455 /* Handled with regular texture -> swapchain blit */
5456 if (src_surface == device->fb.render_targets[0])
5457 TRACE("Blit from active render target to a swapchain\n");
5459 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5461 FIXME("Implement blit from a swapchain to the active render target\n");
5462 return WINED3DERR_INVALIDCALL;
5465 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5467 /* Blit from render target to texture */
5468 BOOL stretchx;
5470 /* P8 read back is not implemented */
5471 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5472 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5474 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5475 return WINED3DERR_INVALIDCALL;
5478 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5480 TRACE("Color keying not supported by frame buffer to texture blit\n");
5481 return WINED3DERR_INVALIDCALL;
5482 /* Destination color key is checked above */
5485 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5486 stretchx = TRUE;
5487 else
5488 stretchx = FALSE;
5490 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5491 * flip the image nor scale it.
5493 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5494 * -> If the app wants a image width an unscaled width, copy it line per line
5495 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5496 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5497 * back buffer. This is slower than reading line per line, thus not used for flipping
5498 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5499 * pixel by pixel. */
5500 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5501 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5503 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5504 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5505 } else {
5506 TRACE("Using hardware stretching to flip / stretch the texture\n");
5507 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5510 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5512 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5513 dst_surface->resource.allocatedMemory = NULL;
5514 dst_surface->resource.heapMemory = NULL;
5516 else
5518 dst_surface->flags &= ~SFLAG_INSYSMEM;
5521 return WINED3D_OK;
5523 else if (src_surface)
5525 /* Blit from offscreen surface to render target */
5526 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5527 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5529 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5531 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5532 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5533 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5535 FIXME("Unsupported blit operation falling back to software\n");
5536 return WINED3DERR_INVALIDCALL;
5539 /* Color keying: Check if we have to do a color keyed blt,
5540 * and if not check if a color key is activated.
5542 * Just modify the color keying parameters in the surface and restore them afterwards
5543 * The surface keeps track of the color key last used to load the opengl surface.
5544 * PreLoad will catch the change to the flags and color key and reload if necessary.
5546 if (flags & WINEDDBLT_KEYSRC)
5548 /* Use color key from surface */
5550 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5552 /* Use color key from DDBltFx */
5553 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5554 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5556 else
5558 /* Do not use color key */
5559 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5562 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5563 src_surface, src_rect, dst_surface, dst_rect);
5565 /* Restore the color key parameters */
5566 src_surface->CKeyFlags = oldCKeyFlags;
5567 src_surface->SrcBltCKey = oldBltCKey;
5569 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5571 return WINED3D_OK;
5574 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5575 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5576 return WINED3DERR_INVALIDCALL;
5579 /* GL locking is done by the caller */
5580 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5581 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5583 struct wined3d_device *device = surface->resource.device;
5584 const struct wined3d_gl_info *gl_info = context->gl_info;
5585 GLint compare_mode = GL_NONE;
5586 struct blt_info info;
5587 GLint old_binding = 0;
5588 RECT rect;
5590 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5592 glDisable(GL_CULL_FACE);
5593 glDisable(GL_BLEND);
5594 glDisable(GL_ALPHA_TEST);
5595 glDisable(GL_SCISSOR_TEST);
5596 glDisable(GL_STENCIL_TEST);
5597 glEnable(GL_DEPTH_TEST);
5598 glDepthFunc(GL_ALWAYS);
5599 glDepthMask(GL_TRUE);
5600 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5601 glViewport(x, y, w, h);
5603 SetRect(&rect, 0, h, w, 0);
5604 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5605 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5606 glGetIntegerv(info.binding, &old_binding);
5607 glBindTexture(info.bind_target, texture);
5608 if (gl_info->supported[ARB_SHADOW])
5610 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5611 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5614 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5615 gl_info, info.tex_type, &surface->ds_current_size);
5617 glBegin(GL_TRIANGLE_STRIP);
5618 glTexCoord3fv(info.coords[0]);
5619 glVertex2f(-1.0f, -1.0f);
5620 glTexCoord3fv(info.coords[1]);
5621 glVertex2f(1.0f, -1.0f);
5622 glTexCoord3fv(info.coords[2]);
5623 glVertex2f(-1.0f, 1.0f);
5624 glTexCoord3fv(info.coords[3]);
5625 glVertex2f(1.0f, 1.0f);
5626 glEnd();
5628 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5629 glBindTexture(info.bind_target, old_binding);
5631 glPopAttrib();
5633 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5636 void surface_modify_ds_location(struct wined3d_surface *surface,
5637 DWORD location, UINT w, UINT h)
5639 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5641 if (location & ~SFLAG_DS_LOCATIONS)
5642 FIXME("Invalid location (%#x) specified.\n", location);
5644 surface->ds_current_size.cx = w;
5645 surface->ds_current_size.cy = h;
5646 surface->flags &= ~SFLAG_DS_LOCATIONS;
5647 surface->flags |= location;
5650 /* Context activation is done by the caller. */
5651 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5653 struct wined3d_device *device = surface->resource.device;
5654 GLsizei w, h;
5656 TRACE("surface %p, new location %#x.\n", surface, location);
5658 /* TODO: Make this work for modes other than FBO */
5659 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5661 if (!(surface->flags & location))
5663 w = surface->ds_current_size.cx;
5664 h = surface->ds_current_size.cy;
5665 surface->ds_current_size.cx = 0;
5666 surface->ds_current_size.cy = 0;
5668 else
5670 w = surface->resource.width;
5671 h = surface->resource.height;
5674 if (surface->ds_current_size.cx == surface->resource.width
5675 && surface->ds_current_size.cy == surface->resource.height)
5677 TRACE("Location (%#x) is already up to date.\n", location);
5678 return;
5681 if (surface->current_renderbuffer)
5683 FIXME("Not supported with fixed up depth stencil.\n");
5684 return;
5687 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5689 /* This mostly happens when a depth / stencil is used without being
5690 * cleared first. In principle we could upload from sysmem, or
5691 * explicitly clear before first usage. For the moment there don't
5692 * appear to be a lot of applications depending on this, so a FIXME
5693 * should do. */
5694 FIXME("No up to date depth stencil location.\n");
5695 surface->flags |= location;
5696 surface->ds_current_size.cx = surface->resource.width;
5697 surface->ds_current_size.cy = surface->resource.height;
5698 return;
5701 if (location == SFLAG_DS_OFFSCREEN)
5703 GLint old_binding = 0;
5704 GLenum bind_target;
5706 /* The render target is allowed to be smaller than the depth/stencil
5707 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5708 * than the offscreen surface. Don't overwrite the offscreen surface
5709 * with undefined data. */
5710 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5711 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5713 TRACE("Copying onscreen depth buffer to depth texture.\n");
5715 ENTER_GL();
5717 if (!device->depth_blt_texture)
5719 glGenTextures(1, &device->depth_blt_texture);
5722 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5723 * directly on the FBO texture. That's because we need to flip. */
5724 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5725 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5726 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5728 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5729 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5731 else
5733 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5734 bind_target = GL_TEXTURE_2D;
5736 glBindTexture(bind_target, device->depth_blt_texture);
5737 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5738 * internal format, because the internal format might include stencil
5739 * data. In principle we should copy stencil data as well, but unless
5740 * the driver supports stencil export it's hard to do, and doesn't
5741 * seem to be needed in practice. If the hardware doesn't support
5742 * writing stencil data, the glCopyTexImage2D() call might trigger
5743 * software fallbacks. */
5744 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5745 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5746 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5747 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5748 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5749 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5750 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5751 glBindTexture(bind_target, old_binding);
5753 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5754 NULL, surface, SFLAG_INTEXTURE);
5755 context_set_draw_buffer(context, GL_NONE);
5756 glReadBuffer(GL_NONE);
5758 /* Do the actual blit */
5759 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5760 checkGLcall("depth_blt");
5762 context_invalidate_state(context, STATE_FRAMEBUFFER);
5764 LEAVE_GL();
5766 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5768 else if (location == SFLAG_DS_ONSCREEN)
5770 TRACE("Copying depth texture to onscreen depth buffer.\n");
5772 ENTER_GL();
5774 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5775 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5776 surface_depth_blt(surface, context, surface->texture_name,
5777 0, surface->pow2Height - h, w, h, surface->texture_target);
5778 checkGLcall("depth_blt");
5780 context_invalidate_state(context, STATE_FRAMEBUFFER);
5782 LEAVE_GL();
5784 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5786 else
5788 ERR("Invalid location (%#x) specified.\n", location);
5791 surface->flags |= location;
5792 surface->ds_current_size.cx = surface->resource.width;
5793 surface->ds_current_size.cy = surface->resource.height;
5796 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5798 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5799 struct wined3d_surface *overlay;
5801 TRACE("surface %p, location %s, persistent %#x.\n",
5802 surface, debug_surflocation(location), persistent);
5804 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5806 if (surface_is_offscreen(surface))
5808 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5809 * for offscreen targets. */
5810 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5811 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5813 else
5815 TRACE("Surface %p is an onscreen surface.\n", surface);
5819 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5820 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5821 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5823 if (persistent)
5825 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5826 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5828 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5830 TRACE("Passing to container.\n");
5831 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5834 surface->flags &= ~SFLAG_LOCATIONS;
5835 surface->flags |= location;
5837 /* Redraw emulated overlays, if any */
5838 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5840 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5842 overlay->surface_ops->surface_draw_overlay(overlay);
5846 else
5848 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5850 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5852 TRACE("Passing to container\n");
5853 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5856 surface->flags &= ~location;
5859 if (!(surface->flags & SFLAG_LOCATIONS))
5861 ERR("Surface %p does not have any up to date location.\n", surface);
5865 static DWORD resource_access_from_location(DWORD location)
5867 switch (location)
5869 case SFLAG_INSYSMEM:
5870 return WINED3D_RESOURCE_ACCESS_CPU;
5872 case SFLAG_INDRAWABLE:
5873 case SFLAG_INSRGBTEX:
5874 case SFLAG_INTEXTURE:
5875 return WINED3D_RESOURCE_ACCESS_GPU;
5877 default:
5878 FIXME("Unhandled location %#x.\n", location);
5879 return 0;
5883 static void surface_load_sysmem(struct wined3d_surface *surface,
5884 const struct wined3d_gl_info *gl_info, const RECT *rect)
5886 surface_prepare_system_memory(surface);
5888 /* Download the surface to system memory. */
5889 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5891 struct wined3d_device *device = surface->resource.device;
5892 struct wined3d_context *context;
5894 /* TODO: Use already acquired context when possible. */
5895 context = context_acquire(device, NULL);
5897 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5898 surface_download_data(surface, gl_info);
5900 context_release(context);
5902 return;
5905 /* Note: It might be faster to download into a texture first. */
5906 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5907 wined3d_surface_get_pitch(surface));
5910 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5911 const struct wined3d_gl_info *gl_info, const RECT *rect)
5913 struct wined3d_device *device = surface->resource.device;
5914 struct wined3d_format format;
5915 CONVERT_TYPES convert;
5916 UINT byte_count;
5917 BYTE *mem;
5919 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5920 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5922 if (surface->flags & SFLAG_INTEXTURE)
5924 RECT r;
5926 surface_get_rect(surface, rect, &r);
5927 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5929 return WINED3D_OK;
5932 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5934 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5935 * path through sysmem. */
5936 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5939 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5941 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5942 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5943 * called. */
5944 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5946 struct wined3d_context *context;
5948 TRACE("Removing the pbo attached to surface %p.\n", surface);
5950 /* TODO: Use already acquired context when possible. */
5951 context = context_acquire(device, NULL);
5953 surface_remove_pbo(surface, gl_info);
5955 context_release(context);
5958 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5960 UINT height = surface->resource.height;
5961 UINT width = surface->resource.width;
5962 UINT src_pitch, dst_pitch;
5964 byte_count = format.conv_byte_count;
5965 src_pitch = wined3d_surface_get_pitch(surface);
5967 /* Stick to the alignment for the converted surface too, makes it
5968 * easier to load the surface. */
5969 dst_pitch = width * byte_count;
5970 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5972 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5974 ERR("Out of memory (%u).\n", dst_pitch * height);
5975 return E_OUTOFMEMORY;
5978 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5979 src_pitch, width, height, dst_pitch, convert, surface);
5981 surface->flags |= SFLAG_CONVERTED;
5983 else
5985 surface->flags &= ~SFLAG_CONVERTED;
5986 mem = surface->resource.allocatedMemory;
5987 byte_count = format.byte_count;
5990 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5992 /* Don't delete PBO memory. */
5993 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5994 HeapFree(GetProcessHeap(), 0, mem);
5996 return WINED3D_OK;
5999 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6000 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6002 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
6003 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6004 struct wined3d_device *device = surface->resource.device;
6005 struct wined3d_context *context;
6006 UINT width, src_pitch, dst_pitch;
6007 struct wined3d_bo_address data;
6008 struct wined3d_format format;
6009 POINT dst_point = {0, 0};
6010 CONVERT_TYPES convert;
6011 BYTE *mem;
6013 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6014 && surface_is_offscreen(surface)
6015 && (surface->flags & SFLAG_INDRAWABLE))
6017 read_from_framebuffer_texture(surface, srgb);
6019 return WINED3D_OK;
6022 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6023 && (surface->resource.format->flags & attach_flags) == attach_flags
6024 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6025 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6026 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6028 if (srgb)
6029 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6030 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6031 else
6032 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6033 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6035 return WINED3D_OK;
6038 /* Upload from system memory */
6040 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6041 TRUE /* We will use textures */, &format, &convert);
6043 if (srgb)
6045 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6047 /* Performance warning... */
6048 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6049 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6052 else
6054 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6056 /* Performance warning... */
6057 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6058 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6062 if (!(surface->flags & SFLAG_INSYSMEM))
6064 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6065 /* Lets hope we get it from somewhere... */
6066 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6069 /* TODO: Use already acquired context when possible. */
6070 context = context_acquire(device, NULL);
6072 surface_prepare_texture(surface, context, srgb);
6073 surface_bind_and_dirtify(surface, context, srgb);
6075 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6077 surface->flags |= SFLAG_GLCKEY;
6078 surface->glCKey = surface->SrcBltCKey;
6080 else surface->flags &= ~SFLAG_GLCKEY;
6082 width = surface->resource.width;
6083 src_pitch = wined3d_surface_get_pitch(surface);
6085 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6086 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6087 * called. */
6088 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6090 TRACE("Removing the pbo attached to surface %p.\n", surface);
6091 surface_remove_pbo(surface, gl_info);
6094 if (format.convert)
6096 /* This code is entered for texture formats which need a fixup. */
6097 UINT height = surface->resource.height;
6099 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6100 dst_pitch = width * format.conv_byte_count;
6101 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6103 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6105 ERR("Out of memory (%u).\n", dst_pitch * height);
6106 context_release(context);
6107 return E_OUTOFMEMORY;
6109 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6111 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6113 /* This code is only entered for color keying fixups */
6114 UINT height = surface->resource.height;
6116 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6117 dst_pitch = width * format.conv_byte_count;
6118 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6120 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6122 ERR("Out of memory (%u).\n", dst_pitch * height);
6123 context_release(context);
6124 return E_OUTOFMEMORY;
6126 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6127 width, height, dst_pitch, convert, surface);
6129 else
6131 mem = surface->resource.allocatedMemory;
6134 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6135 data.addr = mem;
6136 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6138 context_release(context);
6140 /* Don't delete PBO memory. */
6141 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6142 HeapFree(GetProcessHeap(), 0, mem);
6144 return WINED3D_OK;
6147 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6149 struct wined3d_device *device = surface->resource.device;
6150 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6151 BOOL in_fbo = FALSE;
6152 HRESULT hr;
6154 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6156 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6158 if (location == SFLAG_INTEXTURE)
6160 struct wined3d_context *context = context_acquire(device, NULL);
6161 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6162 context_release(context);
6163 return WINED3D_OK;
6165 else
6167 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6168 return WINED3DERR_INVALIDCALL;
6172 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6174 if (surface_is_offscreen(surface))
6176 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6177 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6178 if (location == SFLAG_INDRAWABLE)
6179 location = SFLAG_INTEXTURE;
6180 in_fbo = TRUE;
6182 else
6184 TRACE("Surface %p is an onscreen surface.\n", surface);
6188 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6189 location = SFLAG_INTEXTURE;
6191 if (surface->flags & location)
6193 TRACE("Location already up to date.\n");
6194 return WINED3D_OK;
6197 if (WARN_ON(d3d_surface))
6199 DWORD required_access = resource_access_from_location(location);
6200 if ((surface->resource.access_flags & required_access) != required_access)
6201 WARN("Operation requires %#x access, but surface only has %#x.\n",
6202 required_access, surface->resource.access_flags);
6205 if (!(surface->flags & SFLAG_LOCATIONS))
6207 ERR("Surface %p does not have any up to date location.\n", surface);
6208 surface->flags |= SFLAG_LOST;
6209 return WINED3DERR_DEVICELOST;
6212 switch (location)
6214 case SFLAG_INSYSMEM:
6215 surface_load_sysmem(surface, gl_info, rect);
6216 break;
6218 case SFLAG_INDRAWABLE:
6219 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6220 return hr;
6221 break;
6223 case SFLAG_INTEXTURE:
6224 case SFLAG_INSRGBTEX:
6225 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6226 return hr;
6227 break;
6229 default:
6230 ERR("Don't know how to handle location %#x.\n", location);
6231 break;
6234 if (!rect)
6236 surface->flags |= location;
6238 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6239 surface_evict_sysmem(surface);
6242 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6244 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6245 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6248 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6249 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6251 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6254 return WINED3D_OK;
6257 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6259 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6261 /* Not on a swapchain - must be offscreen */
6262 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6264 /* The front buffer is always onscreen */
6265 if (surface == swapchain->front_buffer) return FALSE;
6267 /* If the swapchain is rendered to an FBO, the backbuffer is
6268 * offscreen, otherwise onscreen */
6269 return swapchain->render_to_fbo;
6272 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6273 /* Context activation is done by the caller. */
6274 static void ffp_blit_free(struct wined3d_device *device) { }
6276 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6277 /* Context activation is done by the caller. */
6278 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6280 BYTE table[256][4];
6281 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6283 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6285 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6286 ENTER_GL();
6287 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6288 LEAVE_GL();
6291 /* Context activation is done by the caller. */
6292 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6294 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6296 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6297 * else the surface is converted in software at upload time in LoadLocation.
6299 if(fixup == COMPLEX_FIXUP_P8 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6300 ffp_blit_p8_upload_palette(surface, context->gl_info);
6302 ENTER_GL();
6303 glEnable(surface->texture_target);
6304 checkGLcall("glEnable(surface->texture_target)");
6305 LEAVE_GL();
6306 return WINED3D_OK;
6309 /* Context activation is done by the caller. */
6310 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6312 ENTER_GL();
6313 glDisable(GL_TEXTURE_2D);
6314 checkGLcall("glDisable(GL_TEXTURE_2D)");
6315 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6317 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6318 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6320 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6322 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6323 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6325 LEAVE_GL();
6328 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6329 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6330 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6332 enum complex_fixup src_fixup;
6334 switch (blit_op)
6336 case WINED3D_BLIT_OP_COLOR_BLIT:
6337 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6338 return FALSE;
6340 src_fixup = get_complex_fixup(src_format->color_fixup);
6341 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6343 TRACE("Checking support for fixup:\n");
6344 dump_color_fixup_desc(src_format->color_fixup);
6347 if (!is_identity_fixup(dst_format->color_fixup))
6349 TRACE("Destination fixups are not supported\n");
6350 return FALSE;
6353 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6355 TRACE("P8 fixup supported\n");
6356 return TRUE;
6359 /* We only support identity conversions. */
6360 if (is_identity_fixup(src_format->color_fixup))
6362 TRACE("[OK]\n");
6363 return TRUE;
6366 TRACE("[FAILED]\n");
6367 return FALSE;
6369 case WINED3D_BLIT_OP_COLOR_FILL:
6370 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6371 return FALSE;
6373 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6375 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6376 return FALSE;
6378 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6380 TRACE("Color fill not supported\n");
6381 return FALSE;
6384 /* FIXME: We should reject color fills on formats with fixups,
6385 * but this would break P8 color fills for example. */
6387 return TRUE;
6389 case WINED3D_BLIT_OP_DEPTH_FILL:
6390 return TRUE;
6392 default:
6393 TRACE("Unsupported blit_op=%d\n", blit_op);
6394 return FALSE;
6398 /* Do not call while under the GL lock. */
6399 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6400 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6402 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6403 struct wined3d_fb_state fb = {&dst_surface, NULL};
6405 return device_clear_render_targets(device, 1, &fb,
6406 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6409 /* Do not call while under the GL lock. */
6410 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6411 struct wined3d_surface *surface, const RECT *rect, float depth)
6413 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6414 struct wined3d_fb_state fb = {NULL, surface};
6416 return device_clear_render_targets(device, 0, &fb,
6417 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6420 const struct blit_shader ffp_blit = {
6421 ffp_blit_alloc,
6422 ffp_blit_free,
6423 ffp_blit_set,
6424 ffp_blit_unset,
6425 ffp_blit_supported,
6426 ffp_blit_color_fill,
6427 ffp_blit_depth_fill,
6430 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6432 return WINED3D_OK;
6435 /* Context activation is done by the caller. */
6436 static void cpu_blit_free(struct wined3d_device *device)
6440 /* Context activation is done by the caller. */
6441 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6443 return WINED3D_OK;
6446 /* Context activation is done by the caller. */
6447 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6451 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6452 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6453 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6455 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6457 return TRUE;
6460 return FALSE;
6463 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6464 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6465 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6467 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6468 const struct wined3d_format *src_format, *dst_format;
6469 struct wined3d_surface *orig_src = src_surface;
6470 WINED3DLOCKED_RECT dlock, slock;
6471 HRESULT hr = WINED3D_OK;
6472 const BYTE *sbuf;
6473 RECT xdst,xsrc;
6474 BYTE *dbuf;
6475 int x, y;
6477 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6478 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6479 flags, fx, debug_d3dtexturefiltertype(filter));
6481 xsrc = *src_rect;
6483 if (!src_surface)
6485 RECT full_rect;
6487 full_rect.left = 0;
6488 full_rect.top = 0;
6489 full_rect.right = dst_surface->resource.width;
6490 full_rect.bottom = dst_surface->resource.height;
6491 IntersectRect(&xdst, &full_rect, dst_rect);
6493 else
6495 BOOL clip_horiz, clip_vert;
6497 xdst = *dst_rect;
6498 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6499 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6501 if (clip_vert || clip_horiz)
6503 /* Now check if this is a special case or not... */
6504 if ((flags & WINEDDBLT_DDFX)
6505 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6506 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6508 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6509 return WINED3D_OK;
6512 if (clip_horiz)
6514 if (xdst.left < 0)
6516 xsrc.left -= xdst.left;
6517 xdst.left = 0;
6519 if (xdst.right > dst_surface->resource.width)
6521 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6522 xdst.right = (int)dst_surface->resource.width;
6526 if (clip_vert)
6528 if (xdst.top < 0)
6530 xsrc.top -= xdst.top;
6531 xdst.top = 0;
6533 if (xdst.bottom > dst_surface->resource.height)
6535 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6536 xdst.bottom = (int)dst_surface->resource.height;
6540 /* And check if after clipping something is still to be done... */
6541 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6542 || (xdst.left >= (int)dst_surface->resource.width)
6543 || (xdst.top >= (int)dst_surface->resource.height)
6544 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6545 || (xsrc.left >= (int)src_surface->resource.width)
6546 || (xsrc.top >= (int)src_surface->resource.height))
6548 TRACE("Nothing to be done after clipping.\n");
6549 return WINED3D_OK;
6554 if (src_surface == dst_surface)
6556 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6557 slock = dlock;
6558 src_format = dst_surface->resource.format;
6559 dst_format = src_format;
6561 else
6563 dst_format = dst_surface->resource.format;
6564 if (src_surface)
6566 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6568 src_surface = surface_convert_format(src_surface, dst_format->id);
6569 if (!src_surface)
6571 /* The conv function writes a FIXME */
6572 WARN("Cannot convert source surface format to dest format.\n");
6573 goto release;
6576 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6577 src_format = src_surface->resource.format;
6579 else
6581 src_format = dst_format;
6583 if (dst_rect)
6584 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6585 else
6586 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6589 bpp = dst_surface->resource.format->byte_count;
6590 srcheight = xsrc.bottom - xsrc.top;
6591 srcwidth = xsrc.right - xsrc.left;
6592 dstheight = xdst.bottom - xdst.top;
6593 dstwidth = xdst.right - xdst.left;
6594 width = (xdst.right - xdst.left) * bpp;
6596 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6598 UINT row_block_count;
6600 if (flags || src_surface == dst_surface)
6602 FIXME("Only plain blits supported on compressed surfaces.\n");
6603 hr = E_NOTIMPL;
6604 goto release;
6607 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6609 if (srcheight != dstheight || srcwidth != dstwidth)
6611 WARN("Stretching not supported on compressed surfaces.\n");
6612 hr = WINED3DERR_INVALIDCALL;
6613 goto release;
6616 dbuf = dlock.pBits;
6617 sbuf = slock.pBits;
6619 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6620 for (y = 0; y < dstheight; y += dst_format->block_height)
6622 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6623 dbuf += dlock.Pitch;
6624 sbuf += slock.Pitch;
6627 goto release;
6630 if (dst_rect && src_surface != dst_surface)
6631 dbuf = dlock.pBits;
6632 else
6633 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6635 /* First, all the 'source-less' blits */
6636 if (flags & WINEDDBLT_COLORFILL)
6638 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6639 flags &= ~WINEDDBLT_COLORFILL;
6642 if (flags & WINEDDBLT_DEPTHFILL)
6644 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6646 if (flags & WINEDDBLT_ROP)
6648 /* Catch some degenerate cases here. */
6649 switch (fx->dwROP)
6651 case BLACKNESS:
6652 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6653 break;
6654 case 0xAA0029: /* No-op */
6655 break;
6656 case WHITENESS:
6657 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6658 break;
6659 case SRCCOPY: /* Well, we do that below? */
6660 break;
6661 default:
6662 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6663 goto error;
6665 flags &= ~WINEDDBLT_ROP;
6667 if (flags & WINEDDBLT_DDROPS)
6669 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6671 /* Now the 'with source' blits. */
6672 if (src_surface)
6674 const BYTE *sbase;
6675 int sx, xinc, sy, yinc;
6677 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6678 goto release;
6680 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6681 && (srcwidth != dstwidth || srcheight != dstheight))
6683 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6684 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6687 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6688 xinc = (srcwidth << 16) / dstwidth;
6689 yinc = (srcheight << 16) / dstheight;
6691 if (!flags)
6693 /* No effects, we can cheat here. */
6694 if (dstwidth == srcwidth)
6696 if (dstheight == srcheight)
6698 /* No stretching in either direction. This needs to be as
6699 * fast as possible. */
6700 sbuf = sbase;
6702 /* Check for overlapping surfaces. */
6703 if (src_surface != dst_surface || xdst.top < xsrc.top
6704 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6706 /* No overlap, or dst above src, so copy from top downwards. */
6707 for (y = 0; y < dstheight; ++y)
6709 memcpy(dbuf, sbuf, width);
6710 sbuf += slock.Pitch;
6711 dbuf += dlock.Pitch;
6714 else if (xdst.top > xsrc.top)
6716 /* Copy from bottom upwards. */
6717 sbuf += (slock.Pitch*dstheight);
6718 dbuf += (dlock.Pitch*dstheight);
6719 for (y = 0; y < dstheight; ++y)
6721 sbuf -= slock.Pitch;
6722 dbuf -= dlock.Pitch;
6723 memcpy(dbuf, sbuf, width);
6726 else
6728 /* Src and dst overlapping on the same line, use memmove. */
6729 for (y = 0; y < dstheight; ++y)
6731 memmove(dbuf, sbuf, width);
6732 sbuf += slock.Pitch;
6733 dbuf += dlock.Pitch;
6737 else
6739 /* Stretching in y direction only. */
6740 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6742 sbuf = sbase + (sy >> 16) * slock.Pitch;
6743 memcpy(dbuf, sbuf, width);
6744 dbuf += dlock.Pitch;
6748 else
6750 /* Stretching in X direction. */
6751 int last_sy = -1;
6752 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6754 sbuf = sbase + (sy >> 16) * slock.Pitch;
6756 if ((sy >> 16) == (last_sy >> 16))
6758 /* This source row is the same as last source row -
6759 * Copy the already stretched row. */
6760 memcpy(dbuf, dbuf - dlock.Pitch, width);
6762 else
6764 #define STRETCH_ROW(type) \
6765 do { \
6766 const type *s = (const type *)sbuf; \
6767 type *d = (type *)dbuf; \
6768 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6769 d[x] = s[sx >> 16]; \
6770 } while(0)
6772 switch(bpp)
6774 case 1:
6775 STRETCH_ROW(BYTE);
6776 break;
6777 case 2:
6778 STRETCH_ROW(WORD);
6779 break;
6780 case 4:
6781 STRETCH_ROW(DWORD);
6782 break;
6783 case 3:
6785 const BYTE *s;
6786 BYTE *d = dbuf;
6787 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6789 DWORD pixel;
6791 s = sbuf + 3 * (sx >> 16);
6792 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6793 d[0] = (pixel ) & 0xff;
6794 d[1] = (pixel >> 8) & 0xff;
6795 d[2] = (pixel >> 16) & 0xff;
6796 d += 3;
6798 break;
6800 default:
6801 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6802 hr = WINED3DERR_NOTAVAILABLE;
6803 goto error;
6805 #undef STRETCH_ROW
6807 dbuf += dlock.Pitch;
6808 last_sy = sy;
6812 else
6814 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6815 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6816 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6817 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6819 /* The color keying flags are checked for correctness in ddraw */
6820 if (flags & WINEDDBLT_KEYSRC)
6822 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6823 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6825 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6827 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6828 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6831 if (flags & WINEDDBLT_KEYDEST)
6833 /* Destination color keys are taken from the source surface! */
6834 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6835 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6837 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6839 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6840 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6843 if (bpp == 1)
6845 keymask = 0xff;
6847 else
6849 keymask = src_format->red_mask
6850 | src_format->green_mask
6851 | src_format->blue_mask;
6853 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6856 if (flags & WINEDDBLT_DDFX)
6858 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6859 LONG tmpxy;
6860 dTopLeft = dbuf;
6861 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6862 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6863 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6865 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6867 /* I don't think we need to do anything about this flag */
6868 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6870 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6872 tmp = dTopRight;
6873 dTopRight = dTopLeft;
6874 dTopLeft = tmp;
6875 tmp = dBottomRight;
6876 dBottomRight = dBottomLeft;
6877 dBottomLeft = tmp;
6878 dstxinc = dstxinc * -1;
6880 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6882 tmp = dTopLeft;
6883 dTopLeft = dBottomLeft;
6884 dBottomLeft = tmp;
6885 tmp = dTopRight;
6886 dTopRight = dBottomRight;
6887 dBottomRight = tmp;
6888 dstyinc = dstyinc * -1;
6890 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6892 /* I don't think we need to do anything about this flag */
6893 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6895 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6897 tmp = dBottomRight;
6898 dBottomRight = dTopLeft;
6899 dTopLeft = tmp;
6900 tmp = dBottomLeft;
6901 dBottomLeft = dTopRight;
6902 dTopRight = tmp;
6903 dstxinc = dstxinc * -1;
6904 dstyinc = dstyinc * -1;
6906 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6908 tmp = dTopLeft;
6909 dTopLeft = dBottomLeft;
6910 dBottomLeft = dBottomRight;
6911 dBottomRight = dTopRight;
6912 dTopRight = tmp;
6913 tmpxy = dstxinc;
6914 dstxinc = dstyinc;
6915 dstyinc = tmpxy;
6916 dstxinc = dstxinc * -1;
6918 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6920 tmp = dTopLeft;
6921 dTopLeft = dTopRight;
6922 dTopRight = dBottomRight;
6923 dBottomRight = dBottomLeft;
6924 dBottomLeft = tmp;
6925 tmpxy = dstxinc;
6926 dstxinc = dstyinc;
6927 dstyinc = tmpxy;
6928 dstyinc = dstyinc * -1;
6930 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6932 /* I don't think we need to do anything about this flag */
6933 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6935 dbuf = dTopLeft;
6936 flags &= ~(WINEDDBLT_DDFX);
6939 #define COPY_COLORKEY_FX(type) \
6940 do { \
6941 const type *s; \
6942 type *d = (type *)dbuf, *dx, tmp; \
6943 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6945 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6946 dx = d; \
6947 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6949 tmp = s[sx >> 16]; \
6950 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6951 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6953 dx[0] = tmp; \
6955 dx = (type *)(((BYTE *)dx) + dstxinc); \
6957 d = (type *)(((BYTE *)d) + dstyinc); \
6959 } while(0)
6961 switch (bpp)
6963 case 1:
6964 COPY_COLORKEY_FX(BYTE);
6965 break;
6966 case 2:
6967 COPY_COLORKEY_FX(WORD);
6968 break;
6969 case 4:
6970 COPY_COLORKEY_FX(DWORD);
6971 break;
6972 case 3:
6974 const BYTE *s;
6975 BYTE *d = dbuf, *dx;
6976 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6978 sbuf = sbase + (sy >> 16) * slock.Pitch;
6979 dx = d;
6980 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6982 DWORD pixel, dpixel = 0;
6983 s = sbuf + 3 * (sx>>16);
6984 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6985 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6986 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6987 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6989 dx[0] = (pixel ) & 0xff;
6990 dx[1] = (pixel >> 8) & 0xff;
6991 dx[2] = (pixel >> 16) & 0xff;
6993 dx += dstxinc;
6995 d += dstyinc;
6997 break;
6999 default:
7000 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7001 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7002 hr = WINED3DERR_NOTAVAILABLE;
7003 goto error;
7004 #undef COPY_COLORKEY_FX
7009 error:
7010 if (flags && FIXME_ON(d3d_surface))
7012 FIXME("\tUnsupported flags: %#x.\n", flags);
7015 release:
7016 wined3d_surface_unmap(dst_surface);
7017 if (src_surface && src_surface != dst_surface)
7018 wined3d_surface_unmap(src_surface);
7019 /* Release the converted surface, if any. */
7020 if (src_surface && src_surface != orig_src)
7021 wined3d_surface_decref(src_surface);
7023 return hr;
7026 /* Do not call while under the GL lock. */
7027 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7028 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7030 static const RECT src_rect;
7031 WINEDDBLTFX BltFx;
7033 memset(&BltFx, 0, sizeof(BltFx));
7034 BltFx.dwSize = sizeof(BltFx);
7035 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7036 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7037 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7040 /* Do not call while under the GL lock. */
7041 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7042 struct wined3d_surface *surface, const RECT *rect, float depth)
7044 FIXME("Depth filling not implemented by cpu_blit.\n");
7045 return WINED3DERR_INVALIDCALL;
7048 const struct blit_shader cpu_blit = {
7049 cpu_blit_alloc,
7050 cpu_blit_free,
7051 cpu_blit_set,
7052 cpu_blit_unset,
7053 cpu_blit_supported,
7054 cpu_blit_color_fill,
7055 cpu_blit_depth_fill,
7058 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7059 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7060 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7061 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7063 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7064 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7065 unsigned int resource_size;
7066 HRESULT hr;
7068 if (multisample_quality > 0)
7070 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7071 multisample_quality = 0;
7074 /* Quick lockable sanity check.
7075 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7076 * this function is too deep to need to care about things like this.
7077 * Levels need to be checked too, since they all affect what can be done. */
7078 switch (pool)
7080 case WINED3DPOOL_SCRATCH:
7081 if (!lockable)
7083 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7084 "which are mutually exclusive, setting lockable to TRUE.\n");
7085 lockable = TRUE;
7087 break;
7089 case WINED3DPOOL_SYSTEMMEM:
7090 if (!lockable)
7091 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7092 break;
7094 case WINED3DPOOL_MANAGED:
7095 if (usage & WINED3DUSAGE_DYNAMIC)
7096 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7097 break;
7099 case WINED3DPOOL_DEFAULT:
7100 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7101 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7102 break;
7104 default:
7105 FIXME("Unknown pool %#x.\n", pool);
7106 break;
7109 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7110 FIXME("Trying to create a render target that isn't in the default pool.\n");
7112 /* FIXME: Check that the format is supported by the device. */
7114 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7115 if (!resource_size)
7116 return WINED3DERR_INVALIDCALL;
7118 surface->surface_type = surface_type;
7120 switch (surface_type)
7122 case SURFACE_OPENGL:
7123 surface->surface_ops = &surface_ops;
7124 break;
7126 case SURFACE_GDI:
7127 surface->surface_ops = &gdi_surface_ops;
7128 break;
7130 default:
7131 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7132 return WINED3DERR_INVALIDCALL;
7135 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7136 multisample_type, multisample_quality, usage, pool, width, height, 1,
7137 resource_size, parent, parent_ops, &surface_resource_ops);
7138 if (FAILED(hr))
7140 WARN("Failed to initialize resource, returning %#x.\n", hr);
7141 return hr;
7144 /* "Standalone" surface. */
7145 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7147 surface->texture_level = level;
7148 list_init(&surface->overlays);
7150 /* Flags */
7151 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7152 if (discard)
7153 surface->flags |= SFLAG_DISCARD;
7154 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7155 surface->flags |= SFLAG_LOCKABLE;
7156 /* I'm not sure if this qualifies as a hack or as an optimization. It
7157 * seems reasonable to assume that lockable render targets will get
7158 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7159 * creation. However, the other reason we want to do this is that several
7160 * ddraw applications access surface memory while the surface isn't
7161 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7162 * future locks prevents these from crashing. */
7163 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7164 surface->flags |= SFLAG_DYNLOCK;
7166 /* Mark the texture as dirty so that it gets loaded first time around. */
7167 surface_add_dirty_rect(surface, NULL);
7168 list_init(&surface->renderbuffers);
7170 TRACE("surface %p, memory %p, size %u\n",
7171 surface, surface->resource.allocatedMemory, surface->resource.size);
7173 /* Call the private setup routine */
7174 hr = surface->surface_ops->surface_private_setup(surface);
7175 if (FAILED(hr))
7177 ERR("Private setup failed, returning %#x\n", hr);
7178 surface->surface_ops->surface_cleanup(surface);
7179 return hr;
7182 return hr;
7185 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7186 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7187 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7188 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7190 struct wined3d_surface *object;
7191 HRESULT hr;
7193 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7194 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7195 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7196 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7197 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7199 if (surface_type == SURFACE_OPENGL && !device->adapter)
7201 ERR("OpenGL surfaces are not available without OpenGL.\n");
7202 return WINED3DERR_NOTAVAILABLE;
7205 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7206 if (!object)
7208 ERR("Failed to allocate surface memory.\n");
7209 return WINED3DERR_OUTOFVIDEOMEMORY;
7212 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7213 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7214 if (FAILED(hr))
7216 WARN("Failed to initialize surface, returning %#x.\n", hr);
7217 HeapFree(GetProcessHeap(), 0, object);
7218 return hr;
7221 TRACE("Created surface %p.\n", object);
7222 *surface = object;
7224 return hr;