wined3d: Disable sRGB decoding in draw_textured_quad().
[wine/multimedia.git] / dlls / wined3d / surface.c
blobd62e31cc7c61d38ad9d076dacb8c2c582fe907ea
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 if (flags & ~simple_blit)
1559 WARN("Using fallback for complex blit (%#x).\n", flags);
1560 goto fallback;
1563 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1564 src_swapchain = src_surface->container.u.swapchain;
1565 else
1566 src_swapchain = NULL;
1568 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1569 dst_swapchain = dst_surface->container.u.swapchain;
1570 else
1571 dst_swapchain = NULL;
1573 /* This isn't strictly needed. FBO blits for example could deal with
1574 * cross-swapchain blits by first downloading the source to a texture
1575 * before switching to the destination context. We just have this here to
1576 * not have to deal with the issue, since cross-swapchain blits should be
1577 * rare. */
1578 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1580 FIXME("Using fallback for cross-swapchain blit.\n");
1581 goto fallback;
1584 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1585 if (src_surface)
1586 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1587 else
1588 src_ds_flags = 0;
1590 if (src_ds_flags || dst_ds_flags)
1592 if (flags & WINEDDBLT_DEPTHFILL)
1594 float depth;
1596 TRACE("Depth fill.\n");
1598 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1599 return WINED3DERR_INVALIDCALL;
1601 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1602 return WINED3D_OK;
1604 else
1606 /* Accessing depth / stencil surfaces is supposed to fail while in
1607 * a scene, except for fills, which seem to work. */
1608 if (device->inScene)
1610 WARN("Rejecting depth / stencil access while in scene.\n");
1611 return WINED3DERR_INVALIDCALL;
1614 if (src_ds_flags != dst_ds_flags)
1616 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if (src_rect.top || src_rect.left
1621 || src_rect.bottom != src_surface->resource.height
1622 || src_rect.right != src_surface->resource.width)
1624 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1625 wine_dbgstr_rect(&src_rect));
1626 return WINED3DERR_INVALIDCALL;
1629 if (dst_rect.top || dst_rect.left
1630 || dst_rect.bottom != dst_surface->resource.height
1631 || dst_rect.right != dst_surface->resource.width)
1633 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1634 wine_dbgstr_rect(&src_rect));
1635 return WINED3DERR_INVALIDCALL;
1638 if (src_surface->resource.height != dst_surface->resource.height
1639 || src_surface->resource.width != dst_surface->resource.width)
1641 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1642 return WINED3DERR_INVALIDCALL;
1645 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1646 return WINED3D_OK;
1649 else
1651 if (flags & WINEDDBLT_COLORFILL)
1653 WINED3DCOLORVALUE color;
1655 TRACE("Color fill.\n");
1657 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1658 goto fallback;
1660 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1661 return WINED3D_OK;
1663 else
1665 TRACE("Color blit.\n");
1667 /* Use present for back -> front blits. The idea behind this is
1668 * that present is potentially faster than a blit, in particular
1669 * when FBO blits aren't available. Some ddraw applications like
1670 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1671 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1672 * applications can't blit directly to the frontbuffer. */
1673 if (dst_swapchain && dst_swapchain->back_buffers
1674 && dst_surface == dst_swapchain->front_buffer
1675 && src_surface == dst_swapchain->back_buffers[0])
1677 WINED3DSWAPEFFECT swap_effect = dst_swapchain->presentParms.SwapEffect;
1679 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1681 /* Set the swap effect to COPY, we don't want the backbuffer
1682 * to become undefined. */
1683 dst_swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
1684 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1685 dst_swapchain->presentParms.SwapEffect = swap_effect;
1687 return WINED3D_OK;
1690 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1691 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1692 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1694 TRACE("Using FBO blit.\n");
1696 surface_blt_fbo(device, filter,
1697 src_surface, src_surface->draw_binding, &src_rect,
1698 dst_surface, dst_surface->draw_binding, &dst_rect);
1699 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1700 return WINED3D_OK;
1703 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1704 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1705 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1707 TRACE("Using arbfp blit.\n");
1709 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1710 return WINED3D_OK;
1715 fallback:
1717 /* Special cases for render targets. */
1718 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1719 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1721 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1722 src_surface, &src_rect, flags, fx, filter)))
1723 return WINED3D_OK;
1726 cpu:
1728 /* For the rest call the X11 surface implementation. For render targets
1729 * this should be implemented OpenGL accelerated in BltOverride, other
1730 * blits are rather rare. */
1731 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1734 /* Do not call while under the GL lock. */
1735 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1736 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1738 RECT src_rect, dst_rect;
1739 DWORD flags = 0;
1741 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1742 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1744 surface_get_rect(src_surface, src_rect_in, &src_rect);
1746 dst_rect.left = dst_x;
1747 dst_rect.top = dst_y;
1748 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1749 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1751 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1752 flags |= WINEDDBLT_KEYSRC;
1753 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1754 flags |= WINEDDBLT_KEYDEST;
1755 if (trans & WINEDDBLTFAST_WAIT)
1756 flags |= WINEDDBLT_WAIT;
1757 if (trans & WINEDDBLTFAST_DONOTWAIT)
1758 flags |= WINEDDBLT_DONOTWAIT;
1760 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1763 /* Context activation is done by the caller. */
1764 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1766 if (!surface->resource.heapMemory)
1768 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1769 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1770 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1773 ENTER_GL();
1774 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1775 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1776 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1777 surface->resource.size, surface->resource.allocatedMemory));
1778 checkGLcall("glGetBufferSubDataARB");
1779 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1780 checkGLcall("glDeleteBuffersARB");
1781 LEAVE_GL();
1783 surface->pbo = 0;
1784 surface->flags &= ~SFLAG_PBO;
1787 /* Do not call while under the GL lock. */
1788 static void surface_unload(struct wined3d_resource *resource)
1790 struct wined3d_surface *surface = surface_from_resource(resource);
1791 struct wined3d_renderbuffer_entry *entry, *entry2;
1792 struct wined3d_device *device = resource->device;
1793 const struct wined3d_gl_info *gl_info;
1794 struct wined3d_context *context;
1796 TRACE("surface %p.\n", surface);
1798 if (resource->pool == WINED3DPOOL_DEFAULT)
1800 /* Default pool resources are supposed to be destroyed before Reset is called.
1801 * Implicit resources stay however. So this means we have an implicit render target
1802 * or depth stencil. The content may be destroyed, but we still have to tear down
1803 * opengl resources, so we cannot leave early.
1805 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1806 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1807 * or the depth stencil into an FBO the texture or render buffer will be removed
1808 * and all flags get lost
1810 surface_init_sysmem(surface);
1812 else
1814 /* Load the surface into system memory */
1815 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1816 surface_modify_location(surface, surface->draw_binding, FALSE);
1818 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1819 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1820 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1822 context = context_acquire(device, NULL);
1823 gl_info = context->gl_info;
1825 /* Destroy PBOs, but load them into real sysmem before */
1826 if (surface->flags & SFLAG_PBO)
1827 surface_remove_pbo(surface, gl_info);
1829 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1830 * all application-created targets the application has to release the surface
1831 * before calling _Reset
1833 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1835 ENTER_GL();
1836 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1837 LEAVE_GL();
1838 list_remove(&entry->entry);
1839 HeapFree(GetProcessHeap(), 0, entry);
1841 list_init(&surface->renderbuffers);
1842 surface->current_renderbuffer = NULL;
1844 ENTER_GL();
1846 /* If we're in a texture, the texture name belongs to the texture.
1847 * Otherwise, destroy it. */
1848 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1850 glDeleteTextures(1, &surface->texture_name);
1851 surface->texture_name = 0;
1852 glDeleteTextures(1, &surface->texture_name_srgb);
1853 surface->texture_name_srgb = 0;
1855 if (surface->rb_multisample)
1857 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1858 surface->rb_multisample = 0;
1860 if (surface->rb_resolved)
1862 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1863 surface->rb_resolved = 0;
1866 LEAVE_GL();
1868 context_release(context);
1870 resource_unload(resource);
1873 static const struct wined3d_resource_ops surface_resource_ops =
1875 surface_unload,
1878 static const struct wined3d_surface_ops surface_ops =
1880 surface_private_setup,
1881 surface_cleanup,
1882 surface_realize_palette,
1883 surface_draw_overlay,
1884 surface_preload,
1885 surface_map,
1886 surface_unmap,
1887 surface_getdc,
1890 /*****************************************************************************
1891 * Initializes the GDI surface, aka creates the DIB section we render to
1892 * The DIB section creation is done by calling GetDC, which will create the
1893 * section and releasing the dc to allow the app to use it. The dib section
1894 * will stay until the surface is released
1896 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1897 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1898 * avoid confusion in the shared surface code.
1900 * Returns:
1901 * WINED3D_OK on success
1902 * The return values of called methods on failure
1904 *****************************************************************************/
1905 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1907 HRESULT hr;
1909 TRACE("surface %p.\n", surface);
1911 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1913 ERR("Overlays not yet supported by GDI surfaces.\n");
1914 return WINED3DERR_INVALIDCALL;
1917 /* Sysmem textures have memory already allocated - release it,
1918 * this avoids an unnecessary memcpy. */
1919 hr = surface_create_dib_section(surface);
1920 if (SUCCEEDED(hr))
1922 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1923 surface->resource.heapMemory = NULL;
1924 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1927 /* We don't mind the nonpow2 stuff in GDI. */
1928 surface->pow2Width = surface->resource.width;
1929 surface->pow2Height = surface->resource.height;
1931 return WINED3D_OK;
1934 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1936 TRACE("surface %p.\n", surface);
1938 if (surface->flags & SFLAG_DIBSECTION)
1940 /* Release the DC. */
1941 SelectObject(surface->hDC, surface->dib.holdbitmap);
1942 DeleteDC(surface->hDC);
1943 /* Release the DIB section. */
1944 DeleteObject(surface->dib.DIBsection);
1945 surface->dib.bitmap_data = NULL;
1946 surface->resource.allocatedMemory = NULL;
1949 if (surface->flags & SFLAG_USERPTR)
1950 wined3d_surface_set_mem(surface, NULL);
1951 if (surface->overlay_dest)
1952 list_remove(&surface->overlay_entry);
1954 HeapFree(GetProcessHeap(), 0, surface->palette9);
1956 resource_cleanup(&surface->resource);
1959 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1961 struct wined3d_palette *palette = surface->palette;
1963 TRACE("surface %p.\n", surface);
1965 if (!palette) return;
1967 if (surface->flags & SFLAG_DIBSECTION)
1969 RGBQUAD col[256];
1970 unsigned int i;
1972 TRACE("Updating the DC's palette.\n");
1974 for (i = 0; i < 256; ++i)
1976 col[i].rgbRed = palette->palents[i].peRed;
1977 col[i].rgbGreen = palette->palents[i].peGreen;
1978 col[i].rgbBlue = palette->palents[i].peBlue;
1979 col[i].rgbReserved = 0;
1981 SetDIBColorTable(surface->hDC, 0, 256, col);
1984 /* Update the image because of the palette change. Some games like e.g.
1985 * Red Alert call SetEntries a lot to implement fading. */
1986 /* Tell the swapchain to update the screen. */
1987 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1989 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1990 if (surface == swapchain->front_buffer)
1992 x11_copy_to_screen(swapchain, NULL);
1997 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
1999 FIXME("GDI surfaces can't draw overlays yet.\n");
2000 return E_FAIL;
2003 static void gdi_surface_preload(struct wined3d_surface *surface)
2005 TRACE("surface %p.\n", surface);
2007 ERR("Preloading GDI surfaces is not supported.\n");
2010 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2012 TRACE("surface %p, rect %s, flags %#x.\n",
2013 surface, wine_dbgstr_rect(rect), flags);
2015 if (!surface->resource.allocatedMemory)
2017 /* This happens on gdi surfaces if the application set a user pointer
2018 * and resets it. Recreate the DIB section. */
2019 surface_create_dib_section(surface);
2020 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2024 static void gdi_surface_unmap(struct wined3d_surface *surface)
2026 TRACE("surface %p.\n", surface);
2028 /* Tell the swapchain to update the screen. */
2029 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2031 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2032 if (surface == swapchain->front_buffer)
2034 x11_copy_to_screen(swapchain, &surface->lockedRect);
2038 memset(&surface->lockedRect, 0, sizeof(RECT));
2041 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2043 WINED3DLOCKED_RECT lock;
2044 HRESULT hr;
2046 TRACE("surface %p.\n", surface);
2048 /* Should have a DIB section already. */
2049 if (!(surface->flags & SFLAG_DIBSECTION))
2051 WARN("DC not supported on this surface\n");
2052 return WINED3DERR_INVALIDCALL;
2055 /* Map the surface. */
2056 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2057 if (FAILED(hr))
2058 ERR("Map failed, hr %#x.\n", hr);
2060 return hr;
2063 static const struct wined3d_surface_ops gdi_surface_ops =
2065 gdi_surface_private_setup,
2066 surface_gdi_cleanup,
2067 gdi_surface_realize_palette,
2068 gdi_surface_draw_overlay,
2069 gdi_surface_preload,
2070 gdi_surface_map,
2071 gdi_surface_unmap,
2072 gdi_surface_getdc,
2075 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2077 GLuint *name;
2078 DWORD flag;
2080 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2082 if(srgb)
2084 name = &surface->texture_name_srgb;
2085 flag = SFLAG_INSRGBTEX;
2087 else
2089 name = &surface->texture_name;
2090 flag = SFLAG_INTEXTURE;
2093 if (!*name && new_name)
2095 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2096 * surface has no texture name yet. See if we can get rid of this. */
2097 if (surface->flags & flag)
2098 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2099 surface_modify_location(surface, flag, FALSE);
2102 *name = new_name;
2103 surface_force_reload(surface);
2106 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2108 TRACE("surface %p, target %#x.\n", surface, target);
2110 if (surface->texture_target != target)
2112 if (target == GL_TEXTURE_RECTANGLE_ARB)
2114 surface->flags &= ~SFLAG_NORMCOORD;
2116 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2118 surface->flags |= SFLAG_NORMCOORD;
2121 surface->texture_target = target;
2122 surface_force_reload(surface);
2125 /* Context activation is done by the caller. */
2126 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2128 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2130 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2132 struct wined3d_texture *texture = surface->container.u.texture;
2134 TRACE("Passing to container (%p).\n", texture);
2135 texture->texture_ops->texture_bind(texture, context, srgb);
2137 else
2139 if (surface->texture_level)
2141 ERR("Standalone surface %p is non-zero texture level %u.\n",
2142 surface, surface->texture_level);
2145 if (srgb)
2146 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2148 ENTER_GL();
2150 if (!surface->texture_name)
2152 glGenTextures(1, &surface->texture_name);
2153 checkGLcall("glGenTextures");
2155 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2157 context_bind_texture(context, surface->texture_target, surface->texture_name);
2158 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2159 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2160 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2161 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2162 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2163 checkGLcall("glTexParameteri");
2165 else
2167 context_bind_texture(context, surface->texture_target, surface->texture_name);
2170 LEAVE_GL();
2174 /* This call just downloads data, the caller is responsible for binding the
2175 * correct texture. */
2176 /* Context activation is done by the caller. */
2177 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2179 const struct wined3d_format *format = surface->resource.format;
2181 /* Only support read back of converted P8 surfaces. */
2182 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2184 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2185 return;
2188 ENTER_GL();
2190 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2192 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2193 surface, surface->texture_level, format->glFormat, format->glType,
2194 surface->resource.allocatedMemory);
2196 if (surface->flags & SFLAG_PBO)
2198 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2199 checkGLcall("glBindBufferARB");
2200 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2201 checkGLcall("glGetCompressedTexImageARB");
2202 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2203 checkGLcall("glBindBufferARB");
2205 else
2207 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2208 surface->texture_level, surface->resource.allocatedMemory));
2209 checkGLcall("glGetCompressedTexImageARB");
2212 LEAVE_GL();
2214 else
2216 void *mem;
2217 GLenum gl_format = format->glFormat;
2218 GLenum gl_type = format->glType;
2219 int src_pitch = 0;
2220 int dst_pitch = 0;
2222 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2223 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2225 gl_format = GL_ALPHA;
2226 gl_type = GL_UNSIGNED_BYTE;
2229 if (surface->flags & SFLAG_NONPOW2)
2231 unsigned char alignment = surface->resource.device->surface_alignment;
2232 src_pitch = format->byte_count * surface->pow2Width;
2233 dst_pitch = wined3d_surface_get_pitch(surface);
2234 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2235 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2237 else
2239 mem = surface->resource.allocatedMemory;
2242 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2243 surface, surface->texture_level, gl_format, gl_type, mem);
2245 if (surface->flags & SFLAG_PBO)
2247 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2248 checkGLcall("glBindBufferARB");
2250 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2251 checkGLcall("glGetTexImage");
2253 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2254 checkGLcall("glBindBufferARB");
2256 else
2258 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2259 checkGLcall("glGetTexImage");
2261 LEAVE_GL();
2263 if (surface->flags & SFLAG_NONPOW2)
2265 const BYTE *src_data;
2266 BYTE *dst_data;
2267 UINT y;
2269 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2270 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2271 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2273 * We're doing this...
2275 * instead of boxing the texture :
2276 * |<-texture width ->| -->pow2width| /\
2277 * |111111111111111111| | |
2278 * |222 Texture 222222| boxed empty | texture height
2279 * |3333 Data 33333333| | |
2280 * |444444444444444444| | \/
2281 * ----------------------------------- |
2282 * | boxed empty | boxed empty | pow2height
2283 * | | | \/
2284 * -----------------------------------
2287 * we're repacking the data to the expected texture width
2289 * |<-texture width ->| -->pow2width| /\
2290 * |111111111111111111222222222222222| |
2291 * |222333333333333333333444444444444| texture height
2292 * |444444 | |
2293 * | | \/
2294 * | | |
2295 * | empty | pow2height
2296 * | | \/
2297 * -----------------------------------
2299 * == is the same as
2301 * |<-texture width ->| /\
2302 * |111111111111111111|
2303 * |222222222222222222|texture height
2304 * |333333333333333333|
2305 * |444444444444444444| \/
2306 * --------------------
2308 * this also means that any references to allocatedMemory should work with the data as if were a
2309 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2311 * internally the texture is still stored in a boxed format so any references to textureName will
2312 * get a boxed texture with width pow2width and not a texture of width resource.width.
2314 * Performance should not be an issue, because applications normally do not lock the surfaces when
2315 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2316 * and doesn't have to be re-read. */
2317 src_data = mem;
2318 dst_data = surface->resource.allocatedMemory;
2319 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2320 for (y = 1; y < surface->resource.height; ++y)
2322 /* skip the first row */
2323 src_data += src_pitch;
2324 dst_data += dst_pitch;
2325 memcpy(dst_data, src_data, dst_pitch);
2328 HeapFree(GetProcessHeap(), 0, mem);
2332 /* Surface has now been downloaded */
2333 surface->flags |= SFLAG_INSYSMEM;
2336 /* This call just uploads data, the caller is responsible for binding the
2337 * correct texture. */
2338 /* Context activation is done by the caller. */
2339 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2340 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2341 BOOL srgb, const struct wined3d_bo_address *data)
2343 UINT update_w = src_rect->right - src_rect->left;
2344 UINT update_h = src_rect->bottom - src_rect->top;
2346 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2347 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2348 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2350 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2351 update_h *= format->heightscale;
2353 ENTER_GL();
2355 if (data->buffer_object)
2357 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2358 checkGLcall("glBindBufferARB");
2361 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2363 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2364 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2365 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2366 const BYTE *addr = data->addr;
2367 GLenum internal;
2369 addr += (src_rect->top / format->block_height) * src_pitch;
2370 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2372 if (srgb)
2373 internal = format->glGammaInternal;
2374 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2375 internal = format->rtInternal;
2376 else
2377 internal = format->glInternal;
2379 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2380 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2381 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2383 if (row_length == src_pitch)
2385 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2386 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2388 else
2390 UINT row, y;
2392 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2393 * can't use the unpack row length like below. */
2394 for (row = 0, y = dst_point->y; row < row_count; ++row)
2396 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2397 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2398 y += format->block_height;
2399 addr += src_pitch;
2402 checkGLcall("glCompressedTexSubImage2DARB");
2404 else
2406 const BYTE *addr = data->addr;
2408 addr += src_rect->top * src_w * format->byte_count;
2409 addr += src_rect->left * format->byte_count;
2411 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2412 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2413 update_w, update_h, format->glFormat, format->glType, addr);
2415 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2416 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2417 update_w, update_h, format->glFormat, format->glType, addr);
2418 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2419 checkGLcall("glTexSubImage2D");
2422 if (data->buffer_object)
2424 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2425 checkGLcall("glBindBufferARB");
2428 LEAVE_GL();
2430 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2432 struct wined3d_device *device = surface->resource.device;
2433 unsigned int i;
2435 for (i = 0; i < device->context_count; ++i)
2437 context_surface_update(device->contexts[i], surface);
2442 /* This call just allocates the texture, the caller is responsible for binding
2443 * the correct texture. */
2444 /* Context activation is done by the caller. */
2445 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2446 const struct wined3d_format *format, BOOL srgb)
2448 BOOL enable_client_storage = FALSE;
2449 GLsizei width = surface->pow2Width;
2450 GLsizei height = surface->pow2Height;
2451 const BYTE *mem = NULL;
2452 GLenum internal;
2454 if (srgb)
2456 internal = format->glGammaInternal;
2458 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2460 internal = format->rtInternal;
2462 else
2464 internal = format->glInternal;
2467 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2469 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",
2470 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2471 internal, width, height, format->glFormat, format->glType);
2473 ENTER_GL();
2475 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2477 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2478 || !surface->resource.allocatedMemory)
2480 /* In some cases we want to disable client storage.
2481 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2482 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2483 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2484 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2486 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2487 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2488 surface->flags &= ~SFLAG_CLIENT;
2489 enable_client_storage = TRUE;
2491 else
2493 surface->flags |= SFLAG_CLIENT;
2495 /* Point OpenGL to our allocated texture memory. Do not use
2496 * resource.allocatedMemory here because it might point into a
2497 * PBO. Instead use heapMemory, but get the alignment right. */
2498 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2499 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2503 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2505 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2506 internal, width, height, 0, surface->resource.size, mem));
2507 checkGLcall("glCompressedTexImage2DARB");
2509 else
2511 glTexImage2D(surface->texture_target, surface->texture_level,
2512 internal, width, height, 0, format->glFormat, format->glType, mem);
2513 checkGLcall("glTexImage2D");
2516 if(enable_client_storage) {
2517 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2518 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2520 LEAVE_GL();
2523 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2524 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2525 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2526 /* GL locking is done by the caller */
2527 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2529 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2530 struct wined3d_renderbuffer_entry *entry;
2531 GLuint renderbuffer = 0;
2532 unsigned int src_width, src_height;
2533 unsigned int width, height;
2535 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2537 width = rt->pow2Width;
2538 height = rt->pow2Height;
2540 else
2542 width = surface->pow2Width;
2543 height = surface->pow2Height;
2546 src_width = surface->pow2Width;
2547 src_height = surface->pow2Height;
2549 /* A depth stencil smaller than the render target is not valid */
2550 if (width > src_width || height > src_height) return;
2552 /* Remove any renderbuffer set if the sizes match */
2553 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2554 || (width == src_width && height == src_height))
2556 surface->current_renderbuffer = NULL;
2557 return;
2560 /* Look if we've already got a renderbuffer of the correct dimensions */
2561 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2563 if (entry->width == width && entry->height == height)
2565 renderbuffer = entry->id;
2566 surface->current_renderbuffer = entry;
2567 break;
2571 if (!renderbuffer)
2573 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2574 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2575 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2576 surface->resource.format->glInternal, width, height);
2578 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2579 entry->width = width;
2580 entry->height = height;
2581 entry->id = renderbuffer;
2582 list_add_head(&surface->renderbuffers, &entry->entry);
2584 surface->current_renderbuffer = entry;
2587 checkGLcall("set_compatible_renderbuffer");
2590 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2592 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2594 TRACE("surface %p.\n", surface);
2596 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2598 ERR("Surface %p is not on a swapchain.\n", surface);
2599 return GL_NONE;
2602 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2604 if (swapchain->render_to_fbo)
2606 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2607 return GL_COLOR_ATTACHMENT0;
2609 TRACE("Returning GL_BACK\n");
2610 return GL_BACK;
2612 else if (surface == swapchain->front_buffer)
2614 TRACE("Returning GL_FRONT\n");
2615 return GL_FRONT;
2618 FIXME("Higher back buffer, returning GL_BACK\n");
2619 return GL_BACK;
2622 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2623 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2625 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2627 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2628 /* No partial locking for textures yet. */
2629 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2631 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2632 if (dirty_rect)
2634 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2635 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2636 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2637 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2639 else
2641 surface->dirtyRect.left = 0;
2642 surface->dirtyRect.top = 0;
2643 surface->dirtyRect.right = surface->resource.width;
2644 surface->dirtyRect.bottom = surface->resource.height;
2647 /* if the container is a texture then mark it dirty. */
2648 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2650 TRACE("Passing to container.\n");
2651 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2655 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2657 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2658 BOOL ck_changed;
2660 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2662 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2664 ERR("Not supported on scratch surfaces.\n");
2665 return WINED3DERR_INVALIDCALL;
2668 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2670 /* Reload if either the texture and sysmem have different ideas about the
2671 * color key, or the actual key values changed. */
2672 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2673 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2674 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2676 TRACE("Reloading because of color keying\n");
2677 /* To perform the color key conversion we need a sysmem copy of
2678 * the surface. Make sure we have it. */
2680 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2681 /* Make sure the texture is reloaded because of the color key change,
2682 * this kills performance though :( */
2683 /* TODO: This is not necessarily needed with hw palettized texture support. */
2684 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2685 /* Switching color keying on / off may change the internal format. */
2686 if (ck_changed)
2687 surface_force_reload(surface);
2689 else if (!(surface->flags & flag))
2691 TRACE("Reloading because surface is dirty.\n");
2693 else
2695 TRACE("surface is already in texture\n");
2696 return WINED3D_OK;
2699 /* No partial locking for textures yet. */
2700 surface_load_location(surface, flag, NULL);
2701 surface_evict_sysmem(surface);
2703 return WINED3D_OK;
2706 /* See also float_16_to_32() in wined3d_private.h */
2707 static inline unsigned short float_32_to_16(const float *in)
2709 int exp = 0;
2710 float tmp = fabsf(*in);
2711 unsigned int mantissa;
2712 unsigned short ret;
2714 /* Deal with special numbers */
2715 if (*in == 0.0f)
2716 return 0x0000;
2717 if (isnan(*in))
2718 return 0x7c01;
2719 if (isinf(*in))
2720 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2722 if (tmp < powf(2, 10))
2726 tmp = tmp * 2.0f;
2727 exp--;
2728 } while (tmp < powf(2, 10));
2730 else if (tmp >= powf(2, 11))
2734 tmp /= 2.0f;
2735 exp++;
2736 } while (tmp >= powf(2, 11));
2739 mantissa = (unsigned int)tmp;
2740 if (tmp - mantissa >= 0.5f)
2741 ++mantissa; /* Round to nearest, away from zero. */
2743 exp += 10; /* Normalize the mantissa. */
2744 exp += 15; /* Exponent is encoded with excess 15. */
2746 if (exp > 30) /* too big */
2748 ret = 0x7c00; /* INF */
2750 else if (exp <= 0)
2752 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2753 while (exp <= 0)
2755 mantissa = mantissa >> 1;
2756 ++exp;
2758 ret = mantissa & 0x3ff;
2760 else
2762 ret = (exp << 10) | (mantissa & 0x3ff);
2765 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2766 return ret;
2769 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2771 ULONG refcount;
2773 TRACE("Surface %p, container %p of type %#x.\n",
2774 surface, surface->container.u.base, surface->container.type);
2776 switch (surface->container.type)
2778 case WINED3D_CONTAINER_TEXTURE:
2779 return wined3d_texture_incref(surface->container.u.texture);
2781 case WINED3D_CONTAINER_SWAPCHAIN:
2782 return wined3d_swapchain_incref(surface->container.u.swapchain);
2784 default:
2785 ERR("Unhandled container type %#x.\n", surface->container.type);
2786 case WINED3D_CONTAINER_NONE:
2787 break;
2790 refcount = InterlockedIncrement(&surface->resource.ref);
2791 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2793 return refcount;
2796 /* Do not call while under the GL lock. */
2797 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2799 ULONG refcount;
2801 TRACE("Surface %p, container %p of type %#x.\n",
2802 surface, surface->container.u.base, surface->container.type);
2804 switch (surface->container.type)
2806 case WINED3D_CONTAINER_TEXTURE:
2807 return wined3d_texture_decref(surface->container.u.texture);
2809 case WINED3D_CONTAINER_SWAPCHAIN:
2810 return wined3d_swapchain_decref(surface->container.u.swapchain);
2812 default:
2813 ERR("Unhandled container type %#x.\n", surface->container.type);
2814 case WINED3D_CONTAINER_NONE:
2815 break;
2818 refcount = InterlockedDecrement(&surface->resource.ref);
2819 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2821 if (!refcount)
2823 surface->surface_ops->surface_cleanup(surface);
2824 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2826 TRACE("Destroyed surface %p.\n", surface);
2827 HeapFree(GetProcessHeap(), 0, surface);
2830 return refcount;
2833 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2835 return resource_set_priority(&surface->resource, priority);
2838 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2840 return resource_get_priority(&surface->resource);
2843 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2845 TRACE("surface %p.\n", surface);
2847 surface->surface_ops->surface_preload(surface);
2850 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2852 TRACE("surface %p.\n", surface);
2854 return surface->resource.parent;
2857 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2859 TRACE("surface %p.\n", surface);
2861 return &surface->resource;
2864 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2866 TRACE("surface %p, flags %#x.\n", surface, flags);
2868 switch (flags)
2870 case WINEDDGBS_CANBLT:
2871 case WINEDDGBS_ISBLTDONE:
2872 return WINED3D_OK;
2874 default:
2875 return WINED3DERR_INVALIDCALL;
2879 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2881 TRACE("surface %p, flags %#x.\n", surface, flags);
2883 /* XXX: DDERR_INVALIDSURFACETYPE */
2885 switch (flags)
2887 case WINEDDGFS_CANFLIP:
2888 case WINEDDGFS_ISFLIPDONE:
2889 return WINED3D_OK;
2891 default:
2892 return WINED3DERR_INVALIDCALL;
2896 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2898 TRACE("surface %p.\n", surface);
2900 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2901 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2904 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2906 TRACE("surface %p.\n", surface);
2908 /* So far we don't lose anything :) */
2909 surface->flags &= ~SFLAG_LOST;
2910 return WINED3D_OK;
2913 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2915 TRACE("surface %p, palette %p.\n", surface, palette);
2917 if (surface->palette == palette)
2919 TRACE("Nop palette change.\n");
2920 return WINED3D_OK;
2923 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2924 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2926 surface->palette = palette;
2928 if (palette)
2930 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2931 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2933 surface->surface_ops->surface_realize_palette(surface);
2936 return WINED3D_OK;
2939 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2940 DWORD flags, const WINEDDCOLORKEY *color_key)
2942 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2944 if (flags & WINEDDCKEY_COLORSPACE)
2946 FIXME(" colorkey value not supported (%08x) !\n", flags);
2947 return WINED3DERR_INVALIDCALL;
2950 /* Dirtify the surface, but only if a key was changed. */
2951 if (color_key)
2953 switch (flags & ~WINEDDCKEY_COLORSPACE)
2955 case WINEDDCKEY_DESTBLT:
2956 surface->DestBltCKey = *color_key;
2957 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2958 break;
2960 case WINEDDCKEY_DESTOVERLAY:
2961 surface->DestOverlayCKey = *color_key;
2962 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2963 break;
2965 case WINEDDCKEY_SRCOVERLAY:
2966 surface->SrcOverlayCKey = *color_key;
2967 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2968 break;
2970 case WINEDDCKEY_SRCBLT:
2971 surface->SrcBltCKey = *color_key;
2972 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2973 break;
2976 else
2978 switch (flags & ~WINEDDCKEY_COLORSPACE)
2980 case WINEDDCKEY_DESTBLT:
2981 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2982 break;
2984 case WINEDDCKEY_DESTOVERLAY:
2985 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2986 break;
2988 case WINEDDCKEY_SRCOVERLAY:
2989 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2990 break;
2992 case WINEDDCKEY_SRCBLT:
2993 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2994 break;
2998 return WINED3D_OK;
3001 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3003 TRACE("surface %p.\n", surface);
3005 return surface->palette;
3008 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3010 const struct wined3d_format *format = surface->resource.format;
3011 DWORD pitch;
3013 TRACE("surface %p.\n", surface);
3015 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3017 /* Since compressed formats are block based, pitch means the amount of
3018 * bytes to the next row of block rather than the next row of pixels. */
3019 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3020 pitch = row_block_count * format->block_byte_count;
3022 else
3024 unsigned char alignment = surface->resource.device->surface_alignment;
3025 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3026 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3029 TRACE("Returning %u.\n", pitch);
3031 return pitch;
3034 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3036 TRACE("surface %p, mem %p.\n", surface, mem);
3038 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3040 WARN("Surface is locked or the DC is in use.\n");
3041 return WINED3DERR_INVALIDCALL;
3044 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3045 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3047 ERR("Not supported on render targets.\n");
3048 return WINED3DERR_INVALIDCALL;
3051 if (mem && mem != surface->resource.allocatedMemory)
3053 void *release = NULL;
3055 /* Do I have to copy the old surface content? */
3056 if (surface->flags & SFLAG_DIBSECTION)
3058 SelectObject(surface->hDC, surface->dib.holdbitmap);
3059 DeleteDC(surface->hDC);
3060 /* Release the DIB section. */
3061 DeleteObject(surface->dib.DIBsection);
3062 surface->dib.bitmap_data = NULL;
3063 surface->resource.allocatedMemory = NULL;
3064 surface->hDC = NULL;
3065 surface->flags &= ~SFLAG_DIBSECTION;
3067 else if (!(surface->flags & SFLAG_USERPTR))
3069 release = surface->resource.heapMemory;
3070 surface->resource.heapMemory = NULL;
3072 surface->resource.allocatedMemory = mem;
3073 surface->flags |= SFLAG_USERPTR;
3075 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3076 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3078 /* For client textures OpenGL has to be notified. */
3079 if (surface->flags & SFLAG_CLIENT)
3080 surface_release_client_storage(surface);
3082 /* Now free the old memory if any. */
3083 HeapFree(GetProcessHeap(), 0, release);
3085 else if (surface->flags & SFLAG_USERPTR)
3087 /* HeapMemory should be NULL already. */
3088 if (surface->resource.heapMemory)
3089 ERR("User pointer surface has heap memory allocated.\n");
3091 if (!mem)
3093 surface->resource.allocatedMemory = NULL;
3094 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3096 if (surface->flags & SFLAG_CLIENT)
3097 surface_release_client_storage(surface);
3099 surface_prepare_system_memory(surface);
3102 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3105 return WINED3D_OK;
3108 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3110 LONG w, h;
3112 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3114 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3116 WARN("Not an overlay surface.\n");
3117 return WINEDDERR_NOTAOVERLAYSURFACE;
3120 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3121 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3122 surface->overlay_destrect.left = x;
3123 surface->overlay_destrect.top = y;
3124 surface->overlay_destrect.right = x + w;
3125 surface->overlay_destrect.bottom = y + h;
3127 surface->surface_ops->surface_draw_overlay(surface);
3129 return WINED3D_OK;
3132 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3134 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3136 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3138 TRACE("Not an overlay surface.\n");
3139 return WINEDDERR_NOTAOVERLAYSURFACE;
3142 if (!surface->overlay_dest)
3144 TRACE("Overlay not visible.\n");
3145 *x = 0;
3146 *y = 0;
3147 return WINEDDERR_OVERLAYNOTVISIBLE;
3150 *x = surface->overlay_destrect.left;
3151 *y = surface->overlay_destrect.top;
3153 TRACE("Returning position %d, %d.\n", *x, *y);
3155 return WINED3D_OK;
3158 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3159 DWORD flags, struct wined3d_surface *ref)
3161 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3163 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3165 TRACE("Not an overlay surface.\n");
3166 return WINEDDERR_NOTAOVERLAYSURFACE;
3169 return WINED3D_OK;
3172 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3173 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3175 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3176 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3178 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3180 WARN("Not an overlay surface.\n");
3181 return WINEDDERR_NOTAOVERLAYSURFACE;
3183 else if (!dst_surface)
3185 WARN("Dest surface is NULL.\n");
3186 return WINED3DERR_INVALIDCALL;
3189 if (src_rect)
3191 surface->overlay_srcrect = *src_rect;
3193 else
3195 surface->overlay_srcrect.left = 0;
3196 surface->overlay_srcrect.top = 0;
3197 surface->overlay_srcrect.right = surface->resource.width;
3198 surface->overlay_srcrect.bottom = surface->resource.height;
3201 if (dst_rect)
3203 surface->overlay_destrect = *dst_rect;
3205 else
3207 surface->overlay_destrect.left = 0;
3208 surface->overlay_destrect.top = 0;
3209 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3210 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3213 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3215 list_remove(&surface->overlay_entry);
3218 if (flags & WINEDDOVER_SHOW)
3220 if (surface->overlay_dest != dst_surface)
3222 surface->overlay_dest = dst_surface;
3223 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3226 else if (flags & WINEDDOVER_HIDE)
3228 /* tests show that the rectangles are erased on hide */
3229 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3230 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3231 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3232 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3233 surface->overlay_dest = NULL;
3236 surface->surface_ops->surface_draw_overlay(surface);
3238 return WINED3D_OK;
3241 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3243 TRACE("surface %p, clipper %p.\n", surface, clipper);
3245 surface->clipper = clipper;
3247 return WINED3D_OK;
3250 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3252 TRACE("surface %p.\n", surface);
3254 return surface->clipper;
3257 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3259 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3261 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3263 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3265 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3266 return WINED3DERR_INVALIDCALL;
3269 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3270 surface->pow2Width, surface->pow2Height);
3271 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3272 surface->resource.format = format;
3274 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3275 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3276 format->glFormat, format->glInternal, format->glType);
3278 return WINED3D_OK;
3281 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3282 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3284 unsigned short *dst_s;
3285 const float *src_f;
3286 unsigned int x, y;
3288 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3290 for (y = 0; y < h; ++y)
3292 src_f = (const float *)(src + y * pitch_in);
3293 dst_s = (unsigned short *) (dst + y * pitch_out);
3294 for (x = 0; x < w; ++x)
3296 dst_s[x] = float_32_to_16(src_f + x);
3301 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3302 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3304 static const unsigned char convert_5to8[] =
3306 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3307 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3308 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3309 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3311 static const unsigned char convert_6to8[] =
3313 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3314 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3315 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3316 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3317 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3318 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3319 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3320 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3322 unsigned int x, y;
3324 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3326 for (y = 0; y < h; ++y)
3328 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3329 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3330 for (x = 0; x < w; ++x)
3332 WORD pixel = src_line[x];
3333 dst_line[x] = 0xff000000
3334 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3335 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3336 | convert_5to8[(pixel & 0x001f)];
3341 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3342 * in both cases we're just setting the X / Alpha channel to 0xff. */
3343 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3344 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3346 unsigned int x, y;
3348 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3350 for (y = 0; y < h; ++y)
3352 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3353 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3355 for (x = 0; x < w; ++x)
3357 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3362 static inline BYTE cliptobyte(int x)
3364 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3367 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3368 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3370 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3371 unsigned int x, y;
3373 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3375 for (y = 0; y < h; ++y)
3377 const BYTE *src_line = src + y * pitch_in;
3378 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3379 for (x = 0; x < w; ++x)
3381 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3382 * C = Y - 16; D = U - 128; E = V - 128;
3383 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3384 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3385 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3386 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3387 * U and V are shared between the pixels. */
3388 if (!(x & 1)) /* For every even pixel, read new U and V. */
3390 d = (int) src_line[1] - 128;
3391 e = (int) src_line[3] - 128;
3392 r2 = 409 * e + 128;
3393 g2 = - 100 * d - 208 * e + 128;
3394 b2 = 516 * d + 128;
3396 c2 = 298 * ((int) src_line[0] - 16);
3397 dst_line[x] = 0xff000000
3398 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3399 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3400 | cliptobyte((c2 + b2) >> 8); /* blue */
3401 /* Scale RGB values to 0..255 range,
3402 * then clip them if still not in range (may be negative),
3403 * then shift them within DWORD if necessary. */
3404 src_line += 2;
3409 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3410 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3412 unsigned int x, y;
3413 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3415 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3417 for (y = 0; y < h; ++y)
3419 const BYTE *src_line = src + y * pitch_in;
3420 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3421 for (x = 0; x < w; ++x)
3423 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3424 * C = Y - 16; D = U - 128; E = V - 128;
3425 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3426 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3427 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3428 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3429 * U and V are shared between the pixels. */
3430 if (!(x & 1)) /* For every even pixel, read new U and V. */
3432 d = (int) src_line[1] - 128;
3433 e = (int) src_line[3] - 128;
3434 r2 = 409 * e + 128;
3435 g2 = - 100 * d - 208 * e + 128;
3436 b2 = 516 * d + 128;
3438 c2 = 298 * ((int) src_line[0] - 16);
3439 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3440 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3441 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3442 /* Scale RGB values to 0..255 range,
3443 * then clip them if still not in range (may be negative),
3444 * then shift them within DWORD if necessary. */
3445 src_line += 2;
3450 struct d3dfmt_convertor_desc
3452 enum wined3d_format_id from, to;
3453 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3456 static const struct d3dfmt_convertor_desc convertors[] =
3458 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3459 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3460 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3461 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3462 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3463 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3466 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3467 enum wined3d_format_id to)
3469 unsigned int i;
3471 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3473 if (convertors[i].from == from && convertors[i].to == to)
3474 return &convertors[i];
3477 return NULL;
3480 /*****************************************************************************
3481 * surface_convert_format
3483 * Creates a duplicate of a surface in a different format. Is used by Blt to
3484 * blit between surfaces with different formats.
3486 * Parameters
3487 * source: Source surface
3488 * fmt: Requested destination format
3490 *****************************************************************************/
3491 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3493 const struct d3dfmt_convertor_desc *conv;
3494 WINED3DLOCKED_RECT lock_src, lock_dst;
3495 struct wined3d_surface *ret = NULL;
3496 HRESULT hr;
3498 conv = find_convertor(source->resource.format->id, to_fmt);
3499 if (!conv)
3501 FIXME("Cannot find a conversion function from format %s to %s.\n",
3502 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3503 return NULL;
3506 wined3d_surface_create(source->resource.device, source->resource.width,
3507 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3508 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3509 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3510 if (!ret)
3512 ERR("Failed to create a destination surface for conversion.\n");
3513 return NULL;
3516 memset(&lock_src, 0, sizeof(lock_src));
3517 memset(&lock_dst, 0, sizeof(lock_dst));
3519 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3520 if (FAILED(hr))
3522 ERR("Failed to lock the source surface.\n");
3523 wined3d_surface_decref(ret);
3524 return NULL;
3526 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3527 if (FAILED(hr))
3529 ERR("Failed to lock the destination surface.\n");
3530 wined3d_surface_unmap(source);
3531 wined3d_surface_decref(ret);
3532 return NULL;
3535 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3536 source->resource.width, source->resource.height);
3538 wined3d_surface_unmap(ret);
3539 wined3d_surface_unmap(source);
3541 return ret;
3544 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3545 unsigned int bpp, UINT pitch, DWORD color)
3547 BYTE *first;
3548 int x, y;
3550 /* Do first row */
3552 #define COLORFILL_ROW(type) \
3553 do { \
3554 type *d = (type *)buf; \
3555 for (x = 0; x < width; ++x) \
3556 d[x] = (type)color; \
3557 } while(0)
3559 switch (bpp)
3561 case 1:
3562 COLORFILL_ROW(BYTE);
3563 break;
3565 case 2:
3566 COLORFILL_ROW(WORD);
3567 break;
3569 case 3:
3571 BYTE *d = buf;
3572 for (x = 0; x < width; ++x, d += 3)
3574 d[0] = (color ) & 0xFF;
3575 d[1] = (color >> 8) & 0xFF;
3576 d[2] = (color >> 16) & 0xFF;
3578 break;
3580 case 4:
3581 COLORFILL_ROW(DWORD);
3582 break;
3584 default:
3585 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3586 return WINED3DERR_NOTAVAILABLE;
3589 #undef COLORFILL_ROW
3591 /* Now copy first row. */
3592 first = buf;
3593 for (y = 1; y < height; ++y)
3595 buf += pitch;
3596 memcpy(buf, first, width * bpp);
3599 return WINED3D_OK;
3602 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3604 TRACE("surface %p.\n", surface);
3606 if (!(surface->flags & SFLAG_LOCKED))
3608 WARN("Trying to unmap unmapped surface.\n");
3609 return WINEDDERR_NOTLOCKED;
3611 surface->flags &= ~SFLAG_LOCKED;
3613 surface->surface_ops->surface_unmap(surface);
3615 return WINED3D_OK;
3618 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3619 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3621 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3622 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3624 if (surface->flags & SFLAG_LOCKED)
3626 WARN("Surface is already mapped.\n");
3627 return WINED3DERR_INVALIDCALL;
3629 surface->flags |= SFLAG_LOCKED;
3631 if (!(surface->flags & SFLAG_LOCKABLE))
3632 WARN("Trying to lock unlockable surface.\n");
3634 surface->surface_ops->surface_map(surface, rect, flags);
3636 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3638 if (!rect)
3640 locked_rect->pBits = surface->resource.allocatedMemory;
3641 surface->lockedRect.left = 0;
3642 surface->lockedRect.top = 0;
3643 surface->lockedRect.right = surface->resource.width;
3644 surface->lockedRect.bottom = surface->resource.height;
3646 else
3648 const struct wined3d_format *format = surface->resource.format;
3650 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3652 /* Compressed textures are block based, so calculate the offset of
3653 * the block that contains the top-left pixel of the locked rectangle. */
3654 locked_rect->pBits = surface->resource.allocatedMemory
3655 + ((rect->top / format->block_height) * locked_rect->Pitch)
3656 + ((rect->left / format->block_width) * format->block_byte_count);
3658 else
3660 locked_rect->pBits = surface->resource.allocatedMemory
3661 + (locked_rect->Pitch * rect->top)
3662 + (rect->left * format->byte_count);
3664 surface->lockedRect.left = rect->left;
3665 surface->lockedRect.top = rect->top;
3666 surface->lockedRect.right = rect->right;
3667 surface->lockedRect.bottom = rect->bottom;
3670 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3671 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3673 return WINED3D_OK;
3676 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3678 HRESULT hr;
3680 TRACE("surface %p, dc %p.\n", surface, dc);
3682 if (surface->flags & SFLAG_USERPTR)
3684 ERR("Not supported on surfaces with application-provided memory.\n");
3685 return WINEDDERR_NODC;
3688 /* Give more detailed info for ddraw. */
3689 if (surface->flags & SFLAG_DCINUSE)
3690 return WINEDDERR_DCALREADYCREATED;
3692 /* Can't GetDC if the surface is locked. */
3693 if (surface->flags & SFLAG_LOCKED)
3694 return WINED3DERR_INVALIDCALL;
3696 hr = surface->surface_ops->surface_getdc(surface);
3697 if (FAILED(hr))
3698 return hr;
3700 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3701 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3703 /* GetDC on palettized formats is unsupported in D3D9, and the method
3704 * is missing in D3D8, so this should only be used for DX <=7
3705 * surfaces (with non-device palettes). */
3706 const PALETTEENTRY *pal = NULL;
3708 if (surface->palette)
3710 pal = surface->palette->palents;
3712 else
3714 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3715 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3717 if (dds_primary && dds_primary->palette)
3718 pal = dds_primary->palette->palents;
3721 if (pal)
3723 RGBQUAD col[256];
3724 unsigned int i;
3726 for (i = 0; i < 256; ++i)
3728 col[i].rgbRed = pal[i].peRed;
3729 col[i].rgbGreen = pal[i].peGreen;
3730 col[i].rgbBlue = pal[i].peBlue;
3731 col[i].rgbReserved = 0;
3733 SetDIBColorTable(surface->hDC, 0, 256, col);
3737 surface->flags |= SFLAG_DCINUSE;
3739 *dc = surface->hDC;
3740 TRACE("Returning dc %p.\n", *dc);
3742 return WINED3D_OK;
3745 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3747 TRACE("surface %p, dc %p.\n", surface, dc);
3749 if (!(surface->flags & SFLAG_DCINUSE))
3750 return WINEDDERR_NODC;
3752 if (surface->hDC != dc)
3754 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3755 dc, surface->hDC);
3756 return WINEDDERR_NODC;
3759 /* Copy the contents of the DIB over to the PBO. */
3760 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3761 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3763 /* We locked first, so unlock now. */
3764 wined3d_surface_unmap(surface);
3766 surface->flags &= ~SFLAG_DCINUSE;
3768 return WINED3D_OK;
3771 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3773 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3775 if (flags)
3777 static UINT once;
3778 if (!once++)
3779 FIXME("Ignoring flags %#x.\n", flags);
3780 else
3781 WARN("Ignoring flags %#x.\n", flags);
3784 /* FIXME: This will also prevent overlay flips, since overlays aren't on
3785 * a swapchain either. */
3786 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3788 ERR("Flipped surface is not on a swapchain.\n");
3789 return WINEDDERR_NOTFLIPPABLE;
3792 /* Flipping is only supported on render targets and overlays. */
3793 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3795 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3796 return WINEDDERR_NOTFLIPPABLE;
3799 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
3801 flip_surface(surface, override);
3803 /* Update the overlay if it is visible */
3804 if (surface->overlay_dest)
3805 return surface->surface_ops->surface_draw_overlay(surface);
3806 else
3807 return WINED3D_OK;
3810 return wined3d_surface_blt(surface, NULL, override, NULL, 0, NULL, WINED3DTEXF_POINT);
3813 /* Do not call while under the GL lock. */
3814 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3816 struct wined3d_device *device = surface->resource.device;
3818 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3820 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3822 struct wined3d_texture *texture = surface->container.u.texture;
3824 TRACE("Passing to container (%p).\n", texture);
3825 texture->texture_ops->texture_preload(texture, srgb);
3827 else
3829 struct wined3d_context *context;
3831 TRACE("(%p) : About to load surface\n", surface);
3833 /* TODO: Use already acquired context when possible. */
3834 context = context_acquire(device, NULL);
3836 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3837 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3839 if (palette9_changed(surface))
3841 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3842 /* TODO: This is not necessarily needed with hw palettized texture support */
3843 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3844 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3845 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3849 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3851 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3853 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3854 GLclampf tmp;
3855 tmp = 0.9f;
3856 ENTER_GL();
3857 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3858 LEAVE_GL();
3861 context_release(context);
3865 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3867 if (!surface->resource.allocatedMemory)
3869 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3870 surface->resource.size + RESOURCE_ALIGNMENT);
3871 if (!surface->resource.heapMemory)
3873 ERR("Out of memory\n");
3874 return FALSE;
3876 surface->resource.allocatedMemory =
3877 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3879 else
3881 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3884 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3886 return TRUE;
3889 /* Read the framebuffer back into the surface */
3890 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3892 struct wined3d_device *device = surface->resource.device;
3893 const struct wined3d_gl_info *gl_info;
3894 struct wined3d_context *context;
3895 BYTE *mem;
3896 GLint fmt;
3897 GLint type;
3898 BYTE *row, *top, *bottom;
3899 int i;
3900 BOOL bpp;
3901 RECT local_rect;
3902 BOOL srcIsUpsideDown;
3903 GLint rowLen = 0;
3904 GLint skipPix = 0;
3905 GLint skipRow = 0;
3907 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3908 static BOOL warned = FALSE;
3909 if(!warned) {
3910 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3911 warned = TRUE;
3913 return;
3916 context = context_acquire(device, surface);
3917 context_apply_blit_state(context, device);
3918 gl_info = context->gl_info;
3920 ENTER_GL();
3922 /* Select the correct read buffer, and give some debug output.
3923 * There is no need to keep track of the current read buffer or reset it, every part of the code
3924 * that reads sets the read buffer as desired.
3926 if (surface_is_offscreen(surface))
3928 /* Mapping the primary render target which is not on a swapchain.
3929 * Read from the back buffer. */
3930 TRACE("Mapping offscreen render target.\n");
3931 glReadBuffer(device->offscreenBuffer);
3932 srcIsUpsideDown = TRUE;
3934 else
3936 /* Onscreen surfaces are always part of a swapchain */
3937 GLenum buffer = surface_get_gl_buffer(surface);
3938 TRACE("Mapping %#x buffer.\n", buffer);
3939 glReadBuffer(buffer);
3940 checkGLcall("glReadBuffer");
3941 srcIsUpsideDown = FALSE;
3944 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3945 if (!rect)
3947 local_rect.left = 0;
3948 local_rect.top = 0;
3949 local_rect.right = surface->resource.width;
3950 local_rect.bottom = surface->resource.height;
3952 else
3954 local_rect = *rect;
3956 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3958 switch (surface->resource.format->id)
3960 case WINED3DFMT_P8_UINT:
3962 if (primary_render_target_is_p8(device))
3964 /* In case of P8 render targets the index is stored in the alpha component */
3965 fmt = GL_ALPHA;
3966 type = GL_UNSIGNED_BYTE;
3967 mem = dest;
3968 bpp = surface->resource.format->byte_count;
3970 else
3972 /* GL can't return palettized data, so read ARGB pixels into a
3973 * separate block of memory and convert them into palettized format
3974 * in software. Slow, but if the app means to use palettized render
3975 * targets and locks it...
3977 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3978 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3979 * for the color channels when palettizing the colors.
3981 fmt = GL_RGB;
3982 type = GL_UNSIGNED_BYTE;
3983 pitch *= 3;
3984 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3985 if (!mem)
3987 ERR("Out of memory\n");
3988 LEAVE_GL();
3989 return;
3991 bpp = surface->resource.format->byte_count * 3;
3994 break;
3996 default:
3997 mem = dest;
3998 fmt = surface->resource.format->glFormat;
3999 type = surface->resource.format->glType;
4000 bpp = surface->resource.format->byte_count;
4003 if (surface->flags & SFLAG_PBO)
4005 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4006 checkGLcall("glBindBufferARB");
4007 if (mem)
4009 ERR("mem not null for pbo -- unexpected\n");
4010 mem = NULL;
4014 /* Save old pixel store pack state */
4015 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4016 checkGLcall("glGetIntegerv");
4017 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4018 checkGLcall("glGetIntegerv");
4019 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4020 checkGLcall("glGetIntegerv");
4022 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4023 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4024 checkGLcall("glPixelStorei");
4025 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4026 checkGLcall("glPixelStorei");
4027 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4028 checkGLcall("glPixelStorei");
4030 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4031 local_rect.right - local_rect.left,
4032 local_rect.bottom - local_rect.top,
4033 fmt, type, mem);
4034 checkGLcall("glReadPixels");
4036 /* Reset previous pixel store pack state */
4037 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4038 checkGLcall("glPixelStorei");
4039 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4040 checkGLcall("glPixelStorei");
4041 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4042 checkGLcall("glPixelStorei");
4044 if (surface->flags & SFLAG_PBO)
4046 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4047 checkGLcall("glBindBufferARB");
4049 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4050 * to get a pointer to it and perform the flipping in software. This is a lot
4051 * faster than calling glReadPixels for each line. In case we want more speed
4052 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4053 if (!srcIsUpsideDown)
4055 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4056 checkGLcall("glBindBufferARB");
4058 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4059 checkGLcall("glMapBufferARB");
4063 /* TODO: Merge this with the palettization loop below for P8 targets */
4064 if(!srcIsUpsideDown) {
4065 UINT len, off;
4066 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4067 Flip the lines in software */
4068 len = (local_rect.right - local_rect.left) * bpp;
4069 off = local_rect.left * bpp;
4071 row = HeapAlloc(GetProcessHeap(), 0, len);
4072 if(!row) {
4073 ERR("Out of memory\n");
4074 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4075 HeapFree(GetProcessHeap(), 0, mem);
4076 LEAVE_GL();
4077 return;
4080 top = mem + pitch * local_rect.top;
4081 bottom = mem + pitch * (local_rect.bottom - 1);
4082 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4083 memcpy(row, top + off, len);
4084 memcpy(top + off, bottom + off, len);
4085 memcpy(bottom + off, row, len);
4086 top += pitch;
4087 bottom -= pitch;
4089 HeapFree(GetProcessHeap(), 0, row);
4091 /* Unmap the temp PBO buffer */
4092 if (surface->flags & SFLAG_PBO)
4094 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4095 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4099 LEAVE_GL();
4100 context_release(context);
4102 /* For P8 textures we need to perform an inverse palette lookup. This is
4103 * done by searching for a palette index which matches the RGB value.
4104 * Note this isn't guaranteed to work when there are multiple entries for
4105 * the same color but we have no choice. In case of P8 render targets,
4106 * the index is stored in the alpha component so no conversion is needed. */
4107 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4109 const PALETTEENTRY *pal = NULL;
4110 DWORD width = pitch / 3;
4111 int x, y, c;
4113 if (surface->palette)
4115 pal = surface->palette->palents;
4117 else
4119 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4120 HeapFree(GetProcessHeap(), 0, mem);
4121 return;
4124 for(y = local_rect.top; y < local_rect.bottom; y++) {
4125 for(x = local_rect.left; x < local_rect.right; x++) {
4126 /* start lines pixels */
4127 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4128 const BYTE *green = blue + 1;
4129 const BYTE *red = green + 1;
4131 for(c = 0; c < 256; c++) {
4132 if(*red == pal[c].peRed &&
4133 *green == pal[c].peGreen &&
4134 *blue == pal[c].peBlue)
4136 *((BYTE *) dest + y * width + x) = c;
4137 break;
4142 HeapFree(GetProcessHeap(), 0, mem);
4146 /* Read the framebuffer contents into a texture */
4147 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4149 struct wined3d_device *device = surface->resource.device;
4150 struct wined3d_context *context;
4152 if (!surface_is_offscreen(surface))
4154 /* We would need to flip onscreen surfaces, but there's no efficient
4155 * way to do that here. It makes more sense for the caller to
4156 * explicitly go through sysmem. */
4157 ERR("Not supported for onscreen targets.\n");
4158 return;
4161 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4162 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4163 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4165 context = context_acquire(device, surface);
4166 device_invalidate_state(device, STATE_FRAMEBUFFER);
4168 surface_prepare_texture(surface, context, srgb);
4169 surface_bind_and_dirtify(surface, context, srgb);
4171 TRACE("Reading back offscreen render target %p.\n", surface);
4173 ENTER_GL();
4175 glReadBuffer(device->offscreenBuffer);
4176 checkGLcall("glReadBuffer");
4178 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4179 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4180 checkGLcall("glCopyTexSubImage2D");
4182 LEAVE_GL();
4184 context_release(context);
4187 /* Context activation is done by the caller. */
4188 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4189 struct wined3d_context *context, BOOL srgb)
4191 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4192 CONVERT_TYPES convert;
4193 struct wined3d_format format;
4195 if (surface->flags & alloc_flag) return;
4197 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4198 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4199 else surface->flags &= ~SFLAG_CONVERTED;
4201 surface_bind_and_dirtify(surface, context, srgb);
4202 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4203 surface->flags |= alloc_flag;
4206 /* Context activation is done by the caller. */
4207 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4209 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4211 struct wined3d_texture *texture = surface->container.u.texture;
4212 UINT sub_count = texture->level_count * texture->layer_count;
4213 UINT i;
4215 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4217 for (i = 0; i < sub_count; ++i)
4219 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4220 surface_prepare_texture_internal(s, context, srgb);
4223 return;
4226 surface_prepare_texture_internal(surface, context, srgb);
4229 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4231 if (multisample)
4233 if (surface->rb_multisample)
4234 return;
4236 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4237 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4238 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4239 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4240 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4242 else
4244 if (surface->rb_resolved)
4245 return;
4247 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4248 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4249 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4250 surface->pow2Width, surface->pow2Height);
4251 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4255 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4256 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4258 struct wined3d_device *device = surface->resource.device;
4259 UINT pitch = wined3d_surface_get_pitch(surface);
4260 const struct wined3d_gl_info *gl_info;
4261 struct wined3d_context *context;
4262 RECT local_rect;
4263 UINT w, h;
4265 surface_get_rect(surface, rect, &local_rect);
4267 mem += local_rect.top * pitch + local_rect.left * bpp;
4268 w = local_rect.right - local_rect.left;
4269 h = local_rect.bottom - local_rect.top;
4271 /* Activate the correct context for the render target */
4272 context = context_acquire(device, surface);
4273 context_apply_blit_state(context, device);
4274 gl_info = context->gl_info;
4276 ENTER_GL();
4278 if (!surface_is_offscreen(surface))
4280 GLenum buffer = surface_get_gl_buffer(surface);
4281 TRACE("Unlocking %#x buffer.\n", buffer);
4282 context_set_draw_buffer(context, buffer);
4284 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4285 glPixelZoom(1.0f, -1.0f);
4287 else
4289 /* Primary offscreen render target */
4290 TRACE("Offscreen render target.\n");
4291 context_set_draw_buffer(context, device->offscreenBuffer);
4293 glPixelZoom(1.0f, 1.0f);
4296 glRasterPos3i(local_rect.left, local_rect.top, 1);
4297 checkGLcall("glRasterPos3i");
4299 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4300 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4302 if (surface->flags & SFLAG_PBO)
4304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4305 checkGLcall("glBindBufferARB");
4308 glDrawPixels(w, h, fmt, type, mem);
4309 checkGLcall("glDrawPixels");
4311 if (surface->flags & SFLAG_PBO)
4313 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4314 checkGLcall("glBindBufferARB");
4317 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4318 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4320 LEAVE_GL();
4322 if (wined3d_settings.strict_draw_ordering
4323 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4324 && surface->container.u.swapchain->front_buffer == surface))
4325 wglFlush();
4327 context_release(context);
4330 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4331 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4333 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4334 const struct wined3d_device *device = surface->resource.device;
4335 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4336 BOOL blit_supported = FALSE;
4338 /* Copy the default values from the surface. Below we might perform fixups */
4339 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4340 *format = *surface->resource.format;
4341 *convert = NO_CONVERSION;
4343 /* Ok, now look if we have to do any conversion */
4344 switch (surface->resource.format->id)
4346 case WINED3DFMT_P8_UINT:
4347 /* Below the call to blit_supported is disabled for Wine 1.2
4348 * because the function isn't operating correctly yet. At the
4349 * moment 8-bit blits are handled in software and if certain GL
4350 * extensions are around, surface conversion is performed at
4351 * upload time. The blit_supported call recognizes it as a
4352 * destination fixup. This type of upload 'fixup' and 8-bit to
4353 * 8-bit blits need to be handled by the blit_shader.
4354 * TODO: get rid of this #if 0. */
4355 #if 0
4356 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4357 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4358 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4359 #endif
4360 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4362 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4363 * texturing. Further also use conversion in case of color keying.
4364 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4365 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4366 * conflicts with this.
4368 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4369 || colorkey_active || !use_texturing)
4371 format->glFormat = GL_RGBA;
4372 format->glInternal = GL_RGBA;
4373 format->glType = GL_UNSIGNED_BYTE;
4374 format->conv_byte_count = 4;
4375 if (colorkey_active)
4376 *convert = CONVERT_PALETTED_CK;
4377 else
4378 *convert = CONVERT_PALETTED;
4380 break;
4382 case WINED3DFMT_B2G3R3_UNORM:
4383 /* **********************
4384 GL_UNSIGNED_BYTE_3_3_2
4385 ********************** */
4386 if (colorkey_active) {
4387 /* This texture format will never be used.. So do not care about color keying
4388 up until the point in time it will be needed :-) */
4389 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4391 break;
4393 case WINED3DFMT_B5G6R5_UNORM:
4394 if (colorkey_active)
4396 *convert = CONVERT_CK_565;
4397 format->glFormat = GL_RGBA;
4398 format->glInternal = GL_RGB5_A1;
4399 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4400 format->conv_byte_count = 2;
4402 break;
4404 case WINED3DFMT_B5G5R5X1_UNORM:
4405 if (colorkey_active)
4407 *convert = CONVERT_CK_5551;
4408 format->glFormat = GL_BGRA;
4409 format->glInternal = GL_RGB5_A1;
4410 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4411 format->conv_byte_count = 2;
4413 break;
4415 case WINED3DFMT_B8G8R8_UNORM:
4416 if (colorkey_active)
4418 *convert = CONVERT_CK_RGB24;
4419 format->glFormat = GL_RGBA;
4420 format->glInternal = GL_RGBA8;
4421 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4422 format->conv_byte_count = 4;
4424 break;
4426 case WINED3DFMT_B8G8R8X8_UNORM:
4427 if (colorkey_active)
4429 *convert = CONVERT_RGB32_888;
4430 format->glFormat = GL_RGBA;
4431 format->glInternal = GL_RGBA8;
4432 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4433 format->conv_byte_count = 4;
4435 break;
4437 default:
4438 break;
4441 return WINED3D_OK;
4444 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4446 const struct wined3d_device *device = surface->resource.device;
4447 const struct wined3d_palette *pal = surface->palette;
4448 BOOL index_in_alpha = FALSE;
4449 unsigned int i;
4451 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4452 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4453 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4454 * duplicate entries. Store the color key in the unused alpha component to speed the
4455 * download up and to make conversion unneeded. */
4456 index_in_alpha = primary_render_target_is_p8(device);
4458 if (!pal)
4460 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4461 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4463 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4464 if (index_in_alpha)
4466 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4467 * there's no palette at this time. */
4468 for (i = 0; i < 256; i++) table[i][3] = i;
4471 else
4473 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4474 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4475 * capability flag is present (wine does advertise this capability) */
4476 for (i = 0; i < 256; ++i)
4478 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4479 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4480 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4481 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4485 else
4487 TRACE("Using surface palette %p\n", pal);
4488 /* Get the surface's palette */
4489 for (i = 0; i < 256; ++i)
4491 table[i][0] = pal->palents[i].peRed;
4492 table[i][1] = pal->palents[i].peGreen;
4493 table[i][2] = pal->palents[i].peBlue;
4495 /* When index_in_alpha is set the palette index is stored in the
4496 * alpha component. In case of a readback we can then read
4497 * GL_ALPHA. Color keying is handled in BltOverride using a
4498 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4499 * color key itself is passed to glAlphaFunc in other cases the
4500 * alpha component of pixels that should be masked away is set to 0. */
4501 if (index_in_alpha)
4503 table[i][3] = i;
4505 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4506 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4508 table[i][3] = 0x00;
4510 else if (pal->flags & WINEDDPCAPS_ALPHA)
4512 table[i][3] = pal->palents[i].peFlags;
4514 else
4516 table[i][3] = 0xFF;
4522 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4523 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4525 const BYTE *source;
4526 BYTE *dest;
4527 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4529 switch (convert) {
4530 case NO_CONVERSION:
4532 memcpy(dst, src, pitch * height);
4533 break;
4535 case CONVERT_PALETTED:
4536 case CONVERT_PALETTED_CK:
4538 BYTE table[256][4];
4539 unsigned int x, y;
4541 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4543 for (y = 0; y < height; y++)
4545 source = src + pitch * y;
4546 dest = dst + outpitch * y;
4547 /* This is an 1 bpp format, using the width here is fine */
4548 for (x = 0; x < width; x++) {
4549 BYTE color = *source++;
4550 *dest++ = table[color][0];
4551 *dest++ = table[color][1];
4552 *dest++ = table[color][2];
4553 *dest++ = table[color][3];
4557 break;
4559 case CONVERT_CK_565:
4561 /* Converting the 565 format in 5551 packed to emulate color-keying.
4563 Note : in all these conversion, it would be best to average the averaging
4564 pixels to get the color of the pixel that will be color-keyed to
4565 prevent 'color bleeding'. This will be done later on if ever it is
4566 too visible.
4568 Note2: Nvidia documents say that their driver does not support alpha + color keying
4569 on the same surface and disables color keying in such a case
4571 unsigned int x, y;
4572 const WORD *Source;
4573 WORD *Dest;
4575 TRACE("Color keyed 565\n");
4577 for (y = 0; y < height; y++) {
4578 Source = (const WORD *)(src + y * pitch);
4579 Dest = (WORD *) (dst + y * outpitch);
4580 for (x = 0; x < width; x++ ) {
4581 WORD color = *Source++;
4582 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4583 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4584 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4585 *Dest |= 0x0001;
4586 Dest++;
4590 break;
4592 case CONVERT_CK_5551:
4594 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4595 unsigned int x, y;
4596 const WORD *Source;
4597 WORD *Dest;
4598 TRACE("Color keyed 5551\n");
4599 for (y = 0; y < height; y++) {
4600 Source = (const WORD *)(src + y * pitch);
4601 Dest = (WORD *) (dst + y * outpitch);
4602 for (x = 0; x < width; x++ ) {
4603 WORD color = *Source++;
4604 *Dest = color;
4605 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4606 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4607 *Dest |= (1 << 15);
4608 else
4609 *Dest &= ~(1 << 15);
4610 Dest++;
4614 break;
4616 case CONVERT_CK_RGB24:
4618 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4619 unsigned int x, y;
4620 for (y = 0; y < height; y++)
4622 source = src + pitch * y;
4623 dest = dst + outpitch * y;
4624 for (x = 0; x < width; x++) {
4625 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4626 DWORD dstcolor = color << 8;
4627 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4628 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4629 dstcolor |= 0xff;
4630 *(DWORD*)dest = dstcolor;
4631 source += 3;
4632 dest += 4;
4636 break;
4638 case CONVERT_RGB32_888:
4640 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4641 unsigned int x, y;
4642 for (y = 0; y < height; y++)
4644 source = src + pitch * y;
4645 dest = dst + outpitch * y;
4646 for (x = 0; x < width; x++) {
4647 DWORD color = 0xffffff & *(const DWORD*)source;
4648 DWORD dstcolor = color << 8;
4649 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4650 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4651 dstcolor |= 0xff;
4652 *(DWORD*)dest = dstcolor;
4653 source += 4;
4654 dest += 4;
4658 break;
4660 default:
4661 ERR("Unsupported conversion type %#x.\n", convert);
4663 return WINED3D_OK;
4666 BOOL palette9_changed(struct wined3d_surface *surface)
4668 struct wined3d_device *device = surface->resource.device;
4670 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4671 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4673 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4674 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4676 return FALSE;
4679 if (surface->palette9)
4681 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4683 return FALSE;
4686 else
4688 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4690 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4692 return TRUE;
4695 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4697 /* Flip the surface contents */
4698 /* Flip the DC */
4700 HDC tmp;
4701 tmp = front->hDC;
4702 front->hDC = back->hDC;
4703 back->hDC = tmp;
4706 /* Flip the DIBsection */
4708 HBITMAP tmp;
4709 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4710 tmp = front->dib.DIBsection;
4711 front->dib.DIBsection = back->dib.DIBsection;
4712 back->dib.DIBsection = tmp;
4714 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4715 else front->flags &= ~SFLAG_DIBSECTION;
4716 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4717 else back->flags &= ~SFLAG_DIBSECTION;
4720 /* Flip the surface data */
4722 void* tmp;
4724 tmp = front->dib.bitmap_data;
4725 front->dib.bitmap_data = back->dib.bitmap_data;
4726 back->dib.bitmap_data = tmp;
4728 tmp = front->resource.allocatedMemory;
4729 front->resource.allocatedMemory = back->resource.allocatedMemory;
4730 back->resource.allocatedMemory = tmp;
4732 tmp = front->resource.heapMemory;
4733 front->resource.heapMemory = back->resource.heapMemory;
4734 back->resource.heapMemory = tmp;
4737 /* Flip the PBO */
4739 GLuint tmp_pbo = front->pbo;
4740 front->pbo = back->pbo;
4741 back->pbo = tmp_pbo;
4744 /* client_memory should not be different, but just in case */
4746 BOOL tmp;
4747 tmp = front->dib.client_memory;
4748 front->dib.client_memory = back->dib.client_memory;
4749 back->dib.client_memory = tmp;
4752 /* Flip the opengl texture */
4754 GLuint tmp;
4756 tmp = back->texture_name;
4757 back->texture_name = front->texture_name;
4758 front->texture_name = tmp;
4760 tmp = back->texture_name_srgb;
4761 back->texture_name_srgb = front->texture_name_srgb;
4762 front->texture_name_srgb = tmp;
4764 tmp = back->rb_multisample;
4765 back->rb_multisample = front->rb_multisample;
4766 front->rb_multisample = tmp;
4768 tmp = back->rb_resolved;
4769 back->rb_resolved = front->rb_resolved;
4770 front->rb_resolved = tmp;
4772 resource_unload(&back->resource);
4773 resource_unload(&front->resource);
4777 DWORD tmp_flags = back->flags;
4778 back->flags = front->flags;
4779 front->flags = tmp_flags;
4783 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4784 * pixel copy calls. */
4785 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4786 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4788 struct wined3d_device *device = dst_surface->resource.device;
4789 float xrel, yrel;
4790 UINT row;
4791 struct wined3d_context *context;
4792 BOOL upsidedown = FALSE;
4793 RECT dst_rect = *dst_rect_in;
4795 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4796 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4798 if(dst_rect.top > dst_rect.bottom) {
4799 UINT tmp = dst_rect.bottom;
4800 dst_rect.bottom = dst_rect.top;
4801 dst_rect.top = tmp;
4802 upsidedown = TRUE;
4805 context = context_acquire(device, src_surface);
4806 context_apply_blit_state(context, device);
4807 surface_internal_preload(dst_surface, SRGB_RGB);
4808 ENTER_GL();
4810 /* Bind the target texture */
4811 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4812 if (surface_is_offscreen(src_surface))
4814 TRACE("Reading from an offscreen target\n");
4815 upsidedown = !upsidedown;
4816 glReadBuffer(device->offscreenBuffer);
4818 else
4820 glReadBuffer(surface_get_gl_buffer(src_surface));
4822 checkGLcall("glReadBuffer");
4824 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4825 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4827 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4829 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4831 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4832 ERR("Texture filtering not supported in direct blit\n");
4835 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4836 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4838 ERR("Texture filtering not supported in direct blit\n");
4841 if (upsidedown
4842 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4843 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4845 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4847 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4848 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4849 src_rect->left, src_surface->resource.height - src_rect->bottom,
4850 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4852 else
4854 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4855 /* I have to process this row by row to swap the image,
4856 * otherwise it would be upside down, so stretching in y direction
4857 * doesn't cost extra time
4859 * However, stretching in x direction can be avoided if not necessary
4861 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4862 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4864 /* Well, that stuff works, but it's very slow.
4865 * find a better way instead
4867 UINT col;
4869 for (col = dst_rect.left; col < dst_rect.right; ++col)
4871 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4872 dst_rect.left + col /* x offset */, row /* y offset */,
4873 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4876 else
4878 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4879 dst_rect.left /* x offset */, row /* y offset */,
4880 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4884 checkGLcall("glCopyTexSubImage2D");
4886 LEAVE_GL();
4887 context_release(context);
4889 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4890 * path is never entered
4892 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4895 /* Uses the hardware to stretch and flip the image */
4896 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4897 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4899 struct wined3d_device *device = dst_surface->resource.device;
4900 struct wined3d_swapchain *src_swapchain = NULL;
4901 GLuint src, backup = 0;
4902 float left, right, top, bottom; /* Texture coordinates */
4903 UINT fbwidth = src_surface->resource.width;
4904 UINT fbheight = src_surface->resource.height;
4905 struct wined3d_context *context;
4906 GLenum drawBuffer = GL_BACK;
4907 GLenum texture_target;
4908 BOOL noBackBufferBackup;
4909 BOOL src_offscreen;
4910 BOOL upsidedown = FALSE;
4911 RECT dst_rect = *dst_rect_in;
4913 TRACE("Using hwstretch blit\n");
4914 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4915 context = context_acquire(device, src_surface);
4916 context_apply_blit_state(context, device);
4917 surface_internal_preload(dst_surface, SRGB_RGB);
4919 src_offscreen = surface_is_offscreen(src_surface);
4920 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4921 if (!noBackBufferBackup && !src_surface->texture_name)
4923 /* Get it a description */
4924 surface_internal_preload(src_surface, SRGB_RGB);
4926 ENTER_GL();
4928 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4929 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4931 if (context->aux_buffers >= 2)
4933 /* Got more than one aux buffer? Use the 2nd aux buffer */
4934 drawBuffer = GL_AUX1;
4936 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4938 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4939 drawBuffer = GL_AUX0;
4942 if(noBackBufferBackup) {
4943 glGenTextures(1, &backup);
4944 checkGLcall("glGenTextures");
4945 context_bind_texture(context, GL_TEXTURE_2D, backup);
4946 texture_target = GL_TEXTURE_2D;
4947 } else {
4948 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4949 * we are reading from the back buffer, the backup can be used as source texture
4951 texture_target = src_surface->texture_target;
4952 context_bind_texture(context, texture_target, src_surface->texture_name);
4953 glEnable(texture_target);
4954 checkGLcall("glEnable(texture_target)");
4956 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4957 src_surface->flags &= ~SFLAG_INTEXTURE;
4960 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4961 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4963 if(dst_rect.top > dst_rect.bottom) {
4964 UINT tmp = dst_rect.bottom;
4965 dst_rect.bottom = dst_rect.top;
4966 dst_rect.top = tmp;
4967 upsidedown = TRUE;
4970 if (src_offscreen)
4972 TRACE("Reading from an offscreen target\n");
4973 upsidedown = !upsidedown;
4974 glReadBuffer(device->offscreenBuffer);
4976 else
4978 glReadBuffer(surface_get_gl_buffer(src_surface));
4981 /* TODO: Only back up the part that will be overwritten */
4982 glCopyTexSubImage2D(texture_target, 0,
4983 0, 0 /* read offsets */,
4984 0, 0,
4985 fbwidth,
4986 fbheight);
4988 checkGLcall("glCopyTexSubImage2D");
4990 /* No issue with overriding these - the sampler is dirty due to blit usage */
4991 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4992 wined3d_gl_mag_filter(magLookup, Filter));
4993 checkGLcall("glTexParameteri");
4994 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4995 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4996 checkGLcall("glTexParameteri");
4998 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4999 src_swapchain = src_surface->container.u.swapchain;
5000 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5002 src = backup ? backup : src_surface->texture_name;
5004 else
5006 glReadBuffer(GL_FRONT);
5007 checkGLcall("glReadBuffer(GL_FRONT)");
5009 glGenTextures(1, &src);
5010 checkGLcall("glGenTextures(1, &src)");
5011 context_bind_texture(context, GL_TEXTURE_2D, src);
5013 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5014 * out for power of 2 sizes
5016 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5017 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5018 checkGLcall("glTexImage2D");
5019 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5020 0, 0 /* read offsets */,
5021 0, 0,
5022 fbwidth,
5023 fbheight);
5025 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5026 checkGLcall("glTexParameteri");
5027 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5028 checkGLcall("glTexParameteri");
5030 glReadBuffer(GL_BACK);
5031 checkGLcall("glReadBuffer(GL_BACK)");
5033 if(texture_target != GL_TEXTURE_2D) {
5034 glDisable(texture_target);
5035 glEnable(GL_TEXTURE_2D);
5036 texture_target = GL_TEXTURE_2D;
5039 checkGLcall("glEnd and previous");
5041 left = src_rect->left;
5042 right = src_rect->right;
5044 if (!upsidedown)
5046 top = src_surface->resource.height - src_rect->top;
5047 bottom = src_surface->resource.height - src_rect->bottom;
5049 else
5051 top = src_surface->resource.height - src_rect->bottom;
5052 bottom = src_surface->resource.height - src_rect->top;
5055 if (src_surface->flags & SFLAG_NORMCOORD)
5057 left /= src_surface->pow2Width;
5058 right /= src_surface->pow2Width;
5059 top /= src_surface->pow2Height;
5060 bottom /= src_surface->pow2Height;
5063 /* draw the source texture stretched and upside down. The correct surface is bound already */
5064 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5065 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5067 context_set_draw_buffer(context, drawBuffer);
5068 glReadBuffer(drawBuffer);
5070 glBegin(GL_QUADS);
5071 /* bottom left */
5072 glTexCoord2f(left, bottom);
5073 glVertex2i(0, 0);
5075 /* top left */
5076 glTexCoord2f(left, top);
5077 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5079 /* top right */
5080 glTexCoord2f(right, top);
5081 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5083 /* bottom right */
5084 glTexCoord2f(right, bottom);
5085 glVertex2i(dst_rect.right - dst_rect.left, 0);
5086 glEnd();
5087 checkGLcall("glEnd and previous");
5089 if (texture_target != dst_surface->texture_target)
5091 glDisable(texture_target);
5092 glEnable(dst_surface->texture_target);
5093 texture_target = dst_surface->texture_target;
5096 /* Now read the stretched and upside down image into the destination texture */
5097 context_bind_texture(context, texture_target, dst_surface->texture_name);
5098 glCopyTexSubImage2D(texture_target,
5100 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5101 0, 0, /* We blitted the image to the origin */
5102 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5103 checkGLcall("glCopyTexSubImage2D");
5105 if(drawBuffer == GL_BACK) {
5106 /* Write the back buffer backup back */
5107 if(backup) {
5108 if(texture_target != GL_TEXTURE_2D) {
5109 glDisable(texture_target);
5110 glEnable(GL_TEXTURE_2D);
5111 texture_target = GL_TEXTURE_2D;
5113 context_bind_texture(context, GL_TEXTURE_2D, backup);
5115 else
5117 if (texture_target != src_surface->texture_target)
5119 glDisable(texture_target);
5120 glEnable(src_surface->texture_target);
5121 texture_target = src_surface->texture_target;
5123 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5126 glBegin(GL_QUADS);
5127 /* top left */
5128 glTexCoord2f(0.0f, 0.0f);
5129 glVertex2i(0, fbheight);
5131 /* bottom left */
5132 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5133 glVertex2i(0, 0);
5135 /* bottom right */
5136 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5137 (float)fbheight / (float)src_surface->pow2Height);
5138 glVertex2i(fbwidth, 0);
5140 /* top right */
5141 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5142 glVertex2i(fbwidth, fbheight);
5143 glEnd();
5145 glDisable(texture_target);
5146 checkGLcall("glDisable(texture_target)");
5148 /* Cleanup */
5149 if (src != src_surface->texture_name && src != backup)
5151 glDeleteTextures(1, &src);
5152 checkGLcall("glDeleteTextures(1, &src)");
5154 if(backup) {
5155 glDeleteTextures(1, &backup);
5156 checkGLcall("glDeleteTextures(1, &backup)");
5159 LEAVE_GL();
5161 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5163 context_release(context);
5165 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5166 * path is never entered
5168 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5171 /* Front buffer coordinates are always full screen coordinates, but our GL
5172 * drawable is limited to the window's client area. The sysmem and texture
5173 * copies do have the full screen size. Note that GL has a bottom-left
5174 * origin, while D3D has a top-left origin. */
5175 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5177 UINT drawable_height;
5179 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5180 && surface == surface->container.u.swapchain->front_buffer)
5182 POINT offset = {0, 0};
5183 RECT windowsize;
5185 ScreenToClient(window, &offset);
5186 OffsetRect(rect, offset.x, offset.y);
5188 GetClientRect(window, &windowsize);
5189 drawable_height = windowsize.bottom - windowsize.top;
5191 else
5193 drawable_height = surface->resource.height;
5196 rect->top = drawable_height - rect->top;
5197 rect->bottom = drawable_height - rect->bottom;
5200 static void surface_blt_to_drawable(struct wined3d_device *device,
5201 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5202 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5203 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5205 struct wined3d_context *context;
5206 RECT src_rect, dst_rect;
5208 src_rect = *src_rect_in;
5209 dst_rect = *dst_rect_in;
5211 /* Make sure the surface is up-to-date. This should probably use
5212 * surface_load_location() and worry about the destination surface too,
5213 * unless we're overwriting it completely. */
5214 surface_internal_preload(src_surface, SRGB_RGB);
5216 /* Activate the destination context, set it up for blitting */
5217 context = context_acquire(device, dst_surface);
5218 context_apply_blit_state(context, device);
5220 if (!surface_is_offscreen(dst_surface))
5221 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5223 device->blitter->set_shader(device->blit_priv, context, src_surface);
5225 ENTER_GL();
5227 if (color_key)
5229 glEnable(GL_ALPHA_TEST);
5230 checkGLcall("glEnable(GL_ALPHA_TEST)");
5232 /* When the primary render target uses P8, the alpha component
5233 * contains the palette index. Which means that the colorkey is one of
5234 * the palette entries. In other cases pixels that should be masked
5235 * away have alpha set to 0. */
5236 if (primary_render_target_is_p8(device))
5237 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5238 else
5239 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5240 checkGLcall("glAlphaFunc");
5242 else
5244 glDisable(GL_ALPHA_TEST);
5245 checkGLcall("glDisable(GL_ALPHA_TEST)");
5248 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5250 if (color_key)
5252 glDisable(GL_ALPHA_TEST);
5253 checkGLcall("glDisable(GL_ALPHA_TEST)");
5256 LEAVE_GL();
5258 /* Leave the opengl state valid for blitting */
5259 device->blitter->unset_shader(context->gl_info);
5261 if (wined3d_settings.strict_draw_ordering
5262 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5263 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5264 wglFlush(); /* Flush to ensure ordering across contexts. */
5266 context_release(context);
5269 /* Do not call while under the GL lock. */
5270 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5272 struct wined3d_device *device = s->resource.device;
5273 const struct blit_shader *blitter;
5275 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5276 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5277 if (!blitter)
5279 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5280 return WINED3DERR_INVALIDCALL;
5283 return blitter->color_fill(device, s, rect, color);
5286 /* Do not call while under the GL lock. */
5287 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5288 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5289 WINED3DTEXTUREFILTERTYPE Filter)
5291 struct wined3d_device *device = dst_surface->resource.device;
5292 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5293 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5295 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5296 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5297 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5299 /* Get the swapchain. One of the surfaces has to be a primary surface */
5300 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5302 WARN("Destination is in sysmem, rejecting gl blt\n");
5303 return WINED3DERR_INVALIDCALL;
5306 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5307 dstSwapchain = dst_surface->container.u.swapchain;
5309 if (src_surface)
5311 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5313 WARN("Src is in sysmem, rejecting gl blt\n");
5314 return WINED3DERR_INVALIDCALL;
5317 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5318 srcSwapchain = src_surface->container.u.swapchain;
5321 /* Early sort out of cases where no render target is used */
5322 if (!dstSwapchain && !srcSwapchain
5323 && src_surface != device->fb.render_targets[0]
5324 && dst_surface != device->fb.render_targets[0])
5326 TRACE("No surface is render target, not using hardware blit.\n");
5327 return WINED3DERR_INVALIDCALL;
5330 /* No destination color keying supported */
5331 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5333 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5334 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5335 return WINED3DERR_INVALIDCALL;
5338 if (dstSwapchain && dstSwapchain == srcSwapchain)
5340 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5341 return WINED3DERR_INVALIDCALL;
5344 if (dstSwapchain && srcSwapchain)
5346 FIXME("Implement hardware blit between two different swapchains\n");
5347 return WINED3DERR_INVALIDCALL;
5350 if (dstSwapchain)
5352 /* Handled with regular texture -> swapchain blit */
5353 if (src_surface == device->fb.render_targets[0])
5354 TRACE("Blit from active render target to a swapchain\n");
5356 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5358 FIXME("Implement blit from a swapchain to the active render target\n");
5359 return WINED3DERR_INVALIDCALL;
5362 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5364 /* Blit from render target to texture */
5365 BOOL stretchx;
5367 /* P8 read back is not implemented */
5368 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5369 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5371 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5372 return WINED3DERR_INVALIDCALL;
5375 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5377 TRACE("Color keying not supported by frame buffer to texture blit\n");
5378 return WINED3DERR_INVALIDCALL;
5379 /* Destination color key is checked above */
5382 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5383 stretchx = TRUE;
5384 else
5385 stretchx = FALSE;
5387 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5388 * flip the image nor scale it.
5390 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5391 * -> If the app wants a image width an unscaled width, copy it line per line
5392 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5393 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5394 * back buffer. This is slower than reading line per line, thus not used for flipping
5395 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5396 * pixel by pixel. */
5397 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5398 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5400 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5401 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5402 } else {
5403 TRACE("Using hardware stretching to flip / stretch the texture\n");
5404 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5407 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5409 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5410 dst_surface->resource.allocatedMemory = NULL;
5411 dst_surface->resource.heapMemory = NULL;
5413 else
5415 dst_surface->flags &= ~SFLAG_INSYSMEM;
5418 return WINED3D_OK;
5420 else if (src_surface)
5422 /* Blit from offscreen surface to render target */
5423 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5424 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5426 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5428 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5429 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5430 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5432 FIXME("Unsupported blit operation falling back to software\n");
5433 return WINED3DERR_INVALIDCALL;
5436 /* Color keying: Check if we have to do a color keyed blt,
5437 * and if not check if a color key is activated.
5439 * Just modify the color keying parameters in the surface and restore them afterwards
5440 * The surface keeps track of the color key last used to load the opengl surface.
5441 * PreLoad will catch the change to the flags and color key and reload if necessary.
5443 if (flags & WINEDDBLT_KEYSRC)
5445 /* Use color key from surface */
5447 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5449 /* Use color key from DDBltFx */
5450 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5451 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5453 else
5455 /* Do not use color key */
5456 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5459 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5460 src_surface, src_rect, dst_surface, dst_rect);
5462 /* Restore the color key parameters */
5463 src_surface->CKeyFlags = oldCKeyFlags;
5464 src_surface->SrcBltCKey = oldBltCKey;
5466 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5468 return WINED3D_OK;
5471 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5472 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5473 return WINED3DERR_INVALIDCALL;
5476 /* GL locking is done by the caller */
5477 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5478 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5480 struct wined3d_device *device = surface->resource.device;
5481 const struct wined3d_gl_info *gl_info = context->gl_info;
5482 GLint compare_mode = GL_NONE;
5483 struct blt_info info;
5484 GLint old_binding = 0;
5485 RECT rect;
5487 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5489 glDisable(GL_CULL_FACE);
5490 glDisable(GL_BLEND);
5491 glDisable(GL_ALPHA_TEST);
5492 glDisable(GL_SCISSOR_TEST);
5493 glDisable(GL_STENCIL_TEST);
5494 glEnable(GL_DEPTH_TEST);
5495 glDepthFunc(GL_ALWAYS);
5496 glDepthMask(GL_TRUE);
5497 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5498 glViewport(x, y, w, h);
5500 SetRect(&rect, 0, h, w, 0);
5501 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5502 context_active_texture(context, context->gl_info, 0);
5503 glGetIntegerv(info.binding, &old_binding);
5504 glBindTexture(info.bind_target, texture);
5505 if (gl_info->supported[ARB_SHADOW])
5507 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5508 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5511 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5512 gl_info, info.tex_type, &surface->ds_current_size);
5514 glBegin(GL_TRIANGLE_STRIP);
5515 glTexCoord3fv(info.coords[0]);
5516 glVertex2f(-1.0f, -1.0f);
5517 glTexCoord3fv(info.coords[1]);
5518 glVertex2f(1.0f, -1.0f);
5519 glTexCoord3fv(info.coords[2]);
5520 glVertex2f(-1.0f, 1.0f);
5521 glTexCoord3fv(info.coords[3]);
5522 glVertex2f(1.0f, 1.0f);
5523 glEnd();
5525 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5526 glBindTexture(info.bind_target, old_binding);
5528 glPopAttrib();
5530 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5533 void surface_modify_ds_location(struct wined3d_surface *surface,
5534 DWORD location, UINT w, UINT h)
5536 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5538 if (location & ~SFLAG_DS_LOCATIONS)
5539 FIXME("Invalid location (%#x) specified.\n", location);
5541 surface->ds_current_size.cx = w;
5542 surface->ds_current_size.cy = h;
5543 surface->flags &= ~SFLAG_DS_LOCATIONS;
5544 surface->flags |= location;
5547 /* Context activation is done by the caller. */
5548 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5550 struct wined3d_device *device = surface->resource.device;
5551 GLsizei w, h;
5553 TRACE("surface %p, new location %#x.\n", surface, location);
5555 /* TODO: Make this work for modes other than FBO */
5556 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5558 if (!(surface->flags & location))
5560 w = surface->ds_current_size.cx;
5561 h = surface->ds_current_size.cy;
5562 surface->ds_current_size.cx = 0;
5563 surface->ds_current_size.cy = 0;
5565 else
5567 w = surface->resource.width;
5568 h = surface->resource.height;
5571 if (surface->ds_current_size.cx == surface->resource.width
5572 && surface->ds_current_size.cy == surface->resource.height)
5574 TRACE("Location (%#x) is already up to date.\n", location);
5575 return;
5578 if (surface->current_renderbuffer)
5580 FIXME("Not supported with fixed up depth stencil.\n");
5581 return;
5584 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5586 /* This mostly happens when a depth / stencil is used without being
5587 * cleared first. In principle we could upload from sysmem, or
5588 * explicitly clear before first usage. For the moment there don't
5589 * appear to be a lot of applications depending on this, so a FIXME
5590 * should do. */
5591 FIXME("No up to date depth stencil location.\n");
5592 surface->flags |= location;
5593 surface->ds_current_size.cx = surface->resource.width;
5594 surface->ds_current_size.cy = surface->resource.height;
5595 return;
5598 if (location == SFLAG_DS_OFFSCREEN)
5600 GLint old_binding = 0;
5601 GLenum bind_target;
5603 /* The render target is allowed to be smaller than the depth/stencil
5604 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5605 * than the offscreen surface. Don't overwrite the offscreen surface
5606 * with undefined data. */
5607 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5608 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5610 TRACE("Copying onscreen depth buffer to depth texture.\n");
5612 ENTER_GL();
5614 if (!device->depth_blt_texture)
5616 glGenTextures(1, &device->depth_blt_texture);
5619 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5620 * directly on the FBO texture. That's because we need to flip. */
5621 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5622 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5623 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5625 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5626 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5628 else
5630 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5631 bind_target = GL_TEXTURE_2D;
5633 glBindTexture(bind_target, device->depth_blt_texture);
5634 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5635 * internal format, because the internal format might include stencil
5636 * data. In principle we should copy stencil data as well, but unless
5637 * the driver supports stencil export it's hard to do, and doesn't
5638 * seem to be needed in practice. If the hardware doesn't support
5639 * writing stencil data, the glCopyTexImage2D() call might trigger
5640 * software fallbacks. */
5641 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5642 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5643 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5644 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5645 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5646 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5647 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5648 glBindTexture(bind_target, old_binding);
5650 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5651 NULL, surface, SFLAG_INTEXTURE);
5652 context_set_draw_buffer(context, GL_NONE);
5653 glReadBuffer(GL_NONE);
5655 /* Do the actual blit */
5656 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5657 checkGLcall("depth_blt");
5659 context_invalidate_state(context, STATE_FRAMEBUFFER);
5661 LEAVE_GL();
5663 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5665 else if (location == SFLAG_DS_ONSCREEN)
5667 TRACE("Copying depth texture to onscreen depth buffer.\n");
5669 ENTER_GL();
5671 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5672 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5673 surface_depth_blt(surface, context, surface->texture_name,
5674 0, surface->pow2Height - h, w, h, surface->texture_target);
5675 checkGLcall("depth_blt");
5677 context_invalidate_state(context, STATE_FRAMEBUFFER);
5679 LEAVE_GL();
5681 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5683 else
5685 ERR("Invalid location (%#x) specified.\n", location);
5688 surface->flags |= location;
5689 surface->ds_current_size.cx = surface->resource.width;
5690 surface->ds_current_size.cy = surface->resource.height;
5693 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5695 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5696 struct wined3d_surface *overlay;
5698 TRACE("surface %p, location %s, persistent %#x.\n",
5699 surface, debug_surflocation(location), persistent);
5701 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5702 && (location & SFLAG_INDRAWABLE))
5703 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5705 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5706 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5707 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5709 if (persistent)
5711 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5712 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5714 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5716 TRACE("Passing to container.\n");
5717 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5720 surface->flags &= ~SFLAG_LOCATIONS;
5721 surface->flags |= location;
5723 /* Redraw emulated overlays, if any */
5724 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5726 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5728 overlay->surface_ops->surface_draw_overlay(overlay);
5732 else
5734 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5736 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5738 TRACE("Passing to container\n");
5739 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5742 surface->flags &= ~location;
5745 if (!(surface->flags & SFLAG_LOCATIONS))
5747 ERR("Surface %p does not have any up to date location.\n", surface);
5751 static DWORD resource_access_from_location(DWORD location)
5753 switch (location)
5755 case SFLAG_INSYSMEM:
5756 return WINED3D_RESOURCE_ACCESS_CPU;
5758 case SFLAG_INDRAWABLE:
5759 case SFLAG_INSRGBTEX:
5760 case SFLAG_INTEXTURE:
5761 case SFLAG_INRB_MULTISAMPLE:
5762 case SFLAG_INRB_RESOLVED:
5763 return WINED3D_RESOURCE_ACCESS_GPU;
5765 default:
5766 FIXME("Unhandled location %#x.\n", location);
5767 return 0;
5771 static void surface_load_sysmem(struct wined3d_surface *surface,
5772 const struct wined3d_gl_info *gl_info, const RECT *rect)
5774 surface_prepare_system_memory(surface);
5776 /* Download the surface to system memory. */
5777 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5779 struct wined3d_device *device = surface->resource.device;
5780 struct wined3d_context *context;
5782 /* TODO: Use already acquired context when possible. */
5783 context = context_acquire(device, NULL);
5785 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5786 surface_download_data(surface, gl_info);
5788 context_release(context);
5790 return;
5793 /* Note: It might be faster to download into a texture first. */
5794 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5795 wined3d_surface_get_pitch(surface));
5798 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5799 const struct wined3d_gl_info *gl_info, const RECT *rect)
5801 struct wined3d_device *device = surface->resource.device;
5802 struct wined3d_format format;
5803 CONVERT_TYPES convert;
5804 UINT byte_count;
5805 BYTE *mem;
5807 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5809 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5810 return WINED3DERR_INVALIDCALL;
5813 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5814 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5816 if (surface->flags & SFLAG_INTEXTURE)
5818 RECT r;
5820 surface_get_rect(surface, rect, &r);
5821 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5823 return WINED3D_OK;
5826 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5828 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5829 * path through sysmem. */
5830 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5833 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5835 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5836 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5837 * called. */
5838 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5840 struct wined3d_context *context;
5842 TRACE("Removing the pbo attached to surface %p.\n", surface);
5844 /* TODO: Use already acquired context when possible. */
5845 context = context_acquire(device, NULL);
5847 surface_remove_pbo(surface, gl_info);
5849 context_release(context);
5852 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5854 UINT height = surface->resource.height;
5855 UINT width = surface->resource.width;
5856 UINT src_pitch, dst_pitch;
5858 byte_count = format.conv_byte_count;
5859 src_pitch = wined3d_surface_get_pitch(surface);
5861 /* Stick to the alignment for the converted surface too, makes it
5862 * easier to load the surface. */
5863 dst_pitch = width * byte_count;
5864 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5866 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5868 ERR("Out of memory (%u).\n", dst_pitch * height);
5869 return E_OUTOFMEMORY;
5872 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5873 src_pitch, width, height, dst_pitch, convert, surface);
5875 surface->flags |= SFLAG_CONVERTED;
5877 else
5879 surface->flags &= ~SFLAG_CONVERTED;
5880 mem = surface->resource.allocatedMemory;
5881 byte_count = format.byte_count;
5884 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5886 /* Don't delete PBO memory. */
5887 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5888 HeapFree(GetProcessHeap(), 0, mem);
5890 return WINED3D_OK;
5893 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5894 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5896 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5897 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5898 struct wined3d_device *device = surface->resource.device;
5899 struct wined3d_context *context;
5900 UINT width, src_pitch, dst_pitch;
5901 struct wined3d_bo_address data;
5902 struct wined3d_format format;
5903 POINT dst_point = {0, 0};
5904 CONVERT_TYPES convert;
5905 BYTE *mem;
5907 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5908 && surface_is_offscreen(surface)
5909 && (surface->flags & SFLAG_INDRAWABLE))
5911 read_from_framebuffer_texture(surface, srgb);
5913 return WINED3D_OK;
5916 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5917 && (surface->resource.format->flags & attach_flags) == attach_flags
5918 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5919 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5920 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5922 if (srgb)
5923 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5924 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5925 else
5926 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5927 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5929 return WINED3D_OK;
5932 /* Upload from system memory */
5934 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5935 TRUE /* We will use textures */, &format, &convert);
5937 if (srgb)
5939 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5941 /* Performance warning... */
5942 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5943 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5946 else
5948 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5950 /* Performance warning... */
5951 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5952 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5956 if (!(surface->flags & SFLAG_INSYSMEM))
5958 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5959 /* Lets hope we get it from somewhere... */
5960 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5963 /* TODO: Use already acquired context when possible. */
5964 context = context_acquire(device, NULL);
5966 surface_prepare_texture(surface, context, srgb);
5967 surface_bind_and_dirtify(surface, context, srgb);
5969 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5971 surface->flags |= SFLAG_GLCKEY;
5972 surface->glCKey = surface->SrcBltCKey;
5974 else surface->flags &= ~SFLAG_GLCKEY;
5976 width = surface->resource.width;
5977 src_pitch = wined3d_surface_get_pitch(surface);
5979 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5980 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5981 * called. */
5982 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
5984 TRACE("Removing the pbo attached to surface %p.\n", surface);
5985 surface_remove_pbo(surface, gl_info);
5988 if (format.convert)
5990 /* This code is entered for texture formats which need a fixup. */
5991 UINT height = surface->resource.height;
5993 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5994 dst_pitch = width * format.conv_byte_count;
5995 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5997 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5999 ERR("Out of memory (%u).\n", dst_pitch * height);
6000 context_release(context);
6001 return E_OUTOFMEMORY;
6003 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6005 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6007 /* This code is only entered for color keying fixups */
6008 UINT height = surface->resource.height;
6010 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6011 dst_pitch = width * format.conv_byte_count;
6012 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6014 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6016 ERR("Out of memory (%u).\n", dst_pitch * height);
6017 context_release(context);
6018 return E_OUTOFMEMORY;
6020 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6021 width, height, dst_pitch, convert, surface);
6023 else
6025 mem = surface->resource.allocatedMemory;
6028 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6029 data.addr = mem;
6030 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6032 context_release(context);
6034 /* Don't delete PBO memory. */
6035 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6036 HeapFree(GetProcessHeap(), 0, mem);
6038 return WINED3D_OK;
6041 static void surface_multisample_resolve(struct wined3d_surface *surface)
6043 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6045 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6046 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6048 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6049 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6052 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6054 struct wined3d_device *device = surface->resource.device;
6055 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6056 HRESULT hr;
6058 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6060 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6062 if (location == SFLAG_INTEXTURE)
6064 struct wined3d_context *context = context_acquire(device, NULL);
6065 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6066 context_release(context);
6067 return WINED3D_OK;
6069 else
6071 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6072 return WINED3DERR_INVALIDCALL;
6076 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6077 location = SFLAG_INTEXTURE;
6079 if (surface->flags & location)
6081 TRACE("Location already up to date.\n");
6082 return WINED3D_OK;
6085 if (WARN_ON(d3d_surface))
6087 DWORD required_access = resource_access_from_location(location);
6088 if ((surface->resource.access_flags & required_access) != required_access)
6089 WARN("Operation requires %#x access, but surface only has %#x.\n",
6090 required_access, surface->resource.access_flags);
6093 if (!(surface->flags & SFLAG_LOCATIONS))
6095 ERR("Surface %p does not have any up to date location.\n", surface);
6096 surface->flags |= SFLAG_LOST;
6097 return WINED3DERR_DEVICELOST;
6100 switch (location)
6102 case SFLAG_INSYSMEM:
6103 surface_load_sysmem(surface, gl_info, rect);
6104 break;
6106 case SFLAG_INDRAWABLE:
6107 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6108 return hr;
6109 break;
6111 case SFLAG_INRB_RESOLVED:
6112 surface_multisample_resolve(surface);
6113 break;
6115 case SFLAG_INTEXTURE:
6116 case SFLAG_INSRGBTEX:
6117 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6118 return hr;
6119 break;
6121 default:
6122 ERR("Don't know how to handle location %#x.\n", location);
6123 break;
6126 if (!rect)
6128 surface->flags |= location;
6130 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6131 surface_evict_sysmem(surface);
6134 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6135 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6137 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6140 return WINED3D_OK;
6143 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6145 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6147 /* Not on a swapchain - must be offscreen */
6148 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6150 /* The front buffer is always onscreen */
6151 if (surface == swapchain->front_buffer) return FALSE;
6153 /* If the swapchain is rendered to an FBO, the backbuffer is
6154 * offscreen, otherwise onscreen */
6155 return swapchain->render_to_fbo;
6158 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6159 /* Context activation is done by the caller. */
6160 static void ffp_blit_free(struct wined3d_device *device) { }
6162 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6163 /* Context activation is done by the caller. */
6164 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6166 BYTE table[256][4];
6167 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6169 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6171 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6172 ENTER_GL();
6173 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6174 LEAVE_GL();
6177 /* Context activation is done by the caller. */
6178 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6180 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6182 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6183 * else the surface is converted in software at upload time in LoadLocation.
6185 if(fixup == COMPLEX_FIXUP_P8 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6186 ffp_blit_p8_upload_palette(surface, context->gl_info);
6188 ENTER_GL();
6189 glEnable(surface->texture_target);
6190 checkGLcall("glEnable(surface->texture_target)");
6191 LEAVE_GL();
6192 return WINED3D_OK;
6195 /* Context activation is done by the caller. */
6196 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6198 ENTER_GL();
6199 glDisable(GL_TEXTURE_2D);
6200 checkGLcall("glDisable(GL_TEXTURE_2D)");
6201 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6203 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6204 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6206 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6208 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6209 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6211 LEAVE_GL();
6214 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6215 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6216 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6218 enum complex_fixup src_fixup;
6220 switch (blit_op)
6222 case WINED3D_BLIT_OP_COLOR_BLIT:
6223 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6224 return FALSE;
6226 src_fixup = get_complex_fixup(src_format->color_fixup);
6227 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6229 TRACE("Checking support for fixup:\n");
6230 dump_color_fixup_desc(src_format->color_fixup);
6233 if (!is_identity_fixup(dst_format->color_fixup))
6235 TRACE("Destination fixups are not supported\n");
6236 return FALSE;
6239 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6241 TRACE("P8 fixup supported\n");
6242 return TRUE;
6245 /* We only support identity conversions. */
6246 if (is_identity_fixup(src_format->color_fixup))
6248 TRACE("[OK]\n");
6249 return TRUE;
6252 TRACE("[FAILED]\n");
6253 return FALSE;
6255 case WINED3D_BLIT_OP_COLOR_FILL:
6256 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6257 return FALSE;
6259 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6261 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6262 return FALSE;
6264 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6266 TRACE("Color fill not supported\n");
6267 return FALSE;
6270 /* FIXME: We should reject color fills on formats with fixups,
6271 * but this would break P8 color fills for example. */
6273 return TRUE;
6275 case WINED3D_BLIT_OP_DEPTH_FILL:
6276 return TRUE;
6278 default:
6279 TRACE("Unsupported blit_op=%d\n", blit_op);
6280 return FALSE;
6284 /* Do not call while under the GL lock. */
6285 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6286 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6288 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6289 struct wined3d_fb_state fb = {&dst_surface, NULL};
6291 return device_clear_render_targets(device, 1, &fb,
6292 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6295 /* Do not call while under the GL lock. */
6296 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6297 struct wined3d_surface *surface, const RECT *rect, float depth)
6299 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6300 struct wined3d_fb_state fb = {NULL, surface};
6302 return device_clear_render_targets(device, 0, &fb,
6303 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6306 const struct blit_shader ffp_blit = {
6307 ffp_blit_alloc,
6308 ffp_blit_free,
6309 ffp_blit_set,
6310 ffp_blit_unset,
6311 ffp_blit_supported,
6312 ffp_blit_color_fill,
6313 ffp_blit_depth_fill,
6316 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6318 return WINED3D_OK;
6321 /* Context activation is done by the caller. */
6322 static void cpu_blit_free(struct wined3d_device *device)
6326 /* Context activation is done by the caller. */
6327 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6329 return WINED3D_OK;
6332 /* Context activation is done by the caller. */
6333 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6337 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6338 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6339 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6341 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6343 return TRUE;
6346 return FALSE;
6349 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6350 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6351 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6353 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6354 const struct wined3d_format *src_format, *dst_format;
6355 struct wined3d_surface *orig_src = src_surface;
6356 WINED3DLOCKED_RECT dlock, slock;
6357 HRESULT hr = WINED3D_OK;
6358 const BYTE *sbuf;
6359 RECT xdst,xsrc;
6360 BYTE *dbuf;
6361 int x, y;
6363 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6364 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6365 flags, fx, debug_d3dtexturefiltertype(filter));
6367 xsrc = *src_rect;
6369 if (!src_surface)
6371 RECT full_rect;
6373 full_rect.left = 0;
6374 full_rect.top = 0;
6375 full_rect.right = dst_surface->resource.width;
6376 full_rect.bottom = dst_surface->resource.height;
6377 IntersectRect(&xdst, &full_rect, dst_rect);
6379 else
6381 BOOL clip_horiz, clip_vert;
6383 xdst = *dst_rect;
6384 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6385 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6387 if (clip_vert || clip_horiz)
6389 /* Now check if this is a special case or not... */
6390 if ((flags & WINEDDBLT_DDFX)
6391 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6392 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6394 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6395 return WINED3D_OK;
6398 if (clip_horiz)
6400 if (xdst.left < 0)
6402 xsrc.left -= xdst.left;
6403 xdst.left = 0;
6405 if (xdst.right > dst_surface->resource.width)
6407 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6408 xdst.right = (int)dst_surface->resource.width;
6412 if (clip_vert)
6414 if (xdst.top < 0)
6416 xsrc.top -= xdst.top;
6417 xdst.top = 0;
6419 if (xdst.bottom > dst_surface->resource.height)
6421 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6422 xdst.bottom = (int)dst_surface->resource.height;
6426 /* And check if after clipping something is still to be done... */
6427 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6428 || (xdst.left >= (int)dst_surface->resource.width)
6429 || (xdst.top >= (int)dst_surface->resource.height)
6430 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6431 || (xsrc.left >= (int)src_surface->resource.width)
6432 || (xsrc.top >= (int)src_surface->resource.height))
6434 TRACE("Nothing to be done after clipping.\n");
6435 return WINED3D_OK;
6440 if (src_surface == dst_surface)
6442 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6443 slock = dlock;
6444 src_format = dst_surface->resource.format;
6445 dst_format = src_format;
6447 else
6449 dst_format = dst_surface->resource.format;
6450 if (src_surface)
6452 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6454 src_surface = surface_convert_format(src_surface, dst_format->id);
6455 if (!src_surface)
6457 /* The conv function writes a FIXME */
6458 WARN("Cannot convert source surface format to dest format.\n");
6459 goto release;
6462 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6463 src_format = src_surface->resource.format;
6465 else
6467 src_format = dst_format;
6469 if (dst_rect)
6470 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6471 else
6472 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6475 bpp = dst_surface->resource.format->byte_count;
6476 srcheight = xsrc.bottom - xsrc.top;
6477 srcwidth = xsrc.right - xsrc.left;
6478 dstheight = xdst.bottom - xdst.top;
6479 dstwidth = xdst.right - xdst.left;
6480 width = (xdst.right - xdst.left) * bpp;
6482 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6484 UINT row_block_count;
6486 if (flags || src_surface == dst_surface)
6488 FIXME("Only plain blits supported on compressed surfaces.\n");
6489 hr = E_NOTIMPL;
6490 goto release;
6493 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6495 if (srcheight != dstheight || srcwidth != dstwidth)
6497 WARN("Stretching not supported on compressed surfaces.\n");
6498 hr = WINED3DERR_INVALIDCALL;
6499 goto release;
6502 dbuf = dlock.pBits;
6503 sbuf = slock.pBits;
6505 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6506 for (y = 0; y < dstheight; y += dst_format->block_height)
6508 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6509 dbuf += dlock.Pitch;
6510 sbuf += slock.Pitch;
6513 goto release;
6516 if (dst_rect && src_surface != dst_surface)
6517 dbuf = dlock.pBits;
6518 else
6519 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6521 /* First, all the 'source-less' blits */
6522 if (flags & WINEDDBLT_COLORFILL)
6524 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6525 flags &= ~WINEDDBLT_COLORFILL;
6528 if (flags & WINEDDBLT_DEPTHFILL)
6530 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6532 if (flags & WINEDDBLT_ROP)
6534 /* Catch some degenerate cases here. */
6535 switch (fx->dwROP)
6537 case BLACKNESS:
6538 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6539 break;
6540 case 0xAA0029: /* No-op */
6541 break;
6542 case WHITENESS:
6543 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6544 break;
6545 case SRCCOPY: /* Well, we do that below? */
6546 break;
6547 default:
6548 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6549 goto error;
6551 flags &= ~WINEDDBLT_ROP;
6553 if (flags & WINEDDBLT_DDROPS)
6555 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6557 /* Now the 'with source' blits. */
6558 if (src_surface)
6560 const BYTE *sbase;
6561 int sx, xinc, sy, yinc;
6563 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6564 goto release;
6566 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6567 && (srcwidth != dstwidth || srcheight != dstheight))
6569 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6570 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6573 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6574 xinc = (srcwidth << 16) / dstwidth;
6575 yinc = (srcheight << 16) / dstheight;
6577 if (!flags)
6579 /* No effects, we can cheat here. */
6580 if (dstwidth == srcwidth)
6582 if (dstheight == srcheight)
6584 /* No stretching in either direction. This needs to be as
6585 * fast as possible. */
6586 sbuf = sbase;
6588 /* Check for overlapping surfaces. */
6589 if (src_surface != dst_surface || xdst.top < xsrc.top
6590 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6592 /* No overlap, or dst above src, so copy from top downwards. */
6593 for (y = 0; y < dstheight; ++y)
6595 memcpy(dbuf, sbuf, width);
6596 sbuf += slock.Pitch;
6597 dbuf += dlock.Pitch;
6600 else if (xdst.top > xsrc.top)
6602 /* Copy from bottom upwards. */
6603 sbuf += (slock.Pitch*dstheight);
6604 dbuf += (dlock.Pitch*dstheight);
6605 for (y = 0; y < dstheight; ++y)
6607 sbuf -= slock.Pitch;
6608 dbuf -= dlock.Pitch;
6609 memcpy(dbuf, sbuf, width);
6612 else
6614 /* Src and dst overlapping on the same line, use memmove. */
6615 for (y = 0; y < dstheight; ++y)
6617 memmove(dbuf, sbuf, width);
6618 sbuf += slock.Pitch;
6619 dbuf += dlock.Pitch;
6623 else
6625 /* Stretching in y direction only. */
6626 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6628 sbuf = sbase + (sy >> 16) * slock.Pitch;
6629 memcpy(dbuf, sbuf, width);
6630 dbuf += dlock.Pitch;
6634 else
6636 /* Stretching in X direction. */
6637 int last_sy = -1;
6638 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6640 sbuf = sbase + (sy >> 16) * slock.Pitch;
6642 if ((sy >> 16) == (last_sy >> 16))
6644 /* This source row is the same as last source row -
6645 * Copy the already stretched row. */
6646 memcpy(dbuf, dbuf - dlock.Pitch, width);
6648 else
6650 #define STRETCH_ROW(type) \
6651 do { \
6652 const type *s = (const type *)sbuf; \
6653 type *d = (type *)dbuf; \
6654 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6655 d[x] = s[sx >> 16]; \
6656 } while(0)
6658 switch(bpp)
6660 case 1:
6661 STRETCH_ROW(BYTE);
6662 break;
6663 case 2:
6664 STRETCH_ROW(WORD);
6665 break;
6666 case 4:
6667 STRETCH_ROW(DWORD);
6668 break;
6669 case 3:
6671 const BYTE *s;
6672 BYTE *d = dbuf;
6673 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6675 DWORD pixel;
6677 s = sbuf + 3 * (sx >> 16);
6678 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6679 d[0] = (pixel ) & 0xff;
6680 d[1] = (pixel >> 8) & 0xff;
6681 d[2] = (pixel >> 16) & 0xff;
6682 d += 3;
6684 break;
6686 default:
6687 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6688 hr = WINED3DERR_NOTAVAILABLE;
6689 goto error;
6691 #undef STRETCH_ROW
6693 dbuf += dlock.Pitch;
6694 last_sy = sy;
6698 else
6700 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6701 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6702 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6703 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6705 /* The color keying flags are checked for correctness in ddraw */
6706 if (flags & WINEDDBLT_KEYSRC)
6708 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6709 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6711 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6713 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6714 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6717 if (flags & WINEDDBLT_KEYDEST)
6719 /* Destination color keys are taken from the source surface! */
6720 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6721 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6723 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6725 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6726 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6729 if (bpp == 1)
6731 keymask = 0xff;
6733 else
6735 keymask = src_format->red_mask
6736 | src_format->green_mask
6737 | src_format->blue_mask;
6739 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6742 if (flags & WINEDDBLT_DDFX)
6744 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6745 LONG tmpxy;
6746 dTopLeft = dbuf;
6747 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6748 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6749 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6751 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6753 /* I don't think we need to do anything about this flag */
6754 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6756 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6758 tmp = dTopRight;
6759 dTopRight = dTopLeft;
6760 dTopLeft = tmp;
6761 tmp = dBottomRight;
6762 dBottomRight = dBottomLeft;
6763 dBottomLeft = tmp;
6764 dstxinc = dstxinc * -1;
6766 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6768 tmp = dTopLeft;
6769 dTopLeft = dBottomLeft;
6770 dBottomLeft = tmp;
6771 tmp = dTopRight;
6772 dTopRight = dBottomRight;
6773 dBottomRight = tmp;
6774 dstyinc = dstyinc * -1;
6776 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6778 /* I don't think we need to do anything about this flag */
6779 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6781 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6783 tmp = dBottomRight;
6784 dBottomRight = dTopLeft;
6785 dTopLeft = tmp;
6786 tmp = dBottomLeft;
6787 dBottomLeft = dTopRight;
6788 dTopRight = tmp;
6789 dstxinc = dstxinc * -1;
6790 dstyinc = dstyinc * -1;
6792 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6794 tmp = dTopLeft;
6795 dTopLeft = dBottomLeft;
6796 dBottomLeft = dBottomRight;
6797 dBottomRight = dTopRight;
6798 dTopRight = tmp;
6799 tmpxy = dstxinc;
6800 dstxinc = dstyinc;
6801 dstyinc = tmpxy;
6802 dstxinc = dstxinc * -1;
6804 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6806 tmp = dTopLeft;
6807 dTopLeft = dTopRight;
6808 dTopRight = dBottomRight;
6809 dBottomRight = dBottomLeft;
6810 dBottomLeft = tmp;
6811 tmpxy = dstxinc;
6812 dstxinc = dstyinc;
6813 dstyinc = tmpxy;
6814 dstyinc = dstyinc * -1;
6816 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6818 /* I don't think we need to do anything about this flag */
6819 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6821 dbuf = dTopLeft;
6822 flags &= ~(WINEDDBLT_DDFX);
6825 #define COPY_COLORKEY_FX(type) \
6826 do { \
6827 const type *s; \
6828 type *d = (type *)dbuf, *dx, tmp; \
6829 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6831 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6832 dx = d; \
6833 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6835 tmp = s[sx >> 16]; \
6836 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6837 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6839 dx[0] = tmp; \
6841 dx = (type *)(((BYTE *)dx) + dstxinc); \
6843 d = (type *)(((BYTE *)d) + dstyinc); \
6845 } while(0)
6847 switch (bpp)
6849 case 1:
6850 COPY_COLORKEY_FX(BYTE);
6851 break;
6852 case 2:
6853 COPY_COLORKEY_FX(WORD);
6854 break;
6855 case 4:
6856 COPY_COLORKEY_FX(DWORD);
6857 break;
6858 case 3:
6860 const BYTE *s;
6861 BYTE *d = dbuf, *dx;
6862 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6864 sbuf = sbase + (sy >> 16) * slock.Pitch;
6865 dx = d;
6866 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6868 DWORD pixel, dpixel = 0;
6869 s = sbuf + 3 * (sx>>16);
6870 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6871 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6872 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6873 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6875 dx[0] = (pixel ) & 0xff;
6876 dx[1] = (pixel >> 8) & 0xff;
6877 dx[2] = (pixel >> 16) & 0xff;
6879 dx += dstxinc;
6881 d += dstyinc;
6883 break;
6885 default:
6886 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6887 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6888 hr = WINED3DERR_NOTAVAILABLE;
6889 goto error;
6890 #undef COPY_COLORKEY_FX
6895 error:
6896 if (flags && FIXME_ON(d3d_surface))
6898 FIXME("\tUnsupported flags: %#x.\n", flags);
6901 release:
6902 wined3d_surface_unmap(dst_surface);
6903 if (src_surface && src_surface != dst_surface)
6904 wined3d_surface_unmap(src_surface);
6905 /* Release the converted surface, if any. */
6906 if (src_surface && src_surface != orig_src)
6907 wined3d_surface_decref(src_surface);
6909 return hr;
6912 /* Do not call while under the GL lock. */
6913 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6914 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6916 static const RECT src_rect;
6917 WINEDDBLTFX BltFx;
6919 memset(&BltFx, 0, sizeof(BltFx));
6920 BltFx.dwSize = sizeof(BltFx);
6921 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6922 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6923 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
6926 /* Do not call while under the GL lock. */
6927 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6928 struct wined3d_surface *surface, const RECT *rect, float depth)
6930 FIXME("Depth filling not implemented by cpu_blit.\n");
6931 return WINED3DERR_INVALIDCALL;
6934 const struct blit_shader cpu_blit = {
6935 cpu_blit_alloc,
6936 cpu_blit_free,
6937 cpu_blit_set,
6938 cpu_blit_unset,
6939 cpu_blit_supported,
6940 cpu_blit_color_fill,
6941 cpu_blit_depth_fill,
6944 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
6945 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
6946 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
6947 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
6949 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6950 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
6951 unsigned int resource_size;
6952 HRESULT hr;
6954 if (multisample_quality > 0)
6956 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6957 multisample_quality = 0;
6960 /* Quick lockable sanity check.
6961 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6962 * this function is too deep to need to care about things like this.
6963 * Levels need to be checked too, since they all affect what can be done. */
6964 switch (pool)
6966 case WINED3DPOOL_SCRATCH:
6967 if (!lockable)
6969 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
6970 "which are mutually exclusive, setting lockable to TRUE.\n");
6971 lockable = TRUE;
6973 break;
6975 case WINED3DPOOL_SYSTEMMEM:
6976 if (!lockable)
6977 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
6978 break;
6980 case WINED3DPOOL_MANAGED:
6981 if (usage & WINED3DUSAGE_DYNAMIC)
6982 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6983 break;
6985 case WINED3DPOOL_DEFAULT:
6986 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6987 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6988 break;
6990 default:
6991 FIXME("Unknown pool %#x.\n", pool);
6992 break;
6995 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
6996 FIXME("Trying to create a render target that isn't in the default pool.\n");
6998 /* FIXME: Check that the format is supported by the device. */
7000 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7001 if (!resource_size)
7002 return WINED3DERR_INVALIDCALL;
7004 surface->surface_type = surface_type;
7006 switch (surface_type)
7008 case SURFACE_OPENGL:
7009 surface->surface_ops = &surface_ops;
7010 break;
7012 case SURFACE_GDI:
7013 surface->surface_ops = &gdi_surface_ops;
7014 break;
7016 default:
7017 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7018 return WINED3DERR_INVALIDCALL;
7021 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7022 multisample_type, multisample_quality, usage, pool, width, height, 1,
7023 resource_size, parent, parent_ops, &surface_resource_ops);
7024 if (FAILED(hr))
7026 WARN("Failed to initialize resource, returning %#x.\n", hr);
7027 return hr;
7030 /* "Standalone" surface. */
7031 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7033 surface->texture_level = level;
7034 list_init(&surface->overlays);
7036 /* Flags */
7037 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7038 if (discard)
7039 surface->flags |= SFLAG_DISCARD;
7040 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7041 surface->flags |= SFLAG_LOCKABLE;
7042 /* I'm not sure if this qualifies as a hack or as an optimization. It
7043 * seems reasonable to assume that lockable render targets will get
7044 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7045 * creation. However, the other reason we want to do this is that several
7046 * ddraw applications access surface memory while the surface isn't
7047 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7048 * future locks prevents these from crashing. */
7049 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7050 surface->flags |= SFLAG_DYNLOCK;
7052 /* Mark the texture as dirty so that it gets loaded first time around. */
7053 surface_add_dirty_rect(surface, NULL);
7054 list_init(&surface->renderbuffers);
7056 TRACE("surface %p, memory %p, size %u\n",
7057 surface, surface->resource.allocatedMemory, surface->resource.size);
7059 /* Call the private setup routine */
7060 hr = surface->surface_ops->surface_private_setup(surface);
7061 if (FAILED(hr))
7063 ERR("Private setup failed, returning %#x\n", hr);
7064 surface->surface_ops->surface_cleanup(surface);
7065 return hr;
7068 return hr;
7071 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7072 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7073 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7074 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7076 struct wined3d_surface *object;
7077 HRESULT hr;
7079 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7080 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7081 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7082 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7083 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7085 if (surface_type == SURFACE_OPENGL && !device->adapter)
7087 ERR("OpenGL surfaces are not available without OpenGL.\n");
7088 return WINED3DERR_NOTAVAILABLE;
7091 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7092 if (!object)
7094 ERR("Failed to allocate surface memory.\n");
7095 return WINED3DERR_OUTOFVIDEOMEMORY;
7098 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7099 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7100 if (FAILED(hr))
7102 WARN("Failed to initialize surface, returning %#x.\n", hr);
7103 HeapFree(GetProcessHeap(), 0, object);
7104 return hr;
7107 TRACE("Created surface %p.\n", object);
7108 *surface = object;
7110 return hr;