wined3d: Use the CPU blitter for blitting to converted surfaces.
[wine/multimedia.git] / dlls / wined3d / surface.c
blobb92a1a157c83d59a4bdef64e5c405684a92e8fd7
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)
48 || surface->rb_multisample || surface->rb_resolved
49 || !list_empty(&surface->renderbuffers))
51 struct wined3d_renderbuffer_entry *entry, *entry2;
52 const struct wined3d_gl_info *gl_info;
53 struct wined3d_context *context;
55 context = context_acquire(surface->resource.device, NULL);
56 gl_info = context->gl_info;
58 ENTER_GL();
60 if (surface->texture_name)
62 TRACE("Deleting texture %u.\n", surface->texture_name);
63 glDeleteTextures(1, &surface->texture_name);
66 if (surface->flags & SFLAG_PBO)
68 TRACE("Deleting PBO %u.\n", surface->pbo);
69 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
72 if (surface->rb_multisample)
74 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
75 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
78 if (surface->rb_resolved)
80 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
81 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
84 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
86 TRACE("Deleting renderbuffer %u.\n", entry->id);
87 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
88 HeapFree(GetProcessHeap(), 0, entry);
91 LEAVE_GL();
93 context_release(context);
96 if (surface->flags & SFLAG_DIBSECTION)
98 /* Release the DC. */
99 SelectObject(surface->hDC, surface->dib.holdbitmap);
100 DeleteDC(surface->hDC);
101 /* Release the DIB section. */
102 DeleteObject(surface->dib.DIBsection);
103 surface->dib.bitmap_data = NULL;
104 surface->resource.allocatedMemory = NULL;
107 if (surface->flags & SFLAG_USERPTR)
108 wined3d_surface_set_mem(surface, NULL);
109 if (surface->overlay_dest)
110 list_remove(&surface->overlay_entry);
112 HeapFree(GetProcessHeap(), 0, surface->palette9);
114 resource_cleanup(&surface->resource);
117 void surface_update_draw_binding(struct wined3d_surface *surface)
119 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
120 surface->draw_binding = SFLAG_INDRAWABLE;
121 else if (surface->resource.multisample_type)
122 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
123 else
124 surface->draw_binding = SFLAG_INTEXTURE;
127 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
129 TRACE("surface %p, container %p.\n", surface, container);
131 if (!container && type != WINED3D_CONTAINER_NONE)
132 ERR("Setting NULL container of type %#x.\n", type);
134 if (type == WINED3D_CONTAINER_SWAPCHAIN)
136 surface->get_drawable_size = get_drawable_size_swapchain;
138 else
140 switch (wined3d_settings.offscreen_rendering_mode)
142 case ORM_FBO:
143 surface->get_drawable_size = get_drawable_size_fbo;
144 break;
146 case ORM_BACKBUFFER:
147 surface->get_drawable_size = get_drawable_size_backbuffer;
148 break;
150 default:
151 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
152 return;
156 surface->container.type = type;
157 surface->container.u.base = container;
158 surface_update_draw_binding(surface);
161 struct blt_info
163 GLenum binding;
164 GLenum bind_target;
165 enum tex_types tex_type;
166 GLfloat coords[4][3];
169 struct float_rect
171 float l;
172 float t;
173 float r;
174 float b;
177 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
179 f->l = ((r->left * 2.0f) / w) - 1.0f;
180 f->t = ((r->top * 2.0f) / h) - 1.0f;
181 f->r = ((r->right * 2.0f) / w) - 1.0f;
182 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
185 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
187 GLfloat (*coords)[3] = info->coords;
188 struct float_rect f;
190 switch (target)
192 default:
193 FIXME("Unsupported texture target %#x\n", target);
194 /* Fall back to GL_TEXTURE_2D */
195 case GL_TEXTURE_2D:
196 info->binding = GL_TEXTURE_BINDING_2D;
197 info->bind_target = GL_TEXTURE_2D;
198 info->tex_type = tex_2d;
199 coords[0][0] = (float)rect->left / w;
200 coords[0][1] = (float)rect->top / h;
201 coords[0][2] = 0.0f;
203 coords[1][0] = (float)rect->right / w;
204 coords[1][1] = (float)rect->top / h;
205 coords[1][2] = 0.0f;
207 coords[2][0] = (float)rect->left / w;
208 coords[2][1] = (float)rect->bottom / h;
209 coords[2][2] = 0.0f;
211 coords[3][0] = (float)rect->right / w;
212 coords[3][1] = (float)rect->bottom / h;
213 coords[3][2] = 0.0f;
214 break;
216 case GL_TEXTURE_RECTANGLE_ARB:
217 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
218 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
219 info->tex_type = tex_rect;
220 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
221 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
222 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
223 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
224 break;
226 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
227 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
228 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
229 info->tex_type = tex_cube;
230 cube_coords_float(rect, w, h, &f);
232 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
233 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
234 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
235 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
236 break;
238 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
239 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
240 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
241 info->tex_type = tex_cube;
242 cube_coords_float(rect, w, h, &f);
244 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
245 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
246 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
247 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
248 break;
250 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
251 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
252 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
253 info->tex_type = tex_cube;
254 cube_coords_float(rect, w, h, &f);
256 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
257 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
258 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
259 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
260 break;
262 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
263 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
264 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
265 info->tex_type = tex_cube;
266 cube_coords_float(rect, w, h, &f);
268 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
269 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
270 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
271 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
272 break;
274 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
275 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
276 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
277 info->tex_type = tex_cube;
278 cube_coords_float(rect, w, h, &f);
280 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
281 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
282 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
283 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
284 break;
286 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
287 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
288 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
289 info->tex_type = tex_cube;
290 cube_coords_float(rect, w, h, &f);
292 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
293 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
294 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
295 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
296 break;
300 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
302 if (rect_in)
303 *rect_out = *rect_in;
304 else
306 rect_out->left = 0;
307 rect_out->top = 0;
308 rect_out->right = surface->resource.width;
309 rect_out->bottom = surface->resource.height;
313 /* GL locking and context activation is done by the caller */
314 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
315 const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
317 struct blt_info info;
319 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
321 glEnable(info.bind_target);
322 checkGLcall("glEnable(bind_target)");
324 context_bind_texture(context, info.bind_target, src_surface->texture_name);
326 /* Filtering for StretchRect */
327 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
328 wined3d_gl_mag_filter(magLookup, Filter));
329 checkGLcall("glTexParameteri");
330 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
331 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
334 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
335 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
336 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
337 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
338 checkGLcall("glTexEnvi");
340 /* Draw a quad */
341 glBegin(GL_TRIANGLE_STRIP);
342 glTexCoord3fv(info.coords[0]);
343 glVertex2i(dst_rect->left, dst_rect->top);
345 glTexCoord3fv(info.coords[1]);
346 glVertex2i(dst_rect->right, dst_rect->top);
348 glTexCoord3fv(info.coords[2]);
349 glVertex2i(dst_rect->left, dst_rect->bottom);
351 glTexCoord3fv(info.coords[3]);
352 glVertex2i(dst_rect->right, dst_rect->bottom);
353 glEnd();
355 /* Unbind the texture */
356 context_bind_texture(context, info.bind_target, 0);
358 /* We changed the filtering settings on the texture. Inform the
359 * container about this to get the filters reset properly next draw. */
360 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
362 struct wined3d_texture *texture = src_surface->container.u.texture;
363 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
364 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
365 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
366 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
370 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
372 const struct wined3d_format *format = surface->resource.format;
373 SYSTEM_INFO sysInfo;
374 BITMAPINFO *b_info;
375 int extraline = 0;
376 DWORD *masks;
377 UINT usage;
378 HDC dc;
380 TRACE("surface %p.\n", surface);
382 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
384 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
385 return WINED3DERR_INVALIDCALL;
388 switch (format->byte_count)
390 case 2:
391 case 4:
392 /* Allocate extra space to store the RGB bit masks. */
393 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
394 break;
396 case 3:
397 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
398 break;
400 default:
401 /* Allocate extra space for a palette. */
402 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
403 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
404 break;
407 if (!b_info)
408 return E_OUTOFMEMORY;
410 /* Some applications access the surface in via DWORDs, and do not take
411 * the necessary care at the end of the surface. So we need at least
412 * 4 extra bytes at the end of the surface. Check against the page size,
413 * if the last page used for the surface has at least 4 spare bytes we're
414 * safe, otherwise add an extra line to the DIB section. */
415 GetSystemInfo(&sysInfo);
416 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
418 extraline = 1;
419 TRACE("Adding an extra line to the DIB section.\n");
422 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
423 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
424 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
425 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
426 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
427 * wined3d_surface_get_pitch(surface);
428 b_info->bmiHeader.biPlanes = 1;
429 b_info->bmiHeader.biBitCount = format->byte_count * 8;
431 b_info->bmiHeader.biXPelsPerMeter = 0;
432 b_info->bmiHeader.biYPelsPerMeter = 0;
433 b_info->bmiHeader.biClrUsed = 0;
434 b_info->bmiHeader.biClrImportant = 0;
436 /* Get the bit masks */
437 masks = (DWORD *)b_info->bmiColors;
438 switch (surface->resource.format->id)
440 case WINED3DFMT_B8G8R8_UNORM:
441 usage = DIB_RGB_COLORS;
442 b_info->bmiHeader.biCompression = BI_RGB;
443 break;
445 case WINED3DFMT_B5G5R5X1_UNORM:
446 case WINED3DFMT_B5G5R5A1_UNORM:
447 case WINED3DFMT_B4G4R4A4_UNORM:
448 case WINED3DFMT_B4G4R4X4_UNORM:
449 case WINED3DFMT_B2G3R3_UNORM:
450 case WINED3DFMT_B2G3R3A8_UNORM:
451 case WINED3DFMT_R10G10B10A2_UNORM:
452 case WINED3DFMT_R8G8B8A8_UNORM:
453 case WINED3DFMT_R8G8B8X8_UNORM:
454 case WINED3DFMT_B10G10R10A2_UNORM:
455 case WINED3DFMT_B5G6R5_UNORM:
456 case WINED3DFMT_R16G16B16A16_UNORM:
457 usage = 0;
458 b_info->bmiHeader.biCompression = BI_BITFIELDS;
459 masks[0] = format->red_mask;
460 masks[1] = format->green_mask;
461 masks[2] = format->blue_mask;
462 break;
464 default:
465 /* Don't know palette */
466 b_info->bmiHeader.biCompression = BI_RGB;
467 usage = 0;
468 break;
471 if (!(dc = GetDC(0)))
473 HeapFree(GetProcessHeap(), 0, b_info);
474 return HRESULT_FROM_WIN32(GetLastError());
477 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
478 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
479 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
480 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
481 ReleaseDC(0, dc);
483 if (!surface->dib.DIBsection)
485 ERR("Failed to create DIB section.\n");
486 HeapFree(GetProcessHeap(), 0, b_info);
487 return HRESULT_FROM_WIN32(GetLastError());
490 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
491 /* Copy the existing surface to the dib section. */
492 if (surface->resource.allocatedMemory)
494 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
495 surface->resource.height * wined3d_surface_get_pitch(surface));
497 else
499 /* This is to make maps read the GL texture although memory is allocated. */
500 surface->flags &= ~SFLAG_INSYSMEM;
502 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
504 HeapFree(GetProcessHeap(), 0, b_info);
506 /* Now allocate a DC. */
507 surface->hDC = CreateCompatibleDC(0);
508 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
509 TRACE("Using wined3d palette %p.\n", surface->palette);
510 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
512 surface->flags |= SFLAG_DIBSECTION;
514 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
515 surface->resource.heapMemory = NULL;
517 return WINED3D_OK;
520 static void surface_prepare_system_memory(struct wined3d_surface *surface)
522 struct wined3d_device *device = surface->resource.device;
523 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
525 TRACE("surface %p.\n", surface);
527 /* Performance optimization: Count how often a surface is locked, if it is
528 * locked regularly do not throw away the system memory copy. This avoids
529 * the need to download the surface from OpenGL all the time. The surface
530 * is still downloaded if the OpenGL texture is changed. */
531 if (!(surface->flags & SFLAG_DYNLOCK))
533 if (++surface->lockCount > MAXLOCKCOUNT)
535 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
536 surface->flags |= SFLAG_DYNLOCK;
540 /* Create a PBO for dynamically locked surfaces but don't do it for
541 * converted or NPOT surfaces. Also don't create a PBO for systemmem
542 * surfaces. */
543 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
544 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
545 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
547 struct wined3d_context *context;
548 GLenum error;
550 context = context_acquire(device, NULL);
551 ENTER_GL();
553 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
554 error = glGetError();
555 if (!surface->pbo || error != GL_NO_ERROR)
556 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
558 TRACE("Binding PBO %u.\n", surface->pbo);
560 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
561 checkGLcall("glBindBufferARB");
563 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
564 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
565 checkGLcall("glBufferDataARB");
567 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
568 checkGLcall("glBindBufferARB");
570 /* We don't need the system memory anymore and we can't even use it for PBOs. */
571 if (!(surface->flags & SFLAG_CLIENT))
573 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
574 surface->resource.heapMemory = NULL;
576 surface->resource.allocatedMemory = NULL;
577 surface->flags |= SFLAG_PBO;
578 LEAVE_GL();
579 context_release(context);
581 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
583 /* Whatever surface we have, make sure that there is memory allocated
584 * for the downloaded copy, or a PBO to map. */
585 if (!surface->resource.heapMemory)
586 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
588 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
589 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
591 if (surface->flags & SFLAG_INSYSMEM)
592 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
596 static void surface_evict_sysmem(struct wined3d_surface *surface)
598 if (surface->flags & SFLAG_DONOTFREE)
599 return;
601 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
602 surface->resource.allocatedMemory = NULL;
603 surface->resource.heapMemory = NULL;
604 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
607 /* Context activation is done by the caller. */
608 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
609 struct wined3d_context *context, BOOL srgb)
611 struct wined3d_device *device = surface->resource.device;
612 DWORD active_sampler;
614 /* We don't need a specific texture unit, but after binding the texture
615 * the current unit is dirty. Read the unit back instead of switching to
616 * 0, this avoids messing around with the state manager's GL states. The
617 * current texture unit should always be a valid one.
619 * To be more specific, this is tricky because we can implicitly be
620 * called from sampler() in state.c. This means we can't touch anything
621 * other than whatever happens to be the currently active texture, or we
622 * would risk marking already applied sampler states dirty again. */
623 active_sampler = device->rev_tex_unit_map[context->active_texture];
625 if (active_sampler != WINED3D_UNMAPPED_STAGE)
626 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
627 surface_bind(surface, context, srgb);
630 static void surface_force_reload(struct wined3d_surface *surface)
632 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
635 static void surface_release_client_storage(struct wined3d_surface *surface)
637 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
639 ENTER_GL();
640 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
641 if (surface->texture_name)
643 surface_bind_and_dirtify(surface, context, FALSE);
644 glTexImage2D(surface->texture_target, surface->texture_level,
645 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
647 if (surface->texture_name_srgb)
649 surface_bind_and_dirtify(surface, context, TRUE);
650 glTexImage2D(surface->texture_target, surface->texture_level,
651 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
653 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
654 LEAVE_GL();
656 context_release(context);
658 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
659 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
660 surface_force_reload(surface);
663 static HRESULT surface_private_setup(struct wined3d_surface *surface)
665 /* TODO: Check against the maximum texture sizes supported by the video card. */
666 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
667 unsigned int pow2Width, pow2Height;
669 TRACE("surface %p.\n", surface);
671 surface->texture_name = 0;
672 surface->texture_target = GL_TEXTURE_2D;
674 /* Non-power2 support */
675 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
677 pow2Width = surface->resource.width;
678 pow2Height = surface->resource.height;
680 else
682 /* Find the nearest pow2 match */
683 pow2Width = pow2Height = 1;
684 while (pow2Width < surface->resource.width)
685 pow2Width <<= 1;
686 while (pow2Height < surface->resource.height)
687 pow2Height <<= 1;
689 surface->pow2Width = pow2Width;
690 surface->pow2Height = pow2Height;
692 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
694 /* TODO: Add support for non power two compressed textures. */
695 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
697 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
698 surface, surface->resource.width, surface->resource.height);
699 return WINED3DERR_NOTAVAILABLE;
703 if (pow2Width != surface->resource.width
704 || pow2Height != surface->resource.height)
706 surface->flags |= SFLAG_NONPOW2;
709 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
710 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
712 /* One of three options:
713 * 1: Do the same as we do with NPOT and scale the texture, (any
714 * texture ops would require the texture to be scaled which is
715 * potentially slow)
716 * 2: Set the texture to the maximum size (bad idea).
717 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
718 * 4: Create the surface, but allow it to be used only for DirectDraw
719 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
720 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
721 * the render target. */
722 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
724 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
725 return WINED3DERR_NOTAVAILABLE;
728 /* We should never use this surface in combination with OpenGL! */
729 TRACE("Creating an oversized surface: %ux%u.\n",
730 surface->pow2Width, surface->pow2Height);
732 else
734 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
735 * and EXT_PALETTED_TEXTURE is used in combination with texture
736 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
737 * EXT_PALETTED_TEXTURE doesn't work in combination with
738 * ARB_TEXTURE_RECTANGLE. */
739 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
740 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
741 && gl_info->supported[EXT_PALETTED_TEXTURE]
742 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
744 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
745 surface->pow2Width = surface->resource.width;
746 surface->pow2Height = surface->resource.height;
747 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
751 switch (wined3d_settings.offscreen_rendering_mode)
753 case ORM_FBO:
754 surface->get_drawable_size = get_drawable_size_fbo;
755 break;
757 case ORM_BACKBUFFER:
758 surface->get_drawable_size = get_drawable_size_backbuffer;
759 break;
761 default:
762 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
763 return WINED3DERR_INVALIDCALL;
766 surface->flags |= SFLAG_INSYSMEM;
768 return WINED3D_OK;
771 static void surface_realize_palette(struct wined3d_surface *surface)
773 struct wined3d_palette *palette = surface->palette;
775 TRACE("surface %p.\n", surface);
777 if (!palette) return;
779 if (surface->resource.format->id == WINED3DFMT_P8_UINT
780 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
782 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
784 /* Make sure the texture is up to date. This call doesn't do
785 * anything if the texture is already up to date. */
786 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
788 /* We want to force a palette refresh, so mark the drawable as not being up to date */
789 if (!surface_is_offscreen(surface))
790 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
792 else
794 if (!(surface->flags & SFLAG_INSYSMEM))
796 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
797 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
799 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
803 if (surface->flags & SFLAG_DIBSECTION)
805 RGBQUAD col[256];
806 unsigned int i;
808 TRACE("Updating the DC's palette.\n");
810 for (i = 0; i < 256; ++i)
812 col[i].rgbRed = palette->palents[i].peRed;
813 col[i].rgbGreen = palette->palents[i].peGreen;
814 col[i].rgbBlue = palette->palents[i].peBlue;
815 col[i].rgbReserved = 0;
817 SetDIBColorTable(surface->hDC, 0, 256, col);
820 /* Propagate the changes to the drawable when we have a palette. */
821 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
822 surface_load_location(surface, surface->draw_binding, NULL);
825 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
827 HRESULT hr;
829 /* If there's no destination surface there is nothing to do. */
830 if (!surface->overlay_dest)
831 return WINED3D_OK;
833 /* Blt calls ModifyLocation on the dest surface, which in turn calls
834 * DrawOverlay to update the overlay. Prevent an endless recursion. */
835 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
836 return WINED3D_OK;
838 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
839 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
840 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
841 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
843 return hr;
846 static void surface_preload(struct wined3d_surface *surface)
848 TRACE("surface %p.\n", surface);
850 surface_internal_preload(surface, SRGB_ANY);
853 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
855 struct wined3d_device *device = surface->resource.device;
856 const RECT *pass_rect = rect;
858 TRACE("surface %p, rect %s, flags %#x.\n",
859 surface, wine_dbgstr_rect(rect), flags);
861 if (flags & WINED3DLOCK_DISCARD)
863 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
864 surface_prepare_system_memory(surface);
865 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
867 else
869 /* surface_load_location() does not check if the rectangle specifies
870 * the full surface. Most callers don't need that, so do it here. */
871 if (rect && !rect->top && !rect->left
872 && rect->right == surface->resource.width
873 && rect->bottom == surface->resource.height)
874 pass_rect = NULL;
876 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
877 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
878 || surface == device->fb.render_targets[0])))
879 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
882 if (surface->flags & SFLAG_PBO)
884 const struct wined3d_gl_info *gl_info;
885 struct wined3d_context *context;
887 context = context_acquire(device, NULL);
888 gl_info = context->gl_info;
890 ENTER_GL();
891 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
892 checkGLcall("glBindBufferARB");
894 /* This shouldn't happen but could occur if some other function
895 * didn't handle the PBO properly. */
896 if (surface->resource.allocatedMemory)
897 ERR("The surface already has PBO memory allocated.\n");
899 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
900 checkGLcall("glMapBufferARB");
902 /* Make sure the PBO isn't set anymore in order not to break non-PBO
903 * calls. */
904 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
905 checkGLcall("glBindBufferARB");
907 LEAVE_GL();
908 context_release(context);
911 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
913 if (!rect)
914 surface_add_dirty_rect(surface, NULL);
915 else
917 WINED3DBOX b;
919 b.Left = rect->left;
920 b.Top = rect->top;
921 b.Right = rect->right;
922 b.Bottom = rect->bottom;
923 b.Front = 0;
924 b.Back = 1;
925 surface_add_dirty_rect(surface, &b);
930 static void surface_unmap(struct wined3d_surface *surface)
932 struct wined3d_device *device = surface->resource.device;
933 BOOL fullsurface;
935 TRACE("surface %p.\n", surface);
937 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
939 if (surface->flags & SFLAG_PBO)
941 const struct wined3d_gl_info *gl_info;
942 struct wined3d_context *context;
944 TRACE("Freeing PBO memory.\n");
946 context = context_acquire(device, NULL);
947 gl_info = context->gl_info;
949 ENTER_GL();
950 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
951 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
952 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
953 checkGLcall("glUnmapBufferARB");
954 LEAVE_GL();
955 context_release(context);
957 surface->resource.allocatedMemory = NULL;
960 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
962 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
964 TRACE("Not dirtified, nothing to do.\n");
965 goto done;
968 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
969 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
971 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
973 static BOOL warned = FALSE;
974 if (!warned)
976 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
977 warned = TRUE;
979 goto done;
982 if (!surface->dirtyRect.left && !surface->dirtyRect.top
983 && surface->dirtyRect.right == surface->resource.width
984 && surface->dirtyRect.bottom == surface->resource.height)
986 fullsurface = TRUE;
988 else
990 /* TODO: Proper partial rectangle tracking. */
991 fullsurface = FALSE;
992 surface->flags |= SFLAG_INSYSMEM;
995 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
997 /* Partial rectangle tracking is not commonly implemented, it is only
998 * done for render targets. INSYSMEM was set before to tell
999 * surface_load_location() where to read the rectangle from.
1000 * Indrawable is set because all modifications from the partial
1001 * sysmem copy are written back to the drawable, thus the surface is
1002 * merged again in the drawable. The sysmem copy is not fully up to
1003 * date because only a subrectangle was read in Map(). */
1004 if (!fullsurface)
1006 surface_modify_location(surface, surface->draw_binding, TRUE);
1007 surface_evict_sysmem(surface);
1010 surface->dirtyRect.left = surface->resource.width;
1011 surface->dirtyRect.top = surface->resource.height;
1012 surface->dirtyRect.right = 0;
1013 surface->dirtyRect.bottom = 0;
1015 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1017 FIXME("Depth / stencil buffer locking is not implemented.\n");
1020 done:
1021 /* Overlays have to be redrawn manually after changes with the GL implementation */
1022 if (surface->overlay_dest)
1023 surface->surface_ops->surface_draw_overlay(surface);
1026 static HRESULT surface_getdc(struct wined3d_surface *surface)
1028 WINED3DLOCKED_RECT lock;
1029 HRESULT hr;
1031 TRACE("surface %p.\n", surface);
1033 /* Create a DIB section if there isn't a dc yet. */
1034 if (!surface->hDC)
1036 if (surface->flags & SFLAG_CLIENT)
1038 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1039 surface_release_client_storage(surface);
1041 hr = surface_create_dib_section(surface);
1042 if (FAILED(hr))
1043 return WINED3DERR_INVALIDCALL;
1045 /* Use the DIB section from now on if we are not using a PBO. */
1046 if (!(surface->flags & SFLAG_PBO))
1047 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1050 /* Map the surface. */
1051 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1052 if (FAILED(hr))
1053 ERR("Map failed, hr %#x.\n", hr);
1055 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1056 * activates the allocatedMemory. */
1057 if (surface->flags & SFLAG_PBO)
1058 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
1060 return hr;
1063 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1065 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1066 return FALSE;
1067 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1068 return FALSE;
1069 return TRUE;
1072 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1073 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1075 const struct wined3d_gl_info *gl_info;
1076 struct wined3d_context *context;
1077 DWORD src_mask, dst_mask;
1078 GLbitfield gl_mask;
1080 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1081 device, src_surface, wine_dbgstr_rect(src_rect),
1082 dst_surface, wine_dbgstr_rect(dst_rect));
1084 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1085 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1087 if (src_mask != dst_mask)
1089 ERR("Incompatible formats %s and %s.\n",
1090 debug_d3dformat(src_surface->resource.format->id),
1091 debug_d3dformat(dst_surface->resource.format->id));
1092 return;
1095 if (!src_mask)
1097 ERR("Not a depth / stencil format: %s.\n",
1098 debug_d3dformat(src_surface->resource.format->id));
1099 return;
1102 gl_mask = 0;
1103 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1104 gl_mask |= GL_DEPTH_BUFFER_BIT;
1105 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1106 gl_mask |= GL_STENCIL_BUFFER_BIT;
1108 /* Make sure the locations are up-to-date. Loading the destination
1109 * surface isn't required if the entire surface is overwritten. */
1110 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1111 if (!surface_is_full_rect(dst_surface, dst_rect))
1112 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1114 context = context_acquire(device, NULL);
1115 if (!context->valid)
1117 context_release(context);
1118 WARN("Invalid context, skipping blit.\n");
1119 return;
1122 gl_info = context->gl_info;
1124 ENTER_GL();
1126 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1127 glReadBuffer(GL_NONE);
1128 checkGLcall("glReadBuffer()");
1129 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1131 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1132 context_set_draw_buffer(context, GL_NONE);
1133 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1135 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1137 glDepthMask(GL_TRUE);
1138 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1140 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1142 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1144 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1145 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1147 glStencilMask(~0U);
1148 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1151 glDisable(GL_SCISSOR_TEST);
1152 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1154 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1155 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1156 checkGLcall("glBlitFramebuffer()");
1158 LEAVE_GL();
1160 if (wined3d_settings.strict_draw_ordering)
1161 wglFlush(); /* Flush to ensure ordering across contexts. */
1163 context_release(context);
1166 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1167 * Depth / stencil is not supported. */
1168 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1169 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1170 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1172 const struct wined3d_gl_info *gl_info;
1173 struct wined3d_context *context;
1174 RECT src_rect, dst_rect;
1175 GLenum gl_filter;
1176 GLenum buffer;
1178 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1179 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1180 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1181 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1182 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1184 src_rect = *src_rect_in;
1185 dst_rect = *dst_rect_in;
1187 switch (filter)
1189 case WINED3DTEXF_LINEAR:
1190 gl_filter = GL_LINEAR;
1191 break;
1193 default:
1194 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1195 case WINED3DTEXF_NONE:
1196 case WINED3DTEXF_POINT:
1197 gl_filter = GL_NEAREST;
1198 break;
1201 /* Resolve the source surface first if needed. */
1202 if (src_location == SFLAG_INRB_MULTISAMPLE
1203 && (src_surface->resource.format->id != dst_surface->resource.format->id
1204 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1205 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1206 src_location = SFLAG_INRB_RESOLVED;
1208 /* Make sure the locations are up-to-date. Loading the destination
1209 * surface isn't required if the entire surface is overwritten. (And is
1210 * in fact harmful if we're being called by surface_load_location() with
1211 * the purpose of loading the destination surface.) */
1212 surface_load_location(src_surface, src_location, NULL);
1213 if (!surface_is_full_rect(dst_surface, &dst_rect))
1214 surface_load_location(dst_surface, dst_location, NULL);
1216 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1217 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1218 else context = context_acquire(device, NULL);
1220 if (!context->valid)
1222 context_release(context);
1223 WARN("Invalid context, skipping blit.\n");
1224 return;
1227 gl_info = context->gl_info;
1229 if (src_location == SFLAG_INDRAWABLE)
1231 TRACE("Source surface %p is onscreen.\n", src_surface);
1232 buffer = surface_get_gl_buffer(src_surface);
1233 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1235 else
1237 TRACE("Source surface %p is offscreen.\n", src_surface);
1238 buffer = GL_COLOR_ATTACHMENT0;
1241 ENTER_GL();
1242 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1243 glReadBuffer(buffer);
1244 checkGLcall("glReadBuffer()");
1245 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1246 LEAVE_GL();
1248 if (dst_location == SFLAG_INDRAWABLE)
1250 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1251 buffer = surface_get_gl_buffer(dst_surface);
1252 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1254 else
1256 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1257 buffer = GL_COLOR_ATTACHMENT0;
1260 ENTER_GL();
1261 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1262 context_set_draw_buffer(context, buffer);
1263 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1264 context_invalidate_state(context, STATE_FRAMEBUFFER);
1266 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1267 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1268 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1269 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1270 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1272 glDisable(GL_SCISSOR_TEST);
1273 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1275 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1276 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1277 checkGLcall("glBlitFramebuffer()");
1279 LEAVE_GL();
1281 if (wined3d_settings.strict_draw_ordering
1282 || (dst_location == SFLAG_INDRAWABLE
1283 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1284 wglFlush();
1286 context_release(context);
1289 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1290 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1291 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1293 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1294 return FALSE;
1296 /* Source and/or destination need to be on the GL side */
1297 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1298 return FALSE;
1300 switch (blit_op)
1302 case WINED3D_BLIT_OP_COLOR_BLIT:
1303 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1304 return FALSE;
1305 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1306 return FALSE;
1307 break;
1309 case WINED3D_BLIT_OP_DEPTH_BLIT:
1310 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1311 return FALSE;
1312 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1313 return FALSE;
1314 break;
1316 default:
1317 return FALSE;
1320 if (!(src_format->id == dst_format->id
1321 || (is_identity_fixup(src_format->color_fixup)
1322 && is_identity_fixup(dst_format->color_fixup))))
1323 return FALSE;
1325 return TRUE;
1328 /* This function checks if the primary render target uses the 8bit paletted format. */
1329 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1331 if (device->fb.render_targets && device->fb.render_targets[0])
1333 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1334 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1335 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1336 return TRUE;
1338 return FALSE;
1341 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1342 DWORD color, WINED3DCOLORVALUE *float_color)
1344 const struct wined3d_format *format = surface->resource.format;
1345 const struct wined3d_device *device = surface->resource.device;
1347 switch (format->id)
1349 case WINED3DFMT_P8_UINT:
1350 if (surface->palette)
1352 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1353 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1354 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1356 else
1358 float_color->r = 0.0f;
1359 float_color->g = 0.0f;
1360 float_color->b = 0.0f;
1362 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1363 break;
1365 case WINED3DFMT_B5G6R5_UNORM:
1366 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1367 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1368 float_color->b = (color & 0x1f) / 31.0f;
1369 float_color->a = 1.0f;
1370 break;
1372 case WINED3DFMT_B8G8R8_UNORM:
1373 case WINED3DFMT_B8G8R8X8_UNORM:
1374 float_color->r = D3DCOLOR_R(color);
1375 float_color->g = D3DCOLOR_G(color);
1376 float_color->b = D3DCOLOR_B(color);
1377 float_color->a = 1.0f;
1378 break;
1380 case WINED3DFMT_B8G8R8A8_UNORM:
1381 float_color->r = D3DCOLOR_R(color);
1382 float_color->g = D3DCOLOR_G(color);
1383 float_color->b = D3DCOLOR_B(color);
1384 float_color->a = D3DCOLOR_A(color);
1385 break;
1387 default:
1388 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1389 return FALSE;
1392 return TRUE;
1395 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1397 const struct wined3d_format *format = surface->resource.format;
1399 switch (format->id)
1401 case WINED3DFMT_S1_UINT_D15_UNORM:
1402 *float_depth = depth / (float)0x00007fff;
1403 break;
1405 case WINED3DFMT_D16_UNORM:
1406 *float_depth = depth / (float)0x0000ffff;
1407 break;
1409 case WINED3DFMT_D24_UNORM_S8_UINT:
1410 case WINED3DFMT_X8D24_UNORM:
1411 *float_depth = depth / (float)0x00ffffff;
1412 break;
1414 case WINED3DFMT_D32_UNORM:
1415 *float_depth = depth / (float)0xffffffff;
1416 break;
1418 default:
1419 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1420 return FALSE;
1423 return TRUE;
1426 /* Do not call while under the GL lock. */
1427 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1429 const struct wined3d_resource *resource = &surface->resource;
1430 struct wined3d_device *device = resource->device;
1431 const struct blit_shader *blitter;
1433 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1434 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1435 if (!blitter)
1437 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1438 return WINED3DERR_INVALIDCALL;
1441 return blitter->depth_fill(device, surface, rect, depth);
1444 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1445 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1447 struct wined3d_device *device = src_surface->resource.device;
1449 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1450 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1451 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1452 return WINED3DERR_INVALIDCALL;
1454 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1456 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1457 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1458 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1460 return WINED3D_OK;
1463 /* Do not call while under the GL lock. */
1464 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1465 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1466 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1468 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1469 struct wined3d_device *device = dst_surface->resource.device;
1470 DWORD src_ds_flags, dst_ds_flags;
1471 RECT src_rect, dst_rect;
1473 static const DWORD simple_blit = WINEDDBLT_ASYNC
1474 | WINEDDBLT_COLORFILL
1475 | WINEDDBLT_WAIT
1476 | WINEDDBLT_DEPTHFILL
1477 | WINEDDBLT_DONOTWAIT;
1479 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1480 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1481 flags, fx, debug_d3dtexturefiltertype(filter));
1482 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1484 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1486 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1487 return WINEDDERR_SURFACEBUSY;
1490 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1492 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1493 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1494 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1495 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1496 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1498 /* The destination rect can be out of bounds on the condition
1499 * that a clipper is set for the surface. */
1500 if (dst_surface->clipper)
1501 FIXME("Blit clipping not implemented.\n");
1502 else
1503 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1504 return WINEDDERR_INVALIDRECT;
1507 if (src_surface)
1509 surface_get_rect(src_surface, src_rect_in, &src_rect);
1511 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1512 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1513 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1514 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1515 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1517 WARN("Application gave us bad source rectangle for Blt.\n");
1518 return WINEDDERR_INVALIDRECT;
1521 else
1523 memset(&src_rect, 0, sizeof(src_rect));
1526 if (!fx || !(fx->dwDDFX))
1527 flags &= ~WINEDDBLT_DDFX;
1529 if (flags & WINEDDBLT_WAIT)
1530 flags &= ~WINEDDBLT_WAIT;
1532 if (flags & WINEDDBLT_ASYNC)
1534 static unsigned int once;
1536 if (!once++)
1537 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1538 flags &= ~WINEDDBLT_ASYNC;
1541 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1542 if (flags & WINEDDBLT_DONOTWAIT)
1544 static unsigned int once;
1546 if (!once++)
1547 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1548 flags &= ~WINEDDBLT_DONOTWAIT;
1551 if (!device->d3d_initialized)
1553 WARN("D3D not initialized, using fallback.\n");
1554 goto cpu;
1557 /* We want to avoid invalidating the sysmem location for converted
1558 * surfaces, since otherwise we'd have to convert the data back when
1559 * locking them. */
1560 if (dst_surface->flags & SFLAG_CONVERTED)
1562 WARN("Converted surface, using CPU blit.\n");
1563 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1566 if (flags & ~simple_blit)
1568 WARN("Using fallback for complex blit (%#x).\n", flags);
1569 goto fallback;
1572 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1573 src_swapchain = src_surface->container.u.swapchain;
1574 else
1575 src_swapchain = NULL;
1577 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1578 dst_swapchain = dst_surface->container.u.swapchain;
1579 else
1580 dst_swapchain = NULL;
1582 /* This isn't strictly needed. FBO blits for example could deal with
1583 * cross-swapchain blits by first downloading the source to a texture
1584 * before switching to the destination context. We just have this here to
1585 * not have to deal with the issue, since cross-swapchain blits should be
1586 * rare. */
1587 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1589 FIXME("Using fallback for cross-swapchain blit.\n");
1590 goto fallback;
1593 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1594 if (src_surface)
1595 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1596 else
1597 src_ds_flags = 0;
1599 if (src_ds_flags || dst_ds_flags)
1601 if (flags & WINEDDBLT_DEPTHFILL)
1603 float depth;
1605 TRACE("Depth fill.\n");
1607 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1608 return WINED3DERR_INVALIDCALL;
1610 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1611 return WINED3D_OK;
1613 else
1615 /* Accessing depth / stencil surfaces is supposed to fail while in
1616 * a scene, except for fills, which seem to work. */
1617 if (device->inScene)
1619 WARN("Rejecting depth / stencil access while in scene.\n");
1620 return WINED3DERR_INVALIDCALL;
1623 if (src_ds_flags != dst_ds_flags)
1625 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1626 return WINED3DERR_INVALIDCALL;
1629 if (src_rect.top || src_rect.left
1630 || src_rect.bottom != src_surface->resource.height
1631 || src_rect.right != src_surface->resource.width)
1633 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1634 wine_dbgstr_rect(&src_rect));
1635 return WINED3DERR_INVALIDCALL;
1638 if (dst_rect.top || dst_rect.left
1639 || dst_rect.bottom != dst_surface->resource.height
1640 || dst_rect.right != dst_surface->resource.width)
1642 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1643 wine_dbgstr_rect(&src_rect));
1644 return WINED3DERR_INVALIDCALL;
1647 if (src_surface->resource.height != dst_surface->resource.height
1648 || src_surface->resource.width != dst_surface->resource.width)
1650 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1651 return WINED3DERR_INVALIDCALL;
1654 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1655 return WINED3D_OK;
1658 else
1660 if (flags & WINEDDBLT_COLORFILL)
1662 WINED3DCOLORVALUE color;
1664 TRACE("Color fill.\n");
1666 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1667 goto fallback;
1669 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1670 return WINED3D_OK;
1672 else
1674 TRACE("Color blit.\n");
1676 /* Use present for back -> front blits. The idea behind this is
1677 * that present is potentially faster than a blit, in particular
1678 * when FBO blits aren't available. Some ddraw applications like
1679 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1680 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1681 * applications can't blit directly to the frontbuffer. */
1682 if (dst_swapchain && dst_swapchain->back_buffers
1683 && dst_surface == dst_swapchain->front_buffer
1684 && src_surface == dst_swapchain->back_buffers[0])
1686 WINED3DSWAPEFFECT swap_effect = dst_swapchain->presentParms.SwapEffect;
1688 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1690 /* Set the swap effect to COPY, we don't want the backbuffer
1691 * to become undefined. */
1692 dst_swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
1693 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1694 dst_swapchain->presentParms.SwapEffect = swap_effect;
1696 return WINED3D_OK;
1699 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1700 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1701 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1703 TRACE("Using FBO blit.\n");
1705 surface_blt_fbo(device, filter,
1706 src_surface, src_surface->draw_binding, &src_rect,
1707 dst_surface, dst_surface->draw_binding, &dst_rect);
1708 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1709 return WINED3D_OK;
1712 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1713 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1714 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1716 TRACE("Using arbfp blit.\n");
1718 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1719 return WINED3D_OK;
1724 fallback:
1726 /* Special cases for render targets. */
1727 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1728 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1730 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1731 src_surface, &src_rect, flags, fx, filter)))
1732 return WINED3D_OK;
1735 cpu:
1737 /* For the rest call the X11 surface implementation. For render targets
1738 * this should be implemented OpenGL accelerated in BltOverride, other
1739 * blits are rather rare. */
1740 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1743 /* Do not call while under the GL lock. */
1744 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1745 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1747 RECT src_rect, dst_rect;
1748 DWORD flags = 0;
1750 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1751 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1753 surface_get_rect(src_surface, src_rect_in, &src_rect);
1755 dst_rect.left = dst_x;
1756 dst_rect.top = dst_y;
1757 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1758 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1760 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1761 flags |= WINEDDBLT_KEYSRC;
1762 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1763 flags |= WINEDDBLT_KEYDEST;
1764 if (trans & WINEDDBLTFAST_WAIT)
1765 flags |= WINEDDBLT_WAIT;
1766 if (trans & WINEDDBLTFAST_DONOTWAIT)
1767 flags |= WINEDDBLT_DONOTWAIT;
1769 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1772 /* Context activation is done by the caller. */
1773 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1775 if (!surface->resource.heapMemory)
1777 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1778 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1779 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1782 ENTER_GL();
1783 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1784 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1785 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1786 surface->resource.size, surface->resource.allocatedMemory));
1787 checkGLcall("glGetBufferSubDataARB");
1788 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1789 checkGLcall("glDeleteBuffersARB");
1790 LEAVE_GL();
1792 surface->pbo = 0;
1793 surface->flags &= ~SFLAG_PBO;
1796 /* Do not call while under the GL lock. */
1797 static void surface_unload(struct wined3d_resource *resource)
1799 struct wined3d_surface *surface = surface_from_resource(resource);
1800 struct wined3d_renderbuffer_entry *entry, *entry2;
1801 struct wined3d_device *device = resource->device;
1802 const struct wined3d_gl_info *gl_info;
1803 struct wined3d_context *context;
1805 TRACE("surface %p.\n", surface);
1807 if (resource->pool == WINED3DPOOL_DEFAULT)
1809 /* Default pool resources are supposed to be destroyed before Reset is called.
1810 * Implicit resources stay however. So this means we have an implicit render target
1811 * or depth stencil. The content may be destroyed, but we still have to tear down
1812 * opengl resources, so we cannot leave early.
1814 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1815 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1816 * or the depth stencil into an FBO the texture or render buffer will be removed
1817 * and all flags get lost
1819 surface_init_sysmem(surface);
1821 else
1823 /* Load the surface into system memory */
1824 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1825 surface_modify_location(surface, surface->draw_binding, FALSE);
1827 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1828 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1829 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1831 context = context_acquire(device, NULL);
1832 gl_info = context->gl_info;
1834 /* Destroy PBOs, but load them into real sysmem before */
1835 if (surface->flags & SFLAG_PBO)
1836 surface_remove_pbo(surface, gl_info);
1838 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1839 * all application-created targets the application has to release the surface
1840 * before calling _Reset
1842 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1844 ENTER_GL();
1845 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1846 LEAVE_GL();
1847 list_remove(&entry->entry);
1848 HeapFree(GetProcessHeap(), 0, entry);
1850 list_init(&surface->renderbuffers);
1851 surface->current_renderbuffer = NULL;
1853 ENTER_GL();
1855 /* If we're in a texture, the texture name belongs to the texture.
1856 * Otherwise, destroy it. */
1857 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1859 glDeleteTextures(1, &surface->texture_name);
1860 surface->texture_name = 0;
1861 glDeleteTextures(1, &surface->texture_name_srgb);
1862 surface->texture_name_srgb = 0;
1864 if (surface->rb_multisample)
1866 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1867 surface->rb_multisample = 0;
1869 if (surface->rb_resolved)
1871 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1872 surface->rb_resolved = 0;
1875 LEAVE_GL();
1877 context_release(context);
1879 resource_unload(resource);
1882 static const struct wined3d_resource_ops surface_resource_ops =
1884 surface_unload,
1887 static const struct wined3d_surface_ops surface_ops =
1889 surface_private_setup,
1890 surface_cleanup,
1891 surface_realize_palette,
1892 surface_draw_overlay,
1893 surface_preload,
1894 surface_map,
1895 surface_unmap,
1896 surface_getdc,
1899 /*****************************************************************************
1900 * Initializes the GDI surface, aka creates the DIB section we render to
1901 * The DIB section creation is done by calling GetDC, which will create the
1902 * section and releasing the dc to allow the app to use it. The dib section
1903 * will stay until the surface is released
1905 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1906 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1907 * avoid confusion in the shared surface code.
1909 * Returns:
1910 * WINED3D_OK on success
1911 * The return values of called methods on failure
1913 *****************************************************************************/
1914 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1916 HRESULT hr;
1918 TRACE("surface %p.\n", surface);
1920 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1922 ERR("Overlays not yet supported by GDI surfaces.\n");
1923 return WINED3DERR_INVALIDCALL;
1926 /* Sysmem textures have memory already allocated - release it,
1927 * this avoids an unnecessary memcpy. */
1928 hr = surface_create_dib_section(surface);
1929 if (SUCCEEDED(hr))
1931 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1932 surface->resource.heapMemory = NULL;
1933 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1936 /* We don't mind the nonpow2 stuff in GDI. */
1937 surface->pow2Width = surface->resource.width;
1938 surface->pow2Height = surface->resource.height;
1940 return WINED3D_OK;
1943 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1945 TRACE("surface %p.\n", surface);
1947 if (surface->flags & SFLAG_DIBSECTION)
1949 /* Release the DC. */
1950 SelectObject(surface->hDC, surface->dib.holdbitmap);
1951 DeleteDC(surface->hDC);
1952 /* Release the DIB section. */
1953 DeleteObject(surface->dib.DIBsection);
1954 surface->dib.bitmap_data = NULL;
1955 surface->resource.allocatedMemory = NULL;
1958 if (surface->flags & SFLAG_USERPTR)
1959 wined3d_surface_set_mem(surface, NULL);
1960 if (surface->overlay_dest)
1961 list_remove(&surface->overlay_entry);
1963 HeapFree(GetProcessHeap(), 0, surface->palette9);
1965 resource_cleanup(&surface->resource);
1968 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1970 struct wined3d_palette *palette = surface->palette;
1972 TRACE("surface %p.\n", surface);
1974 if (!palette) return;
1976 if (surface->flags & SFLAG_DIBSECTION)
1978 RGBQUAD col[256];
1979 unsigned int i;
1981 TRACE("Updating the DC's palette.\n");
1983 for (i = 0; i < 256; ++i)
1985 col[i].rgbRed = palette->palents[i].peRed;
1986 col[i].rgbGreen = palette->palents[i].peGreen;
1987 col[i].rgbBlue = palette->palents[i].peBlue;
1988 col[i].rgbReserved = 0;
1990 SetDIBColorTable(surface->hDC, 0, 256, col);
1993 /* Update the image because of the palette change. Some games like e.g.
1994 * Red Alert call SetEntries a lot to implement fading. */
1995 /* Tell the swapchain to update the screen. */
1996 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1998 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1999 if (surface == swapchain->front_buffer)
2001 x11_copy_to_screen(swapchain, NULL);
2006 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2008 FIXME("GDI surfaces can't draw overlays yet.\n");
2009 return E_FAIL;
2012 static void gdi_surface_preload(struct wined3d_surface *surface)
2014 TRACE("surface %p.\n", surface);
2016 ERR("Preloading GDI surfaces is not supported.\n");
2019 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2021 TRACE("surface %p, rect %s, flags %#x.\n",
2022 surface, wine_dbgstr_rect(rect), flags);
2024 if (!surface->resource.allocatedMemory)
2026 /* This happens on gdi surfaces if the application set a user pointer
2027 * and resets it. Recreate the DIB section. */
2028 surface_create_dib_section(surface);
2029 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2033 static void gdi_surface_unmap(struct wined3d_surface *surface)
2035 TRACE("surface %p.\n", surface);
2037 /* Tell the swapchain to update the screen. */
2038 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2040 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2041 if (surface == swapchain->front_buffer)
2043 x11_copy_to_screen(swapchain, &surface->lockedRect);
2047 memset(&surface->lockedRect, 0, sizeof(RECT));
2050 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2052 WINED3DLOCKED_RECT lock;
2053 HRESULT hr;
2055 TRACE("surface %p.\n", surface);
2057 /* Should have a DIB section already. */
2058 if (!(surface->flags & SFLAG_DIBSECTION))
2060 WARN("DC not supported on this surface\n");
2061 return WINED3DERR_INVALIDCALL;
2064 /* Map the surface. */
2065 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2066 if (FAILED(hr))
2067 ERR("Map failed, hr %#x.\n", hr);
2069 return hr;
2072 static const struct wined3d_surface_ops gdi_surface_ops =
2074 gdi_surface_private_setup,
2075 surface_gdi_cleanup,
2076 gdi_surface_realize_palette,
2077 gdi_surface_draw_overlay,
2078 gdi_surface_preload,
2079 gdi_surface_map,
2080 gdi_surface_unmap,
2081 gdi_surface_getdc,
2084 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2086 GLuint *name;
2087 DWORD flag;
2089 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2091 if(srgb)
2093 name = &surface->texture_name_srgb;
2094 flag = SFLAG_INSRGBTEX;
2096 else
2098 name = &surface->texture_name;
2099 flag = SFLAG_INTEXTURE;
2102 if (!*name && new_name)
2104 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2105 * surface has no texture name yet. See if we can get rid of this. */
2106 if (surface->flags & flag)
2107 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2108 surface_modify_location(surface, flag, FALSE);
2111 *name = new_name;
2112 surface_force_reload(surface);
2115 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2117 TRACE("surface %p, target %#x.\n", surface, target);
2119 if (surface->texture_target != target)
2121 if (target == GL_TEXTURE_RECTANGLE_ARB)
2123 surface->flags &= ~SFLAG_NORMCOORD;
2125 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2127 surface->flags |= SFLAG_NORMCOORD;
2130 surface->texture_target = target;
2131 surface_force_reload(surface);
2134 /* Context activation is done by the caller. */
2135 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2137 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2139 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2141 struct wined3d_texture *texture = surface->container.u.texture;
2143 TRACE("Passing to container (%p).\n", texture);
2144 texture->texture_ops->texture_bind(texture, context, srgb);
2146 else
2148 if (surface->texture_level)
2150 ERR("Standalone surface %p is non-zero texture level %u.\n",
2151 surface, surface->texture_level);
2154 if (srgb)
2155 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2157 ENTER_GL();
2159 if (!surface->texture_name)
2161 glGenTextures(1, &surface->texture_name);
2162 checkGLcall("glGenTextures");
2164 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2166 context_bind_texture(context, surface->texture_target, surface->texture_name);
2167 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2168 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2169 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2170 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2171 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2172 checkGLcall("glTexParameteri");
2174 else
2176 context_bind_texture(context, surface->texture_target, surface->texture_name);
2179 LEAVE_GL();
2183 /* This call just downloads data, the caller is responsible for binding the
2184 * correct texture. */
2185 /* Context activation is done by the caller. */
2186 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2188 const struct wined3d_format *format = surface->resource.format;
2190 /* Only support read back of converted P8 surfaces. */
2191 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2193 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2194 return;
2197 ENTER_GL();
2199 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2201 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2202 surface, surface->texture_level, format->glFormat, format->glType,
2203 surface->resource.allocatedMemory);
2205 if (surface->flags & SFLAG_PBO)
2207 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2208 checkGLcall("glBindBufferARB");
2209 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2210 checkGLcall("glGetCompressedTexImageARB");
2211 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2212 checkGLcall("glBindBufferARB");
2214 else
2216 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2217 surface->texture_level, surface->resource.allocatedMemory));
2218 checkGLcall("glGetCompressedTexImageARB");
2221 LEAVE_GL();
2223 else
2225 void *mem;
2226 GLenum gl_format = format->glFormat;
2227 GLenum gl_type = format->glType;
2228 int src_pitch = 0;
2229 int dst_pitch = 0;
2231 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2232 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2234 gl_format = GL_ALPHA;
2235 gl_type = GL_UNSIGNED_BYTE;
2238 if (surface->flags & SFLAG_NONPOW2)
2240 unsigned char alignment = surface->resource.device->surface_alignment;
2241 src_pitch = format->byte_count * surface->pow2Width;
2242 dst_pitch = wined3d_surface_get_pitch(surface);
2243 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2244 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2246 else
2248 mem = surface->resource.allocatedMemory;
2251 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2252 surface, surface->texture_level, gl_format, gl_type, mem);
2254 if (surface->flags & SFLAG_PBO)
2256 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2257 checkGLcall("glBindBufferARB");
2259 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2260 checkGLcall("glGetTexImage");
2262 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2263 checkGLcall("glBindBufferARB");
2265 else
2267 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2268 checkGLcall("glGetTexImage");
2270 LEAVE_GL();
2272 if (surface->flags & SFLAG_NONPOW2)
2274 const BYTE *src_data;
2275 BYTE *dst_data;
2276 UINT y;
2278 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2279 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2280 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2282 * We're doing this...
2284 * instead of boxing the texture :
2285 * |<-texture width ->| -->pow2width| /\
2286 * |111111111111111111| | |
2287 * |222 Texture 222222| boxed empty | texture height
2288 * |3333 Data 33333333| | |
2289 * |444444444444444444| | \/
2290 * ----------------------------------- |
2291 * | boxed empty | boxed empty | pow2height
2292 * | | | \/
2293 * -----------------------------------
2296 * we're repacking the data to the expected texture width
2298 * |<-texture width ->| -->pow2width| /\
2299 * |111111111111111111222222222222222| |
2300 * |222333333333333333333444444444444| texture height
2301 * |444444 | |
2302 * | | \/
2303 * | | |
2304 * | empty | pow2height
2305 * | | \/
2306 * -----------------------------------
2308 * == is the same as
2310 * |<-texture width ->| /\
2311 * |111111111111111111|
2312 * |222222222222222222|texture height
2313 * |333333333333333333|
2314 * |444444444444444444| \/
2315 * --------------------
2317 * this also means that any references to allocatedMemory should work with the data as if were a
2318 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2320 * internally the texture is still stored in a boxed format so any references to textureName will
2321 * get a boxed texture with width pow2width and not a texture of width resource.width.
2323 * Performance should not be an issue, because applications normally do not lock the surfaces when
2324 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2325 * and doesn't have to be re-read. */
2326 src_data = mem;
2327 dst_data = surface->resource.allocatedMemory;
2328 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2329 for (y = 1; y < surface->resource.height; ++y)
2331 /* skip the first row */
2332 src_data += src_pitch;
2333 dst_data += dst_pitch;
2334 memcpy(dst_data, src_data, dst_pitch);
2337 HeapFree(GetProcessHeap(), 0, mem);
2341 /* Surface has now been downloaded */
2342 surface->flags |= SFLAG_INSYSMEM;
2345 /* This call just uploads data, the caller is responsible for binding the
2346 * correct texture. */
2347 /* Context activation is done by the caller. */
2348 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2349 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2350 BOOL srgb, const struct wined3d_bo_address *data)
2352 UINT update_w = src_rect->right - src_rect->left;
2353 UINT update_h = src_rect->bottom - src_rect->top;
2355 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2356 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2357 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2359 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2360 update_h *= format->heightscale;
2362 ENTER_GL();
2364 if (data->buffer_object)
2366 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2367 checkGLcall("glBindBufferARB");
2370 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2372 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2373 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2374 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2375 const BYTE *addr = data->addr;
2376 GLenum internal;
2378 addr += (src_rect->top / format->block_height) * src_pitch;
2379 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2381 if (srgb)
2382 internal = format->glGammaInternal;
2383 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2384 internal = format->rtInternal;
2385 else
2386 internal = format->glInternal;
2388 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2389 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2390 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2392 if (row_length == src_pitch)
2394 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2395 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2397 else
2399 UINT row, y;
2401 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2402 * can't use the unpack row length like below. */
2403 for (row = 0, y = dst_point->y; row < row_count; ++row)
2405 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2406 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2407 y += format->block_height;
2408 addr += src_pitch;
2411 checkGLcall("glCompressedTexSubImage2DARB");
2413 else
2415 const BYTE *addr = data->addr;
2417 addr += src_rect->top * src_w * format->byte_count;
2418 addr += src_rect->left * format->byte_count;
2420 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2421 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2422 update_w, update_h, format->glFormat, format->glType, addr);
2424 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2425 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2426 update_w, update_h, format->glFormat, format->glType, addr);
2427 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2428 checkGLcall("glTexSubImage2D");
2431 if (data->buffer_object)
2433 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2434 checkGLcall("glBindBufferARB");
2437 LEAVE_GL();
2439 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2441 struct wined3d_device *device = surface->resource.device;
2442 unsigned int i;
2444 for (i = 0; i < device->context_count; ++i)
2446 context_surface_update(device->contexts[i], surface);
2451 /* This call just allocates the texture, the caller is responsible for binding
2452 * the correct texture. */
2453 /* Context activation is done by the caller. */
2454 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2455 const struct wined3d_format *format, BOOL srgb)
2457 BOOL enable_client_storage = FALSE;
2458 GLsizei width = surface->pow2Width;
2459 GLsizei height = surface->pow2Height;
2460 const BYTE *mem = NULL;
2461 GLenum internal;
2463 if (srgb)
2465 internal = format->glGammaInternal;
2467 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2469 internal = format->rtInternal;
2471 else
2473 internal = format->glInternal;
2476 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2478 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",
2479 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2480 internal, width, height, format->glFormat, format->glType);
2482 ENTER_GL();
2484 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2486 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2487 || !surface->resource.allocatedMemory)
2489 /* In some cases we want to disable client storage.
2490 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2491 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2492 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2493 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2495 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2496 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2497 surface->flags &= ~SFLAG_CLIENT;
2498 enable_client_storage = TRUE;
2500 else
2502 surface->flags |= SFLAG_CLIENT;
2504 /* Point OpenGL to our allocated texture memory. Do not use
2505 * resource.allocatedMemory here because it might point into a
2506 * PBO. Instead use heapMemory, but get the alignment right. */
2507 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2508 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2512 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2514 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2515 internal, width, height, 0, surface->resource.size, mem));
2516 checkGLcall("glCompressedTexImage2DARB");
2518 else
2520 glTexImage2D(surface->texture_target, surface->texture_level,
2521 internal, width, height, 0, format->glFormat, format->glType, mem);
2522 checkGLcall("glTexImage2D");
2525 if(enable_client_storage) {
2526 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2527 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2529 LEAVE_GL();
2532 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2533 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2534 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2535 /* GL locking is done by the caller */
2536 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2538 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2539 struct wined3d_renderbuffer_entry *entry;
2540 GLuint renderbuffer = 0;
2541 unsigned int src_width, src_height;
2542 unsigned int width, height;
2544 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2546 width = rt->pow2Width;
2547 height = rt->pow2Height;
2549 else
2551 width = surface->pow2Width;
2552 height = surface->pow2Height;
2555 src_width = surface->pow2Width;
2556 src_height = surface->pow2Height;
2558 /* A depth stencil smaller than the render target is not valid */
2559 if (width > src_width || height > src_height) return;
2561 /* Remove any renderbuffer set if the sizes match */
2562 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2563 || (width == src_width && height == src_height))
2565 surface->current_renderbuffer = NULL;
2566 return;
2569 /* Look if we've already got a renderbuffer of the correct dimensions */
2570 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2572 if (entry->width == width && entry->height == height)
2574 renderbuffer = entry->id;
2575 surface->current_renderbuffer = entry;
2576 break;
2580 if (!renderbuffer)
2582 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2583 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2584 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2585 surface->resource.format->glInternal, width, height);
2587 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2588 entry->width = width;
2589 entry->height = height;
2590 entry->id = renderbuffer;
2591 list_add_head(&surface->renderbuffers, &entry->entry);
2593 surface->current_renderbuffer = entry;
2596 checkGLcall("set_compatible_renderbuffer");
2599 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2601 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2603 TRACE("surface %p.\n", surface);
2605 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2607 ERR("Surface %p is not on a swapchain.\n", surface);
2608 return GL_NONE;
2611 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2613 if (swapchain->render_to_fbo)
2615 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2616 return GL_COLOR_ATTACHMENT0;
2618 TRACE("Returning GL_BACK\n");
2619 return GL_BACK;
2621 else if (surface == swapchain->front_buffer)
2623 TRACE("Returning GL_FRONT\n");
2624 return GL_FRONT;
2627 FIXME("Higher back buffer, returning GL_BACK\n");
2628 return GL_BACK;
2631 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2632 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2634 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2636 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2637 /* No partial locking for textures yet. */
2638 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2640 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2641 if (dirty_rect)
2643 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2644 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2645 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2646 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2648 else
2650 surface->dirtyRect.left = 0;
2651 surface->dirtyRect.top = 0;
2652 surface->dirtyRect.right = surface->resource.width;
2653 surface->dirtyRect.bottom = surface->resource.height;
2656 /* if the container is a texture then mark it dirty. */
2657 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2659 TRACE("Passing to container.\n");
2660 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2664 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2666 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2667 BOOL ck_changed;
2669 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2671 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2673 ERR("Not supported on scratch surfaces.\n");
2674 return WINED3DERR_INVALIDCALL;
2677 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2679 /* Reload if either the texture and sysmem have different ideas about the
2680 * color key, or the actual key values changed. */
2681 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2682 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2683 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2685 TRACE("Reloading because of color keying\n");
2686 /* To perform the color key conversion we need a sysmem copy of
2687 * the surface. Make sure we have it. */
2689 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2690 /* Make sure the texture is reloaded because of the color key change,
2691 * this kills performance though :( */
2692 /* TODO: This is not necessarily needed with hw palettized texture support. */
2693 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2694 /* Switching color keying on / off may change the internal format. */
2695 if (ck_changed)
2696 surface_force_reload(surface);
2698 else if (!(surface->flags & flag))
2700 TRACE("Reloading because surface is dirty.\n");
2702 else
2704 TRACE("surface is already in texture\n");
2705 return WINED3D_OK;
2708 /* No partial locking for textures yet. */
2709 surface_load_location(surface, flag, NULL);
2710 surface_evict_sysmem(surface);
2712 return WINED3D_OK;
2715 /* See also float_16_to_32() in wined3d_private.h */
2716 static inline unsigned short float_32_to_16(const float *in)
2718 int exp = 0;
2719 float tmp = fabsf(*in);
2720 unsigned int mantissa;
2721 unsigned short ret;
2723 /* Deal with special numbers */
2724 if (*in == 0.0f)
2725 return 0x0000;
2726 if (isnan(*in))
2727 return 0x7c01;
2728 if (isinf(*in))
2729 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2731 if (tmp < powf(2, 10))
2735 tmp = tmp * 2.0f;
2736 exp--;
2737 } while (tmp < powf(2, 10));
2739 else if (tmp >= powf(2, 11))
2743 tmp /= 2.0f;
2744 exp++;
2745 } while (tmp >= powf(2, 11));
2748 mantissa = (unsigned int)tmp;
2749 if (tmp - mantissa >= 0.5f)
2750 ++mantissa; /* Round to nearest, away from zero. */
2752 exp += 10; /* Normalize the mantissa. */
2753 exp += 15; /* Exponent is encoded with excess 15. */
2755 if (exp > 30) /* too big */
2757 ret = 0x7c00; /* INF */
2759 else if (exp <= 0)
2761 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2762 while (exp <= 0)
2764 mantissa = mantissa >> 1;
2765 ++exp;
2767 ret = mantissa & 0x3ff;
2769 else
2771 ret = (exp << 10) | (mantissa & 0x3ff);
2774 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2775 return ret;
2778 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2780 ULONG refcount;
2782 TRACE("Surface %p, container %p of type %#x.\n",
2783 surface, surface->container.u.base, surface->container.type);
2785 switch (surface->container.type)
2787 case WINED3D_CONTAINER_TEXTURE:
2788 return wined3d_texture_incref(surface->container.u.texture);
2790 case WINED3D_CONTAINER_SWAPCHAIN:
2791 return wined3d_swapchain_incref(surface->container.u.swapchain);
2793 default:
2794 ERR("Unhandled container type %#x.\n", surface->container.type);
2795 case WINED3D_CONTAINER_NONE:
2796 break;
2799 refcount = InterlockedIncrement(&surface->resource.ref);
2800 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2802 return refcount;
2805 /* Do not call while under the GL lock. */
2806 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2808 ULONG refcount;
2810 TRACE("Surface %p, container %p of type %#x.\n",
2811 surface, surface->container.u.base, surface->container.type);
2813 switch (surface->container.type)
2815 case WINED3D_CONTAINER_TEXTURE:
2816 return wined3d_texture_decref(surface->container.u.texture);
2818 case WINED3D_CONTAINER_SWAPCHAIN:
2819 return wined3d_swapchain_decref(surface->container.u.swapchain);
2821 default:
2822 ERR("Unhandled container type %#x.\n", surface->container.type);
2823 case WINED3D_CONTAINER_NONE:
2824 break;
2827 refcount = InterlockedDecrement(&surface->resource.ref);
2828 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2830 if (!refcount)
2832 surface->surface_ops->surface_cleanup(surface);
2833 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2835 TRACE("Destroyed surface %p.\n", surface);
2836 HeapFree(GetProcessHeap(), 0, surface);
2839 return refcount;
2842 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2844 return resource_set_priority(&surface->resource, priority);
2847 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2849 return resource_get_priority(&surface->resource);
2852 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2854 TRACE("surface %p.\n", surface);
2856 surface->surface_ops->surface_preload(surface);
2859 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2861 TRACE("surface %p.\n", surface);
2863 return surface->resource.parent;
2866 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2868 TRACE("surface %p.\n", surface);
2870 return &surface->resource;
2873 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2875 TRACE("surface %p, flags %#x.\n", surface, flags);
2877 switch (flags)
2879 case WINEDDGBS_CANBLT:
2880 case WINEDDGBS_ISBLTDONE:
2881 return WINED3D_OK;
2883 default:
2884 return WINED3DERR_INVALIDCALL;
2888 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2890 TRACE("surface %p, flags %#x.\n", surface, flags);
2892 /* XXX: DDERR_INVALIDSURFACETYPE */
2894 switch (flags)
2896 case WINEDDGFS_CANFLIP:
2897 case WINEDDGFS_ISFLIPDONE:
2898 return WINED3D_OK;
2900 default:
2901 return WINED3DERR_INVALIDCALL;
2905 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2907 TRACE("surface %p.\n", surface);
2909 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2910 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2913 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2915 TRACE("surface %p.\n", surface);
2917 /* So far we don't lose anything :) */
2918 surface->flags &= ~SFLAG_LOST;
2919 return WINED3D_OK;
2922 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2924 TRACE("surface %p, palette %p.\n", surface, palette);
2926 if (surface->palette == palette)
2928 TRACE("Nop palette change.\n");
2929 return WINED3D_OK;
2932 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2933 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2935 surface->palette = palette;
2937 if (palette)
2939 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2940 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2942 surface->surface_ops->surface_realize_palette(surface);
2945 return WINED3D_OK;
2948 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2949 DWORD flags, const WINEDDCOLORKEY *color_key)
2951 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2953 if (flags & WINEDDCKEY_COLORSPACE)
2955 FIXME(" colorkey value not supported (%08x) !\n", flags);
2956 return WINED3DERR_INVALIDCALL;
2959 /* Dirtify the surface, but only if a key was changed. */
2960 if (color_key)
2962 switch (flags & ~WINEDDCKEY_COLORSPACE)
2964 case WINEDDCKEY_DESTBLT:
2965 surface->DestBltCKey = *color_key;
2966 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2967 break;
2969 case WINEDDCKEY_DESTOVERLAY:
2970 surface->DestOverlayCKey = *color_key;
2971 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2972 break;
2974 case WINEDDCKEY_SRCOVERLAY:
2975 surface->SrcOverlayCKey = *color_key;
2976 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2977 break;
2979 case WINEDDCKEY_SRCBLT:
2980 surface->SrcBltCKey = *color_key;
2981 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2982 break;
2985 else
2987 switch (flags & ~WINEDDCKEY_COLORSPACE)
2989 case WINEDDCKEY_DESTBLT:
2990 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2991 break;
2993 case WINEDDCKEY_DESTOVERLAY:
2994 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2995 break;
2997 case WINEDDCKEY_SRCOVERLAY:
2998 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2999 break;
3001 case WINEDDCKEY_SRCBLT:
3002 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3003 break;
3007 return WINED3D_OK;
3010 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3012 TRACE("surface %p.\n", surface);
3014 return surface->palette;
3017 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3019 const struct wined3d_format *format = surface->resource.format;
3020 DWORD pitch;
3022 TRACE("surface %p.\n", surface);
3024 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3026 /* Since compressed formats are block based, pitch means the amount of
3027 * bytes to the next row of block rather than the next row of pixels. */
3028 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3029 pitch = row_block_count * format->block_byte_count;
3031 else
3033 unsigned char alignment = surface->resource.device->surface_alignment;
3034 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3035 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3038 TRACE("Returning %u.\n", pitch);
3040 return pitch;
3043 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3045 TRACE("surface %p, mem %p.\n", surface, mem);
3047 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3049 WARN("Surface is locked or the DC is in use.\n");
3050 return WINED3DERR_INVALIDCALL;
3053 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3054 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3056 ERR("Not supported on render targets.\n");
3057 return WINED3DERR_INVALIDCALL;
3060 if (mem && mem != surface->resource.allocatedMemory)
3062 void *release = NULL;
3064 /* Do I have to copy the old surface content? */
3065 if (surface->flags & SFLAG_DIBSECTION)
3067 SelectObject(surface->hDC, surface->dib.holdbitmap);
3068 DeleteDC(surface->hDC);
3069 /* Release the DIB section. */
3070 DeleteObject(surface->dib.DIBsection);
3071 surface->dib.bitmap_data = NULL;
3072 surface->resource.allocatedMemory = NULL;
3073 surface->hDC = NULL;
3074 surface->flags &= ~SFLAG_DIBSECTION;
3076 else if (!(surface->flags & SFLAG_USERPTR))
3078 release = surface->resource.heapMemory;
3079 surface->resource.heapMemory = NULL;
3081 surface->resource.allocatedMemory = mem;
3082 surface->flags |= SFLAG_USERPTR;
3084 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3085 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3087 /* For client textures OpenGL has to be notified. */
3088 if (surface->flags & SFLAG_CLIENT)
3089 surface_release_client_storage(surface);
3091 /* Now free the old memory if any. */
3092 HeapFree(GetProcessHeap(), 0, release);
3094 else if (surface->flags & SFLAG_USERPTR)
3096 /* HeapMemory should be NULL already. */
3097 if (surface->resource.heapMemory)
3098 ERR("User pointer surface has heap memory allocated.\n");
3100 if (!mem)
3102 surface->resource.allocatedMemory = NULL;
3103 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3105 if (surface->flags & SFLAG_CLIENT)
3106 surface_release_client_storage(surface);
3108 surface_prepare_system_memory(surface);
3111 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3114 return WINED3D_OK;
3117 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3119 LONG w, h;
3121 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3123 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3125 WARN("Not an overlay surface.\n");
3126 return WINEDDERR_NOTAOVERLAYSURFACE;
3129 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3130 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3131 surface->overlay_destrect.left = x;
3132 surface->overlay_destrect.top = y;
3133 surface->overlay_destrect.right = x + w;
3134 surface->overlay_destrect.bottom = y + h;
3136 surface->surface_ops->surface_draw_overlay(surface);
3138 return WINED3D_OK;
3141 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3143 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3145 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3147 TRACE("Not an overlay surface.\n");
3148 return WINEDDERR_NOTAOVERLAYSURFACE;
3151 if (!surface->overlay_dest)
3153 TRACE("Overlay not visible.\n");
3154 *x = 0;
3155 *y = 0;
3156 return WINEDDERR_OVERLAYNOTVISIBLE;
3159 *x = surface->overlay_destrect.left;
3160 *y = surface->overlay_destrect.top;
3162 TRACE("Returning position %d, %d.\n", *x, *y);
3164 return WINED3D_OK;
3167 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3168 DWORD flags, struct wined3d_surface *ref)
3170 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3172 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3174 TRACE("Not an overlay surface.\n");
3175 return WINEDDERR_NOTAOVERLAYSURFACE;
3178 return WINED3D_OK;
3181 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3182 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3184 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3185 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3187 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3189 WARN("Not an overlay surface.\n");
3190 return WINEDDERR_NOTAOVERLAYSURFACE;
3192 else if (!dst_surface)
3194 WARN("Dest surface is NULL.\n");
3195 return WINED3DERR_INVALIDCALL;
3198 if (src_rect)
3200 surface->overlay_srcrect = *src_rect;
3202 else
3204 surface->overlay_srcrect.left = 0;
3205 surface->overlay_srcrect.top = 0;
3206 surface->overlay_srcrect.right = surface->resource.width;
3207 surface->overlay_srcrect.bottom = surface->resource.height;
3210 if (dst_rect)
3212 surface->overlay_destrect = *dst_rect;
3214 else
3216 surface->overlay_destrect.left = 0;
3217 surface->overlay_destrect.top = 0;
3218 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3219 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3222 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3224 list_remove(&surface->overlay_entry);
3227 if (flags & WINEDDOVER_SHOW)
3229 if (surface->overlay_dest != dst_surface)
3231 surface->overlay_dest = dst_surface;
3232 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3235 else if (flags & WINEDDOVER_HIDE)
3237 /* tests show that the rectangles are erased on hide */
3238 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3239 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3240 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3241 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3242 surface->overlay_dest = NULL;
3245 surface->surface_ops->surface_draw_overlay(surface);
3247 return WINED3D_OK;
3250 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3252 TRACE("surface %p, clipper %p.\n", surface, clipper);
3254 surface->clipper = clipper;
3256 return WINED3D_OK;
3259 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3261 TRACE("surface %p.\n", surface);
3263 return surface->clipper;
3266 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3268 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3270 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3272 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3274 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3275 return WINED3DERR_INVALIDCALL;
3278 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3279 surface->pow2Width, surface->pow2Height);
3280 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3281 surface->resource.format = format;
3283 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3284 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3285 format->glFormat, format->glInternal, format->glType);
3287 return WINED3D_OK;
3290 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3291 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3293 unsigned short *dst_s;
3294 const float *src_f;
3295 unsigned int x, y;
3297 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3299 for (y = 0; y < h; ++y)
3301 src_f = (const float *)(src + y * pitch_in);
3302 dst_s = (unsigned short *) (dst + y * pitch_out);
3303 for (x = 0; x < w; ++x)
3305 dst_s[x] = float_32_to_16(src_f + x);
3310 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3311 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3313 static const unsigned char convert_5to8[] =
3315 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3316 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3317 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3318 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3320 static const unsigned char convert_6to8[] =
3322 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3323 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3324 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3325 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3326 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3327 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3328 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3329 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3331 unsigned int x, y;
3333 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3335 for (y = 0; y < h; ++y)
3337 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3338 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3339 for (x = 0; x < w; ++x)
3341 WORD pixel = src_line[x];
3342 dst_line[x] = 0xff000000
3343 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3344 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3345 | convert_5to8[(pixel & 0x001f)];
3350 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3351 * in both cases we're just setting the X / Alpha channel to 0xff. */
3352 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3353 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3355 unsigned int x, y;
3357 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3359 for (y = 0; y < h; ++y)
3361 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3362 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3364 for (x = 0; x < w; ++x)
3366 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3371 static inline BYTE cliptobyte(int x)
3373 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3376 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3377 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3379 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3380 unsigned int x, y;
3382 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3384 for (y = 0; y < h; ++y)
3386 const BYTE *src_line = src + y * pitch_in;
3387 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3388 for (x = 0; x < w; ++x)
3390 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3391 * C = Y - 16; D = U - 128; E = V - 128;
3392 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3393 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3394 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3395 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3396 * U and V are shared between the pixels. */
3397 if (!(x & 1)) /* For every even pixel, read new U and V. */
3399 d = (int) src_line[1] - 128;
3400 e = (int) src_line[3] - 128;
3401 r2 = 409 * e + 128;
3402 g2 = - 100 * d - 208 * e + 128;
3403 b2 = 516 * d + 128;
3405 c2 = 298 * ((int) src_line[0] - 16);
3406 dst_line[x] = 0xff000000
3407 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3408 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3409 | cliptobyte((c2 + b2) >> 8); /* blue */
3410 /* Scale RGB values to 0..255 range,
3411 * then clip them if still not in range (may be negative),
3412 * then shift them within DWORD if necessary. */
3413 src_line += 2;
3418 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3419 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3421 unsigned int x, y;
3422 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3424 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3426 for (y = 0; y < h; ++y)
3428 const BYTE *src_line = src + y * pitch_in;
3429 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3430 for (x = 0; x < w; ++x)
3432 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3433 * C = Y - 16; D = U - 128; E = V - 128;
3434 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3435 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3436 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3437 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3438 * U and V are shared between the pixels. */
3439 if (!(x & 1)) /* For every even pixel, read new U and V. */
3441 d = (int) src_line[1] - 128;
3442 e = (int) src_line[3] - 128;
3443 r2 = 409 * e + 128;
3444 g2 = - 100 * d - 208 * e + 128;
3445 b2 = 516 * d + 128;
3447 c2 = 298 * ((int) src_line[0] - 16);
3448 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3449 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3450 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3451 /* Scale RGB values to 0..255 range,
3452 * then clip them if still not in range (may be negative),
3453 * then shift them within DWORD if necessary. */
3454 src_line += 2;
3459 struct d3dfmt_convertor_desc
3461 enum wined3d_format_id from, to;
3462 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3465 static const struct d3dfmt_convertor_desc convertors[] =
3467 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3468 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3469 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3470 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3471 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3472 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3475 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3476 enum wined3d_format_id to)
3478 unsigned int i;
3480 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3482 if (convertors[i].from == from && convertors[i].to == to)
3483 return &convertors[i];
3486 return NULL;
3489 /*****************************************************************************
3490 * surface_convert_format
3492 * Creates a duplicate of a surface in a different format. Is used by Blt to
3493 * blit between surfaces with different formats.
3495 * Parameters
3496 * source: Source surface
3497 * fmt: Requested destination format
3499 *****************************************************************************/
3500 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3502 const struct d3dfmt_convertor_desc *conv;
3503 WINED3DLOCKED_RECT lock_src, lock_dst;
3504 struct wined3d_surface *ret = NULL;
3505 HRESULT hr;
3507 conv = find_convertor(source->resource.format->id, to_fmt);
3508 if (!conv)
3510 FIXME("Cannot find a conversion function from format %s to %s.\n",
3511 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3512 return NULL;
3515 wined3d_surface_create(source->resource.device, source->resource.width,
3516 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3517 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3518 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3519 if (!ret)
3521 ERR("Failed to create a destination surface for conversion.\n");
3522 return NULL;
3525 memset(&lock_src, 0, sizeof(lock_src));
3526 memset(&lock_dst, 0, sizeof(lock_dst));
3528 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3529 if (FAILED(hr))
3531 ERR("Failed to lock the source surface.\n");
3532 wined3d_surface_decref(ret);
3533 return NULL;
3535 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3536 if (FAILED(hr))
3538 ERR("Failed to lock the destination surface.\n");
3539 wined3d_surface_unmap(source);
3540 wined3d_surface_decref(ret);
3541 return NULL;
3544 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3545 source->resource.width, source->resource.height);
3547 wined3d_surface_unmap(ret);
3548 wined3d_surface_unmap(source);
3550 return ret;
3553 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3554 unsigned int bpp, UINT pitch, DWORD color)
3556 BYTE *first;
3557 int x, y;
3559 /* Do first row */
3561 #define COLORFILL_ROW(type) \
3562 do { \
3563 type *d = (type *)buf; \
3564 for (x = 0; x < width; ++x) \
3565 d[x] = (type)color; \
3566 } while(0)
3568 switch (bpp)
3570 case 1:
3571 COLORFILL_ROW(BYTE);
3572 break;
3574 case 2:
3575 COLORFILL_ROW(WORD);
3576 break;
3578 case 3:
3580 BYTE *d = buf;
3581 for (x = 0; x < width; ++x, d += 3)
3583 d[0] = (color ) & 0xFF;
3584 d[1] = (color >> 8) & 0xFF;
3585 d[2] = (color >> 16) & 0xFF;
3587 break;
3589 case 4:
3590 COLORFILL_ROW(DWORD);
3591 break;
3593 default:
3594 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3595 return WINED3DERR_NOTAVAILABLE;
3598 #undef COLORFILL_ROW
3600 /* Now copy first row. */
3601 first = buf;
3602 for (y = 1; y < height; ++y)
3604 buf += pitch;
3605 memcpy(buf, first, width * bpp);
3608 return WINED3D_OK;
3611 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3613 TRACE("surface %p.\n", surface);
3615 if (!(surface->flags & SFLAG_LOCKED))
3617 WARN("Trying to unmap unmapped surface.\n");
3618 return WINEDDERR_NOTLOCKED;
3620 surface->flags &= ~SFLAG_LOCKED;
3622 surface->surface_ops->surface_unmap(surface);
3624 return WINED3D_OK;
3627 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3628 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3630 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3631 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3633 if (surface->flags & SFLAG_LOCKED)
3635 WARN("Surface is already mapped.\n");
3636 return WINED3DERR_INVALIDCALL;
3638 surface->flags |= SFLAG_LOCKED;
3640 if (!(surface->flags & SFLAG_LOCKABLE))
3641 WARN("Trying to lock unlockable surface.\n");
3643 surface->surface_ops->surface_map(surface, rect, flags);
3645 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3647 if (!rect)
3649 locked_rect->pBits = surface->resource.allocatedMemory;
3650 surface->lockedRect.left = 0;
3651 surface->lockedRect.top = 0;
3652 surface->lockedRect.right = surface->resource.width;
3653 surface->lockedRect.bottom = surface->resource.height;
3655 else
3657 const struct wined3d_format *format = surface->resource.format;
3659 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3661 /* Compressed textures are block based, so calculate the offset of
3662 * the block that contains the top-left pixel of the locked rectangle. */
3663 locked_rect->pBits = surface->resource.allocatedMemory
3664 + ((rect->top / format->block_height) * locked_rect->Pitch)
3665 + ((rect->left / format->block_width) * format->block_byte_count);
3667 else
3669 locked_rect->pBits = surface->resource.allocatedMemory
3670 + (locked_rect->Pitch * rect->top)
3671 + (rect->left * format->byte_count);
3673 surface->lockedRect.left = rect->left;
3674 surface->lockedRect.top = rect->top;
3675 surface->lockedRect.right = rect->right;
3676 surface->lockedRect.bottom = rect->bottom;
3679 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3680 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3682 return WINED3D_OK;
3685 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3687 HRESULT hr;
3689 TRACE("surface %p, dc %p.\n", surface, dc);
3691 if (surface->flags & SFLAG_USERPTR)
3693 ERR("Not supported on surfaces with application-provided memory.\n");
3694 return WINEDDERR_NODC;
3697 /* Give more detailed info for ddraw. */
3698 if (surface->flags & SFLAG_DCINUSE)
3699 return WINEDDERR_DCALREADYCREATED;
3701 /* Can't GetDC if the surface is locked. */
3702 if (surface->flags & SFLAG_LOCKED)
3703 return WINED3DERR_INVALIDCALL;
3705 hr = surface->surface_ops->surface_getdc(surface);
3706 if (FAILED(hr))
3707 return hr;
3709 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3710 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3712 /* GetDC on palettized formats is unsupported in D3D9, and the method
3713 * is missing in D3D8, so this should only be used for DX <=7
3714 * surfaces (with non-device palettes). */
3715 const PALETTEENTRY *pal = NULL;
3717 if (surface->palette)
3719 pal = surface->palette->palents;
3721 else
3723 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3724 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3726 if (dds_primary && dds_primary->palette)
3727 pal = dds_primary->palette->palents;
3730 if (pal)
3732 RGBQUAD col[256];
3733 unsigned int i;
3735 for (i = 0; i < 256; ++i)
3737 col[i].rgbRed = pal[i].peRed;
3738 col[i].rgbGreen = pal[i].peGreen;
3739 col[i].rgbBlue = pal[i].peBlue;
3740 col[i].rgbReserved = 0;
3742 SetDIBColorTable(surface->hDC, 0, 256, col);
3746 surface->flags |= SFLAG_DCINUSE;
3748 *dc = surface->hDC;
3749 TRACE("Returning dc %p.\n", *dc);
3751 return WINED3D_OK;
3754 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3756 TRACE("surface %p, dc %p.\n", surface, dc);
3758 if (!(surface->flags & SFLAG_DCINUSE))
3759 return WINEDDERR_NODC;
3761 if (surface->hDC != dc)
3763 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3764 dc, surface->hDC);
3765 return WINEDDERR_NODC;
3768 /* Copy the contents of the DIB over to the PBO. */
3769 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3770 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3772 /* We locked first, so unlock now. */
3773 wined3d_surface_unmap(surface);
3775 surface->flags &= ~SFLAG_DCINUSE;
3777 return WINED3D_OK;
3780 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3782 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3784 if (flags)
3786 static UINT once;
3787 if (!once++)
3788 FIXME("Ignoring flags %#x.\n", flags);
3789 else
3790 WARN("Ignoring flags %#x.\n", flags);
3793 /* FIXME: This will also prevent overlay flips, since overlays aren't on
3794 * a swapchain either. */
3795 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3797 ERR("Flipped surface is not on a swapchain.\n");
3798 return WINEDDERR_NOTFLIPPABLE;
3801 /* Flipping is only supported on render targets and overlays. */
3802 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3804 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3805 return WINEDDERR_NOTFLIPPABLE;
3808 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
3810 flip_surface(surface, override);
3812 /* Update the overlay if it is visible */
3813 if (surface->overlay_dest)
3814 return surface->surface_ops->surface_draw_overlay(surface);
3815 else
3816 return WINED3D_OK;
3819 return wined3d_surface_blt(surface, NULL, override, NULL, 0, NULL, WINED3DTEXF_POINT);
3822 /* Do not call while under the GL lock. */
3823 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3825 struct wined3d_device *device = surface->resource.device;
3827 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3829 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3831 struct wined3d_texture *texture = surface->container.u.texture;
3833 TRACE("Passing to container (%p).\n", texture);
3834 texture->texture_ops->texture_preload(texture, srgb);
3836 else
3838 struct wined3d_context *context;
3840 TRACE("(%p) : About to load surface\n", surface);
3842 /* TODO: Use already acquired context when possible. */
3843 context = context_acquire(device, NULL);
3845 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3846 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3848 if (palette9_changed(surface))
3850 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3851 /* TODO: This is not necessarily needed with hw palettized texture support */
3852 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3853 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3854 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3858 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3860 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3862 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3863 GLclampf tmp;
3864 tmp = 0.9f;
3865 ENTER_GL();
3866 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3867 LEAVE_GL();
3870 context_release(context);
3874 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3876 if (!surface->resource.allocatedMemory)
3878 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3879 surface->resource.size + RESOURCE_ALIGNMENT);
3880 if (!surface->resource.heapMemory)
3882 ERR("Out of memory\n");
3883 return FALSE;
3885 surface->resource.allocatedMemory =
3886 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3888 else
3890 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3893 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3895 return TRUE;
3898 /* Read the framebuffer back into the surface */
3899 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3901 struct wined3d_device *device = surface->resource.device;
3902 const struct wined3d_gl_info *gl_info;
3903 struct wined3d_context *context;
3904 BYTE *mem;
3905 GLint fmt;
3906 GLint type;
3907 BYTE *row, *top, *bottom;
3908 int i;
3909 BOOL bpp;
3910 RECT local_rect;
3911 BOOL srcIsUpsideDown;
3912 GLint rowLen = 0;
3913 GLint skipPix = 0;
3914 GLint skipRow = 0;
3916 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3917 static BOOL warned = FALSE;
3918 if(!warned) {
3919 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3920 warned = TRUE;
3922 return;
3925 context = context_acquire(device, surface);
3926 context_apply_blit_state(context, device);
3927 gl_info = context->gl_info;
3929 ENTER_GL();
3931 /* Select the correct read buffer, and give some debug output.
3932 * There is no need to keep track of the current read buffer or reset it, every part of the code
3933 * that reads sets the read buffer as desired.
3935 if (surface_is_offscreen(surface))
3937 /* Mapping the primary render target which is not on a swapchain.
3938 * Read from the back buffer. */
3939 TRACE("Mapping offscreen render target.\n");
3940 glReadBuffer(device->offscreenBuffer);
3941 srcIsUpsideDown = TRUE;
3943 else
3945 /* Onscreen surfaces are always part of a swapchain */
3946 GLenum buffer = surface_get_gl_buffer(surface);
3947 TRACE("Mapping %#x buffer.\n", buffer);
3948 glReadBuffer(buffer);
3949 checkGLcall("glReadBuffer");
3950 srcIsUpsideDown = FALSE;
3953 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3954 if (!rect)
3956 local_rect.left = 0;
3957 local_rect.top = 0;
3958 local_rect.right = surface->resource.width;
3959 local_rect.bottom = surface->resource.height;
3961 else
3963 local_rect = *rect;
3965 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3967 switch (surface->resource.format->id)
3969 case WINED3DFMT_P8_UINT:
3971 if (primary_render_target_is_p8(device))
3973 /* In case of P8 render targets the index is stored in the alpha component */
3974 fmt = GL_ALPHA;
3975 type = GL_UNSIGNED_BYTE;
3976 mem = dest;
3977 bpp = surface->resource.format->byte_count;
3979 else
3981 /* GL can't return palettized data, so read ARGB pixels into a
3982 * separate block of memory and convert them into palettized format
3983 * in software. Slow, but if the app means to use palettized render
3984 * targets and locks it...
3986 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3987 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3988 * for the color channels when palettizing the colors.
3990 fmt = GL_RGB;
3991 type = GL_UNSIGNED_BYTE;
3992 pitch *= 3;
3993 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3994 if (!mem)
3996 ERR("Out of memory\n");
3997 LEAVE_GL();
3998 return;
4000 bpp = surface->resource.format->byte_count * 3;
4003 break;
4005 default:
4006 mem = dest;
4007 fmt = surface->resource.format->glFormat;
4008 type = surface->resource.format->glType;
4009 bpp = surface->resource.format->byte_count;
4012 if (surface->flags & SFLAG_PBO)
4014 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4015 checkGLcall("glBindBufferARB");
4016 if (mem)
4018 ERR("mem not null for pbo -- unexpected\n");
4019 mem = NULL;
4023 /* Save old pixel store pack state */
4024 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4025 checkGLcall("glGetIntegerv");
4026 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4027 checkGLcall("glGetIntegerv");
4028 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4029 checkGLcall("glGetIntegerv");
4031 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4032 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4033 checkGLcall("glPixelStorei");
4034 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4035 checkGLcall("glPixelStorei");
4036 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4037 checkGLcall("glPixelStorei");
4039 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4040 local_rect.right - local_rect.left,
4041 local_rect.bottom - local_rect.top,
4042 fmt, type, mem);
4043 checkGLcall("glReadPixels");
4045 /* Reset previous pixel store pack state */
4046 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4047 checkGLcall("glPixelStorei");
4048 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4049 checkGLcall("glPixelStorei");
4050 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4051 checkGLcall("glPixelStorei");
4053 if (surface->flags & SFLAG_PBO)
4055 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4056 checkGLcall("glBindBufferARB");
4058 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4059 * to get a pointer to it and perform the flipping in software. This is a lot
4060 * faster than calling glReadPixels for each line. In case we want more speed
4061 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4062 if (!srcIsUpsideDown)
4064 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4065 checkGLcall("glBindBufferARB");
4067 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4068 checkGLcall("glMapBufferARB");
4072 /* TODO: Merge this with the palettization loop below for P8 targets */
4073 if(!srcIsUpsideDown) {
4074 UINT len, off;
4075 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4076 Flip the lines in software */
4077 len = (local_rect.right - local_rect.left) * bpp;
4078 off = local_rect.left * bpp;
4080 row = HeapAlloc(GetProcessHeap(), 0, len);
4081 if(!row) {
4082 ERR("Out of memory\n");
4083 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4084 HeapFree(GetProcessHeap(), 0, mem);
4085 LEAVE_GL();
4086 return;
4089 top = mem + pitch * local_rect.top;
4090 bottom = mem + pitch * (local_rect.bottom - 1);
4091 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4092 memcpy(row, top + off, len);
4093 memcpy(top + off, bottom + off, len);
4094 memcpy(bottom + off, row, len);
4095 top += pitch;
4096 bottom -= pitch;
4098 HeapFree(GetProcessHeap(), 0, row);
4100 /* Unmap the temp PBO buffer */
4101 if (surface->flags & SFLAG_PBO)
4103 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4104 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4108 LEAVE_GL();
4109 context_release(context);
4111 /* For P8 textures we need to perform an inverse palette lookup. This is
4112 * done by searching for a palette index which matches the RGB value.
4113 * Note this isn't guaranteed to work when there are multiple entries for
4114 * the same color but we have no choice. In case of P8 render targets,
4115 * the index is stored in the alpha component so no conversion is needed. */
4116 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4118 const PALETTEENTRY *pal = NULL;
4119 DWORD width = pitch / 3;
4120 int x, y, c;
4122 if (surface->palette)
4124 pal = surface->palette->palents;
4126 else
4128 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4129 HeapFree(GetProcessHeap(), 0, mem);
4130 return;
4133 for(y = local_rect.top; y < local_rect.bottom; y++) {
4134 for(x = local_rect.left; x < local_rect.right; x++) {
4135 /* start lines pixels */
4136 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4137 const BYTE *green = blue + 1;
4138 const BYTE *red = green + 1;
4140 for(c = 0; c < 256; c++) {
4141 if(*red == pal[c].peRed &&
4142 *green == pal[c].peGreen &&
4143 *blue == pal[c].peBlue)
4145 *((BYTE *) dest + y * width + x) = c;
4146 break;
4151 HeapFree(GetProcessHeap(), 0, mem);
4155 /* Read the framebuffer contents into a texture */
4156 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4158 struct wined3d_device *device = surface->resource.device;
4159 struct wined3d_context *context;
4161 if (!surface_is_offscreen(surface))
4163 /* We would need to flip onscreen surfaces, but there's no efficient
4164 * way to do that here. It makes more sense for the caller to
4165 * explicitly go through sysmem. */
4166 ERR("Not supported for onscreen targets.\n");
4167 return;
4170 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4171 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4172 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4174 context = context_acquire(device, surface);
4175 device_invalidate_state(device, STATE_FRAMEBUFFER);
4177 surface_prepare_texture(surface, context, srgb);
4178 surface_bind_and_dirtify(surface, context, srgb);
4180 TRACE("Reading back offscreen render target %p.\n", surface);
4182 ENTER_GL();
4184 glReadBuffer(device->offscreenBuffer);
4185 checkGLcall("glReadBuffer");
4187 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4188 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4189 checkGLcall("glCopyTexSubImage2D");
4191 LEAVE_GL();
4193 context_release(context);
4196 /* Context activation is done by the caller. */
4197 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4198 struct wined3d_context *context, BOOL srgb)
4200 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4201 CONVERT_TYPES convert;
4202 struct wined3d_format format;
4204 if (surface->flags & alloc_flag) return;
4206 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4207 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4208 else surface->flags &= ~SFLAG_CONVERTED;
4210 surface_bind_and_dirtify(surface, context, srgb);
4211 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4212 surface->flags |= alloc_flag;
4215 /* Context activation is done by the caller. */
4216 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4218 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4220 struct wined3d_texture *texture = surface->container.u.texture;
4221 UINT sub_count = texture->level_count * texture->layer_count;
4222 UINT i;
4224 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4226 for (i = 0; i < sub_count; ++i)
4228 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4229 surface_prepare_texture_internal(s, context, srgb);
4232 return;
4235 surface_prepare_texture_internal(surface, context, srgb);
4238 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4240 if (multisample)
4242 if (surface->rb_multisample)
4243 return;
4245 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4246 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4247 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4248 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4249 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4251 else
4253 if (surface->rb_resolved)
4254 return;
4256 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4257 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4258 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4259 surface->pow2Width, surface->pow2Height);
4260 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4264 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4265 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4267 struct wined3d_device *device = surface->resource.device;
4268 UINT pitch = wined3d_surface_get_pitch(surface);
4269 const struct wined3d_gl_info *gl_info;
4270 struct wined3d_context *context;
4271 RECT local_rect;
4272 UINT w, h;
4274 surface_get_rect(surface, rect, &local_rect);
4276 mem += local_rect.top * pitch + local_rect.left * bpp;
4277 w = local_rect.right - local_rect.left;
4278 h = local_rect.bottom - local_rect.top;
4280 /* Activate the correct context for the render target */
4281 context = context_acquire(device, surface);
4282 context_apply_blit_state(context, device);
4283 gl_info = context->gl_info;
4285 ENTER_GL();
4287 if (!surface_is_offscreen(surface))
4289 GLenum buffer = surface_get_gl_buffer(surface);
4290 TRACE("Unlocking %#x buffer.\n", buffer);
4291 context_set_draw_buffer(context, buffer);
4293 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4294 glPixelZoom(1.0f, -1.0f);
4296 else
4298 /* Primary offscreen render target */
4299 TRACE("Offscreen render target.\n");
4300 context_set_draw_buffer(context, device->offscreenBuffer);
4302 glPixelZoom(1.0f, 1.0f);
4305 glRasterPos3i(local_rect.left, local_rect.top, 1);
4306 checkGLcall("glRasterPos3i");
4308 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4309 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4311 if (surface->flags & SFLAG_PBO)
4313 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4314 checkGLcall("glBindBufferARB");
4317 glDrawPixels(w, h, fmt, type, mem);
4318 checkGLcall("glDrawPixels");
4320 if (surface->flags & SFLAG_PBO)
4322 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4323 checkGLcall("glBindBufferARB");
4326 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4327 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4329 LEAVE_GL();
4331 if (wined3d_settings.strict_draw_ordering
4332 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4333 && surface->container.u.swapchain->front_buffer == surface))
4334 wglFlush();
4336 context_release(context);
4339 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4340 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4342 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4343 const struct wined3d_device *device = surface->resource.device;
4344 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4345 BOOL blit_supported = FALSE;
4347 /* Copy the default values from the surface. Below we might perform fixups */
4348 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4349 *format = *surface->resource.format;
4350 *convert = NO_CONVERSION;
4352 /* Ok, now look if we have to do any conversion */
4353 switch (surface->resource.format->id)
4355 case WINED3DFMT_P8_UINT:
4356 /* Below the call to blit_supported is disabled for Wine 1.2
4357 * because the function isn't operating correctly yet. At the
4358 * moment 8-bit blits are handled in software and if certain GL
4359 * extensions are around, surface conversion is performed at
4360 * upload time. The blit_supported call recognizes it as a
4361 * destination fixup. This type of upload 'fixup' and 8-bit to
4362 * 8-bit blits need to be handled by the blit_shader.
4363 * TODO: get rid of this #if 0. */
4364 #if 0
4365 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4366 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4367 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4368 #endif
4369 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4371 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4372 * texturing. Further also use conversion in case of color keying.
4373 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4374 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4375 * conflicts with this.
4377 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4378 || colorkey_active || !use_texturing)
4380 format->glFormat = GL_RGBA;
4381 format->glInternal = GL_RGBA;
4382 format->glType = GL_UNSIGNED_BYTE;
4383 format->conv_byte_count = 4;
4384 if (colorkey_active)
4385 *convert = CONVERT_PALETTED_CK;
4386 else
4387 *convert = CONVERT_PALETTED;
4389 break;
4391 case WINED3DFMT_B2G3R3_UNORM:
4392 /* **********************
4393 GL_UNSIGNED_BYTE_3_3_2
4394 ********************** */
4395 if (colorkey_active) {
4396 /* This texture format will never be used.. So do not care about color keying
4397 up until the point in time it will be needed :-) */
4398 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4400 break;
4402 case WINED3DFMT_B5G6R5_UNORM:
4403 if (colorkey_active)
4405 *convert = CONVERT_CK_565;
4406 format->glFormat = GL_RGBA;
4407 format->glInternal = GL_RGB5_A1;
4408 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4409 format->conv_byte_count = 2;
4411 break;
4413 case WINED3DFMT_B5G5R5X1_UNORM:
4414 if (colorkey_active)
4416 *convert = CONVERT_CK_5551;
4417 format->glFormat = GL_BGRA;
4418 format->glInternal = GL_RGB5_A1;
4419 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4420 format->conv_byte_count = 2;
4422 break;
4424 case WINED3DFMT_B8G8R8_UNORM:
4425 if (colorkey_active)
4427 *convert = CONVERT_CK_RGB24;
4428 format->glFormat = GL_RGBA;
4429 format->glInternal = GL_RGBA8;
4430 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4431 format->conv_byte_count = 4;
4433 break;
4435 case WINED3DFMT_B8G8R8X8_UNORM:
4436 if (colorkey_active)
4438 *convert = CONVERT_RGB32_888;
4439 format->glFormat = GL_RGBA;
4440 format->glInternal = GL_RGBA8;
4441 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4442 format->conv_byte_count = 4;
4444 break;
4446 default:
4447 break;
4450 return WINED3D_OK;
4453 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4455 const struct wined3d_device *device = surface->resource.device;
4456 const struct wined3d_palette *pal = surface->palette;
4457 BOOL index_in_alpha = FALSE;
4458 unsigned int i;
4460 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4461 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4462 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4463 * duplicate entries. Store the color key in the unused alpha component to speed the
4464 * download up and to make conversion unneeded. */
4465 index_in_alpha = primary_render_target_is_p8(device);
4467 if (!pal)
4469 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4470 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4472 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4473 if (index_in_alpha)
4475 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4476 * there's no palette at this time. */
4477 for (i = 0; i < 256; i++) table[i][3] = i;
4480 else
4482 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4483 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4484 * capability flag is present (wine does advertise this capability) */
4485 for (i = 0; i < 256; ++i)
4487 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4488 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4489 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4490 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4494 else
4496 TRACE("Using surface palette %p\n", pal);
4497 /* Get the surface's palette */
4498 for (i = 0; i < 256; ++i)
4500 table[i][0] = pal->palents[i].peRed;
4501 table[i][1] = pal->palents[i].peGreen;
4502 table[i][2] = pal->palents[i].peBlue;
4504 /* When index_in_alpha is set the palette index is stored in the
4505 * alpha component. In case of a readback we can then read
4506 * GL_ALPHA. Color keying is handled in BltOverride using a
4507 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4508 * color key itself is passed to glAlphaFunc in other cases the
4509 * alpha component of pixels that should be masked away is set to 0. */
4510 if (index_in_alpha)
4512 table[i][3] = i;
4514 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4515 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4517 table[i][3] = 0x00;
4519 else if (pal->flags & WINEDDPCAPS_ALPHA)
4521 table[i][3] = pal->palents[i].peFlags;
4523 else
4525 table[i][3] = 0xFF;
4531 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4532 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4534 const BYTE *source;
4535 BYTE *dest;
4536 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4538 switch (convert) {
4539 case NO_CONVERSION:
4541 memcpy(dst, src, pitch * height);
4542 break;
4544 case CONVERT_PALETTED:
4545 case CONVERT_PALETTED_CK:
4547 BYTE table[256][4];
4548 unsigned int x, y;
4550 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4552 for (y = 0; y < height; y++)
4554 source = src + pitch * y;
4555 dest = dst + outpitch * y;
4556 /* This is an 1 bpp format, using the width here is fine */
4557 for (x = 0; x < width; x++) {
4558 BYTE color = *source++;
4559 *dest++ = table[color][0];
4560 *dest++ = table[color][1];
4561 *dest++ = table[color][2];
4562 *dest++ = table[color][3];
4566 break;
4568 case CONVERT_CK_565:
4570 /* Converting the 565 format in 5551 packed to emulate color-keying.
4572 Note : in all these conversion, it would be best to average the averaging
4573 pixels to get the color of the pixel that will be color-keyed to
4574 prevent 'color bleeding'. This will be done later on if ever it is
4575 too visible.
4577 Note2: Nvidia documents say that their driver does not support alpha + color keying
4578 on the same surface and disables color keying in such a case
4580 unsigned int x, y;
4581 const WORD *Source;
4582 WORD *Dest;
4584 TRACE("Color keyed 565\n");
4586 for (y = 0; y < height; y++) {
4587 Source = (const WORD *)(src + y * pitch);
4588 Dest = (WORD *) (dst + y * outpitch);
4589 for (x = 0; x < width; x++ ) {
4590 WORD color = *Source++;
4591 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4592 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4593 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4594 *Dest |= 0x0001;
4595 Dest++;
4599 break;
4601 case CONVERT_CK_5551:
4603 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4604 unsigned int x, y;
4605 const WORD *Source;
4606 WORD *Dest;
4607 TRACE("Color keyed 5551\n");
4608 for (y = 0; y < height; y++) {
4609 Source = (const WORD *)(src + y * pitch);
4610 Dest = (WORD *) (dst + y * outpitch);
4611 for (x = 0; x < width; x++ ) {
4612 WORD color = *Source++;
4613 *Dest = color;
4614 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4615 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4616 *Dest |= (1 << 15);
4617 else
4618 *Dest &= ~(1 << 15);
4619 Dest++;
4623 break;
4625 case CONVERT_CK_RGB24:
4627 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4628 unsigned int x, y;
4629 for (y = 0; y < height; y++)
4631 source = src + pitch * y;
4632 dest = dst + outpitch * y;
4633 for (x = 0; x < width; x++) {
4634 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4635 DWORD dstcolor = color << 8;
4636 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4637 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4638 dstcolor |= 0xff;
4639 *(DWORD*)dest = dstcolor;
4640 source += 3;
4641 dest += 4;
4645 break;
4647 case CONVERT_RGB32_888:
4649 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4650 unsigned int x, y;
4651 for (y = 0; y < height; y++)
4653 source = src + pitch * y;
4654 dest = dst + outpitch * y;
4655 for (x = 0; x < width; x++) {
4656 DWORD color = 0xffffff & *(const DWORD*)source;
4657 DWORD dstcolor = color << 8;
4658 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4659 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4660 dstcolor |= 0xff;
4661 *(DWORD*)dest = dstcolor;
4662 source += 4;
4663 dest += 4;
4667 break;
4669 default:
4670 ERR("Unsupported conversion type %#x.\n", convert);
4672 return WINED3D_OK;
4675 BOOL palette9_changed(struct wined3d_surface *surface)
4677 struct wined3d_device *device = surface->resource.device;
4679 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4680 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4682 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4683 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4685 return FALSE;
4688 if (surface->palette9)
4690 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4692 return FALSE;
4695 else
4697 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4699 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4701 return TRUE;
4704 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4706 /* Flip the surface contents */
4707 /* Flip the DC */
4709 HDC tmp;
4710 tmp = front->hDC;
4711 front->hDC = back->hDC;
4712 back->hDC = tmp;
4715 /* Flip the DIBsection */
4717 HBITMAP tmp;
4718 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4719 tmp = front->dib.DIBsection;
4720 front->dib.DIBsection = back->dib.DIBsection;
4721 back->dib.DIBsection = tmp;
4723 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4724 else front->flags &= ~SFLAG_DIBSECTION;
4725 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4726 else back->flags &= ~SFLAG_DIBSECTION;
4729 /* Flip the surface data */
4731 void* tmp;
4733 tmp = front->dib.bitmap_data;
4734 front->dib.bitmap_data = back->dib.bitmap_data;
4735 back->dib.bitmap_data = tmp;
4737 tmp = front->resource.allocatedMemory;
4738 front->resource.allocatedMemory = back->resource.allocatedMemory;
4739 back->resource.allocatedMemory = tmp;
4741 tmp = front->resource.heapMemory;
4742 front->resource.heapMemory = back->resource.heapMemory;
4743 back->resource.heapMemory = tmp;
4746 /* Flip the PBO */
4748 GLuint tmp_pbo = front->pbo;
4749 front->pbo = back->pbo;
4750 back->pbo = tmp_pbo;
4753 /* client_memory should not be different, but just in case */
4755 BOOL tmp;
4756 tmp = front->dib.client_memory;
4757 front->dib.client_memory = back->dib.client_memory;
4758 back->dib.client_memory = tmp;
4761 /* Flip the opengl texture */
4763 GLuint tmp;
4765 tmp = back->texture_name;
4766 back->texture_name = front->texture_name;
4767 front->texture_name = tmp;
4769 tmp = back->texture_name_srgb;
4770 back->texture_name_srgb = front->texture_name_srgb;
4771 front->texture_name_srgb = tmp;
4773 tmp = back->rb_multisample;
4774 back->rb_multisample = front->rb_multisample;
4775 front->rb_multisample = tmp;
4777 tmp = back->rb_resolved;
4778 back->rb_resolved = front->rb_resolved;
4779 front->rb_resolved = tmp;
4781 resource_unload(&back->resource);
4782 resource_unload(&front->resource);
4786 DWORD tmp_flags = back->flags;
4787 back->flags = front->flags;
4788 front->flags = tmp_flags;
4792 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4793 * pixel copy calls. */
4794 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4795 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4797 struct wined3d_device *device = dst_surface->resource.device;
4798 float xrel, yrel;
4799 UINT row;
4800 struct wined3d_context *context;
4801 BOOL upsidedown = FALSE;
4802 RECT dst_rect = *dst_rect_in;
4804 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4805 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4807 if(dst_rect.top > dst_rect.bottom) {
4808 UINT tmp = dst_rect.bottom;
4809 dst_rect.bottom = dst_rect.top;
4810 dst_rect.top = tmp;
4811 upsidedown = TRUE;
4814 context = context_acquire(device, src_surface);
4815 context_apply_blit_state(context, device);
4816 surface_internal_preload(dst_surface, SRGB_RGB);
4817 ENTER_GL();
4819 /* Bind the target texture */
4820 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4821 if (surface_is_offscreen(src_surface))
4823 TRACE("Reading from an offscreen target\n");
4824 upsidedown = !upsidedown;
4825 glReadBuffer(device->offscreenBuffer);
4827 else
4829 glReadBuffer(surface_get_gl_buffer(src_surface));
4831 checkGLcall("glReadBuffer");
4833 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4834 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4836 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4838 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4840 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4841 ERR("Texture filtering not supported in direct blit\n");
4844 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4845 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4847 ERR("Texture filtering not supported in direct blit\n");
4850 if (upsidedown
4851 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4852 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4854 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4856 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4857 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4858 src_rect->left, src_surface->resource.height - src_rect->bottom,
4859 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4861 else
4863 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4864 /* I have to process this row by row to swap the image,
4865 * otherwise it would be upside down, so stretching in y direction
4866 * doesn't cost extra time
4868 * However, stretching in x direction can be avoided if not necessary
4870 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4871 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4873 /* Well, that stuff works, but it's very slow.
4874 * find a better way instead
4876 UINT col;
4878 for (col = dst_rect.left; col < dst_rect.right; ++col)
4880 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4881 dst_rect.left + col /* x offset */, row /* y offset */,
4882 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4885 else
4887 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4888 dst_rect.left /* x offset */, row /* y offset */,
4889 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4893 checkGLcall("glCopyTexSubImage2D");
4895 LEAVE_GL();
4896 context_release(context);
4898 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4899 * path is never entered
4901 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4904 /* Uses the hardware to stretch and flip the image */
4905 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4906 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4908 struct wined3d_device *device = dst_surface->resource.device;
4909 struct wined3d_swapchain *src_swapchain = NULL;
4910 GLuint src, backup = 0;
4911 float left, right, top, bottom; /* Texture coordinates */
4912 UINT fbwidth = src_surface->resource.width;
4913 UINT fbheight = src_surface->resource.height;
4914 struct wined3d_context *context;
4915 GLenum drawBuffer = GL_BACK;
4916 GLenum texture_target;
4917 BOOL noBackBufferBackup;
4918 BOOL src_offscreen;
4919 BOOL upsidedown = FALSE;
4920 RECT dst_rect = *dst_rect_in;
4922 TRACE("Using hwstretch blit\n");
4923 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4924 context = context_acquire(device, src_surface);
4925 context_apply_blit_state(context, device);
4926 surface_internal_preload(dst_surface, SRGB_RGB);
4928 src_offscreen = surface_is_offscreen(src_surface);
4929 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4930 if (!noBackBufferBackup && !src_surface->texture_name)
4932 /* Get it a description */
4933 surface_internal_preload(src_surface, SRGB_RGB);
4935 ENTER_GL();
4937 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4938 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4940 if (context->aux_buffers >= 2)
4942 /* Got more than one aux buffer? Use the 2nd aux buffer */
4943 drawBuffer = GL_AUX1;
4945 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4947 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4948 drawBuffer = GL_AUX0;
4951 if(noBackBufferBackup) {
4952 glGenTextures(1, &backup);
4953 checkGLcall("glGenTextures");
4954 context_bind_texture(context, GL_TEXTURE_2D, backup);
4955 texture_target = GL_TEXTURE_2D;
4956 } else {
4957 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4958 * we are reading from the back buffer, the backup can be used as source texture
4960 texture_target = src_surface->texture_target;
4961 context_bind_texture(context, texture_target, src_surface->texture_name);
4962 glEnable(texture_target);
4963 checkGLcall("glEnable(texture_target)");
4965 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4966 src_surface->flags &= ~SFLAG_INTEXTURE;
4969 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4970 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4972 if(dst_rect.top > dst_rect.bottom) {
4973 UINT tmp = dst_rect.bottom;
4974 dst_rect.bottom = dst_rect.top;
4975 dst_rect.top = tmp;
4976 upsidedown = TRUE;
4979 if (src_offscreen)
4981 TRACE("Reading from an offscreen target\n");
4982 upsidedown = !upsidedown;
4983 glReadBuffer(device->offscreenBuffer);
4985 else
4987 glReadBuffer(surface_get_gl_buffer(src_surface));
4990 /* TODO: Only back up the part that will be overwritten */
4991 glCopyTexSubImage2D(texture_target, 0,
4992 0, 0 /* read offsets */,
4993 0, 0,
4994 fbwidth,
4995 fbheight);
4997 checkGLcall("glCopyTexSubImage2D");
4999 /* No issue with overriding these - the sampler is dirty due to blit usage */
5000 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5001 wined3d_gl_mag_filter(magLookup, Filter));
5002 checkGLcall("glTexParameteri");
5003 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5004 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5005 checkGLcall("glTexParameteri");
5007 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5008 src_swapchain = src_surface->container.u.swapchain;
5009 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5011 src = backup ? backup : src_surface->texture_name;
5013 else
5015 glReadBuffer(GL_FRONT);
5016 checkGLcall("glReadBuffer(GL_FRONT)");
5018 glGenTextures(1, &src);
5019 checkGLcall("glGenTextures(1, &src)");
5020 context_bind_texture(context, GL_TEXTURE_2D, src);
5022 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5023 * out for power of 2 sizes
5025 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5026 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5027 checkGLcall("glTexImage2D");
5028 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5029 0, 0 /* read offsets */,
5030 0, 0,
5031 fbwidth,
5032 fbheight);
5034 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5035 checkGLcall("glTexParameteri");
5036 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5037 checkGLcall("glTexParameteri");
5039 glReadBuffer(GL_BACK);
5040 checkGLcall("glReadBuffer(GL_BACK)");
5042 if(texture_target != GL_TEXTURE_2D) {
5043 glDisable(texture_target);
5044 glEnable(GL_TEXTURE_2D);
5045 texture_target = GL_TEXTURE_2D;
5048 checkGLcall("glEnd and previous");
5050 left = src_rect->left;
5051 right = src_rect->right;
5053 if (!upsidedown)
5055 top = src_surface->resource.height - src_rect->top;
5056 bottom = src_surface->resource.height - src_rect->bottom;
5058 else
5060 top = src_surface->resource.height - src_rect->bottom;
5061 bottom = src_surface->resource.height - src_rect->top;
5064 if (src_surface->flags & SFLAG_NORMCOORD)
5066 left /= src_surface->pow2Width;
5067 right /= src_surface->pow2Width;
5068 top /= src_surface->pow2Height;
5069 bottom /= src_surface->pow2Height;
5072 /* draw the source texture stretched and upside down. The correct surface is bound already */
5073 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5074 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5076 context_set_draw_buffer(context, drawBuffer);
5077 glReadBuffer(drawBuffer);
5079 glBegin(GL_QUADS);
5080 /* bottom left */
5081 glTexCoord2f(left, bottom);
5082 glVertex2i(0, 0);
5084 /* top left */
5085 glTexCoord2f(left, top);
5086 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5088 /* top right */
5089 glTexCoord2f(right, top);
5090 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5092 /* bottom right */
5093 glTexCoord2f(right, bottom);
5094 glVertex2i(dst_rect.right - dst_rect.left, 0);
5095 glEnd();
5096 checkGLcall("glEnd and previous");
5098 if (texture_target != dst_surface->texture_target)
5100 glDisable(texture_target);
5101 glEnable(dst_surface->texture_target);
5102 texture_target = dst_surface->texture_target;
5105 /* Now read the stretched and upside down image into the destination texture */
5106 context_bind_texture(context, texture_target, dst_surface->texture_name);
5107 glCopyTexSubImage2D(texture_target,
5109 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5110 0, 0, /* We blitted the image to the origin */
5111 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5112 checkGLcall("glCopyTexSubImage2D");
5114 if(drawBuffer == GL_BACK) {
5115 /* Write the back buffer backup back */
5116 if(backup) {
5117 if(texture_target != GL_TEXTURE_2D) {
5118 glDisable(texture_target);
5119 glEnable(GL_TEXTURE_2D);
5120 texture_target = GL_TEXTURE_2D;
5122 context_bind_texture(context, 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 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5135 glBegin(GL_QUADS);
5136 /* top left */
5137 glTexCoord2f(0.0f, 0.0f);
5138 glVertex2i(0, fbheight);
5140 /* bottom left */
5141 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5142 glVertex2i(0, 0);
5144 /* bottom right */
5145 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5146 (float)fbheight / (float)src_surface->pow2Height);
5147 glVertex2i(fbwidth, 0);
5149 /* top right */
5150 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5151 glVertex2i(fbwidth, fbheight);
5152 glEnd();
5154 glDisable(texture_target);
5155 checkGLcall("glDisable(texture_target)");
5157 /* Cleanup */
5158 if (src != src_surface->texture_name && src != backup)
5160 glDeleteTextures(1, &src);
5161 checkGLcall("glDeleteTextures(1, &src)");
5163 if(backup) {
5164 glDeleteTextures(1, &backup);
5165 checkGLcall("glDeleteTextures(1, &backup)");
5168 LEAVE_GL();
5170 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5172 context_release(context);
5174 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5175 * path is never entered
5177 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5180 /* Front buffer coordinates are always full screen coordinates, but our GL
5181 * drawable is limited to the window's client area. The sysmem and texture
5182 * copies do have the full screen size. Note that GL has a bottom-left
5183 * origin, while D3D has a top-left origin. */
5184 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5186 UINT drawable_height;
5188 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5189 && surface == surface->container.u.swapchain->front_buffer)
5191 POINT offset = {0, 0};
5192 RECT windowsize;
5194 ScreenToClient(window, &offset);
5195 OffsetRect(rect, offset.x, offset.y);
5197 GetClientRect(window, &windowsize);
5198 drawable_height = windowsize.bottom - windowsize.top;
5200 else
5202 drawable_height = surface->resource.height;
5205 rect->top = drawable_height - rect->top;
5206 rect->bottom = drawable_height - rect->bottom;
5209 static void surface_blt_to_drawable(struct wined3d_device *device,
5210 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5211 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5212 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5214 struct wined3d_context *context;
5215 RECT src_rect, dst_rect;
5217 src_rect = *src_rect_in;
5218 dst_rect = *dst_rect_in;
5220 /* Make sure the surface is up-to-date. This should probably use
5221 * surface_load_location() and worry about the destination surface too,
5222 * unless we're overwriting it completely. */
5223 surface_internal_preload(src_surface, SRGB_RGB);
5225 /* Activate the destination context, set it up for blitting */
5226 context = context_acquire(device, dst_surface);
5227 context_apply_blit_state(context, device);
5229 if (!surface_is_offscreen(dst_surface))
5230 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5232 device->blitter->set_shader(device->blit_priv, context, src_surface);
5234 ENTER_GL();
5236 if (color_key)
5238 glEnable(GL_ALPHA_TEST);
5239 checkGLcall("glEnable(GL_ALPHA_TEST)");
5241 /* When the primary render target uses P8, the alpha component
5242 * contains the palette index. Which means that the colorkey is one of
5243 * the palette entries. In other cases pixels that should be masked
5244 * away have alpha set to 0. */
5245 if (primary_render_target_is_p8(device))
5246 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5247 else
5248 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5249 checkGLcall("glAlphaFunc");
5251 else
5253 glDisable(GL_ALPHA_TEST);
5254 checkGLcall("glDisable(GL_ALPHA_TEST)");
5257 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5259 if (color_key)
5261 glDisable(GL_ALPHA_TEST);
5262 checkGLcall("glDisable(GL_ALPHA_TEST)");
5265 LEAVE_GL();
5267 /* Leave the opengl state valid for blitting */
5268 device->blitter->unset_shader(context->gl_info);
5270 if (wined3d_settings.strict_draw_ordering
5271 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5272 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5273 wglFlush(); /* Flush to ensure ordering across contexts. */
5275 context_release(context);
5278 /* Do not call while under the GL lock. */
5279 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5281 struct wined3d_device *device = s->resource.device;
5282 const struct blit_shader *blitter;
5284 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5285 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5286 if (!blitter)
5288 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5289 return WINED3DERR_INVALIDCALL;
5292 return blitter->color_fill(device, s, rect, color);
5295 /* Do not call while under the GL lock. */
5296 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5297 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5298 WINED3DTEXTUREFILTERTYPE Filter)
5300 struct wined3d_device *device = dst_surface->resource.device;
5301 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5302 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5304 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5305 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5306 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5308 /* Get the swapchain. One of the surfaces has to be a primary surface */
5309 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5311 WARN("Destination is in sysmem, rejecting gl blt\n");
5312 return WINED3DERR_INVALIDCALL;
5315 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5316 dstSwapchain = dst_surface->container.u.swapchain;
5318 if (src_surface)
5320 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5322 WARN("Src is in sysmem, rejecting gl blt\n");
5323 return WINED3DERR_INVALIDCALL;
5326 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5327 srcSwapchain = src_surface->container.u.swapchain;
5330 /* Early sort out of cases where no render target is used */
5331 if (!dstSwapchain && !srcSwapchain
5332 && src_surface != device->fb.render_targets[0]
5333 && dst_surface != device->fb.render_targets[0])
5335 TRACE("No surface is render target, not using hardware blit.\n");
5336 return WINED3DERR_INVALIDCALL;
5339 /* No destination color keying supported */
5340 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5342 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5343 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5344 return WINED3DERR_INVALIDCALL;
5347 if (dstSwapchain && dstSwapchain == srcSwapchain)
5349 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5350 return WINED3DERR_INVALIDCALL;
5353 if (dstSwapchain && srcSwapchain)
5355 FIXME("Implement hardware blit between two different swapchains\n");
5356 return WINED3DERR_INVALIDCALL;
5359 if (dstSwapchain)
5361 /* Handled with regular texture -> swapchain blit */
5362 if (src_surface == device->fb.render_targets[0])
5363 TRACE("Blit from active render target to a swapchain\n");
5365 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5367 FIXME("Implement blit from a swapchain to the active render target\n");
5368 return WINED3DERR_INVALIDCALL;
5371 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5373 /* Blit from render target to texture */
5374 BOOL stretchx;
5376 /* P8 read back is not implemented */
5377 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5378 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5380 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5381 return WINED3DERR_INVALIDCALL;
5384 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5386 TRACE("Color keying not supported by frame buffer to texture blit\n");
5387 return WINED3DERR_INVALIDCALL;
5388 /* Destination color key is checked above */
5391 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5392 stretchx = TRUE;
5393 else
5394 stretchx = FALSE;
5396 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5397 * flip the image nor scale it.
5399 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5400 * -> If the app wants a image width an unscaled width, copy it line per line
5401 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5402 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5403 * back buffer. This is slower than reading line per line, thus not used for flipping
5404 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5405 * pixel by pixel. */
5406 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5407 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5409 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5410 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5411 } else {
5412 TRACE("Using hardware stretching to flip / stretch the texture\n");
5413 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5416 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5418 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5419 dst_surface->resource.allocatedMemory = NULL;
5420 dst_surface->resource.heapMemory = NULL;
5422 else
5424 dst_surface->flags &= ~SFLAG_INSYSMEM;
5427 return WINED3D_OK;
5429 else if (src_surface)
5431 /* Blit from offscreen surface to render target */
5432 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5433 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5435 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5437 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5438 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5439 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5441 FIXME("Unsupported blit operation falling back to software\n");
5442 return WINED3DERR_INVALIDCALL;
5445 /* Color keying: Check if we have to do a color keyed blt,
5446 * and if not check if a color key is activated.
5448 * Just modify the color keying parameters in the surface and restore them afterwards
5449 * The surface keeps track of the color key last used to load the opengl surface.
5450 * PreLoad will catch the change to the flags and color key and reload if necessary.
5452 if (flags & WINEDDBLT_KEYSRC)
5454 /* Use color key from surface */
5456 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5458 /* Use color key from DDBltFx */
5459 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5460 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5462 else
5464 /* Do not use color key */
5465 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5468 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5469 src_surface, src_rect, dst_surface, dst_rect);
5471 /* Restore the color key parameters */
5472 src_surface->CKeyFlags = oldCKeyFlags;
5473 src_surface->SrcBltCKey = oldBltCKey;
5475 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5477 return WINED3D_OK;
5480 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5481 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5482 return WINED3DERR_INVALIDCALL;
5485 /* GL locking is done by the caller */
5486 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5487 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5489 struct wined3d_device *device = surface->resource.device;
5490 const struct wined3d_gl_info *gl_info = context->gl_info;
5491 GLint compare_mode = GL_NONE;
5492 struct blt_info info;
5493 GLint old_binding = 0;
5494 RECT rect;
5496 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5498 glDisable(GL_CULL_FACE);
5499 glDisable(GL_BLEND);
5500 glDisable(GL_ALPHA_TEST);
5501 glDisable(GL_SCISSOR_TEST);
5502 glDisable(GL_STENCIL_TEST);
5503 glEnable(GL_DEPTH_TEST);
5504 glDepthFunc(GL_ALWAYS);
5505 glDepthMask(GL_TRUE);
5506 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5507 glViewport(x, y, w, h);
5509 SetRect(&rect, 0, h, w, 0);
5510 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5511 context_active_texture(context, context->gl_info, 0);
5512 glGetIntegerv(info.binding, &old_binding);
5513 glBindTexture(info.bind_target, texture);
5514 if (gl_info->supported[ARB_SHADOW])
5516 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5517 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5520 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5521 gl_info, info.tex_type, &surface->ds_current_size);
5523 glBegin(GL_TRIANGLE_STRIP);
5524 glTexCoord3fv(info.coords[0]);
5525 glVertex2f(-1.0f, -1.0f);
5526 glTexCoord3fv(info.coords[1]);
5527 glVertex2f(1.0f, -1.0f);
5528 glTexCoord3fv(info.coords[2]);
5529 glVertex2f(-1.0f, 1.0f);
5530 glTexCoord3fv(info.coords[3]);
5531 glVertex2f(1.0f, 1.0f);
5532 glEnd();
5534 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5535 glBindTexture(info.bind_target, old_binding);
5537 glPopAttrib();
5539 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5542 void surface_modify_ds_location(struct wined3d_surface *surface,
5543 DWORD location, UINT w, UINT h)
5545 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5547 if (location & ~SFLAG_DS_LOCATIONS)
5548 FIXME("Invalid location (%#x) specified.\n", location);
5550 surface->ds_current_size.cx = w;
5551 surface->ds_current_size.cy = h;
5552 surface->flags &= ~SFLAG_DS_LOCATIONS;
5553 surface->flags |= location;
5556 /* Context activation is done by the caller. */
5557 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5559 struct wined3d_device *device = surface->resource.device;
5560 GLsizei w, h;
5562 TRACE("surface %p, new location %#x.\n", surface, location);
5564 /* TODO: Make this work for modes other than FBO */
5565 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5567 if (!(surface->flags & location))
5569 w = surface->ds_current_size.cx;
5570 h = surface->ds_current_size.cy;
5571 surface->ds_current_size.cx = 0;
5572 surface->ds_current_size.cy = 0;
5574 else
5576 w = surface->resource.width;
5577 h = surface->resource.height;
5580 if (surface->ds_current_size.cx == surface->resource.width
5581 && surface->ds_current_size.cy == surface->resource.height)
5583 TRACE("Location (%#x) is already up to date.\n", location);
5584 return;
5587 if (surface->current_renderbuffer)
5589 FIXME("Not supported with fixed up depth stencil.\n");
5590 return;
5593 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5595 /* This mostly happens when a depth / stencil is used without being
5596 * cleared first. In principle we could upload from sysmem, or
5597 * explicitly clear before first usage. For the moment there don't
5598 * appear to be a lot of applications depending on this, so a FIXME
5599 * should do. */
5600 FIXME("No up to date depth stencil location.\n");
5601 surface->flags |= location;
5602 surface->ds_current_size.cx = surface->resource.width;
5603 surface->ds_current_size.cy = surface->resource.height;
5604 return;
5607 if (location == SFLAG_DS_OFFSCREEN)
5609 GLint old_binding = 0;
5610 GLenum bind_target;
5612 /* The render target is allowed to be smaller than the depth/stencil
5613 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5614 * than the offscreen surface. Don't overwrite the offscreen surface
5615 * with undefined data. */
5616 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5617 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5619 TRACE("Copying onscreen depth buffer to depth texture.\n");
5621 ENTER_GL();
5623 if (!device->depth_blt_texture)
5625 glGenTextures(1, &device->depth_blt_texture);
5628 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5629 * directly on the FBO texture. That's because we need to flip. */
5630 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5631 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5632 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5634 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5635 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5637 else
5639 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5640 bind_target = GL_TEXTURE_2D;
5642 glBindTexture(bind_target, device->depth_blt_texture);
5643 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5644 * internal format, because the internal format might include stencil
5645 * data. In principle we should copy stencil data as well, but unless
5646 * the driver supports stencil export it's hard to do, and doesn't
5647 * seem to be needed in practice. If the hardware doesn't support
5648 * writing stencil data, the glCopyTexImage2D() call might trigger
5649 * software fallbacks. */
5650 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5651 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5652 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5653 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5654 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5655 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5656 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5657 glBindTexture(bind_target, old_binding);
5659 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5660 NULL, surface, SFLAG_INTEXTURE);
5661 context_set_draw_buffer(context, GL_NONE);
5662 glReadBuffer(GL_NONE);
5664 /* Do the actual blit */
5665 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5666 checkGLcall("depth_blt");
5668 context_invalidate_state(context, STATE_FRAMEBUFFER);
5670 LEAVE_GL();
5672 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5674 else if (location == SFLAG_DS_ONSCREEN)
5676 TRACE("Copying depth texture to onscreen depth buffer.\n");
5678 ENTER_GL();
5680 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5681 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5682 surface_depth_blt(surface, context, surface->texture_name,
5683 0, surface->pow2Height - h, w, h, surface->texture_target);
5684 checkGLcall("depth_blt");
5686 context_invalidate_state(context, STATE_FRAMEBUFFER);
5688 LEAVE_GL();
5690 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5692 else
5694 ERR("Invalid location (%#x) specified.\n", location);
5697 surface->flags |= location;
5698 surface->ds_current_size.cx = surface->resource.width;
5699 surface->ds_current_size.cy = surface->resource.height;
5702 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5704 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5705 struct wined3d_surface *overlay;
5707 TRACE("surface %p, location %s, persistent %#x.\n",
5708 surface, debug_surflocation(location), persistent);
5710 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5711 && (location & SFLAG_INDRAWABLE))
5712 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5714 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5715 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5716 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5718 if (persistent)
5720 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5721 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5723 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5725 TRACE("Passing to container.\n");
5726 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5729 surface->flags &= ~SFLAG_LOCATIONS;
5730 surface->flags |= location;
5732 /* Redraw emulated overlays, if any */
5733 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5735 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5737 overlay->surface_ops->surface_draw_overlay(overlay);
5741 else
5743 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5745 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5747 TRACE("Passing to container\n");
5748 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5751 surface->flags &= ~location;
5754 if (!(surface->flags & SFLAG_LOCATIONS))
5756 ERR("Surface %p does not have any up to date location.\n", surface);
5760 static DWORD resource_access_from_location(DWORD location)
5762 switch (location)
5764 case SFLAG_INSYSMEM:
5765 return WINED3D_RESOURCE_ACCESS_CPU;
5767 case SFLAG_INDRAWABLE:
5768 case SFLAG_INSRGBTEX:
5769 case SFLAG_INTEXTURE:
5770 case SFLAG_INRB_MULTISAMPLE:
5771 case SFLAG_INRB_RESOLVED:
5772 return WINED3D_RESOURCE_ACCESS_GPU;
5774 default:
5775 FIXME("Unhandled location %#x.\n", location);
5776 return 0;
5780 static void surface_load_sysmem(struct wined3d_surface *surface,
5781 const struct wined3d_gl_info *gl_info, const RECT *rect)
5783 surface_prepare_system_memory(surface);
5785 /* Download the surface to system memory. */
5786 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5788 struct wined3d_device *device = surface->resource.device;
5789 struct wined3d_context *context;
5791 /* TODO: Use already acquired context when possible. */
5792 context = context_acquire(device, NULL);
5794 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5795 surface_download_data(surface, gl_info);
5797 context_release(context);
5799 return;
5802 /* Note: It might be faster to download into a texture first. */
5803 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5804 wined3d_surface_get_pitch(surface));
5807 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5808 const struct wined3d_gl_info *gl_info, const RECT *rect)
5810 struct wined3d_device *device = surface->resource.device;
5811 struct wined3d_format format;
5812 CONVERT_TYPES convert;
5813 UINT byte_count;
5814 BYTE *mem;
5816 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5818 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5819 return WINED3DERR_INVALIDCALL;
5822 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5823 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5825 if (surface->flags & SFLAG_INTEXTURE)
5827 RECT r;
5829 surface_get_rect(surface, rect, &r);
5830 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5832 return WINED3D_OK;
5835 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5837 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5838 * path through sysmem. */
5839 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5842 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5844 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5845 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5846 * called. */
5847 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5849 struct wined3d_context *context;
5851 TRACE("Removing the pbo attached to surface %p.\n", surface);
5853 /* TODO: Use already acquired context when possible. */
5854 context = context_acquire(device, NULL);
5856 surface_remove_pbo(surface, gl_info);
5858 context_release(context);
5861 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5863 UINT height = surface->resource.height;
5864 UINT width = surface->resource.width;
5865 UINT src_pitch, dst_pitch;
5867 byte_count = format.conv_byte_count;
5868 src_pitch = wined3d_surface_get_pitch(surface);
5870 /* Stick to the alignment for the converted surface too, makes it
5871 * easier to load the surface. */
5872 dst_pitch = width * byte_count;
5873 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5875 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5877 ERR("Out of memory (%u).\n", dst_pitch * height);
5878 return E_OUTOFMEMORY;
5881 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5882 src_pitch, width, height, dst_pitch, convert, surface);
5884 surface->flags |= SFLAG_CONVERTED;
5886 else
5888 surface->flags &= ~SFLAG_CONVERTED;
5889 mem = surface->resource.allocatedMemory;
5890 byte_count = format.byte_count;
5893 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5895 /* Don't delete PBO memory. */
5896 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5897 HeapFree(GetProcessHeap(), 0, mem);
5899 return WINED3D_OK;
5902 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5903 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5905 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5906 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5907 struct wined3d_device *device = surface->resource.device;
5908 struct wined3d_context *context;
5909 UINT width, src_pitch, dst_pitch;
5910 struct wined3d_bo_address data;
5911 struct wined3d_format format;
5912 POINT dst_point = {0, 0};
5913 CONVERT_TYPES convert;
5914 BYTE *mem;
5916 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5917 && surface_is_offscreen(surface)
5918 && (surface->flags & SFLAG_INDRAWABLE))
5920 read_from_framebuffer_texture(surface, srgb);
5922 return WINED3D_OK;
5925 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5926 && (surface->resource.format->flags & attach_flags) == attach_flags
5927 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5928 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5929 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5931 if (srgb)
5932 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5933 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5934 else
5935 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5936 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5938 return WINED3D_OK;
5941 /* Upload from system memory */
5943 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5944 TRUE /* We will use textures */, &format, &convert);
5946 if (srgb)
5948 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5950 /* Performance warning... */
5951 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5952 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5955 else
5957 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5959 /* Performance warning... */
5960 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5961 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5965 if (!(surface->flags & SFLAG_INSYSMEM))
5967 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5968 /* Lets hope we get it from somewhere... */
5969 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5972 /* TODO: Use already acquired context when possible. */
5973 context = context_acquire(device, NULL);
5975 surface_prepare_texture(surface, context, srgb);
5976 surface_bind_and_dirtify(surface, context, srgb);
5978 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5980 surface->flags |= SFLAG_GLCKEY;
5981 surface->glCKey = surface->SrcBltCKey;
5983 else surface->flags &= ~SFLAG_GLCKEY;
5985 width = surface->resource.width;
5986 src_pitch = wined3d_surface_get_pitch(surface);
5988 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5989 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5990 * called. */
5991 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
5993 TRACE("Removing the pbo attached to surface %p.\n", surface);
5994 surface_remove_pbo(surface, gl_info);
5997 if (format.convert)
5999 /* This code is entered for texture formats which need a fixup. */
6000 UINT height = surface->resource.height;
6002 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6003 dst_pitch = width * format.conv_byte_count;
6004 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6006 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6008 ERR("Out of memory (%u).\n", dst_pitch * height);
6009 context_release(context);
6010 return E_OUTOFMEMORY;
6012 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6014 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6016 /* This code is only entered for color keying fixups */
6017 UINT height = surface->resource.height;
6019 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6020 dst_pitch = width * format.conv_byte_count;
6021 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6023 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6025 ERR("Out of memory (%u).\n", dst_pitch * height);
6026 context_release(context);
6027 return E_OUTOFMEMORY;
6029 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6030 width, height, dst_pitch, convert, surface);
6032 else
6034 mem = surface->resource.allocatedMemory;
6037 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6038 data.addr = mem;
6039 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6041 context_release(context);
6043 /* Don't delete PBO memory. */
6044 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6045 HeapFree(GetProcessHeap(), 0, mem);
6047 return WINED3D_OK;
6050 static void surface_multisample_resolve(struct wined3d_surface *surface)
6052 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6054 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6055 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6057 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6058 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6061 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6063 struct wined3d_device *device = surface->resource.device;
6064 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6065 HRESULT hr;
6067 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6069 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6071 if (location == SFLAG_INTEXTURE)
6073 struct wined3d_context *context = context_acquire(device, NULL);
6074 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6075 context_release(context);
6076 return WINED3D_OK;
6078 else
6080 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6081 return WINED3DERR_INVALIDCALL;
6085 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6086 location = SFLAG_INTEXTURE;
6088 if (surface->flags & location)
6090 TRACE("Location already up to date.\n");
6091 return WINED3D_OK;
6094 if (WARN_ON(d3d_surface))
6096 DWORD required_access = resource_access_from_location(location);
6097 if ((surface->resource.access_flags & required_access) != required_access)
6098 WARN("Operation requires %#x access, but surface only has %#x.\n",
6099 required_access, surface->resource.access_flags);
6102 if (!(surface->flags & SFLAG_LOCATIONS))
6104 ERR("Surface %p does not have any up to date location.\n", surface);
6105 surface->flags |= SFLAG_LOST;
6106 return WINED3DERR_DEVICELOST;
6109 switch (location)
6111 case SFLAG_INSYSMEM:
6112 surface_load_sysmem(surface, gl_info, rect);
6113 break;
6115 case SFLAG_INDRAWABLE:
6116 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6117 return hr;
6118 break;
6120 case SFLAG_INRB_RESOLVED:
6121 surface_multisample_resolve(surface);
6122 break;
6124 case SFLAG_INTEXTURE:
6125 case SFLAG_INSRGBTEX:
6126 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6127 return hr;
6128 break;
6130 default:
6131 ERR("Don't know how to handle location %#x.\n", location);
6132 break;
6135 if (!rect)
6137 surface->flags |= location;
6139 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6140 surface_evict_sysmem(surface);
6143 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6144 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6146 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6149 return WINED3D_OK;
6152 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6154 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6156 /* Not on a swapchain - must be offscreen */
6157 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6159 /* The front buffer is always onscreen */
6160 if (surface == swapchain->front_buffer) return FALSE;
6162 /* If the swapchain is rendered to an FBO, the backbuffer is
6163 * offscreen, otherwise onscreen */
6164 return swapchain->render_to_fbo;
6167 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6168 /* Context activation is done by the caller. */
6169 static void ffp_blit_free(struct wined3d_device *device) { }
6171 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6172 /* Context activation is done by the caller. */
6173 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6175 BYTE table[256][4];
6176 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6178 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6180 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6181 ENTER_GL();
6182 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6183 LEAVE_GL();
6186 /* Context activation is done by the caller. */
6187 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6189 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6191 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6192 * else the surface is converted in software at upload time in LoadLocation.
6194 if(fixup == COMPLEX_FIXUP_P8 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6195 ffp_blit_p8_upload_palette(surface, context->gl_info);
6197 ENTER_GL();
6198 glEnable(surface->texture_target);
6199 checkGLcall("glEnable(surface->texture_target)");
6200 LEAVE_GL();
6201 return WINED3D_OK;
6204 /* Context activation is done by the caller. */
6205 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6207 ENTER_GL();
6208 glDisable(GL_TEXTURE_2D);
6209 checkGLcall("glDisable(GL_TEXTURE_2D)");
6210 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6212 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6213 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6215 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6217 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6218 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6220 LEAVE_GL();
6223 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6224 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6225 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6227 enum complex_fixup src_fixup;
6229 switch (blit_op)
6231 case WINED3D_BLIT_OP_COLOR_BLIT:
6232 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6233 return FALSE;
6235 src_fixup = get_complex_fixup(src_format->color_fixup);
6236 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6238 TRACE("Checking support for fixup:\n");
6239 dump_color_fixup_desc(src_format->color_fixup);
6242 if (!is_identity_fixup(dst_format->color_fixup))
6244 TRACE("Destination fixups are not supported\n");
6245 return FALSE;
6248 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6250 TRACE("P8 fixup supported\n");
6251 return TRUE;
6254 /* We only support identity conversions. */
6255 if (is_identity_fixup(src_format->color_fixup))
6257 TRACE("[OK]\n");
6258 return TRUE;
6261 TRACE("[FAILED]\n");
6262 return FALSE;
6264 case WINED3D_BLIT_OP_COLOR_FILL:
6265 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6266 return FALSE;
6268 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6270 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6271 return FALSE;
6273 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6275 TRACE("Color fill not supported\n");
6276 return FALSE;
6279 /* FIXME: We should reject color fills on formats with fixups,
6280 * but this would break P8 color fills for example. */
6282 return TRUE;
6284 case WINED3D_BLIT_OP_DEPTH_FILL:
6285 return TRUE;
6287 default:
6288 TRACE("Unsupported blit_op=%d\n", blit_op);
6289 return FALSE;
6293 /* Do not call while under the GL lock. */
6294 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6295 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6297 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6298 struct wined3d_fb_state fb = {&dst_surface, NULL};
6300 return device_clear_render_targets(device, 1, &fb,
6301 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6304 /* Do not call while under the GL lock. */
6305 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6306 struct wined3d_surface *surface, const RECT *rect, float depth)
6308 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6309 struct wined3d_fb_state fb = {NULL, surface};
6311 return device_clear_render_targets(device, 0, &fb,
6312 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6315 const struct blit_shader ffp_blit = {
6316 ffp_blit_alloc,
6317 ffp_blit_free,
6318 ffp_blit_set,
6319 ffp_blit_unset,
6320 ffp_blit_supported,
6321 ffp_blit_color_fill,
6322 ffp_blit_depth_fill,
6325 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6327 return WINED3D_OK;
6330 /* Context activation is done by the caller. */
6331 static void cpu_blit_free(struct wined3d_device *device)
6335 /* Context activation is done by the caller. */
6336 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6338 return WINED3D_OK;
6341 /* Context activation is done by the caller. */
6342 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6346 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6347 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6348 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6350 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6352 return TRUE;
6355 return FALSE;
6358 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6359 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6360 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6362 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6363 const struct wined3d_format *src_format, *dst_format;
6364 struct wined3d_surface *orig_src = src_surface;
6365 WINED3DLOCKED_RECT dlock, slock;
6366 HRESULT hr = WINED3D_OK;
6367 const BYTE *sbuf;
6368 RECT xdst,xsrc;
6369 BYTE *dbuf;
6370 int x, y;
6372 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6373 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6374 flags, fx, debug_d3dtexturefiltertype(filter));
6376 xsrc = *src_rect;
6378 if (!src_surface)
6380 RECT full_rect;
6382 full_rect.left = 0;
6383 full_rect.top = 0;
6384 full_rect.right = dst_surface->resource.width;
6385 full_rect.bottom = dst_surface->resource.height;
6386 IntersectRect(&xdst, &full_rect, dst_rect);
6388 else
6390 BOOL clip_horiz, clip_vert;
6392 xdst = *dst_rect;
6393 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6394 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6396 if (clip_vert || clip_horiz)
6398 /* Now check if this is a special case or not... */
6399 if ((flags & WINEDDBLT_DDFX)
6400 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6401 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6403 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6404 return WINED3D_OK;
6407 if (clip_horiz)
6409 if (xdst.left < 0)
6411 xsrc.left -= xdst.left;
6412 xdst.left = 0;
6414 if (xdst.right > dst_surface->resource.width)
6416 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6417 xdst.right = (int)dst_surface->resource.width;
6421 if (clip_vert)
6423 if (xdst.top < 0)
6425 xsrc.top -= xdst.top;
6426 xdst.top = 0;
6428 if (xdst.bottom > dst_surface->resource.height)
6430 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6431 xdst.bottom = (int)dst_surface->resource.height;
6435 /* And check if after clipping something is still to be done... */
6436 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6437 || (xdst.left >= (int)dst_surface->resource.width)
6438 || (xdst.top >= (int)dst_surface->resource.height)
6439 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6440 || (xsrc.left >= (int)src_surface->resource.width)
6441 || (xsrc.top >= (int)src_surface->resource.height))
6443 TRACE("Nothing to be done after clipping.\n");
6444 return WINED3D_OK;
6449 if (src_surface == dst_surface)
6451 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6452 slock = dlock;
6453 src_format = dst_surface->resource.format;
6454 dst_format = src_format;
6456 else
6458 dst_format = dst_surface->resource.format;
6459 if (src_surface)
6461 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6463 src_surface = surface_convert_format(src_surface, dst_format->id);
6464 if (!src_surface)
6466 /* The conv function writes a FIXME */
6467 WARN("Cannot convert source surface format to dest format.\n");
6468 goto release;
6471 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6472 src_format = src_surface->resource.format;
6474 else
6476 src_format = dst_format;
6478 if (dst_rect)
6479 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6480 else
6481 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6484 bpp = dst_surface->resource.format->byte_count;
6485 srcheight = xsrc.bottom - xsrc.top;
6486 srcwidth = xsrc.right - xsrc.left;
6487 dstheight = xdst.bottom - xdst.top;
6488 dstwidth = xdst.right - xdst.left;
6489 width = (xdst.right - xdst.left) * bpp;
6491 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6493 UINT row_block_count;
6495 if (flags || src_surface == dst_surface)
6497 FIXME("Only plain blits supported on compressed surfaces.\n");
6498 hr = E_NOTIMPL;
6499 goto release;
6502 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6504 if (srcheight != dstheight || srcwidth != dstwidth)
6506 WARN("Stretching not supported on compressed surfaces.\n");
6507 hr = WINED3DERR_INVALIDCALL;
6508 goto release;
6511 dbuf = dlock.pBits;
6512 sbuf = slock.pBits;
6514 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6515 for (y = 0; y < dstheight; y += dst_format->block_height)
6517 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6518 dbuf += dlock.Pitch;
6519 sbuf += slock.Pitch;
6522 goto release;
6525 if (dst_rect && src_surface != dst_surface)
6526 dbuf = dlock.pBits;
6527 else
6528 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6530 /* First, all the 'source-less' blits */
6531 if (flags & WINEDDBLT_COLORFILL)
6533 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6534 flags &= ~WINEDDBLT_COLORFILL;
6537 if (flags & WINEDDBLT_DEPTHFILL)
6539 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6541 if (flags & WINEDDBLT_ROP)
6543 /* Catch some degenerate cases here. */
6544 switch (fx->dwROP)
6546 case BLACKNESS:
6547 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6548 break;
6549 case 0xAA0029: /* No-op */
6550 break;
6551 case WHITENESS:
6552 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6553 break;
6554 case SRCCOPY: /* Well, we do that below? */
6555 break;
6556 default:
6557 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6558 goto error;
6560 flags &= ~WINEDDBLT_ROP;
6562 if (flags & WINEDDBLT_DDROPS)
6564 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6566 /* Now the 'with source' blits. */
6567 if (src_surface)
6569 const BYTE *sbase;
6570 int sx, xinc, sy, yinc;
6572 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6573 goto release;
6575 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6576 && (srcwidth != dstwidth || srcheight != dstheight))
6578 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6579 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6582 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6583 xinc = (srcwidth << 16) / dstwidth;
6584 yinc = (srcheight << 16) / dstheight;
6586 if (!flags)
6588 /* No effects, we can cheat here. */
6589 if (dstwidth == srcwidth)
6591 if (dstheight == srcheight)
6593 /* No stretching in either direction. This needs to be as
6594 * fast as possible. */
6595 sbuf = sbase;
6597 /* Check for overlapping surfaces. */
6598 if (src_surface != dst_surface || xdst.top < xsrc.top
6599 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6601 /* No overlap, or dst above src, so copy from top downwards. */
6602 for (y = 0; y < dstheight; ++y)
6604 memcpy(dbuf, sbuf, width);
6605 sbuf += slock.Pitch;
6606 dbuf += dlock.Pitch;
6609 else if (xdst.top > xsrc.top)
6611 /* Copy from bottom upwards. */
6612 sbuf += (slock.Pitch*dstheight);
6613 dbuf += (dlock.Pitch*dstheight);
6614 for (y = 0; y < dstheight; ++y)
6616 sbuf -= slock.Pitch;
6617 dbuf -= dlock.Pitch;
6618 memcpy(dbuf, sbuf, width);
6621 else
6623 /* Src and dst overlapping on the same line, use memmove. */
6624 for (y = 0; y < dstheight; ++y)
6626 memmove(dbuf, sbuf, width);
6627 sbuf += slock.Pitch;
6628 dbuf += dlock.Pitch;
6632 else
6634 /* Stretching in y direction only. */
6635 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6637 sbuf = sbase + (sy >> 16) * slock.Pitch;
6638 memcpy(dbuf, sbuf, width);
6639 dbuf += dlock.Pitch;
6643 else
6645 /* Stretching in X direction. */
6646 int last_sy = -1;
6647 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6649 sbuf = sbase + (sy >> 16) * slock.Pitch;
6651 if ((sy >> 16) == (last_sy >> 16))
6653 /* This source row is the same as last source row -
6654 * Copy the already stretched row. */
6655 memcpy(dbuf, dbuf - dlock.Pitch, width);
6657 else
6659 #define STRETCH_ROW(type) \
6660 do { \
6661 const type *s = (const type *)sbuf; \
6662 type *d = (type *)dbuf; \
6663 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6664 d[x] = s[sx >> 16]; \
6665 } while(0)
6667 switch(bpp)
6669 case 1:
6670 STRETCH_ROW(BYTE);
6671 break;
6672 case 2:
6673 STRETCH_ROW(WORD);
6674 break;
6675 case 4:
6676 STRETCH_ROW(DWORD);
6677 break;
6678 case 3:
6680 const BYTE *s;
6681 BYTE *d = dbuf;
6682 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6684 DWORD pixel;
6686 s = sbuf + 3 * (sx >> 16);
6687 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6688 d[0] = (pixel ) & 0xff;
6689 d[1] = (pixel >> 8) & 0xff;
6690 d[2] = (pixel >> 16) & 0xff;
6691 d += 3;
6693 break;
6695 default:
6696 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6697 hr = WINED3DERR_NOTAVAILABLE;
6698 goto error;
6700 #undef STRETCH_ROW
6702 dbuf += dlock.Pitch;
6703 last_sy = sy;
6707 else
6709 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6710 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6711 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6712 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6714 /* The color keying flags are checked for correctness in ddraw */
6715 if (flags & WINEDDBLT_KEYSRC)
6717 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6718 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6720 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6722 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6723 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6726 if (flags & WINEDDBLT_KEYDEST)
6728 /* Destination color keys are taken from the source surface! */
6729 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6730 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6732 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6734 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6735 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6738 if (bpp == 1)
6740 keymask = 0xff;
6742 else
6744 keymask = src_format->red_mask
6745 | src_format->green_mask
6746 | src_format->blue_mask;
6748 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6751 if (flags & WINEDDBLT_DDFX)
6753 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6754 LONG tmpxy;
6755 dTopLeft = dbuf;
6756 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6757 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6758 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6760 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6762 /* I don't think we need to do anything about this flag */
6763 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6765 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6767 tmp = dTopRight;
6768 dTopRight = dTopLeft;
6769 dTopLeft = tmp;
6770 tmp = dBottomRight;
6771 dBottomRight = dBottomLeft;
6772 dBottomLeft = tmp;
6773 dstxinc = dstxinc * -1;
6775 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6777 tmp = dTopLeft;
6778 dTopLeft = dBottomLeft;
6779 dBottomLeft = tmp;
6780 tmp = dTopRight;
6781 dTopRight = dBottomRight;
6782 dBottomRight = tmp;
6783 dstyinc = dstyinc * -1;
6785 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6787 /* I don't think we need to do anything about this flag */
6788 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6790 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6792 tmp = dBottomRight;
6793 dBottomRight = dTopLeft;
6794 dTopLeft = tmp;
6795 tmp = dBottomLeft;
6796 dBottomLeft = dTopRight;
6797 dTopRight = tmp;
6798 dstxinc = dstxinc * -1;
6799 dstyinc = dstyinc * -1;
6801 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6803 tmp = dTopLeft;
6804 dTopLeft = dBottomLeft;
6805 dBottomLeft = dBottomRight;
6806 dBottomRight = dTopRight;
6807 dTopRight = tmp;
6808 tmpxy = dstxinc;
6809 dstxinc = dstyinc;
6810 dstyinc = tmpxy;
6811 dstxinc = dstxinc * -1;
6813 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6815 tmp = dTopLeft;
6816 dTopLeft = dTopRight;
6817 dTopRight = dBottomRight;
6818 dBottomRight = dBottomLeft;
6819 dBottomLeft = tmp;
6820 tmpxy = dstxinc;
6821 dstxinc = dstyinc;
6822 dstyinc = tmpxy;
6823 dstyinc = dstyinc * -1;
6825 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6827 /* I don't think we need to do anything about this flag */
6828 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6830 dbuf = dTopLeft;
6831 flags &= ~(WINEDDBLT_DDFX);
6834 #define COPY_COLORKEY_FX(type) \
6835 do { \
6836 const type *s; \
6837 type *d = (type *)dbuf, *dx, tmp; \
6838 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6840 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6841 dx = d; \
6842 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6844 tmp = s[sx >> 16]; \
6845 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6846 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6848 dx[0] = tmp; \
6850 dx = (type *)(((BYTE *)dx) + dstxinc); \
6852 d = (type *)(((BYTE *)d) + dstyinc); \
6854 } while(0)
6856 switch (bpp)
6858 case 1:
6859 COPY_COLORKEY_FX(BYTE);
6860 break;
6861 case 2:
6862 COPY_COLORKEY_FX(WORD);
6863 break;
6864 case 4:
6865 COPY_COLORKEY_FX(DWORD);
6866 break;
6867 case 3:
6869 const BYTE *s;
6870 BYTE *d = dbuf, *dx;
6871 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6873 sbuf = sbase + (sy >> 16) * slock.Pitch;
6874 dx = d;
6875 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6877 DWORD pixel, dpixel = 0;
6878 s = sbuf + 3 * (sx>>16);
6879 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6880 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6881 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6882 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6884 dx[0] = (pixel ) & 0xff;
6885 dx[1] = (pixel >> 8) & 0xff;
6886 dx[2] = (pixel >> 16) & 0xff;
6888 dx += dstxinc;
6890 d += dstyinc;
6892 break;
6894 default:
6895 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6896 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6897 hr = WINED3DERR_NOTAVAILABLE;
6898 goto error;
6899 #undef COPY_COLORKEY_FX
6904 error:
6905 if (flags && FIXME_ON(d3d_surface))
6907 FIXME("\tUnsupported flags: %#x.\n", flags);
6910 release:
6911 wined3d_surface_unmap(dst_surface);
6912 if (src_surface && src_surface != dst_surface)
6913 wined3d_surface_unmap(src_surface);
6914 /* Release the converted surface, if any. */
6915 if (src_surface && src_surface != orig_src)
6916 wined3d_surface_decref(src_surface);
6918 return hr;
6921 /* Do not call while under the GL lock. */
6922 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6923 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6925 static const RECT src_rect;
6926 WINEDDBLTFX BltFx;
6928 memset(&BltFx, 0, sizeof(BltFx));
6929 BltFx.dwSize = sizeof(BltFx);
6930 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6931 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6932 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
6935 /* Do not call while under the GL lock. */
6936 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6937 struct wined3d_surface *surface, const RECT *rect, float depth)
6939 FIXME("Depth filling not implemented by cpu_blit.\n");
6940 return WINED3DERR_INVALIDCALL;
6943 const struct blit_shader cpu_blit = {
6944 cpu_blit_alloc,
6945 cpu_blit_free,
6946 cpu_blit_set,
6947 cpu_blit_unset,
6948 cpu_blit_supported,
6949 cpu_blit_color_fill,
6950 cpu_blit_depth_fill,
6953 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
6954 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
6955 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
6956 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
6958 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6959 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
6960 unsigned int resource_size;
6961 HRESULT hr;
6963 if (multisample_quality > 0)
6965 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6966 multisample_quality = 0;
6969 /* Quick lockable sanity check.
6970 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6971 * this function is too deep to need to care about things like this.
6972 * Levels need to be checked too, since they all affect what can be done. */
6973 switch (pool)
6975 case WINED3DPOOL_SCRATCH:
6976 if (!lockable)
6978 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
6979 "which are mutually exclusive, setting lockable to TRUE.\n");
6980 lockable = TRUE;
6982 break;
6984 case WINED3DPOOL_SYSTEMMEM:
6985 if (!lockable)
6986 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
6987 break;
6989 case WINED3DPOOL_MANAGED:
6990 if (usage & WINED3DUSAGE_DYNAMIC)
6991 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6992 break;
6994 case WINED3DPOOL_DEFAULT:
6995 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6996 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6997 break;
6999 default:
7000 FIXME("Unknown pool %#x.\n", pool);
7001 break;
7004 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7005 FIXME("Trying to create a render target that isn't in the default pool.\n");
7007 /* FIXME: Check that the format is supported by the device. */
7009 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7010 if (!resource_size)
7011 return WINED3DERR_INVALIDCALL;
7013 surface->surface_type = surface_type;
7015 switch (surface_type)
7017 case SURFACE_OPENGL:
7018 surface->surface_ops = &surface_ops;
7019 break;
7021 case SURFACE_GDI:
7022 surface->surface_ops = &gdi_surface_ops;
7023 break;
7025 default:
7026 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7027 return WINED3DERR_INVALIDCALL;
7030 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7031 multisample_type, multisample_quality, usage, pool, width, height, 1,
7032 resource_size, parent, parent_ops, &surface_resource_ops);
7033 if (FAILED(hr))
7035 WARN("Failed to initialize resource, returning %#x.\n", hr);
7036 return hr;
7039 /* "Standalone" surface. */
7040 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7042 surface->texture_level = level;
7043 list_init(&surface->overlays);
7045 /* Flags */
7046 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7047 if (discard)
7048 surface->flags |= SFLAG_DISCARD;
7049 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7050 surface->flags |= SFLAG_LOCKABLE;
7051 /* I'm not sure if this qualifies as a hack or as an optimization. It
7052 * seems reasonable to assume that lockable render targets will get
7053 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7054 * creation. However, the other reason we want to do this is that several
7055 * ddraw applications access surface memory while the surface isn't
7056 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7057 * future locks prevents these from crashing. */
7058 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7059 surface->flags |= SFLAG_DYNLOCK;
7061 /* Mark the texture as dirty so that it gets loaded first time around. */
7062 surface_add_dirty_rect(surface, NULL);
7063 list_init(&surface->renderbuffers);
7065 TRACE("surface %p, memory %p, size %u\n",
7066 surface, surface->resource.allocatedMemory, surface->resource.size);
7068 /* Call the private setup routine */
7069 hr = surface->surface_ops->surface_private_setup(surface);
7070 if (FAILED(hr))
7072 ERR("Private setup failed, returning %#x\n", hr);
7073 surface->surface_ops->surface_cleanup(surface);
7074 return hr;
7077 return hr;
7080 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7081 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7082 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7083 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7085 struct wined3d_surface *object;
7086 HRESULT hr;
7088 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7089 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7090 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7091 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7092 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7094 if (surface_type == SURFACE_OPENGL && !device->adapter)
7096 ERR("OpenGL surfaces are not available without OpenGL.\n");
7097 return WINED3DERR_NOTAVAILABLE;
7100 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7101 if (!object)
7103 ERR("Failed to allocate surface memory.\n");
7104 return WINED3DERR_OUTOFVIDEOMEMORY;
7107 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7108 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7109 if (FAILED(hr))
7111 WARN("Failed to initialize surface, returning %#x.\n", hr);
7112 HeapFree(GetProcessHeap(), 0, object);
7113 return hr;
7116 TRACE("Created surface %p.\n", object);
7117 *surface = object;
7119 return hr;