wined3d: Avoid a drawable -> texture transfer through sysmem in arbfp_blit_surface...
[wine/multimedia.git] / dlls / wined3d / surface.c
blob6713f981e1acf0a29f4779f28498d7f420a943e7
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-2011 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 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
60 ENTER_GL();
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
93 LEAVE_GL();
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 /* Release the DC. */
101 SelectObject(surface->hDC, surface->dib.holdbitmap);
102 DeleteDC(surface->hDC);
103 /* Release the DIB section. */
104 DeleteObject(surface->dib.DIBsection);
105 surface->dib.bitmap_data = NULL;
106 surface->resource.allocatedMemory = NULL;
109 if (surface->flags & SFLAG_USERPTR)
110 wined3d_surface_set_mem(surface, NULL);
111 if (surface->overlay_dest)
112 list_remove(&surface->overlay_entry);
114 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
116 list_remove(&overlay->overlay_entry);
117 overlay->overlay_dest = NULL;
120 resource_cleanup(&surface->resource);
123 void surface_update_draw_binding(struct wined3d_surface *surface)
125 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
126 surface->draw_binding = SFLAG_INDRAWABLE;
127 else if (surface->resource.multisample_type)
128 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
129 else
130 surface->draw_binding = SFLAG_INTEXTURE;
133 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
135 TRACE("surface %p, container %p.\n", surface, container);
137 if (!container && type != WINED3D_CONTAINER_NONE)
138 ERR("Setting NULL container of type %#x.\n", type);
140 if (type == WINED3D_CONTAINER_SWAPCHAIN)
142 surface->get_drawable_size = get_drawable_size_swapchain;
144 else
146 switch (wined3d_settings.offscreen_rendering_mode)
148 case ORM_FBO:
149 surface->get_drawable_size = get_drawable_size_fbo;
150 break;
152 case ORM_BACKBUFFER:
153 surface->get_drawable_size = get_drawable_size_backbuffer;
154 break;
156 default:
157 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
158 return;
162 surface->container.type = type;
163 surface->container.u.base = container;
164 surface_update_draw_binding(surface);
167 struct blt_info
169 GLenum binding;
170 GLenum bind_target;
171 enum tex_types tex_type;
172 GLfloat coords[4][3];
175 struct float_rect
177 float l;
178 float t;
179 float r;
180 float b;
183 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
185 f->l = ((r->left * 2.0f) / w) - 1.0f;
186 f->t = ((r->top * 2.0f) / h) - 1.0f;
187 f->r = ((r->right * 2.0f) / w) - 1.0f;
188 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
191 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
193 GLfloat (*coords)[3] = info->coords;
194 struct float_rect f;
196 switch (target)
198 default:
199 FIXME("Unsupported texture target %#x\n", target);
200 /* Fall back to GL_TEXTURE_2D */
201 case GL_TEXTURE_2D:
202 info->binding = GL_TEXTURE_BINDING_2D;
203 info->bind_target = GL_TEXTURE_2D;
204 info->tex_type = tex_2d;
205 coords[0][0] = (float)rect->left / w;
206 coords[0][1] = (float)rect->top / h;
207 coords[0][2] = 0.0f;
209 coords[1][0] = (float)rect->right / w;
210 coords[1][1] = (float)rect->top / h;
211 coords[1][2] = 0.0f;
213 coords[2][0] = (float)rect->left / w;
214 coords[2][1] = (float)rect->bottom / h;
215 coords[2][2] = 0.0f;
217 coords[3][0] = (float)rect->right / w;
218 coords[3][1] = (float)rect->bottom / h;
219 coords[3][2] = 0.0f;
220 break;
222 case GL_TEXTURE_RECTANGLE_ARB:
223 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
224 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
225 info->tex_type = tex_rect;
226 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
227 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
228 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
229 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
230 break;
232 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
233 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
234 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
235 info->tex_type = tex_cube;
236 cube_coords_float(rect, w, h, &f);
238 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
239 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
240 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
241 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
242 break;
244 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
245 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
246 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
247 info->tex_type = tex_cube;
248 cube_coords_float(rect, w, h, &f);
250 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
251 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
252 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
253 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
254 break;
256 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
257 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
258 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
259 info->tex_type = tex_cube;
260 cube_coords_float(rect, w, h, &f);
262 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
263 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
264 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
265 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
266 break;
268 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
269 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
270 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
271 info->tex_type = tex_cube;
272 cube_coords_float(rect, w, h, &f);
274 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
275 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
276 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
277 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
278 break;
280 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
281 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
282 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
283 info->tex_type = tex_cube;
284 cube_coords_float(rect, w, h, &f);
286 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
287 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
288 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
289 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
290 break;
292 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
293 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
294 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
295 info->tex_type = tex_cube;
296 cube_coords_float(rect, w, h, &f);
298 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
299 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
300 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
301 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
302 break;
306 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
308 if (rect_in)
309 *rect_out = *rect_in;
310 else
312 rect_out->left = 0;
313 rect_out->top = 0;
314 rect_out->right = surface->resource.width;
315 rect_out->bottom = surface->resource.height;
319 /* GL locking and context activation is done by the caller */
320 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
321 const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
323 struct blt_info info;
325 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
327 glEnable(info.bind_target);
328 checkGLcall("glEnable(bind_target)");
330 context_bind_texture(context, info.bind_target, src_surface->texture_name);
332 /* Filtering for StretchRect */
333 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
334 wined3d_gl_mag_filter(magLookup, Filter));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
337 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
338 checkGLcall("glTexParameteri");
339 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
340 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
341 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
342 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
343 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
344 checkGLcall("glTexEnvi");
346 /* Draw a quad */
347 glBegin(GL_TRIANGLE_STRIP);
348 glTexCoord3fv(info.coords[0]);
349 glVertex2i(dst_rect->left, dst_rect->top);
351 glTexCoord3fv(info.coords[1]);
352 glVertex2i(dst_rect->right, dst_rect->top);
354 glTexCoord3fv(info.coords[2]);
355 glVertex2i(dst_rect->left, dst_rect->bottom);
357 glTexCoord3fv(info.coords[3]);
358 glVertex2i(dst_rect->right, dst_rect->bottom);
359 glEnd();
361 /* Unbind the texture */
362 context_bind_texture(context, info.bind_target, 0);
364 /* We changed the filtering settings on the texture. Inform the
365 * container about this to get the filters reset properly next draw. */
366 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
368 struct wined3d_texture *texture = src_surface->container.u.texture;
369 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
370 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
371 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
372 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
376 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
378 const struct wined3d_format *format = surface->resource.format;
379 SYSTEM_INFO sysInfo;
380 BITMAPINFO *b_info;
381 int extraline = 0;
382 DWORD *masks;
383 UINT usage;
384 HDC dc;
386 TRACE("surface %p.\n", surface);
388 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
390 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
391 return WINED3DERR_INVALIDCALL;
394 switch (format->byte_count)
396 case 2:
397 case 4:
398 /* Allocate extra space to store the RGB bit masks. */
399 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
400 break;
402 case 3:
403 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
404 break;
406 default:
407 /* Allocate extra space for a palette. */
408 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
409 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
410 break;
413 if (!b_info)
414 return E_OUTOFMEMORY;
416 /* Some applications access the surface in via DWORDs, and do not take
417 * the necessary care at the end of the surface. So we need at least
418 * 4 extra bytes at the end of the surface. Check against the page size,
419 * if the last page used for the surface has at least 4 spare bytes we're
420 * safe, otherwise add an extra line to the DIB section. */
421 GetSystemInfo(&sysInfo);
422 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
424 extraline = 1;
425 TRACE("Adding an extra line to the DIB section.\n");
428 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
429 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
430 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
431 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
432 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
433 * wined3d_surface_get_pitch(surface);
434 b_info->bmiHeader.biPlanes = 1;
435 b_info->bmiHeader.biBitCount = format->byte_count * 8;
437 b_info->bmiHeader.biXPelsPerMeter = 0;
438 b_info->bmiHeader.biYPelsPerMeter = 0;
439 b_info->bmiHeader.biClrUsed = 0;
440 b_info->bmiHeader.biClrImportant = 0;
442 /* Get the bit masks */
443 masks = (DWORD *)b_info->bmiColors;
444 switch (surface->resource.format->id)
446 case WINED3DFMT_B8G8R8_UNORM:
447 usage = DIB_RGB_COLORS;
448 b_info->bmiHeader.biCompression = BI_RGB;
449 break;
451 case WINED3DFMT_B5G5R5X1_UNORM:
452 case WINED3DFMT_B5G5R5A1_UNORM:
453 case WINED3DFMT_B4G4R4A4_UNORM:
454 case WINED3DFMT_B4G4R4X4_UNORM:
455 case WINED3DFMT_B2G3R3_UNORM:
456 case WINED3DFMT_B2G3R3A8_UNORM:
457 case WINED3DFMT_R10G10B10A2_UNORM:
458 case WINED3DFMT_R8G8B8A8_UNORM:
459 case WINED3DFMT_R8G8B8X8_UNORM:
460 case WINED3DFMT_B10G10R10A2_UNORM:
461 case WINED3DFMT_B5G6R5_UNORM:
462 case WINED3DFMT_R16G16B16A16_UNORM:
463 usage = 0;
464 b_info->bmiHeader.biCompression = BI_BITFIELDS;
465 masks[0] = format->red_mask;
466 masks[1] = format->green_mask;
467 masks[2] = format->blue_mask;
468 break;
470 default:
471 /* Don't know palette */
472 b_info->bmiHeader.biCompression = BI_RGB;
473 usage = 0;
474 break;
477 if (!(dc = GetDC(0)))
479 HeapFree(GetProcessHeap(), 0, b_info);
480 return HRESULT_FROM_WIN32(GetLastError());
483 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
484 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
485 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
486 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
487 ReleaseDC(0, dc);
489 if (!surface->dib.DIBsection)
491 ERR("Failed to create DIB section.\n");
492 HeapFree(GetProcessHeap(), 0, b_info);
493 return HRESULT_FROM_WIN32(GetLastError());
496 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
497 /* Copy the existing surface to the dib section. */
498 if (surface->resource.allocatedMemory)
500 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
501 surface->resource.height * wined3d_surface_get_pitch(surface));
503 else
505 /* This is to make maps read the GL texture although memory is allocated. */
506 surface->flags &= ~SFLAG_INSYSMEM;
508 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
510 HeapFree(GetProcessHeap(), 0, b_info);
512 /* Now allocate a DC. */
513 surface->hDC = CreateCompatibleDC(0);
514 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
515 TRACE("Using wined3d palette %p.\n", surface->palette);
516 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
518 surface->flags |= SFLAG_DIBSECTION;
520 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
521 surface->resource.heapMemory = NULL;
523 return WINED3D_OK;
526 static void surface_prepare_system_memory(struct wined3d_surface *surface)
528 struct wined3d_device *device = surface->resource.device;
529 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
531 TRACE("surface %p.\n", surface);
533 /* Performance optimization: Count how often a surface is locked, if it is
534 * locked regularly do not throw away the system memory copy. This avoids
535 * the need to download the surface from OpenGL all the time. The surface
536 * is still downloaded if the OpenGL texture is changed. */
537 if (!(surface->flags & SFLAG_DYNLOCK))
539 if (++surface->lockCount > MAXLOCKCOUNT)
541 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
542 surface->flags |= SFLAG_DYNLOCK;
546 /* Create a PBO for dynamically locked surfaces but don't do it for
547 * converted or NPOT surfaces. Also don't create a PBO for systemmem
548 * surfaces. */
549 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
550 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
551 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
553 struct wined3d_context *context;
554 GLenum error;
556 context = context_acquire(device, NULL);
557 ENTER_GL();
559 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
560 error = glGetError();
561 if (!surface->pbo || error != GL_NO_ERROR)
562 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
564 TRACE("Binding PBO %u.\n", surface->pbo);
566 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
567 checkGLcall("glBindBufferARB");
569 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
570 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
571 checkGLcall("glBufferDataARB");
573 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
574 checkGLcall("glBindBufferARB");
576 /* We don't need the system memory anymore and we can't even use it for PBOs. */
577 if (!(surface->flags & SFLAG_CLIENT))
579 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
580 surface->resource.heapMemory = NULL;
582 surface->resource.allocatedMemory = NULL;
583 surface->flags |= SFLAG_PBO;
584 LEAVE_GL();
585 context_release(context);
587 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
589 /* Whatever surface we have, make sure that there is memory allocated
590 * for the downloaded copy, or a PBO to map. */
591 if (!surface->resource.heapMemory)
592 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
594 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
595 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
597 if (surface->flags & SFLAG_INSYSMEM)
598 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
602 static void surface_evict_sysmem(struct wined3d_surface *surface)
604 if (surface->flags & SFLAG_DONOTFREE)
605 return;
607 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
608 surface->resource.allocatedMemory = NULL;
609 surface->resource.heapMemory = NULL;
610 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
613 /* Context activation is done by the caller. */
614 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
615 struct wined3d_context *context, BOOL srgb)
617 struct wined3d_device *device = surface->resource.device;
618 DWORD active_sampler;
620 /* We don't need a specific texture unit, but after binding the texture
621 * the current unit is dirty. Read the unit back instead of switching to
622 * 0, this avoids messing around with the state manager's GL states. The
623 * current texture unit should always be a valid one.
625 * To be more specific, this is tricky because we can implicitly be
626 * called from sampler() in state.c. This means we can't touch anything
627 * other than whatever happens to be the currently active texture, or we
628 * would risk marking already applied sampler states dirty again. */
629 active_sampler = device->rev_tex_unit_map[context->active_texture];
631 if (active_sampler != WINED3D_UNMAPPED_STAGE)
632 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
633 surface_bind(surface, context, srgb);
636 static void surface_force_reload(struct wined3d_surface *surface)
638 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
641 static void surface_release_client_storage(struct wined3d_surface *surface)
643 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
645 ENTER_GL();
646 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
647 if (surface->texture_name)
649 surface_bind_and_dirtify(surface, context, FALSE);
650 glTexImage2D(surface->texture_target, surface->texture_level,
651 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
653 if (surface->texture_name_srgb)
655 surface_bind_and_dirtify(surface, context, TRUE);
656 glTexImage2D(surface->texture_target, surface->texture_level,
657 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
659 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
660 LEAVE_GL();
662 context_release(context);
664 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
665 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
666 surface_force_reload(surface);
669 static HRESULT surface_private_setup(struct wined3d_surface *surface)
671 /* TODO: Check against the maximum texture sizes supported by the video card. */
672 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
673 unsigned int pow2Width, pow2Height;
675 TRACE("surface %p.\n", surface);
677 surface->texture_name = 0;
678 surface->texture_target = GL_TEXTURE_2D;
680 /* Non-power2 support */
681 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
683 pow2Width = surface->resource.width;
684 pow2Height = surface->resource.height;
686 else
688 /* Find the nearest pow2 match */
689 pow2Width = pow2Height = 1;
690 while (pow2Width < surface->resource.width)
691 pow2Width <<= 1;
692 while (pow2Height < surface->resource.height)
693 pow2Height <<= 1;
695 surface->pow2Width = pow2Width;
696 surface->pow2Height = pow2Height;
698 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
700 /* TODO: Add support for non power two compressed textures. */
701 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
703 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
704 surface, surface->resource.width, surface->resource.height);
705 return WINED3DERR_NOTAVAILABLE;
709 if (pow2Width != surface->resource.width
710 || pow2Height != surface->resource.height)
712 surface->flags |= SFLAG_NONPOW2;
715 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
716 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
718 /* One of three options:
719 * 1: Do the same as we do with NPOT and scale the texture, (any
720 * texture ops would require the texture to be scaled which is
721 * potentially slow)
722 * 2: Set the texture to the maximum size (bad idea).
723 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
724 * 4: Create the surface, but allow it to be used only for DirectDraw
725 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
726 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
727 * the render target. */
728 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
730 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
731 return WINED3DERR_NOTAVAILABLE;
734 /* We should never use this surface in combination with OpenGL! */
735 TRACE("Creating an oversized surface: %ux%u.\n",
736 surface->pow2Width, surface->pow2Height);
738 else
740 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
741 * and EXT_PALETTED_TEXTURE is used in combination with texture
742 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
743 * EXT_PALETTED_TEXTURE doesn't work in combination with
744 * ARB_TEXTURE_RECTANGLE. */
745 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
746 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
747 && gl_info->supported[EXT_PALETTED_TEXTURE]
748 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
750 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
751 surface->pow2Width = surface->resource.width;
752 surface->pow2Height = surface->resource.height;
753 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
757 switch (wined3d_settings.offscreen_rendering_mode)
759 case ORM_FBO:
760 surface->get_drawable_size = get_drawable_size_fbo;
761 break;
763 case ORM_BACKBUFFER:
764 surface->get_drawable_size = get_drawable_size_backbuffer;
765 break;
767 default:
768 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
769 return WINED3DERR_INVALIDCALL;
772 surface->flags |= SFLAG_INSYSMEM;
774 return WINED3D_OK;
777 static void surface_realize_palette(struct wined3d_surface *surface)
779 struct wined3d_palette *palette = surface->palette;
781 TRACE("surface %p.\n", surface);
783 if (!palette) return;
785 if (surface->resource.format->id == WINED3DFMT_P8_UINT
786 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
788 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
790 /* Make sure the texture is up to date. This call doesn't do
791 * anything if the texture is already up to date. */
792 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
794 /* We want to force a palette refresh, so mark the drawable as not being up to date */
795 if (!surface_is_offscreen(surface))
796 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
798 else
800 if (!(surface->flags & SFLAG_INSYSMEM))
802 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
803 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
805 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
809 if (surface->flags & SFLAG_DIBSECTION)
811 RGBQUAD col[256];
812 unsigned int i;
814 TRACE("Updating the DC's palette.\n");
816 for (i = 0; i < 256; ++i)
818 col[i].rgbRed = palette->palents[i].peRed;
819 col[i].rgbGreen = palette->palents[i].peGreen;
820 col[i].rgbBlue = palette->palents[i].peBlue;
821 col[i].rgbReserved = 0;
823 SetDIBColorTable(surface->hDC, 0, 256, col);
826 /* Propagate the changes to the drawable when we have a palette. */
827 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
828 surface_load_location(surface, surface->draw_binding, NULL);
831 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
833 HRESULT hr;
835 /* If there's no destination surface there is nothing to do. */
836 if (!surface->overlay_dest)
837 return WINED3D_OK;
839 /* Blt calls ModifyLocation on the dest surface, which in turn calls
840 * DrawOverlay to update the overlay. Prevent an endless recursion. */
841 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
842 return WINED3D_OK;
844 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
845 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
846 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
847 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
849 return hr;
852 static void surface_preload(struct wined3d_surface *surface)
854 TRACE("surface %p.\n", surface);
856 surface_internal_preload(surface, SRGB_ANY);
859 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
861 struct wined3d_device *device = surface->resource.device;
862 const RECT *pass_rect = rect;
864 TRACE("surface %p, rect %s, flags %#x.\n",
865 surface, wine_dbgstr_rect(rect), flags);
867 if (flags & WINED3DLOCK_DISCARD)
869 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
870 surface_prepare_system_memory(surface);
871 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
873 else
875 /* surface_load_location() does not check if the rectangle specifies
876 * the full surface. Most callers don't need that, so do it here. */
877 if (rect && !rect->top && !rect->left
878 && rect->right == surface->resource.width
879 && rect->bottom == surface->resource.height)
880 pass_rect = NULL;
882 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
883 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
884 || surface == device->fb.render_targets[0])))
885 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
888 if (surface->flags & SFLAG_PBO)
890 const struct wined3d_gl_info *gl_info;
891 struct wined3d_context *context;
893 context = context_acquire(device, NULL);
894 gl_info = context->gl_info;
896 ENTER_GL();
897 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
898 checkGLcall("glBindBufferARB");
900 /* This shouldn't happen but could occur if some other function
901 * didn't handle the PBO properly. */
902 if (surface->resource.allocatedMemory)
903 ERR("The surface already has PBO memory allocated.\n");
905 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
906 checkGLcall("glMapBufferARB");
908 /* Make sure the PBO isn't set anymore in order not to break non-PBO
909 * calls. */
910 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
911 checkGLcall("glBindBufferARB");
913 LEAVE_GL();
914 context_release(context);
917 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
919 if (!rect)
920 surface_add_dirty_rect(surface, NULL);
921 else
923 WINED3DBOX b;
925 b.Left = rect->left;
926 b.Top = rect->top;
927 b.Right = rect->right;
928 b.Bottom = rect->bottom;
929 b.Front = 0;
930 b.Back = 1;
931 surface_add_dirty_rect(surface, &b);
936 static void surface_unmap(struct wined3d_surface *surface)
938 struct wined3d_device *device = surface->resource.device;
939 BOOL fullsurface;
941 TRACE("surface %p.\n", surface);
943 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
945 if (surface->flags & SFLAG_PBO)
947 const struct wined3d_gl_info *gl_info;
948 struct wined3d_context *context;
950 TRACE("Freeing PBO memory.\n");
952 context = context_acquire(device, NULL);
953 gl_info = context->gl_info;
955 ENTER_GL();
956 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
957 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
958 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
959 checkGLcall("glUnmapBufferARB");
960 LEAVE_GL();
961 context_release(context);
963 surface->resource.allocatedMemory = NULL;
966 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
968 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
970 TRACE("Not dirtified, nothing to do.\n");
971 goto done;
974 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
975 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
977 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
979 static BOOL warned = FALSE;
980 if (!warned)
982 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
983 warned = TRUE;
985 goto done;
988 if (!surface->dirtyRect.left && !surface->dirtyRect.top
989 && surface->dirtyRect.right == surface->resource.width
990 && surface->dirtyRect.bottom == surface->resource.height)
992 fullsurface = TRUE;
994 else
996 /* TODO: Proper partial rectangle tracking. */
997 fullsurface = FALSE;
998 surface->flags |= SFLAG_INSYSMEM;
1001 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
1003 /* Partial rectangle tracking is not commonly implemented, it is only
1004 * done for render targets. INSYSMEM was set before to tell
1005 * surface_load_location() where to read the rectangle from.
1006 * Indrawable is set because all modifications from the partial
1007 * sysmem copy are written back to the drawable, thus the surface is
1008 * merged again in the drawable. The sysmem copy is not fully up to
1009 * date because only a subrectangle was read in Map(). */
1010 if (!fullsurface)
1012 surface_modify_location(surface, surface->draw_binding, TRUE);
1013 surface_evict_sysmem(surface);
1016 surface->dirtyRect.left = surface->resource.width;
1017 surface->dirtyRect.top = surface->resource.height;
1018 surface->dirtyRect.right = 0;
1019 surface->dirtyRect.bottom = 0;
1021 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1023 FIXME("Depth / stencil buffer locking is not implemented.\n");
1026 done:
1027 /* Overlays have to be redrawn manually after changes with the GL implementation */
1028 if (surface->overlay_dest)
1029 surface->surface_ops->surface_draw_overlay(surface);
1032 static HRESULT surface_getdc(struct wined3d_surface *surface)
1034 WINED3DLOCKED_RECT lock;
1035 HRESULT hr;
1037 TRACE("surface %p.\n", surface);
1039 /* Create a DIB section if there isn't a dc yet. */
1040 if (!surface->hDC)
1042 if (surface->flags & SFLAG_CLIENT)
1044 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1045 surface_release_client_storage(surface);
1047 hr = surface_create_dib_section(surface);
1048 if (FAILED(hr))
1049 return WINED3DERR_INVALIDCALL;
1051 /* Use the DIB section from now on if we are not using a PBO. */
1052 if (!(surface->flags & SFLAG_PBO))
1053 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1056 /* Map the surface. */
1057 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1058 if (FAILED(hr))
1059 ERR("Map failed, hr %#x.\n", hr);
1061 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1062 * activates the allocatedMemory. */
1063 if (surface->flags & SFLAG_PBO)
1064 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
1066 return hr;
1069 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1071 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1072 return FALSE;
1073 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1074 return FALSE;
1075 return TRUE;
1078 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1079 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1081 const struct wined3d_gl_info *gl_info;
1082 struct wined3d_context *context;
1083 DWORD src_mask, dst_mask;
1084 GLbitfield gl_mask;
1086 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1087 device, src_surface, wine_dbgstr_rect(src_rect),
1088 dst_surface, wine_dbgstr_rect(dst_rect));
1090 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1091 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1093 if (src_mask != dst_mask)
1095 ERR("Incompatible formats %s and %s.\n",
1096 debug_d3dformat(src_surface->resource.format->id),
1097 debug_d3dformat(dst_surface->resource.format->id));
1098 return;
1101 if (!src_mask)
1103 ERR("Not a depth / stencil format: %s.\n",
1104 debug_d3dformat(src_surface->resource.format->id));
1105 return;
1108 gl_mask = 0;
1109 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1110 gl_mask |= GL_DEPTH_BUFFER_BIT;
1111 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1112 gl_mask |= GL_STENCIL_BUFFER_BIT;
1114 /* Make sure the locations are up-to-date. Loading the destination
1115 * surface isn't required if the entire surface is overwritten. */
1116 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1117 if (!surface_is_full_rect(dst_surface, dst_rect))
1118 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1120 context = context_acquire(device, NULL);
1121 if (!context->valid)
1123 context_release(context);
1124 WARN("Invalid context, skipping blit.\n");
1125 return;
1128 gl_info = context->gl_info;
1130 ENTER_GL();
1132 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1133 glReadBuffer(GL_NONE);
1134 checkGLcall("glReadBuffer()");
1135 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1137 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1138 context_set_draw_buffer(context, GL_NONE);
1139 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1141 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1143 glDepthMask(GL_TRUE);
1144 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1146 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1148 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1150 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1151 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1153 glStencilMask(~0U);
1154 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1157 glDisable(GL_SCISSOR_TEST);
1158 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1160 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1161 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1162 checkGLcall("glBlitFramebuffer()");
1164 LEAVE_GL();
1166 if (wined3d_settings.strict_draw_ordering)
1167 wglFlush(); /* Flush to ensure ordering across contexts. */
1169 context_release(context);
1172 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1173 * Depth / stencil is not supported. */
1174 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1175 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1176 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1178 const struct wined3d_gl_info *gl_info;
1179 struct wined3d_context *context;
1180 RECT src_rect, dst_rect;
1181 GLenum gl_filter;
1182 GLenum buffer;
1184 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1185 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1186 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1187 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1188 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1190 src_rect = *src_rect_in;
1191 dst_rect = *dst_rect_in;
1193 switch (filter)
1195 case WINED3DTEXF_LINEAR:
1196 gl_filter = GL_LINEAR;
1197 break;
1199 default:
1200 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1201 case WINED3DTEXF_NONE:
1202 case WINED3DTEXF_POINT:
1203 gl_filter = GL_NEAREST;
1204 break;
1207 /* Resolve the source surface first if needed. */
1208 if (src_location == SFLAG_INRB_MULTISAMPLE
1209 && (src_surface->resource.format->id != dst_surface->resource.format->id
1210 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1211 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1212 src_location = SFLAG_INRB_RESOLVED;
1214 /* Make sure the locations are up-to-date. Loading the destination
1215 * surface isn't required if the entire surface is overwritten. (And is
1216 * in fact harmful if we're being called by surface_load_location() with
1217 * the purpose of loading the destination surface.) */
1218 surface_load_location(src_surface, src_location, NULL);
1219 if (!surface_is_full_rect(dst_surface, &dst_rect))
1220 surface_load_location(dst_surface, dst_location, NULL);
1222 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1223 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1224 else context = context_acquire(device, NULL);
1226 if (!context->valid)
1228 context_release(context);
1229 WARN("Invalid context, skipping blit.\n");
1230 return;
1233 gl_info = context->gl_info;
1235 if (src_location == SFLAG_INDRAWABLE)
1237 TRACE("Source surface %p is onscreen.\n", src_surface);
1238 buffer = surface_get_gl_buffer(src_surface);
1239 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1241 else
1243 TRACE("Source surface %p is offscreen.\n", src_surface);
1244 buffer = GL_COLOR_ATTACHMENT0;
1247 ENTER_GL();
1248 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1249 glReadBuffer(buffer);
1250 checkGLcall("glReadBuffer()");
1251 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1252 LEAVE_GL();
1254 if (dst_location == SFLAG_INDRAWABLE)
1256 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1257 buffer = surface_get_gl_buffer(dst_surface);
1258 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1260 else
1262 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1263 buffer = GL_COLOR_ATTACHMENT0;
1266 ENTER_GL();
1267 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1268 context_set_draw_buffer(context, buffer);
1269 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1270 context_invalidate_state(context, STATE_FRAMEBUFFER);
1272 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1273 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1274 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1275 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1276 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1278 glDisable(GL_SCISSOR_TEST);
1279 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1281 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1282 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1283 checkGLcall("glBlitFramebuffer()");
1285 LEAVE_GL();
1287 if (wined3d_settings.strict_draw_ordering
1288 || (dst_location == SFLAG_INDRAWABLE
1289 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1290 wglFlush();
1292 context_release(context);
1295 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1296 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1297 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1299 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1300 return FALSE;
1302 /* Source and/or destination need to be on the GL side */
1303 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1304 return FALSE;
1306 switch (blit_op)
1308 case WINED3D_BLIT_OP_COLOR_BLIT:
1309 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1310 return FALSE;
1311 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1312 return FALSE;
1313 break;
1315 case WINED3D_BLIT_OP_DEPTH_BLIT:
1316 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1317 return FALSE;
1318 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1319 return FALSE;
1320 break;
1322 default:
1323 return FALSE;
1326 if (!(src_format->id == dst_format->id
1327 || (is_identity_fixup(src_format->color_fixup)
1328 && is_identity_fixup(dst_format->color_fixup))))
1329 return FALSE;
1331 return TRUE;
1334 /* This function checks if the primary render target uses the 8bit paletted format. */
1335 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1337 if (device->fb.render_targets && device->fb.render_targets[0])
1339 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1340 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1341 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1342 return TRUE;
1344 return FALSE;
1347 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1348 DWORD color, WINED3DCOLORVALUE *float_color)
1350 const struct wined3d_format *format = surface->resource.format;
1351 const struct wined3d_device *device = surface->resource.device;
1353 switch (format->id)
1355 case WINED3DFMT_P8_UINT:
1356 if (surface->palette)
1358 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1359 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1360 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1362 else
1364 float_color->r = 0.0f;
1365 float_color->g = 0.0f;
1366 float_color->b = 0.0f;
1368 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1369 break;
1371 case WINED3DFMT_B5G6R5_UNORM:
1372 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1373 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1374 float_color->b = (color & 0x1f) / 31.0f;
1375 float_color->a = 1.0f;
1376 break;
1378 case WINED3DFMT_B8G8R8_UNORM:
1379 case WINED3DFMT_B8G8R8X8_UNORM:
1380 float_color->r = D3DCOLOR_R(color);
1381 float_color->g = D3DCOLOR_G(color);
1382 float_color->b = D3DCOLOR_B(color);
1383 float_color->a = 1.0f;
1384 break;
1386 case WINED3DFMT_B8G8R8A8_UNORM:
1387 float_color->r = D3DCOLOR_R(color);
1388 float_color->g = D3DCOLOR_G(color);
1389 float_color->b = D3DCOLOR_B(color);
1390 float_color->a = D3DCOLOR_A(color);
1391 break;
1393 default:
1394 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1395 return FALSE;
1398 return TRUE;
1401 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1403 const struct wined3d_format *format = surface->resource.format;
1405 switch (format->id)
1407 case WINED3DFMT_S1_UINT_D15_UNORM:
1408 *float_depth = depth / (float)0x00007fff;
1409 break;
1411 case WINED3DFMT_D16_UNORM:
1412 *float_depth = depth / (float)0x0000ffff;
1413 break;
1415 case WINED3DFMT_D24_UNORM_S8_UINT:
1416 case WINED3DFMT_X8D24_UNORM:
1417 *float_depth = depth / (float)0x00ffffff;
1418 break;
1420 case WINED3DFMT_D32_UNORM:
1421 *float_depth = depth / (float)0xffffffff;
1422 break;
1424 default:
1425 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1426 return FALSE;
1429 return TRUE;
1432 /* Do not call while under the GL lock. */
1433 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1435 const struct wined3d_resource *resource = &surface->resource;
1436 struct wined3d_device *device = resource->device;
1437 const struct blit_shader *blitter;
1439 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1440 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1441 if (!blitter)
1443 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1444 return WINED3DERR_INVALIDCALL;
1447 return blitter->depth_fill(device, surface, rect, depth);
1450 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1451 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1453 struct wined3d_device *device = src_surface->resource.device;
1455 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1456 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1457 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1458 return WINED3DERR_INVALIDCALL;
1460 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1462 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1463 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1464 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1466 return WINED3D_OK;
1469 /* Do not call while under the GL lock. */
1470 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1471 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1472 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1474 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1475 struct wined3d_device *device = dst_surface->resource.device;
1476 DWORD src_ds_flags, dst_ds_flags;
1477 RECT src_rect, dst_rect;
1478 BOOL scale, convert;
1480 static const DWORD simple_blit = WINEDDBLT_ASYNC
1481 | WINEDDBLT_COLORFILL
1482 | WINEDDBLT_WAIT
1483 | WINEDDBLT_DEPTHFILL
1484 | WINEDDBLT_DONOTWAIT;
1486 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1487 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1488 flags, fx, debug_d3dtexturefiltertype(filter));
1489 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1491 if (fx)
1493 TRACE("dwSize %#x.\n", fx->dwSize);
1494 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1495 TRACE("dwROP %#x.\n", fx->dwROP);
1496 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1497 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1498 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1499 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1500 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1501 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1502 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1503 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1504 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1505 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1506 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1507 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1508 TRACE("dwReserved %#x.\n", fx->dwReserved);
1509 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1510 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1511 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1512 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1513 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1514 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1515 fx->ddckDestColorkey.dwColorSpaceLowValue,
1516 fx->ddckDestColorkey.dwColorSpaceHighValue);
1517 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1518 fx->ddckSrcColorkey.dwColorSpaceLowValue,
1519 fx->ddckSrcColorkey.dwColorSpaceHighValue);
1522 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1524 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1525 return WINEDDERR_SURFACEBUSY;
1528 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1530 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1531 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1532 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1533 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1534 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1536 /* The destination rect can be out of bounds on the condition
1537 * that a clipper is set for the surface. */
1538 if (dst_surface->clipper)
1539 FIXME("Blit clipping not implemented.\n");
1540 else
1541 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1542 return WINEDDERR_INVALIDRECT;
1545 if (src_surface)
1547 surface_get_rect(src_surface, src_rect_in, &src_rect);
1549 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1550 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1551 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1552 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1553 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1555 WARN("Application gave us bad source rectangle for Blt.\n");
1556 return WINEDDERR_INVALIDRECT;
1559 else
1561 memset(&src_rect, 0, sizeof(src_rect));
1564 if (!fx || !(fx->dwDDFX))
1565 flags &= ~WINEDDBLT_DDFX;
1567 if (flags & WINEDDBLT_WAIT)
1568 flags &= ~WINEDDBLT_WAIT;
1570 if (flags & WINEDDBLT_ASYNC)
1572 static unsigned int once;
1574 if (!once++)
1575 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1576 flags &= ~WINEDDBLT_ASYNC;
1579 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1580 if (flags & WINEDDBLT_DONOTWAIT)
1582 static unsigned int once;
1584 if (!once++)
1585 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1586 flags &= ~WINEDDBLT_DONOTWAIT;
1589 if (!device->d3d_initialized)
1591 WARN("D3D not initialized, using fallback.\n");
1592 goto cpu;
1595 /* We want to avoid invalidating the sysmem location for converted
1596 * surfaces, since otherwise we'd have to convert the data back when
1597 * locking them. */
1598 if (dst_surface->flags & SFLAG_CONVERTED)
1600 WARN("Converted surface, using CPU blit.\n");
1601 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1604 if (flags & ~simple_blit)
1606 WARN("Using fallback for complex blit (%#x).\n", flags);
1607 goto fallback;
1610 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1611 src_swapchain = src_surface->container.u.swapchain;
1612 else
1613 src_swapchain = NULL;
1615 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1616 dst_swapchain = dst_surface->container.u.swapchain;
1617 else
1618 dst_swapchain = NULL;
1620 /* This isn't strictly needed. FBO blits for example could deal with
1621 * cross-swapchain blits by first downloading the source to a texture
1622 * before switching to the destination context. We just have this here to
1623 * not have to deal with the issue, since cross-swapchain blits should be
1624 * rare. */
1625 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1627 FIXME("Using fallback for cross-swapchain blit.\n");
1628 goto fallback;
1631 scale = src_surface
1632 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1633 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1634 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1636 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1637 if (src_surface)
1638 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1639 else
1640 src_ds_flags = 0;
1642 if (src_ds_flags || dst_ds_flags)
1644 if (flags & WINEDDBLT_DEPTHFILL)
1646 float depth;
1648 TRACE("Depth fill.\n");
1650 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1651 return WINED3DERR_INVALIDCALL;
1653 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1654 return WINED3D_OK;
1656 else
1658 /* Accessing depth / stencil surfaces is supposed to fail while in
1659 * a scene, except for fills, which seem to work. */
1660 if (device->inScene)
1662 WARN("Rejecting depth / stencil access while in scene.\n");
1663 return WINED3DERR_INVALIDCALL;
1666 if (src_ds_flags != dst_ds_flags)
1668 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1669 return WINED3DERR_INVALIDCALL;
1672 if (src_rect.top || src_rect.left
1673 || src_rect.bottom != src_surface->resource.height
1674 || src_rect.right != src_surface->resource.width)
1676 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1677 wine_dbgstr_rect(&src_rect));
1678 return WINED3DERR_INVALIDCALL;
1681 if (dst_rect.top || dst_rect.left
1682 || dst_rect.bottom != dst_surface->resource.height
1683 || dst_rect.right != dst_surface->resource.width)
1685 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1686 wine_dbgstr_rect(&src_rect));
1687 return WINED3DERR_INVALIDCALL;
1690 if (scale)
1692 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1693 return WINED3DERR_INVALIDCALL;
1696 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1697 return WINED3D_OK;
1700 else
1702 /* In principle this would apply to depth blits as well, but we don't
1703 * implement those in the CPU blitter at the moment. */
1704 if ((dst_surface->flags & SFLAG_INSYSMEM)
1705 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1707 if (scale)
1708 TRACE("Not doing sysmem blit because of scaling.\n");
1709 else if (convert)
1710 TRACE("Not doing sysmem blit because of format conversion.\n");
1711 else
1712 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1715 if (flags & WINEDDBLT_COLORFILL)
1717 WINED3DCOLORVALUE color;
1719 TRACE("Color fill.\n");
1721 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1722 goto fallback;
1724 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1725 return WINED3D_OK;
1727 else
1729 TRACE("Color blit.\n");
1731 /* Upload */
1732 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1734 if (scale)
1735 TRACE("Not doing upload because of scaling.\n");
1736 else if (convert)
1737 TRACE("Not doing upload because of format conversion.\n");
1738 else
1740 POINT dst_point = {dst_rect.left, dst_rect.top};
1742 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1744 if (!surface_is_offscreen(dst_surface))
1745 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1746 return WINED3D_OK;
1751 /* Use present for back -> front blits. The idea behind this is
1752 * that present is potentially faster than a blit, in particular
1753 * when FBO blits aren't available. Some ddraw applications like
1754 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1755 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1756 * applications can't blit directly to the frontbuffer. */
1757 if (dst_swapchain && dst_swapchain->back_buffers
1758 && dst_surface == dst_swapchain->front_buffer
1759 && src_surface == dst_swapchain->back_buffers[0])
1761 WINED3DSWAPEFFECT swap_effect = dst_swapchain->presentParms.SwapEffect;
1763 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1765 /* Set the swap effect to COPY, we don't want the backbuffer
1766 * to become undefined. */
1767 dst_swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
1768 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1769 dst_swapchain->presentParms.SwapEffect = swap_effect;
1771 return WINED3D_OK;
1774 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1775 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1776 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1778 TRACE("Using FBO blit.\n");
1780 surface_blt_fbo(device, filter,
1781 src_surface, src_surface->draw_binding, &src_rect,
1782 dst_surface, dst_surface->draw_binding, &dst_rect);
1783 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1784 return WINED3D_OK;
1787 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1788 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1789 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1791 TRACE("Using arbfp blit.\n");
1793 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1794 return WINED3D_OK;
1799 fallback:
1801 /* Special cases for render targets. */
1802 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1803 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1805 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1806 src_surface, &src_rect, flags, fx, filter)))
1807 return WINED3D_OK;
1810 cpu:
1812 /* For the rest call the X11 surface implementation. For render targets
1813 * this should be implemented OpenGL accelerated in BltOverride, other
1814 * blits are rather rare. */
1815 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1818 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1819 struct wined3d_surface *render_target)
1821 TRACE("surface %p, render_target %p.\n", surface, render_target);
1823 /* TODO: Check surface sizes, pools, etc. */
1825 if (render_target->resource.multisample_type)
1826 return WINED3DERR_INVALIDCALL;
1828 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1831 /* Context activation is done by the caller. */
1832 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1834 if (!surface->resource.heapMemory)
1836 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1837 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1838 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1841 ENTER_GL();
1842 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1843 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1844 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1845 surface->resource.size, surface->resource.allocatedMemory));
1846 checkGLcall("glGetBufferSubDataARB");
1847 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1848 checkGLcall("glDeleteBuffersARB");
1849 LEAVE_GL();
1851 surface->pbo = 0;
1852 surface->flags &= ~SFLAG_PBO;
1855 /* Do not call while under the GL lock. */
1856 static void surface_unload(struct wined3d_resource *resource)
1858 struct wined3d_surface *surface = surface_from_resource(resource);
1859 struct wined3d_renderbuffer_entry *entry, *entry2;
1860 struct wined3d_device *device = resource->device;
1861 const struct wined3d_gl_info *gl_info;
1862 struct wined3d_context *context;
1864 TRACE("surface %p.\n", surface);
1866 if (resource->pool == WINED3DPOOL_DEFAULT)
1868 /* Default pool resources are supposed to be destroyed before Reset is called.
1869 * Implicit resources stay however. So this means we have an implicit render target
1870 * or depth stencil. The content may be destroyed, but we still have to tear down
1871 * opengl resources, so we cannot leave early.
1873 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1874 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1875 * or the depth stencil into an FBO the texture or render buffer will be removed
1876 * and all flags get lost
1878 surface_init_sysmem(surface);
1879 /* We also get here when the ddraw swapchain is destroyed, for example
1880 * for a mode switch. In this case this surface won't necessarily be
1881 * an implicit surface. We have to mark it lost so that the
1882 * application can restore it after the mode switch. */
1883 surface->flags |= SFLAG_LOST;
1885 else
1887 /* Load the surface into system memory */
1888 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1889 surface_modify_location(surface, surface->draw_binding, FALSE);
1891 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1892 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1893 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1895 context = context_acquire(device, NULL);
1896 gl_info = context->gl_info;
1898 /* Destroy PBOs, but load them into real sysmem before */
1899 if (surface->flags & SFLAG_PBO)
1900 surface_remove_pbo(surface, gl_info);
1902 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1903 * all application-created targets the application has to release the surface
1904 * before calling _Reset
1906 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1908 ENTER_GL();
1909 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1910 LEAVE_GL();
1911 list_remove(&entry->entry);
1912 HeapFree(GetProcessHeap(), 0, entry);
1914 list_init(&surface->renderbuffers);
1915 surface->current_renderbuffer = NULL;
1917 ENTER_GL();
1919 /* If we're in a texture, the texture name belongs to the texture.
1920 * Otherwise, destroy it. */
1921 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1923 glDeleteTextures(1, &surface->texture_name);
1924 surface->texture_name = 0;
1925 glDeleteTextures(1, &surface->texture_name_srgb);
1926 surface->texture_name_srgb = 0;
1928 if (surface->rb_multisample)
1930 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1931 surface->rb_multisample = 0;
1933 if (surface->rb_resolved)
1935 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1936 surface->rb_resolved = 0;
1939 LEAVE_GL();
1941 context_release(context);
1943 resource_unload(resource);
1946 static const struct wined3d_resource_ops surface_resource_ops =
1948 surface_unload,
1951 static const struct wined3d_surface_ops surface_ops =
1953 surface_private_setup,
1954 surface_realize_palette,
1955 surface_draw_overlay,
1956 surface_preload,
1957 surface_map,
1958 surface_unmap,
1959 surface_getdc,
1962 /*****************************************************************************
1963 * Initializes the GDI surface, aka creates the DIB section we render to
1964 * The DIB section creation is done by calling GetDC, which will create the
1965 * section and releasing the dc to allow the app to use it. The dib section
1966 * will stay until the surface is released
1968 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1969 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1970 * avoid confusion in the shared surface code.
1972 * Returns:
1973 * WINED3D_OK on success
1974 * The return values of called methods on failure
1976 *****************************************************************************/
1977 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1979 HRESULT hr;
1981 TRACE("surface %p.\n", surface);
1983 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1985 ERR("Overlays not yet supported by GDI surfaces.\n");
1986 return WINED3DERR_INVALIDCALL;
1989 /* Sysmem textures have memory already allocated - release it,
1990 * this avoids an unnecessary memcpy. */
1991 hr = surface_create_dib_section(surface);
1992 if (SUCCEEDED(hr))
1994 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1995 surface->resource.heapMemory = NULL;
1996 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1999 /* We don't mind the nonpow2 stuff in GDI. */
2000 surface->pow2Width = surface->resource.width;
2001 surface->pow2Height = surface->resource.height;
2003 return WINED3D_OK;
2006 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
2008 struct wined3d_palette *palette = surface->palette;
2010 TRACE("surface %p.\n", surface);
2012 if (!palette) return;
2014 if (surface->flags & SFLAG_DIBSECTION)
2016 RGBQUAD col[256];
2017 unsigned int i;
2019 TRACE("Updating the DC's palette.\n");
2021 for (i = 0; i < 256; ++i)
2023 col[i].rgbRed = palette->palents[i].peRed;
2024 col[i].rgbGreen = palette->palents[i].peGreen;
2025 col[i].rgbBlue = palette->palents[i].peBlue;
2026 col[i].rgbReserved = 0;
2028 SetDIBColorTable(surface->hDC, 0, 256, col);
2031 /* Update the image because of the palette change. Some games like e.g.
2032 * Red Alert call SetEntries a lot to implement fading. */
2033 /* Tell the swapchain to update the screen. */
2034 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2036 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2037 if (surface == swapchain->front_buffer)
2039 x11_copy_to_screen(swapchain, NULL);
2044 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2046 FIXME("GDI surfaces can't draw overlays yet.\n");
2047 return E_FAIL;
2050 static void gdi_surface_preload(struct wined3d_surface *surface)
2052 TRACE("surface %p.\n", surface);
2054 ERR("Preloading GDI surfaces is not supported.\n");
2057 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2059 TRACE("surface %p, rect %s, flags %#x.\n",
2060 surface, wine_dbgstr_rect(rect), flags);
2062 if (!surface->resource.allocatedMemory)
2064 /* This happens on gdi surfaces if the application set a user pointer
2065 * and resets it. Recreate the DIB section. */
2066 surface_create_dib_section(surface);
2067 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2071 static void gdi_surface_unmap(struct wined3d_surface *surface)
2073 TRACE("surface %p.\n", surface);
2075 /* Tell the swapchain to update the screen. */
2076 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2078 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2079 if (surface == swapchain->front_buffer)
2081 x11_copy_to_screen(swapchain, &surface->lockedRect);
2085 memset(&surface->lockedRect, 0, sizeof(RECT));
2088 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2090 WINED3DLOCKED_RECT lock;
2091 HRESULT hr;
2093 TRACE("surface %p.\n", surface);
2095 /* Should have a DIB section already. */
2096 if (!(surface->flags & SFLAG_DIBSECTION))
2098 WARN("DC not supported on this surface\n");
2099 return WINED3DERR_INVALIDCALL;
2102 /* Map the surface. */
2103 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2104 if (FAILED(hr))
2105 ERR("Map failed, hr %#x.\n", hr);
2107 return hr;
2110 static const struct wined3d_surface_ops gdi_surface_ops =
2112 gdi_surface_private_setup,
2113 gdi_surface_realize_palette,
2114 gdi_surface_draw_overlay,
2115 gdi_surface_preload,
2116 gdi_surface_map,
2117 gdi_surface_unmap,
2118 gdi_surface_getdc,
2121 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2123 GLuint *name;
2124 DWORD flag;
2126 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2128 if(srgb)
2130 name = &surface->texture_name_srgb;
2131 flag = SFLAG_INSRGBTEX;
2133 else
2135 name = &surface->texture_name;
2136 flag = SFLAG_INTEXTURE;
2139 if (!*name && new_name)
2141 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2142 * surface has no texture name yet. See if we can get rid of this. */
2143 if (surface->flags & flag)
2144 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2145 surface_modify_location(surface, flag, FALSE);
2148 *name = new_name;
2149 surface_force_reload(surface);
2152 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2154 TRACE("surface %p, target %#x.\n", surface, target);
2156 if (surface->texture_target != target)
2158 if (target == GL_TEXTURE_RECTANGLE_ARB)
2160 surface->flags &= ~SFLAG_NORMCOORD;
2162 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2164 surface->flags |= SFLAG_NORMCOORD;
2167 surface->texture_target = target;
2168 surface_force_reload(surface);
2171 /* Context activation is done by the caller. */
2172 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2174 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2176 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2178 struct wined3d_texture *texture = surface->container.u.texture;
2180 TRACE("Passing to container (%p).\n", texture);
2181 texture->texture_ops->texture_bind(texture, context, srgb);
2183 else
2185 if (surface->texture_level)
2187 ERR("Standalone surface %p is non-zero texture level %u.\n",
2188 surface, surface->texture_level);
2191 if (srgb)
2192 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2194 ENTER_GL();
2196 if (!surface->texture_name)
2198 glGenTextures(1, &surface->texture_name);
2199 checkGLcall("glGenTextures");
2201 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2203 context_bind_texture(context, surface->texture_target, surface->texture_name);
2204 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2205 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2206 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2207 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2208 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2209 checkGLcall("glTexParameteri");
2211 else
2213 context_bind_texture(context, surface->texture_target, surface->texture_name);
2216 LEAVE_GL();
2220 /* This call just downloads data, the caller is responsible for binding the
2221 * correct texture. */
2222 /* Context activation is done by the caller. */
2223 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2225 const struct wined3d_format *format = surface->resource.format;
2227 /* Only support read back of converted P8 surfaces. */
2228 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2230 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2231 return;
2234 ENTER_GL();
2236 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2238 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2239 surface, surface->texture_level, format->glFormat, format->glType,
2240 surface->resource.allocatedMemory);
2242 if (surface->flags & SFLAG_PBO)
2244 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2245 checkGLcall("glBindBufferARB");
2246 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2247 checkGLcall("glGetCompressedTexImageARB");
2248 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2249 checkGLcall("glBindBufferARB");
2251 else
2253 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2254 surface->texture_level, surface->resource.allocatedMemory));
2255 checkGLcall("glGetCompressedTexImageARB");
2258 LEAVE_GL();
2260 else
2262 void *mem;
2263 GLenum gl_format = format->glFormat;
2264 GLenum gl_type = format->glType;
2265 int src_pitch = 0;
2266 int dst_pitch = 0;
2268 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2269 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2271 gl_format = GL_ALPHA;
2272 gl_type = GL_UNSIGNED_BYTE;
2275 if (surface->flags & SFLAG_NONPOW2)
2277 unsigned char alignment = surface->resource.device->surface_alignment;
2278 src_pitch = format->byte_count * surface->pow2Width;
2279 dst_pitch = wined3d_surface_get_pitch(surface);
2280 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2281 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2283 else
2285 mem = surface->resource.allocatedMemory;
2288 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2289 surface, surface->texture_level, gl_format, gl_type, mem);
2291 if (surface->flags & SFLAG_PBO)
2293 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2294 checkGLcall("glBindBufferARB");
2296 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2297 checkGLcall("glGetTexImage");
2299 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2300 checkGLcall("glBindBufferARB");
2302 else
2304 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2305 checkGLcall("glGetTexImage");
2307 LEAVE_GL();
2309 if (surface->flags & SFLAG_NONPOW2)
2311 const BYTE *src_data;
2312 BYTE *dst_data;
2313 UINT y;
2315 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2316 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2317 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2319 * We're doing this...
2321 * instead of boxing the texture :
2322 * |<-texture width ->| -->pow2width| /\
2323 * |111111111111111111| | |
2324 * |222 Texture 222222| boxed empty | texture height
2325 * |3333 Data 33333333| | |
2326 * |444444444444444444| | \/
2327 * ----------------------------------- |
2328 * | boxed empty | boxed empty | pow2height
2329 * | | | \/
2330 * -----------------------------------
2333 * we're repacking the data to the expected texture width
2335 * |<-texture width ->| -->pow2width| /\
2336 * |111111111111111111222222222222222| |
2337 * |222333333333333333333444444444444| texture height
2338 * |444444 | |
2339 * | | \/
2340 * | | |
2341 * | empty | pow2height
2342 * | | \/
2343 * -----------------------------------
2345 * == is the same as
2347 * |<-texture width ->| /\
2348 * |111111111111111111|
2349 * |222222222222222222|texture height
2350 * |333333333333333333|
2351 * |444444444444444444| \/
2352 * --------------------
2354 * this also means that any references to allocatedMemory should work with the data as if were a
2355 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2357 * internally the texture is still stored in a boxed format so any references to textureName will
2358 * get a boxed texture with width pow2width and not a texture of width resource.width.
2360 * Performance should not be an issue, because applications normally do not lock the surfaces when
2361 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2362 * and doesn't have to be re-read. */
2363 src_data = mem;
2364 dst_data = surface->resource.allocatedMemory;
2365 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2366 for (y = 1; y < surface->resource.height; ++y)
2368 /* skip the first row */
2369 src_data += src_pitch;
2370 dst_data += dst_pitch;
2371 memcpy(dst_data, src_data, dst_pitch);
2374 HeapFree(GetProcessHeap(), 0, mem);
2378 /* Surface has now been downloaded */
2379 surface->flags |= SFLAG_INSYSMEM;
2382 /* This call just uploads data, the caller is responsible for binding the
2383 * correct texture. */
2384 /* Context activation is done by the caller. */
2385 static void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2386 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2387 BOOL srgb, const struct wined3d_bo_address *data)
2389 UINT update_w = src_rect->right - src_rect->left;
2390 UINT update_h = src_rect->bottom - src_rect->top;
2392 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2393 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2394 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2396 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2397 update_h *= format->heightscale;
2399 ENTER_GL();
2401 if (data->buffer_object)
2403 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2404 checkGLcall("glBindBufferARB");
2407 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2409 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2410 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2411 const BYTE *addr = data->addr;
2412 GLenum internal;
2414 addr += (src_rect->top / format->block_height) * src_pitch;
2415 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2417 if (srgb)
2418 internal = format->glGammaInternal;
2419 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2420 internal = format->rtInternal;
2421 else
2422 internal = format->glInternal;
2424 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2425 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2426 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2428 if (row_length == src_pitch)
2430 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2431 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2433 else
2435 UINT row, y;
2437 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2438 * can't use the unpack row length like below. */
2439 for (row = 0, y = dst_point->y; row < row_count; ++row)
2441 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2442 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2443 y += format->block_height;
2444 addr += src_pitch;
2447 checkGLcall("glCompressedTexSubImage2DARB");
2449 else
2451 const BYTE *addr = data->addr;
2453 addr += src_rect->top * src_pitch;
2454 addr += src_rect->left * format->byte_count;
2456 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2457 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2458 update_w, update_h, format->glFormat, format->glType, addr);
2460 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2461 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2462 update_w, update_h, format->glFormat, format->glType, addr);
2463 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2464 checkGLcall("glTexSubImage2D");
2467 if (data->buffer_object)
2469 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2470 checkGLcall("glBindBufferARB");
2473 LEAVE_GL();
2475 if (wined3d_settings.strict_draw_ordering)
2476 wglFlush();
2478 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2480 struct wined3d_device *device = surface->resource.device;
2481 unsigned int i;
2483 for (i = 0; i < device->context_count; ++i)
2485 context_surface_update(device->contexts[i], surface);
2490 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2491 struct wined3d_surface *src_surface, const RECT *src_rect)
2493 const struct wined3d_format *src_format;
2494 const struct wined3d_format *dst_format;
2495 const struct wined3d_gl_info *gl_info;
2496 struct wined3d_context *context;
2497 struct wined3d_bo_address data;
2498 struct wined3d_format format;
2499 UINT update_w, update_h;
2500 CONVERT_TYPES convert;
2501 UINT dst_w, dst_h;
2502 UINT src_w, src_h;
2503 UINT src_pitch;
2504 POINT p;
2505 RECT r;
2507 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2508 dst_surface, wine_dbgstr_point(dst_point),
2509 src_surface, wine_dbgstr_rect(src_rect));
2511 src_format = src_surface->resource.format;
2512 dst_format = dst_surface->resource.format;
2514 if (src_format->id != dst_format->id)
2516 WARN("Source and destination surfaces should have the same format.\n");
2517 return WINED3DERR_INVALIDCALL;
2520 if (!dst_point)
2522 p.x = 0;
2523 p.y = 0;
2524 dst_point = &p;
2526 else if (dst_point->x < 0 || dst_point->y < 0)
2528 WARN("Invalid destination point.\n");
2529 return WINED3DERR_INVALIDCALL;
2532 if (!src_rect)
2534 r.left = 0;
2535 r.top = 0;
2536 r.right = src_surface->resource.width;
2537 r.bottom = src_surface->resource.height;
2538 src_rect = &r;
2540 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2541 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2543 WARN("Invalid source rectangle.\n");
2544 return WINED3DERR_INVALIDCALL;
2547 src_w = src_surface->resource.width;
2548 src_h = src_surface->resource.height;
2550 dst_w = dst_surface->resource.width;
2551 dst_h = dst_surface->resource.height;
2553 update_w = src_rect->right - src_rect->left;
2554 update_h = src_rect->bottom - src_rect->top;
2556 if (update_w > dst_w || dst_point->x > dst_w - update_w
2557 || update_h > dst_h || dst_point->y > dst_h - update_h)
2559 WARN("Destination out of bounds.\n");
2560 return WINED3DERR_INVALIDCALL;
2563 /* NPOT block sizes would be silly. */
2564 if ((src_format->flags & WINED3DFMT_FLAG_COMPRESSED)
2565 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2566 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2568 WARN("Update rect not block-aligned.\n");
2569 return WINED3DERR_INVALIDCALL;
2572 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2573 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2574 if (convert != NO_CONVERSION || format.convert)
2576 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2577 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2580 context = context_acquire(dst_surface->resource.device, NULL);
2581 gl_info = context->gl_info;
2583 /* Only load the surface for partial updates. For newly allocated texture
2584 * the texture wouldn't be the current location, and we'd upload zeroes
2585 * just to overwrite them again. */
2586 if (update_w == dst_w && update_h == dst_h)
2587 surface_prepare_texture(dst_surface, context, FALSE);
2588 else
2589 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2590 surface_bind(dst_surface, context, FALSE);
2592 data.buffer_object = src_surface->pbo;
2593 data.addr = src_surface->resource.allocatedMemory;
2594 src_pitch = wined3d_surface_get_pitch(src_surface);
2596 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2598 invalidate_active_texture(dst_surface->resource.device, context);
2600 context_release(context);
2602 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2603 return WINED3D_OK;
2606 /* This call just allocates the texture, the caller is responsible for binding
2607 * the correct texture. */
2608 /* Context activation is done by the caller. */
2609 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2610 const struct wined3d_format *format, BOOL srgb)
2612 BOOL enable_client_storage = FALSE;
2613 GLsizei width = surface->pow2Width;
2614 GLsizei height = surface->pow2Height;
2615 const BYTE *mem = NULL;
2616 GLenum internal;
2618 if (srgb)
2620 internal = format->glGammaInternal;
2622 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2624 internal = format->rtInternal;
2626 else
2628 internal = format->glInternal;
2631 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2633 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",
2634 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2635 internal, width, height, format->glFormat, format->glType);
2637 ENTER_GL();
2639 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2641 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2642 || !surface->resource.allocatedMemory)
2644 /* In some cases we want to disable client storage.
2645 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2646 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2647 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2648 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2650 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2651 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2652 surface->flags &= ~SFLAG_CLIENT;
2653 enable_client_storage = TRUE;
2655 else
2657 surface->flags |= SFLAG_CLIENT;
2659 /* Point OpenGL to our allocated texture memory. Do not use
2660 * resource.allocatedMemory here because it might point into a
2661 * PBO. Instead use heapMemory, but get the alignment right. */
2662 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2663 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2667 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2669 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2670 internal, width, height, 0, surface->resource.size, mem));
2671 checkGLcall("glCompressedTexImage2DARB");
2673 else
2675 glTexImage2D(surface->texture_target, surface->texture_level,
2676 internal, width, height, 0, format->glFormat, format->glType, mem);
2677 checkGLcall("glTexImage2D");
2680 if(enable_client_storage) {
2681 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2682 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2684 LEAVE_GL();
2687 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2688 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2689 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2690 /* GL locking is done by the caller */
2691 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2693 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2694 struct wined3d_renderbuffer_entry *entry;
2695 GLuint renderbuffer = 0;
2696 unsigned int src_width, src_height;
2697 unsigned int width, height;
2699 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2701 width = rt->pow2Width;
2702 height = rt->pow2Height;
2704 else
2706 width = surface->pow2Width;
2707 height = surface->pow2Height;
2710 src_width = surface->pow2Width;
2711 src_height = surface->pow2Height;
2713 /* A depth stencil smaller than the render target is not valid */
2714 if (width > src_width || height > src_height) return;
2716 /* Remove any renderbuffer set if the sizes match */
2717 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2718 || (width == src_width && height == src_height))
2720 surface->current_renderbuffer = NULL;
2721 return;
2724 /* Look if we've already got a renderbuffer of the correct dimensions */
2725 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2727 if (entry->width == width && entry->height == height)
2729 renderbuffer = entry->id;
2730 surface->current_renderbuffer = entry;
2731 break;
2735 if (!renderbuffer)
2737 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2738 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2739 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2740 surface->resource.format->glInternal, width, height);
2742 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2743 entry->width = width;
2744 entry->height = height;
2745 entry->id = renderbuffer;
2746 list_add_head(&surface->renderbuffers, &entry->entry);
2748 surface->current_renderbuffer = entry;
2751 checkGLcall("set_compatible_renderbuffer");
2754 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2756 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2758 TRACE("surface %p.\n", surface);
2760 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2762 ERR("Surface %p is not on a swapchain.\n", surface);
2763 return GL_NONE;
2766 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2768 if (swapchain->render_to_fbo)
2770 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2771 return GL_COLOR_ATTACHMENT0;
2773 TRACE("Returning GL_BACK\n");
2774 return GL_BACK;
2776 else if (surface == swapchain->front_buffer)
2778 TRACE("Returning GL_FRONT\n");
2779 return GL_FRONT;
2782 FIXME("Higher back buffer, returning GL_BACK\n");
2783 return GL_BACK;
2786 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2787 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2789 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2791 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2792 /* No partial locking for textures yet. */
2793 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2795 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2796 if (dirty_rect)
2798 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2799 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2800 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2801 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2803 else
2805 surface->dirtyRect.left = 0;
2806 surface->dirtyRect.top = 0;
2807 surface->dirtyRect.right = surface->resource.width;
2808 surface->dirtyRect.bottom = surface->resource.height;
2811 /* if the container is a texture then mark it dirty. */
2812 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2814 TRACE("Passing to container.\n");
2815 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2819 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2821 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2822 BOOL ck_changed;
2824 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2826 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2828 ERR("Not supported on scratch surfaces.\n");
2829 return WINED3DERR_INVALIDCALL;
2832 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2834 /* Reload if either the texture and sysmem have different ideas about the
2835 * color key, or the actual key values changed. */
2836 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2837 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2838 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2840 TRACE("Reloading because of color keying\n");
2841 /* To perform the color key conversion we need a sysmem copy of
2842 * the surface. Make sure we have it. */
2844 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2845 /* Make sure the texture is reloaded because of the color key change,
2846 * this kills performance though :( */
2847 /* TODO: This is not necessarily needed with hw palettized texture support. */
2848 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2849 /* Switching color keying on / off may change the internal format. */
2850 if (ck_changed)
2851 surface_force_reload(surface);
2853 else if (!(surface->flags & flag))
2855 TRACE("Reloading because surface is dirty.\n");
2857 else
2859 TRACE("surface is already in texture\n");
2860 return WINED3D_OK;
2863 /* No partial locking for textures yet. */
2864 surface_load_location(surface, flag, NULL);
2865 surface_evict_sysmem(surface);
2867 return WINED3D_OK;
2870 /* See also float_16_to_32() in wined3d_private.h */
2871 static inline unsigned short float_32_to_16(const float *in)
2873 int exp = 0;
2874 float tmp = fabsf(*in);
2875 unsigned int mantissa;
2876 unsigned short ret;
2878 /* Deal with special numbers */
2879 if (*in == 0.0f)
2880 return 0x0000;
2881 if (isnan(*in))
2882 return 0x7c01;
2883 if (isinf(*in))
2884 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2886 if (tmp < powf(2, 10))
2890 tmp = tmp * 2.0f;
2891 exp--;
2892 } while (tmp < powf(2, 10));
2894 else if (tmp >= powf(2, 11))
2898 tmp /= 2.0f;
2899 exp++;
2900 } while (tmp >= powf(2, 11));
2903 mantissa = (unsigned int)tmp;
2904 if (tmp - mantissa >= 0.5f)
2905 ++mantissa; /* Round to nearest, away from zero. */
2907 exp += 10; /* Normalize the mantissa. */
2908 exp += 15; /* Exponent is encoded with excess 15. */
2910 if (exp > 30) /* too big */
2912 ret = 0x7c00; /* INF */
2914 else if (exp <= 0)
2916 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2917 while (exp <= 0)
2919 mantissa = mantissa >> 1;
2920 ++exp;
2922 ret = mantissa & 0x3ff;
2924 else
2926 ret = (exp << 10) | (mantissa & 0x3ff);
2929 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2930 return ret;
2933 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2935 ULONG refcount;
2937 TRACE("Surface %p, container %p of type %#x.\n",
2938 surface, surface->container.u.base, surface->container.type);
2940 switch (surface->container.type)
2942 case WINED3D_CONTAINER_TEXTURE:
2943 return wined3d_texture_incref(surface->container.u.texture);
2945 case WINED3D_CONTAINER_SWAPCHAIN:
2946 return wined3d_swapchain_incref(surface->container.u.swapchain);
2948 default:
2949 ERR("Unhandled container type %#x.\n", surface->container.type);
2950 case WINED3D_CONTAINER_NONE:
2951 break;
2954 refcount = InterlockedIncrement(&surface->resource.ref);
2955 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2957 return refcount;
2960 /* Do not call while under the GL lock. */
2961 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2963 ULONG refcount;
2965 TRACE("Surface %p, container %p of type %#x.\n",
2966 surface, surface->container.u.base, surface->container.type);
2968 switch (surface->container.type)
2970 case WINED3D_CONTAINER_TEXTURE:
2971 return wined3d_texture_decref(surface->container.u.texture);
2973 case WINED3D_CONTAINER_SWAPCHAIN:
2974 return wined3d_swapchain_decref(surface->container.u.swapchain);
2976 default:
2977 ERR("Unhandled container type %#x.\n", surface->container.type);
2978 case WINED3D_CONTAINER_NONE:
2979 break;
2982 refcount = InterlockedDecrement(&surface->resource.ref);
2983 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2985 if (!refcount)
2987 surface_cleanup(surface);
2988 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2990 TRACE("Destroyed surface %p.\n", surface);
2991 HeapFree(GetProcessHeap(), 0, surface);
2994 return refcount;
2997 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2999 return resource_set_priority(&surface->resource, priority);
3002 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
3004 return resource_get_priority(&surface->resource);
3007 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
3009 TRACE("surface %p.\n", surface);
3011 surface->surface_ops->surface_preload(surface);
3014 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
3016 TRACE("surface %p.\n", surface);
3018 return surface->resource.parent;
3021 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
3023 TRACE("surface %p.\n", surface);
3025 return &surface->resource;
3028 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
3030 TRACE("surface %p, flags %#x.\n", surface, flags);
3032 switch (flags)
3034 case WINEDDGBS_CANBLT:
3035 case WINEDDGBS_ISBLTDONE:
3036 return WINED3D_OK;
3038 default:
3039 return WINED3DERR_INVALIDCALL;
3043 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
3045 TRACE("surface %p, flags %#x.\n", surface, flags);
3047 /* XXX: DDERR_INVALIDSURFACETYPE */
3049 switch (flags)
3051 case WINEDDGFS_CANFLIP:
3052 case WINEDDGFS_ISFLIPDONE:
3053 return WINED3D_OK;
3055 default:
3056 return WINED3DERR_INVALIDCALL;
3060 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
3062 TRACE("surface %p.\n", surface);
3064 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
3065 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3068 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
3070 TRACE("surface %p.\n", surface);
3072 surface->flags &= ~SFLAG_LOST;
3073 return WINED3D_OK;
3076 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3078 TRACE("surface %p, palette %p.\n", surface, palette);
3080 if (surface->palette == palette)
3082 TRACE("Nop palette change.\n");
3083 return WINED3D_OK;
3086 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3087 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3089 surface->palette = palette;
3091 if (palette)
3093 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3094 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3096 surface->surface_ops->surface_realize_palette(surface);
3099 return WINED3D_OK;
3102 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3103 DWORD flags, const WINEDDCOLORKEY *color_key)
3105 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3107 if (flags & WINEDDCKEY_COLORSPACE)
3109 FIXME(" colorkey value not supported (%08x) !\n", flags);
3110 return WINED3DERR_INVALIDCALL;
3113 /* Dirtify the surface, but only if a key was changed. */
3114 if (color_key)
3116 switch (flags & ~WINEDDCKEY_COLORSPACE)
3118 case WINEDDCKEY_DESTBLT:
3119 surface->DestBltCKey = *color_key;
3120 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3121 break;
3123 case WINEDDCKEY_DESTOVERLAY:
3124 surface->DestOverlayCKey = *color_key;
3125 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3126 break;
3128 case WINEDDCKEY_SRCOVERLAY:
3129 surface->SrcOverlayCKey = *color_key;
3130 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3131 break;
3133 case WINEDDCKEY_SRCBLT:
3134 surface->SrcBltCKey = *color_key;
3135 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3136 break;
3139 else
3141 switch (flags & ~WINEDDCKEY_COLORSPACE)
3143 case WINEDDCKEY_DESTBLT:
3144 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3145 break;
3147 case WINEDDCKEY_DESTOVERLAY:
3148 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3149 break;
3151 case WINEDDCKEY_SRCOVERLAY:
3152 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3153 break;
3155 case WINEDDCKEY_SRCBLT:
3156 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3157 break;
3161 return WINED3D_OK;
3164 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3166 TRACE("surface %p.\n", surface);
3168 return surface->palette;
3171 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3173 const struct wined3d_format *format = surface->resource.format;
3174 DWORD pitch;
3176 TRACE("surface %p.\n", surface);
3178 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3180 /* Since compressed formats are block based, pitch means the amount of
3181 * bytes to the next row of block rather than the next row of pixels. */
3182 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3183 pitch = row_block_count * format->block_byte_count;
3185 else
3187 unsigned char alignment = surface->resource.device->surface_alignment;
3188 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3189 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3192 TRACE("Returning %u.\n", pitch);
3194 return pitch;
3197 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3199 TRACE("surface %p, mem %p.\n", surface, mem);
3201 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3203 WARN("Surface is locked or the DC is in use.\n");
3204 return WINED3DERR_INVALIDCALL;
3207 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3208 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3210 ERR("Not supported on render targets.\n");
3211 return WINED3DERR_INVALIDCALL;
3214 if (mem && mem != surface->resource.allocatedMemory)
3216 void *release = NULL;
3218 /* Do I have to copy the old surface content? */
3219 if (surface->flags & SFLAG_DIBSECTION)
3221 SelectObject(surface->hDC, surface->dib.holdbitmap);
3222 DeleteDC(surface->hDC);
3223 /* Release the DIB section. */
3224 DeleteObject(surface->dib.DIBsection);
3225 surface->dib.bitmap_data = NULL;
3226 surface->resource.allocatedMemory = NULL;
3227 surface->hDC = NULL;
3228 surface->flags &= ~SFLAG_DIBSECTION;
3230 else if (!(surface->flags & SFLAG_USERPTR))
3232 release = surface->resource.heapMemory;
3233 surface->resource.heapMemory = NULL;
3235 surface->resource.allocatedMemory = mem;
3236 surface->flags |= SFLAG_USERPTR;
3238 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3239 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3241 /* For client textures OpenGL has to be notified. */
3242 if (surface->flags & SFLAG_CLIENT)
3243 surface_release_client_storage(surface);
3245 /* Now free the old memory if any. */
3246 HeapFree(GetProcessHeap(), 0, release);
3248 else if (surface->flags & SFLAG_USERPTR)
3250 /* HeapMemory should be NULL already. */
3251 if (surface->resource.heapMemory)
3252 ERR("User pointer surface has heap memory allocated.\n");
3254 if (!mem)
3256 surface->resource.allocatedMemory = NULL;
3257 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3259 if (surface->flags & SFLAG_CLIENT)
3260 surface_release_client_storage(surface);
3262 surface_prepare_system_memory(surface);
3265 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3268 return WINED3D_OK;
3271 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3273 LONG w, h;
3275 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3277 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3279 WARN("Not an overlay surface.\n");
3280 return WINEDDERR_NOTAOVERLAYSURFACE;
3283 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3284 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3285 surface->overlay_destrect.left = x;
3286 surface->overlay_destrect.top = y;
3287 surface->overlay_destrect.right = x + w;
3288 surface->overlay_destrect.bottom = y + h;
3290 surface->surface_ops->surface_draw_overlay(surface);
3292 return WINED3D_OK;
3295 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3297 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3299 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3301 TRACE("Not an overlay surface.\n");
3302 return WINEDDERR_NOTAOVERLAYSURFACE;
3305 if (!surface->overlay_dest)
3307 TRACE("Overlay not visible.\n");
3308 *x = 0;
3309 *y = 0;
3310 return WINEDDERR_OVERLAYNOTVISIBLE;
3313 *x = surface->overlay_destrect.left;
3314 *y = surface->overlay_destrect.top;
3316 TRACE("Returning position %d, %d.\n", *x, *y);
3318 return WINED3D_OK;
3321 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3322 DWORD flags, struct wined3d_surface *ref)
3324 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3326 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3328 TRACE("Not an overlay surface.\n");
3329 return WINEDDERR_NOTAOVERLAYSURFACE;
3332 return WINED3D_OK;
3335 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3336 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3338 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3339 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3341 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3343 WARN("Not an overlay surface.\n");
3344 return WINEDDERR_NOTAOVERLAYSURFACE;
3346 else if (!dst_surface)
3348 WARN("Dest surface is NULL.\n");
3349 return WINED3DERR_INVALIDCALL;
3352 if (src_rect)
3354 surface->overlay_srcrect = *src_rect;
3356 else
3358 surface->overlay_srcrect.left = 0;
3359 surface->overlay_srcrect.top = 0;
3360 surface->overlay_srcrect.right = surface->resource.width;
3361 surface->overlay_srcrect.bottom = surface->resource.height;
3364 if (dst_rect)
3366 surface->overlay_destrect = *dst_rect;
3368 else
3370 surface->overlay_destrect.left = 0;
3371 surface->overlay_destrect.top = 0;
3372 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3373 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3376 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3378 surface->overlay_dest = NULL;
3379 list_remove(&surface->overlay_entry);
3382 if (flags & WINEDDOVER_SHOW)
3384 if (surface->overlay_dest != dst_surface)
3386 surface->overlay_dest = dst_surface;
3387 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3390 else if (flags & WINEDDOVER_HIDE)
3392 /* tests show that the rectangles are erased on hide */
3393 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3394 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3395 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3396 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3397 surface->overlay_dest = NULL;
3400 surface->surface_ops->surface_draw_overlay(surface);
3402 return WINED3D_OK;
3405 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3407 TRACE("surface %p, clipper %p.\n", surface, clipper);
3409 surface->clipper = clipper;
3411 return WINED3D_OK;
3414 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3416 TRACE("surface %p.\n", surface);
3418 return surface->clipper;
3421 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3423 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3425 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3427 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3429 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3430 return WINED3DERR_INVALIDCALL;
3433 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3434 surface->pow2Width, surface->pow2Height);
3435 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3436 surface->resource.format = format;
3438 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3439 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3440 format->glFormat, format->glInternal, format->glType);
3442 return WINED3D_OK;
3445 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3446 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3448 unsigned short *dst_s;
3449 const float *src_f;
3450 unsigned int x, y;
3452 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3454 for (y = 0; y < h; ++y)
3456 src_f = (const float *)(src + y * pitch_in);
3457 dst_s = (unsigned short *) (dst + y * pitch_out);
3458 for (x = 0; x < w; ++x)
3460 dst_s[x] = float_32_to_16(src_f + x);
3465 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3466 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3468 static const unsigned char convert_5to8[] =
3470 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3471 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3472 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3473 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3475 static const unsigned char convert_6to8[] =
3477 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3478 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3479 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3480 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3481 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3482 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3483 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3484 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3486 unsigned int x, y;
3488 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3490 for (y = 0; y < h; ++y)
3492 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3493 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3494 for (x = 0; x < w; ++x)
3496 WORD pixel = src_line[x];
3497 dst_line[x] = 0xff000000
3498 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3499 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3500 | convert_5to8[(pixel & 0x001f)];
3505 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3506 * in both cases we're just setting the X / Alpha channel to 0xff. */
3507 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3508 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3510 unsigned int x, y;
3512 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3514 for (y = 0; y < h; ++y)
3516 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3517 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3519 for (x = 0; x < w; ++x)
3521 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3526 static inline BYTE cliptobyte(int x)
3528 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3531 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3532 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3534 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3535 unsigned int x, y;
3537 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3539 for (y = 0; y < h; ++y)
3541 const BYTE *src_line = src + y * pitch_in;
3542 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3543 for (x = 0; x < w; ++x)
3545 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3546 * C = Y - 16; D = U - 128; E = V - 128;
3547 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3548 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3549 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3550 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3551 * U and V are shared between the pixels. */
3552 if (!(x & 1)) /* For every even pixel, read new U and V. */
3554 d = (int) src_line[1] - 128;
3555 e = (int) src_line[3] - 128;
3556 r2 = 409 * e + 128;
3557 g2 = - 100 * d - 208 * e + 128;
3558 b2 = 516 * d + 128;
3560 c2 = 298 * ((int) src_line[0] - 16);
3561 dst_line[x] = 0xff000000
3562 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3563 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3564 | cliptobyte((c2 + b2) >> 8); /* blue */
3565 /* Scale RGB values to 0..255 range,
3566 * then clip them if still not in range (may be negative),
3567 * then shift them within DWORD if necessary. */
3568 src_line += 2;
3573 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3574 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3576 unsigned int x, y;
3577 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3579 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3581 for (y = 0; y < h; ++y)
3583 const BYTE *src_line = src + y * pitch_in;
3584 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3585 for (x = 0; x < w; ++x)
3587 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3588 * C = Y - 16; D = U - 128; E = V - 128;
3589 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3590 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3591 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3592 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3593 * U and V are shared between the pixels. */
3594 if (!(x & 1)) /* For every even pixel, read new U and V. */
3596 d = (int) src_line[1] - 128;
3597 e = (int) src_line[3] - 128;
3598 r2 = 409 * e + 128;
3599 g2 = - 100 * d - 208 * e + 128;
3600 b2 = 516 * d + 128;
3602 c2 = 298 * ((int) src_line[0] - 16);
3603 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3604 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3605 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3606 /* Scale RGB values to 0..255 range,
3607 * then clip them if still not in range (may be negative),
3608 * then shift them within DWORD if necessary. */
3609 src_line += 2;
3614 struct d3dfmt_convertor_desc
3616 enum wined3d_format_id from, to;
3617 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3620 static const struct d3dfmt_convertor_desc convertors[] =
3622 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3623 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3624 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3625 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3626 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3627 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3630 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3631 enum wined3d_format_id to)
3633 unsigned int i;
3635 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3637 if (convertors[i].from == from && convertors[i].to == to)
3638 return &convertors[i];
3641 return NULL;
3644 /*****************************************************************************
3645 * surface_convert_format
3647 * Creates a duplicate of a surface in a different format. Is used by Blt to
3648 * blit between surfaces with different formats.
3650 * Parameters
3651 * source: Source surface
3652 * fmt: Requested destination format
3654 *****************************************************************************/
3655 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3657 const struct d3dfmt_convertor_desc *conv;
3658 WINED3DLOCKED_RECT lock_src, lock_dst;
3659 struct wined3d_surface *ret = NULL;
3660 HRESULT hr;
3662 conv = find_convertor(source->resource.format->id, to_fmt);
3663 if (!conv)
3665 FIXME("Cannot find a conversion function from format %s to %s.\n",
3666 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3667 return NULL;
3670 wined3d_surface_create(source->resource.device, source->resource.width,
3671 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3672 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3673 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3674 if (!ret)
3676 ERR("Failed to create a destination surface for conversion.\n");
3677 return NULL;
3680 memset(&lock_src, 0, sizeof(lock_src));
3681 memset(&lock_dst, 0, sizeof(lock_dst));
3683 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3684 if (FAILED(hr))
3686 ERR("Failed to lock the source surface.\n");
3687 wined3d_surface_decref(ret);
3688 return NULL;
3690 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3691 if (FAILED(hr))
3693 ERR("Failed to lock the destination surface.\n");
3694 wined3d_surface_unmap(source);
3695 wined3d_surface_decref(ret);
3696 return NULL;
3699 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3700 source->resource.width, source->resource.height);
3702 wined3d_surface_unmap(ret);
3703 wined3d_surface_unmap(source);
3705 return ret;
3708 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3709 unsigned int bpp, UINT pitch, DWORD color)
3711 BYTE *first;
3712 int x, y;
3714 /* Do first row */
3716 #define COLORFILL_ROW(type) \
3717 do { \
3718 type *d = (type *)buf; \
3719 for (x = 0; x < width; ++x) \
3720 d[x] = (type)color; \
3721 } while(0)
3723 switch (bpp)
3725 case 1:
3726 COLORFILL_ROW(BYTE);
3727 break;
3729 case 2:
3730 COLORFILL_ROW(WORD);
3731 break;
3733 case 3:
3735 BYTE *d = buf;
3736 for (x = 0; x < width; ++x, d += 3)
3738 d[0] = (color ) & 0xFF;
3739 d[1] = (color >> 8) & 0xFF;
3740 d[2] = (color >> 16) & 0xFF;
3742 break;
3744 case 4:
3745 COLORFILL_ROW(DWORD);
3746 break;
3748 default:
3749 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3750 return WINED3DERR_NOTAVAILABLE;
3753 #undef COLORFILL_ROW
3755 /* Now copy first row. */
3756 first = buf;
3757 for (y = 1; y < height; ++y)
3759 buf += pitch;
3760 memcpy(buf, first, width * bpp);
3763 return WINED3D_OK;
3766 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3768 TRACE("surface %p.\n", surface);
3770 if (!(surface->flags & SFLAG_LOCKED))
3772 WARN("Trying to unmap unmapped surface.\n");
3773 return WINEDDERR_NOTLOCKED;
3775 surface->flags &= ~SFLAG_LOCKED;
3777 surface->surface_ops->surface_unmap(surface);
3779 return WINED3D_OK;
3782 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3783 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3785 const struct wined3d_format *format = surface->resource.format;
3787 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3788 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3790 if (surface->flags & SFLAG_LOCKED)
3792 WARN("Surface is already mapped.\n");
3793 return WINED3DERR_INVALIDCALL;
3795 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED
3796 && rect && (rect->left || rect->top
3797 || rect->right != surface->resource.width
3798 || rect->bottom != surface->resource.height))
3800 UINT width_mask = format->block_width - 1;
3801 UINT height_mask = format->block_height - 1;
3803 if ((rect->left & width_mask) || (rect->right & width_mask)
3804 || (rect->top & height_mask) || (rect->bottom & height_mask))
3806 switch (surface->resource.pool)
3808 case WINED3DPOOL_DEFAULT:
3809 WARN("Partial block lock with WINED3DPOOL_DEFAULT\n");
3810 return WINED3DERR_INVALIDCALL;
3812 default:
3813 FIXME("Partial block lock with %s\n", debug_d3dpool(surface->resource.pool));
3818 surface->flags |= SFLAG_LOCKED;
3820 if (!(surface->flags & SFLAG_LOCKABLE))
3821 WARN("Trying to lock unlockable surface.\n");
3823 surface->surface_ops->surface_map(surface, rect, flags);
3825 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3827 if (!rect)
3829 locked_rect->pBits = surface->resource.allocatedMemory;
3830 surface->lockedRect.left = 0;
3831 surface->lockedRect.top = 0;
3832 surface->lockedRect.right = surface->resource.width;
3833 surface->lockedRect.bottom = surface->resource.height;
3835 else
3837 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3839 /* Compressed textures are block based, so calculate the offset of
3840 * the block that contains the top-left pixel of the locked rectangle. */
3841 locked_rect->pBits = surface->resource.allocatedMemory
3842 + ((rect->top / format->block_height) * locked_rect->Pitch)
3843 + ((rect->left / format->block_width) * format->block_byte_count);
3845 else
3847 locked_rect->pBits = surface->resource.allocatedMemory
3848 + (locked_rect->Pitch * rect->top)
3849 + (rect->left * format->byte_count);
3851 surface->lockedRect.left = rect->left;
3852 surface->lockedRect.top = rect->top;
3853 surface->lockedRect.right = rect->right;
3854 surface->lockedRect.bottom = rect->bottom;
3857 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3858 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3860 return WINED3D_OK;
3863 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3865 HRESULT hr;
3867 TRACE("surface %p, dc %p.\n", surface, dc);
3869 if (surface->flags & SFLAG_USERPTR)
3871 ERR("Not supported on surfaces with application-provided memory.\n");
3872 return WINEDDERR_NODC;
3875 /* Give more detailed info for ddraw. */
3876 if (surface->flags & SFLAG_DCINUSE)
3877 return WINEDDERR_DCALREADYCREATED;
3879 /* Can't GetDC if the surface is locked. */
3880 if (surface->flags & SFLAG_LOCKED)
3881 return WINED3DERR_INVALIDCALL;
3883 hr = surface->surface_ops->surface_getdc(surface);
3884 if (FAILED(hr))
3885 return hr;
3887 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3888 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3890 /* GetDC on palettized formats is unsupported in D3D9, and the method
3891 * is missing in D3D8, so this should only be used for DX <=7
3892 * surfaces (with non-device palettes). */
3893 const PALETTEENTRY *pal = NULL;
3895 if (surface->palette)
3897 pal = surface->palette->palents;
3899 else
3901 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3902 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3904 if (dds_primary && dds_primary->palette)
3905 pal = dds_primary->palette->palents;
3908 if (pal)
3910 RGBQUAD col[256];
3911 unsigned int i;
3913 for (i = 0; i < 256; ++i)
3915 col[i].rgbRed = pal[i].peRed;
3916 col[i].rgbGreen = pal[i].peGreen;
3917 col[i].rgbBlue = pal[i].peBlue;
3918 col[i].rgbReserved = 0;
3920 SetDIBColorTable(surface->hDC, 0, 256, col);
3924 surface->flags |= SFLAG_DCINUSE;
3926 *dc = surface->hDC;
3927 TRACE("Returning dc %p.\n", *dc);
3929 return WINED3D_OK;
3932 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3934 TRACE("surface %p, dc %p.\n", surface, dc);
3936 if (!(surface->flags & SFLAG_DCINUSE))
3937 return WINEDDERR_NODC;
3939 if (surface->hDC != dc)
3941 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3942 dc, surface->hDC);
3943 return WINEDDERR_NODC;
3946 /* Copy the contents of the DIB over to the PBO. */
3947 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3948 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3950 /* We locked first, so unlock now. */
3951 wined3d_surface_unmap(surface);
3953 surface->flags &= ~SFLAG_DCINUSE;
3955 return WINED3D_OK;
3958 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3960 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3962 if (flags)
3964 static UINT once;
3965 if (!once++)
3966 FIXME("Ignoring flags %#x.\n", flags);
3967 else
3968 WARN("Ignoring flags %#x.\n", flags);
3971 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3973 ERR("Not supported on swapchain surfaces.\n");
3974 return WINEDDERR_NOTFLIPPABLE;
3977 /* Flipping is only supported on render targets and overlays. */
3978 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3980 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3981 return WINEDDERR_NOTFLIPPABLE;
3984 flip_surface(surface, override);
3986 /* Update overlays if they're visible. */
3987 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3988 return surface->surface_ops->surface_draw_overlay(surface);
3990 return WINED3D_OK;
3993 /* Do not call while under the GL lock. */
3994 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3996 struct wined3d_device *device = surface->resource.device;
3998 TRACE("iface %p, srgb %#x.\n", surface, srgb);
4000 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4002 struct wined3d_texture *texture = surface->container.u.texture;
4004 TRACE("Passing to container (%p).\n", texture);
4005 texture->texture_ops->texture_preload(texture, srgb);
4007 else
4009 struct wined3d_context *context;
4011 TRACE("(%p) : About to load surface\n", surface);
4013 /* TODO: Use already acquired context when possible. */
4014 context = context_acquire(device, NULL);
4016 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
4018 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
4020 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4021 GLclampf tmp;
4022 tmp = 0.9f;
4023 ENTER_GL();
4024 glPrioritizeTextures(1, &surface->texture_name, &tmp);
4025 LEAVE_GL();
4028 context_release(context);
4032 BOOL surface_init_sysmem(struct wined3d_surface *surface)
4034 if (!surface->resource.allocatedMemory)
4036 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4037 surface->resource.size + RESOURCE_ALIGNMENT);
4038 if (!surface->resource.heapMemory)
4040 ERR("Out of memory\n");
4041 return FALSE;
4043 surface->resource.allocatedMemory =
4044 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4046 else
4048 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4051 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4053 return TRUE;
4056 /* Read the framebuffer back into the surface */
4057 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4059 struct wined3d_device *device = surface->resource.device;
4060 const struct wined3d_gl_info *gl_info;
4061 struct wined3d_context *context;
4062 BYTE *mem;
4063 GLint fmt;
4064 GLint type;
4065 BYTE *row, *top, *bottom;
4066 int i;
4067 BOOL bpp;
4068 RECT local_rect;
4069 BOOL srcIsUpsideDown;
4070 GLint rowLen = 0;
4071 GLint skipPix = 0;
4072 GLint skipRow = 0;
4074 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
4075 static BOOL warned = FALSE;
4076 if(!warned) {
4077 ERR("The application tries to lock the render target, but render target locking is disabled\n");
4078 warned = TRUE;
4080 return;
4083 context = context_acquire(device, surface);
4084 context_apply_blit_state(context, device);
4085 gl_info = context->gl_info;
4087 ENTER_GL();
4089 /* Select the correct read buffer, and give some debug output.
4090 * There is no need to keep track of the current read buffer or reset it, every part of the code
4091 * that reads sets the read buffer as desired.
4093 if (surface_is_offscreen(surface))
4095 /* Mapping the primary render target which is not on a swapchain.
4096 * Read from the back buffer. */
4097 TRACE("Mapping offscreen render target.\n");
4098 glReadBuffer(device->offscreenBuffer);
4099 srcIsUpsideDown = TRUE;
4101 else
4103 /* Onscreen surfaces are always part of a swapchain */
4104 GLenum buffer = surface_get_gl_buffer(surface);
4105 TRACE("Mapping %#x buffer.\n", buffer);
4106 glReadBuffer(buffer);
4107 checkGLcall("glReadBuffer");
4108 srcIsUpsideDown = FALSE;
4111 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4112 if (!rect)
4114 local_rect.left = 0;
4115 local_rect.top = 0;
4116 local_rect.right = surface->resource.width;
4117 local_rect.bottom = surface->resource.height;
4119 else
4121 local_rect = *rect;
4123 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4125 switch (surface->resource.format->id)
4127 case WINED3DFMT_P8_UINT:
4129 if (primary_render_target_is_p8(device))
4131 /* In case of P8 render targets the index is stored in the alpha component */
4132 fmt = GL_ALPHA;
4133 type = GL_UNSIGNED_BYTE;
4134 mem = dest;
4135 bpp = surface->resource.format->byte_count;
4137 else
4139 /* GL can't return palettized data, so read ARGB pixels into a
4140 * separate block of memory and convert them into palettized format
4141 * in software. Slow, but if the app means to use palettized render
4142 * targets and locks it...
4144 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4145 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4146 * for the color channels when palettizing the colors.
4148 fmt = GL_RGB;
4149 type = GL_UNSIGNED_BYTE;
4150 pitch *= 3;
4151 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4152 if (!mem)
4154 ERR("Out of memory\n");
4155 LEAVE_GL();
4156 return;
4158 bpp = surface->resource.format->byte_count * 3;
4161 break;
4163 default:
4164 mem = dest;
4165 fmt = surface->resource.format->glFormat;
4166 type = surface->resource.format->glType;
4167 bpp = surface->resource.format->byte_count;
4170 if (surface->flags & SFLAG_PBO)
4172 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4173 checkGLcall("glBindBufferARB");
4174 if (mem)
4176 ERR("mem not null for pbo -- unexpected\n");
4177 mem = NULL;
4181 /* Save old pixel store pack state */
4182 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4183 checkGLcall("glGetIntegerv");
4184 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4185 checkGLcall("glGetIntegerv");
4186 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4187 checkGLcall("glGetIntegerv");
4189 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4190 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4191 checkGLcall("glPixelStorei");
4192 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4193 checkGLcall("glPixelStorei");
4194 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4195 checkGLcall("glPixelStorei");
4197 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4198 local_rect.right - local_rect.left,
4199 local_rect.bottom - local_rect.top,
4200 fmt, type, mem);
4201 checkGLcall("glReadPixels");
4203 /* Reset previous pixel store pack state */
4204 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4205 checkGLcall("glPixelStorei");
4206 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4207 checkGLcall("glPixelStorei");
4208 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4209 checkGLcall("glPixelStorei");
4211 if (surface->flags & SFLAG_PBO)
4213 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4214 checkGLcall("glBindBufferARB");
4216 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4217 * to get a pointer to it and perform the flipping in software. This is a lot
4218 * faster than calling glReadPixels for each line. In case we want more speed
4219 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4220 if (!srcIsUpsideDown)
4222 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4223 checkGLcall("glBindBufferARB");
4225 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4226 checkGLcall("glMapBufferARB");
4230 /* TODO: Merge this with the palettization loop below for P8 targets */
4231 if(!srcIsUpsideDown) {
4232 UINT len, off;
4233 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4234 Flip the lines in software */
4235 len = (local_rect.right - local_rect.left) * bpp;
4236 off = local_rect.left * bpp;
4238 row = HeapAlloc(GetProcessHeap(), 0, len);
4239 if(!row) {
4240 ERR("Out of memory\n");
4241 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4242 HeapFree(GetProcessHeap(), 0, mem);
4243 LEAVE_GL();
4244 return;
4247 top = mem + pitch * local_rect.top;
4248 bottom = mem + pitch * (local_rect.bottom - 1);
4249 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4250 memcpy(row, top + off, len);
4251 memcpy(top + off, bottom + off, len);
4252 memcpy(bottom + off, row, len);
4253 top += pitch;
4254 bottom -= pitch;
4256 HeapFree(GetProcessHeap(), 0, row);
4258 /* Unmap the temp PBO buffer */
4259 if (surface->flags & SFLAG_PBO)
4261 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4262 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4266 LEAVE_GL();
4267 context_release(context);
4269 /* For P8 textures we need to perform an inverse palette lookup. This is
4270 * done by searching for a palette index which matches the RGB value.
4271 * Note this isn't guaranteed to work when there are multiple entries for
4272 * the same color but we have no choice. In case of P8 render targets,
4273 * the index is stored in the alpha component so no conversion is needed. */
4274 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4276 const PALETTEENTRY *pal = NULL;
4277 DWORD width = pitch / 3;
4278 int x, y, c;
4280 if (surface->palette)
4282 pal = surface->palette->palents;
4284 else
4286 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4287 HeapFree(GetProcessHeap(), 0, mem);
4288 return;
4291 for(y = local_rect.top; y < local_rect.bottom; y++) {
4292 for(x = local_rect.left; x < local_rect.right; x++) {
4293 /* start lines pixels */
4294 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4295 const BYTE *green = blue + 1;
4296 const BYTE *red = green + 1;
4298 for(c = 0; c < 256; c++) {
4299 if(*red == pal[c].peRed &&
4300 *green == pal[c].peGreen &&
4301 *blue == pal[c].peBlue)
4303 *((BYTE *) dest + y * width + x) = c;
4304 break;
4309 HeapFree(GetProcessHeap(), 0, mem);
4313 /* Read the framebuffer contents into a texture. Note that this function
4314 * doesn't do any kind of flipping. Using this on an onscreen surface will
4315 * result in a flipped D3D texture. */
4316 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4318 struct wined3d_device *device = surface->resource.device;
4319 struct wined3d_context *context;
4321 context = context_acquire(device, surface);
4322 device_invalidate_state(device, STATE_FRAMEBUFFER);
4324 surface_prepare_texture(surface, context, srgb);
4325 surface_bind_and_dirtify(surface, context, srgb);
4327 TRACE("Reading back offscreen render target %p.\n", surface);
4329 ENTER_GL();
4331 if (surface_is_offscreen(surface))
4332 glReadBuffer(device->offscreenBuffer);
4333 else
4334 glReadBuffer(surface_get_gl_buffer(surface));
4335 checkGLcall("glReadBuffer");
4337 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4338 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4339 checkGLcall("glCopyTexSubImage2D");
4341 LEAVE_GL();
4343 context_release(context);
4346 /* Context activation is done by the caller. */
4347 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4348 struct wined3d_context *context, BOOL srgb)
4350 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4351 CONVERT_TYPES convert;
4352 struct wined3d_format format;
4354 if (surface->flags & alloc_flag) return;
4356 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4357 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4358 else surface->flags &= ~SFLAG_CONVERTED;
4360 surface_bind_and_dirtify(surface, context, srgb);
4361 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4362 surface->flags |= alloc_flag;
4365 /* Context activation is done by the caller. */
4366 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4368 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4370 struct wined3d_texture *texture = surface->container.u.texture;
4371 UINT sub_count = texture->level_count * texture->layer_count;
4372 UINT i;
4374 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4376 for (i = 0; i < sub_count; ++i)
4378 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4379 surface_prepare_texture_internal(s, context, srgb);
4382 return;
4385 surface_prepare_texture_internal(surface, context, srgb);
4388 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4390 if (multisample)
4392 if (surface->rb_multisample)
4393 return;
4395 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4396 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4397 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4398 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4399 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4401 else
4403 if (surface->rb_resolved)
4404 return;
4406 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4407 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4408 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4409 surface->pow2Width, surface->pow2Height);
4410 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4414 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4415 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4417 struct wined3d_device *device = surface->resource.device;
4418 UINT pitch = wined3d_surface_get_pitch(surface);
4419 const struct wined3d_gl_info *gl_info;
4420 struct wined3d_context *context;
4421 RECT local_rect;
4422 UINT w, h;
4424 surface_get_rect(surface, rect, &local_rect);
4426 mem += local_rect.top * pitch + local_rect.left * bpp;
4427 w = local_rect.right - local_rect.left;
4428 h = local_rect.bottom - local_rect.top;
4430 /* Activate the correct context for the render target */
4431 context = context_acquire(device, surface);
4432 context_apply_blit_state(context, device);
4433 gl_info = context->gl_info;
4435 ENTER_GL();
4437 if (!surface_is_offscreen(surface))
4439 GLenum buffer = surface_get_gl_buffer(surface);
4440 TRACE("Unlocking %#x buffer.\n", buffer);
4441 context_set_draw_buffer(context, buffer);
4443 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4444 glPixelZoom(1.0f, -1.0f);
4446 else
4448 /* Primary offscreen render target */
4449 TRACE("Offscreen render target.\n");
4450 context_set_draw_buffer(context, device->offscreenBuffer);
4452 glPixelZoom(1.0f, 1.0f);
4455 glRasterPos3i(local_rect.left, local_rect.top, 1);
4456 checkGLcall("glRasterPos3i");
4458 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4459 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4461 if (surface->flags & SFLAG_PBO)
4463 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4464 checkGLcall("glBindBufferARB");
4467 glDrawPixels(w, h, fmt, type, mem);
4468 checkGLcall("glDrawPixels");
4470 if (surface->flags & SFLAG_PBO)
4472 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4473 checkGLcall("glBindBufferARB");
4476 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4477 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4479 LEAVE_GL();
4481 if (wined3d_settings.strict_draw_ordering
4482 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4483 && surface->container.u.swapchain->front_buffer == surface))
4484 wglFlush();
4486 context_release(context);
4489 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4490 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4492 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4493 const struct wined3d_device *device = surface->resource.device;
4494 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4495 BOOL blit_supported = FALSE;
4497 /* Copy the default values from the surface. Below we might perform fixups */
4498 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4499 *format = *surface->resource.format;
4500 *convert = NO_CONVERSION;
4502 /* Ok, now look if we have to do any conversion */
4503 switch (surface->resource.format->id)
4505 case WINED3DFMT_P8_UINT:
4506 /* Below the call to blit_supported is disabled for Wine 1.2
4507 * because the function isn't operating correctly yet. At the
4508 * moment 8-bit blits are handled in software and if certain GL
4509 * extensions are around, surface conversion is performed at
4510 * upload time. The blit_supported call recognizes it as a
4511 * destination fixup. This type of upload 'fixup' and 8-bit to
4512 * 8-bit blits need to be handled by the blit_shader.
4513 * TODO: get rid of this #if 0. */
4514 #if 0
4515 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4516 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4517 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4518 #endif
4519 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4521 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4522 * texturing. Further also use conversion in case of color keying.
4523 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4524 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4525 * conflicts with this.
4527 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4528 || colorkey_active || !use_texturing)
4530 format->glFormat = GL_RGBA;
4531 format->glInternal = GL_RGBA;
4532 format->glType = GL_UNSIGNED_BYTE;
4533 format->conv_byte_count = 4;
4534 if (colorkey_active)
4535 *convert = CONVERT_PALETTED_CK;
4536 else
4537 *convert = CONVERT_PALETTED;
4539 break;
4541 case WINED3DFMT_B2G3R3_UNORM:
4542 /* **********************
4543 GL_UNSIGNED_BYTE_3_3_2
4544 ********************** */
4545 if (colorkey_active) {
4546 /* This texture format will never be used.. So do not care about color keying
4547 up until the point in time it will be needed :-) */
4548 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4550 break;
4552 case WINED3DFMT_B5G6R5_UNORM:
4553 if (colorkey_active)
4555 *convert = CONVERT_CK_565;
4556 format->glFormat = GL_RGBA;
4557 format->glInternal = GL_RGB5_A1;
4558 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4559 format->conv_byte_count = 2;
4561 break;
4563 case WINED3DFMT_B5G5R5X1_UNORM:
4564 if (colorkey_active)
4566 *convert = CONVERT_CK_5551;
4567 format->glFormat = GL_BGRA;
4568 format->glInternal = GL_RGB5_A1;
4569 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4570 format->conv_byte_count = 2;
4572 break;
4574 case WINED3DFMT_B8G8R8_UNORM:
4575 if (colorkey_active)
4577 *convert = CONVERT_CK_RGB24;
4578 format->glFormat = GL_RGBA;
4579 format->glInternal = GL_RGBA8;
4580 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4581 format->conv_byte_count = 4;
4583 break;
4585 case WINED3DFMT_B8G8R8X8_UNORM:
4586 if (colorkey_active)
4588 *convert = CONVERT_RGB32_888;
4589 format->glFormat = GL_RGBA;
4590 format->glInternal = GL_RGBA8;
4591 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4592 format->conv_byte_count = 4;
4594 break;
4596 default:
4597 break;
4600 return WINED3D_OK;
4603 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4605 const struct wined3d_device *device = surface->resource.device;
4606 const struct wined3d_palette *pal = surface->palette;
4607 BOOL index_in_alpha = FALSE;
4608 unsigned int i;
4610 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4611 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4612 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4613 * duplicate entries. Store the color key in the unused alpha component to speed the
4614 * download up and to make conversion unneeded. */
4615 index_in_alpha = primary_render_target_is_p8(device);
4617 if (!pal)
4619 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4620 if (index_in_alpha)
4622 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4623 * there's no palette at this time. */
4624 for (i = 0; i < 256; i++) table[i][3] = i;
4627 else
4629 TRACE("Using surface palette %p\n", pal);
4630 /* Get the surface's palette */
4631 for (i = 0; i < 256; ++i)
4633 table[i][0] = pal->palents[i].peRed;
4634 table[i][1] = pal->palents[i].peGreen;
4635 table[i][2] = pal->palents[i].peBlue;
4637 /* When index_in_alpha is set the palette index is stored in the
4638 * alpha component. In case of a readback we can then read
4639 * GL_ALPHA. Color keying is handled in BltOverride using a
4640 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4641 * color key itself is passed to glAlphaFunc in other cases the
4642 * alpha component of pixels that should be masked away is set to 0. */
4643 if (index_in_alpha)
4645 table[i][3] = i;
4647 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4648 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4650 table[i][3] = 0x00;
4652 else if (pal->flags & WINEDDPCAPS_ALPHA)
4654 table[i][3] = pal->palents[i].peFlags;
4656 else
4658 table[i][3] = 0xFF;
4664 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4665 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4667 const BYTE *source;
4668 BYTE *dest;
4669 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4671 switch (convert) {
4672 case NO_CONVERSION:
4674 memcpy(dst, src, pitch * height);
4675 break;
4677 case CONVERT_PALETTED:
4678 case CONVERT_PALETTED_CK:
4680 BYTE table[256][4];
4681 unsigned int x, y;
4683 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4685 for (y = 0; y < height; y++)
4687 source = src + pitch * y;
4688 dest = dst + outpitch * y;
4689 /* This is an 1 bpp format, using the width here is fine */
4690 for (x = 0; x < width; x++) {
4691 BYTE color = *source++;
4692 *dest++ = table[color][0];
4693 *dest++ = table[color][1];
4694 *dest++ = table[color][2];
4695 *dest++ = table[color][3];
4699 break;
4701 case CONVERT_CK_565:
4703 /* Converting the 565 format in 5551 packed to emulate color-keying.
4705 Note : in all these conversion, it would be best to average the averaging
4706 pixels to get the color of the pixel that will be color-keyed to
4707 prevent 'color bleeding'. This will be done later on if ever it is
4708 too visible.
4710 Note2: Nvidia documents say that their driver does not support alpha + color keying
4711 on the same surface and disables color keying in such a case
4713 unsigned int x, y;
4714 const WORD *Source;
4715 WORD *Dest;
4717 TRACE("Color keyed 565\n");
4719 for (y = 0; y < height; y++) {
4720 Source = (const WORD *)(src + y * pitch);
4721 Dest = (WORD *) (dst + y * outpitch);
4722 for (x = 0; x < width; x++ ) {
4723 WORD color = *Source++;
4724 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4725 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4726 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4727 *Dest |= 0x0001;
4728 Dest++;
4732 break;
4734 case CONVERT_CK_5551:
4736 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4737 unsigned int x, y;
4738 const WORD *Source;
4739 WORD *Dest;
4740 TRACE("Color keyed 5551\n");
4741 for (y = 0; y < height; y++) {
4742 Source = (const WORD *)(src + y * pitch);
4743 Dest = (WORD *) (dst + y * outpitch);
4744 for (x = 0; x < width; x++ ) {
4745 WORD color = *Source++;
4746 *Dest = color;
4747 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4748 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4749 *Dest |= (1 << 15);
4750 else
4751 *Dest &= ~(1 << 15);
4752 Dest++;
4756 break;
4758 case CONVERT_CK_RGB24:
4760 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4761 unsigned int x, y;
4762 for (y = 0; y < height; y++)
4764 source = src + pitch * y;
4765 dest = dst + outpitch * y;
4766 for (x = 0; x < width; x++) {
4767 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4768 DWORD dstcolor = color << 8;
4769 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4770 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4771 dstcolor |= 0xff;
4772 *(DWORD*)dest = dstcolor;
4773 source += 3;
4774 dest += 4;
4778 break;
4780 case CONVERT_RGB32_888:
4782 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4783 unsigned int x, y;
4784 for (y = 0; y < height; y++)
4786 source = src + pitch * y;
4787 dest = dst + outpitch * y;
4788 for (x = 0; x < width; x++) {
4789 DWORD color = 0xffffff & *(const DWORD*)source;
4790 DWORD dstcolor = color << 8;
4791 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4792 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4793 dstcolor |= 0xff;
4794 *(DWORD*)dest = dstcolor;
4795 source += 4;
4796 dest += 4;
4800 break;
4802 default:
4803 ERR("Unsupported conversion type %#x.\n", convert);
4805 return WINED3D_OK;
4808 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4810 /* Flip the surface contents */
4811 /* Flip the DC */
4813 HDC tmp;
4814 tmp = front->hDC;
4815 front->hDC = back->hDC;
4816 back->hDC = tmp;
4819 /* Flip the DIBsection */
4821 HBITMAP tmp;
4822 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4823 tmp = front->dib.DIBsection;
4824 front->dib.DIBsection = back->dib.DIBsection;
4825 back->dib.DIBsection = tmp;
4827 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4828 else front->flags &= ~SFLAG_DIBSECTION;
4829 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4830 else back->flags &= ~SFLAG_DIBSECTION;
4833 /* Flip the surface data */
4835 void* tmp;
4837 tmp = front->dib.bitmap_data;
4838 front->dib.bitmap_data = back->dib.bitmap_data;
4839 back->dib.bitmap_data = tmp;
4841 tmp = front->resource.allocatedMemory;
4842 front->resource.allocatedMemory = back->resource.allocatedMemory;
4843 back->resource.allocatedMemory = tmp;
4845 tmp = front->resource.heapMemory;
4846 front->resource.heapMemory = back->resource.heapMemory;
4847 back->resource.heapMemory = tmp;
4850 /* Flip the PBO */
4852 GLuint tmp_pbo = front->pbo;
4853 front->pbo = back->pbo;
4854 back->pbo = tmp_pbo;
4857 /* client_memory should not be different, but just in case */
4859 BOOL tmp;
4860 tmp = front->dib.client_memory;
4861 front->dib.client_memory = back->dib.client_memory;
4862 back->dib.client_memory = tmp;
4865 /* Flip the opengl texture */
4867 GLuint tmp;
4869 tmp = back->texture_name;
4870 back->texture_name = front->texture_name;
4871 front->texture_name = tmp;
4873 tmp = back->texture_name_srgb;
4874 back->texture_name_srgb = front->texture_name_srgb;
4875 front->texture_name_srgb = tmp;
4877 tmp = back->rb_multisample;
4878 back->rb_multisample = front->rb_multisample;
4879 front->rb_multisample = tmp;
4881 tmp = back->rb_resolved;
4882 back->rb_resolved = front->rb_resolved;
4883 front->rb_resolved = tmp;
4885 resource_unload(&back->resource);
4886 resource_unload(&front->resource);
4890 DWORD tmp_flags = back->flags;
4891 back->flags = front->flags;
4892 front->flags = tmp_flags;
4896 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4897 * pixel copy calls. */
4898 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4899 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4901 struct wined3d_device *device = dst_surface->resource.device;
4902 float xrel, yrel;
4903 UINT row;
4904 struct wined3d_context *context;
4905 BOOL upsidedown = FALSE;
4906 RECT dst_rect = *dst_rect_in;
4908 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4909 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4911 if(dst_rect.top > dst_rect.bottom) {
4912 UINT tmp = dst_rect.bottom;
4913 dst_rect.bottom = dst_rect.top;
4914 dst_rect.top = tmp;
4915 upsidedown = TRUE;
4918 context = context_acquire(device, src_surface);
4919 context_apply_blit_state(context, device);
4920 surface_internal_preload(dst_surface, SRGB_RGB);
4921 ENTER_GL();
4923 /* Bind the target texture */
4924 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4925 if (surface_is_offscreen(src_surface))
4927 TRACE("Reading from an offscreen target\n");
4928 upsidedown = !upsidedown;
4929 glReadBuffer(device->offscreenBuffer);
4931 else
4933 glReadBuffer(surface_get_gl_buffer(src_surface));
4935 checkGLcall("glReadBuffer");
4937 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4938 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4940 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4942 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4944 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4945 ERR("Texture filtering not supported in direct blit\n");
4948 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4949 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4951 ERR("Texture filtering not supported in direct blit\n");
4954 if (upsidedown
4955 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4956 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4958 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4960 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4961 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4962 src_rect->left, src_surface->resource.height - src_rect->bottom,
4963 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4965 else
4967 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4968 /* I have to process this row by row to swap the image,
4969 * otherwise it would be upside down, so stretching in y direction
4970 * doesn't cost extra time
4972 * However, stretching in x direction can be avoided if not necessary
4974 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4975 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4977 /* Well, that stuff works, but it's very slow.
4978 * find a better way instead
4980 UINT col;
4982 for (col = dst_rect.left; col < dst_rect.right; ++col)
4984 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4985 dst_rect.left + col /* x offset */, row /* y offset */,
4986 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4989 else
4991 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4992 dst_rect.left /* x offset */, row /* y offset */,
4993 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4997 checkGLcall("glCopyTexSubImage2D");
4999 LEAVE_GL();
5000 context_release(context);
5002 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5003 * path is never entered
5005 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5008 /* Uses the hardware to stretch and flip the image */
5009 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
5010 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
5012 struct wined3d_device *device = dst_surface->resource.device;
5013 struct wined3d_swapchain *src_swapchain = NULL;
5014 GLuint src, backup = 0;
5015 float left, right, top, bottom; /* Texture coordinates */
5016 UINT fbwidth = src_surface->resource.width;
5017 UINT fbheight = src_surface->resource.height;
5018 struct wined3d_context *context;
5019 GLenum drawBuffer = GL_BACK;
5020 GLenum texture_target;
5021 BOOL noBackBufferBackup;
5022 BOOL src_offscreen;
5023 BOOL upsidedown = FALSE;
5024 RECT dst_rect = *dst_rect_in;
5026 TRACE("Using hwstretch blit\n");
5027 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5028 context = context_acquire(device, src_surface);
5029 context_apply_blit_state(context, device);
5030 surface_internal_preload(dst_surface, SRGB_RGB);
5032 src_offscreen = surface_is_offscreen(src_surface);
5033 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5034 if (!noBackBufferBackup && !src_surface->texture_name)
5036 /* Get it a description */
5037 surface_internal_preload(src_surface, SRGB_RGB);
5039 ENTER_GL();
5041 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5042 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5044 if (context->aux_buffers >= 2)
5046 /* Got more than one aux buffer? Use the 2nd aux buffer */
5047 drawBuffer = GL_AUX1;
5049 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5051 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5052 drawBuffer = GL_AUX0;
5055 if(noBackBufferBackup) {
5056 glGenTextures(1, &backup);
5057 checkGLcall("glGenTextures");
5058 context_bind_texture(context, GL_TEXTURE_2D, backup);
5059 texture_target = GL_TEXTURE_2D;
5060 } else {
5061 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5062 * we are reading from the back buffer, the backup can be used as source texture
5064 texture_target = src_surface->texture_target;
5065 context_bind_texture(context, texture_target, src_surface->texture_name);
5066 glEnable(texture_target);
5067 checkGLcall("glEnable(texture_target)");
5069 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5070 src_surface->flags &= ~SFLAG_INTEXTURE;
5073 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5074 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5076 if(dst_rect.top > dst_rect.bottom) {
5077 UINT tmp = dst_rect.bottom;
5078 dst_rect.bottom = dst_rect.top;
5079 dst_rect.top = tmp;
5080 upsidedown = TRUE;
5083 if (src_offscreen)
5085 TRACE("Reading from an offscreen target\n");
5086 upsidedown = !upsidedown;
5087 glReadBuffer(device->offscreenBuffer);
5089 else
5091 glReadBuffer(surface_get_gl_buffer(src_surface));
5094 /* TODO: Only back up the part that will be overwritten */
5095 glCopyTexSubImage2D(texture_target, 0,
5096 0, 0 /* read offsets */,
5097 0, 0,
5098 fbwidth,
5099 fbheight);
5101 checkGLcall("glCopyTexSubImage2D");
5103 /* No issue with overriding these - the sampler is dirty due to blit usage */
5104 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5105 wined3d_gl_mag_filter(magLookup, Filter));
5106 checkGLcall("glTexParameteri");
5107 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5108 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5109 checkGLcall("glTexParameteri");
5111 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5112 src_swapchain = src_surface->container.u.swapchain;
5113 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5115 src = backup ? backup : src_surface->texture_name;
5117 else
5119 glReadBuffer(GL_FRONT);
5120 checkGLcall("glReadBuffer(GL_FRONT)");
5122 glGenTextures(1, &src);
5123 checkGLcall("glGenTextures(1, &src)");
5124 context_bind_texture(context, GL_TEXTURE_2D, src);
5126 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5127 * out for power of 2 sizes
5129 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5130 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5131 checkGLcall("glTexImage2D");
5132 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5133 0, 0 /* read offsets */,
5134 0, 0,
5135 fbwidth,
5136 fbheight);
5138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5139 checkGLcall("glTexParameteri");
5140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5141 checkGLcall("glTexParameteri");
5143 glReadBuffer(GL_BACK);
5144 checkGLcall("glReadBuffer(GL_BACK)");
5146 if(texture_target != GL_TEXTURE_2D) {
5147 glDisable(texture_target);
5148 glEnable(GL_TEXTURE_2D);
5149 texture_target = GL_TEXTURE_2D;
5152 checkGLcall("glEnd and previous");
5154 left = src_rect->left;
5155 right = src_rect->right;
5157 if (!upsidedown)
5159 top = src_surface->resource.height - src_rect->top;
5160 bottom = src_surface->resource.height - src_rect->bottom;
5162 else
5164 top = src_surface->resource.height - src_rect->bottom;
5165 bottom = src_surface->resource.height - src_rect->top;
5168 if (src_surface->flags & SFLAG_NORMCOORD)
5170 left /= src_surface->pow2Width;
5171 right /= src_surface->pow2Width;
5172 top /= src_surface->pow2Height;
5173 bottom /= src_surface->pow2Height;
5176 /* draw the source texture stretched and upside down. The correct surface is bound already */
5177 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5178 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5180 context_set_draw_buffer(context, drawBuffer);
5181 glReadBuffer(drawBuffer);
5183 glBegin(GL_QUADS);
5184 /* bottom left */
5185 glTexCoord2f(left, bottom);
5186 glVertex2i(0, 0);
5188 /* top left */
5189 glTexCoord2f(left, top);
5190 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5192 /* top right */
5193 glTexCoord2f(right, top);
5194 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5196 /* bottom right */
5197 glTexCoord2f(right, bottom);
5198 glVertex2i(dst_rect.right - dst_rect.left, 0);
5199 glEnd();
5200 checkGLcall("glEnd and previous");
5202 if (texture_target != dst_surface->texture_target)
5204 glDisable(texture_target);
5205 glEnable(dst_surface->texture_target);
5206 texture_target = dst_surface->texture_target;
5209 /* Now read the stretched and upside down image into the destination texture */
5210 context_bind_texture(context, texture_target, dst_surface->texture_name);
5211 glCopyTexSubImage2D(texture_target,
5213 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5214 0, 0, /* We blitted the image to the origin */
5215 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5216 checkGLcall("glCopyTexSubImage2D");
5218 if(drawBuffer == GL_BACK) {
5219 /* Write the back buffer backup back */
5220 if(backup) {
5221 if(texture_target != GL_TEXTURE_2D) {
5222 glDisable(texture_target);
5223 glEnable(GL_TEXTURE_2D);
5224 texture_target = GL_TEXTURE_2D;
5226 context_bind_texture(context, GL_TEXTURE_2D, backup);
5228 else
5230 if (texture_target != src_surface->texture_target)
5232 glDisable(texture_target);
5233 glEnable(src_surface->texture_target);
5234 texture_target = src_surface->texture_target;
5236 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5239 glBegin(GL_QUADS);
5240 /* top left */
5241 glTexCoord2f(0.0f, 0.0f);
5242 glVertex2i(0, fbheight);
5244 /* bottom left */
5245 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5246 glVertex2i(0, 0);
5248 /* bottom right */
5249 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5250 (float)fbheight / (float)src_surface->pow2Height);
5251 glVertex2i(fbwidth, 0);
5253 /* top right */
5254 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5255 glVertex2i(fbwidth, fbheight);
5256 glEnd();
5258 glDisable(texture_target);
5259 checkGLcall("glDisable(texture_target)");
5261 /* Cleanup */
5262 if (src != src_surface->texture_name && src != backup)
5264 glDeleteTextures(1, &src);
5265 checkGLcall("glDeleteTextures(1, &src)");
5267 if(backup) {
5268 glDeleteTextures(1, &backup);
5269 checkGLcall("glDeleteTextures(1, &backup)");
5272 LEAVE_GL();
5274 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5276 context_release(context);
5278 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5279 * path is never entered
5281 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5284 /* Front buffer coordinates are always full screen coordinates, but our GL
5285 * drawable is limited to the window's client area. The sysmem and texture
5286 * copies do have the full screen size. Note that GL has a bottom-left
5287 * origin, while D3D has a top-left origin. */
5288 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5290 UINT drawable_height;
5292 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5293 && surface == surface->container.u.swapchain->front_buffer)
5295 POINT offset = {0, 0};
5296 RECT windowsize;
5298 ScreenToClient(window, &offset);
5299 OffsetRect(rect, offset.x, offset.y);
5301 GetClientRect(window, &windowsize);
5302 drawable_height = windowsize.bottom - windowsize.top;
5304 else
5306 drawable_height = surface->resource.height;
5309 rect->top = drawable_height - rect->top;
5310 rect->bottom = drawable_height - rect->bottom;
5313 static void surface_blt_to_drawable(const struct wined3d_device *device,
5314 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5315 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5316 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5318 struct wined3d_context *context;
5319 RECT src_rect, dst_rect;
5321 src_rect = *src_rect_in;
5322 dst_rect = *dst_rect_in;
5324 /* Make sure the surface is up-to-date. This should probably use
5325 * surface_load_location() and worry about the destination surface too,
5326 * unless we're overwriting it completely. */
5327 surface_internal_preload(src_surface, SRGB_RGB);
5329 /* Activate the destination context, set it up for blitting */
5330 context = context_acquire(device, dst_surface);
5331 context_apply_blit_state(context, device);
5333 if (!surface_is_offscreen(dst_surface))
5334 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5336 device->blitter->set_shader(device->blit_priv, context, src_surface);
5338 ENTER_GL();
5340 if (color_key)
5342 glEnable(GL_ALPHA_TEST);
5343 checkGLcall("glEnable(GL_ALPHA_TEST)");
5345 /* When the primary render target uses P8, the alpha component
5346 * contains the palette index. Which means that the colorkey is one of
5347 * the palette entries. In other cases pixels that should be masked
5348 * away have alpha set to 0. */
5349 if (primary_render_target_is_p8(device))
5350 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5351 else
5352 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5353 checkGLcall("glAlphaFunc");
5355 else
5357 glDisable(GL_ALPHA_TEST);
5358 checkGLcall("glDisable(GL_ALPHA_TEST)");
5361 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5363 if (color_key)
5365 glDisable(GL_ALPHA_TEST);
5366 checkGLcall("glDisable(GL_ALPHA_TEST)");
5369 LEAVE_GL();
5371 /* Leave the opengl state valid for blitting */
5372 device->blitter->unset_shader(context->gl_info);
5374 if (wined3d_settings.strict_draw_ordering
5375 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5376 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5377 wglFlush(); /* Flush to ensure ordering across contexts. */
5379 context_release(context);
5382 /* Do not call while under the GL lock. */
5383 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5385 struct wined3d_device *device = s->resource.device;
5386 const struct blit_shader *blitter;
5388 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5389 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5390 if (!blitter)
5392 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5393 return WINED3DERR_INVALIDCALL;
5396 return blitter->color_fill(device, s, rect, color);
5399 /* Do not call while under the GL lock. */
5400 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5401 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5402 WINED3DTEXTUREFILTERTYPE Filter)
5404 struct wined3d_device *device = dst_surface->resource.device;
5405 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5406 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5408 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5409 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5410 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5412 /* Get the swapchain. One of the surfaces has to be a primary surface */
5413 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5415 WARN("Destination is in sysmem, rejecting gl blt\n");
5416 return WINED3DERR_INVALIDCALL;
5419 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5420 dstSwapchain = dst_surface->container.u.swapchain;
5422 if (src_surface)
5424 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5426 WARN("Src is in sysmem, rejecting gl blt\n");
5427 return WINED3DERR_INVALIDCALL;
5430 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5431 srcSwapchain = src_surface->container.u.swapchain;
5434 /* Early sort out of cases where no render target is used */
5435 if (!dstSwapchain && !srcSwapchain
5436 && src_surface != device->fb.render_targets[0]
5437 && dst_surface != device->fb.render_targets[0])
5439 TRACE("No surface is render target, not using hardware blit.\n");
5440 return WINED3DERR_INVALIDCALL;
5443 /* No destination color keying supported */
5444 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5446 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5447 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5448 return WINED3DERR_INVALIDCALL;
5451 if (dstSwapchain && dstSwapchain == srcSwapchain)
5453 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5454 return WINED3DERR_INVALIDCALL;
5457 if (dstSwapchain && srcSwapchain)
5459 FIXME("Implement hardware blit between two different swapchains\n");
5460 return WINED3DERR_INVALIDCALL;
5463 if (dstSwapchain)
5465 /* Handled with regular texture -> swapchain blit */
5466 if (src_surface == device->fb.render_targets[0])
5467 TRACE("Blit from active render target to a swapchain\n");
5469 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5471 FIXME("Implement blit from a swapchain to the active render target\n");
5472 return WINED3DERR_INVALIDCALL;
5475 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5477 /* Blit from render target to texture */
5478 BOOL stretchx;
5480 /* P8 read back is not implemented */
5481 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5482 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5484 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5485 return WINED3DERR_INVALIDCALL;
5488 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5490 TRACE("Color keying not supported by frame buffer to texture blit\n");
5491 return WINED3DERR_INVALIDCALL;
5492 /* Destination color key is checked above */
5495 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5496 stretchx = TRUE;
5497 else
5498 stretchx = FALSE;
5500 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5501 * flip the image nor scale it.
5503 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5504 * -> If the app wants a image width an unscaled width, copy it line per line
5505 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5506 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5507 * back buffer. This is slower than reading line per line, thus not used for flipping
5508 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5509 * pixel by pixel. */
5510 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5511 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5513 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5514 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5515 } else {
5516 TRACE("Using hardware stretching to flip / stretch the texture\n");
5517 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5520 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5522 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5523 dst_surface->resource.allocatedMemory = NULL;
5524 dst_surface->resource.heapMemory = NULL;
5526 else
5528 dst_surface->flags &= ~SFLAG_INSYSMEM;
5531 return WINED3D_OK;
5533 else if (src_surface)
5535 /* Blit from offscreen surface to render target */
5536 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5537 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5539 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5541 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5542 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5543 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5545 FIXME("Unsupported blit operation falling back to software\n");
5546 return WINED3DERR_INVALIDCALL;
5549 /* Color keying: Check if we have to do a color keyed blt,
5550 * and if not check if a color key is activated.
5552 * Just modify the color keying parameters in the surface and restore them afterwards
5553 * The surface keeps track of the color key last used to load the opengl surface.
5554 * PreLoad will catch the change to the flags and color key and reload if necessary.
5556 if (flags & WINEDDBLT_KEYSRC)
5558 /* Use color key from surface */
5560 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5562 /* Use color key from DDBltFx */
5563 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5564 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5566 else
5568 /* Do not use color key */
5569 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5572 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5573 src_surface, src_rect, dst_surface, dst_rect);
5575 /* Restore the color key parameters */
5576 src_surface->CKeyFlags = oldCKeyFlags;
5577 src_surface->SrcBltCKey = oldBltCKey;
5579 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5581 return WINED3D_OK;
5584 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5585 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5586 return WINED3DERR_INVALIDCALL;
5589 /* GL locking is done by the caller */
5590 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5591 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5593 struct wined3d_device *device = surface->resource.device;
5594 const struct wined3d_gl_info *gl_info = context->gl_info;
5595 GLint compare_mode = GL_NONE;
5596 struct blt_info info;
5597 GLint old_binding = 0;
5598 RECT rect;
5600 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5602 glDisable(GL_CULL_FACE);
5603 glDisable(GL_BLEND);
5604 glDisable(GL_ALPHA_TEST);
5605 glDisable(GL_SCISSOR_TEST);
5606 glDisable(GL_STENCIL_TEST);
5607 glEnable(GL_DEPTH_TEST);
5608 glDepthFunc(GL_ALWAYS);
5609 glDepthMask(GL_TRUE);
5610 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5611 glViewport(x, y, w, h);
5613 SetRect(&rect, 0, h, w, 0);
5614 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5615 context_active_texture(context, context->gl_info, 0);
5616 glGetIntegerv(info.binding, &old_binding);
5617 glBindTexture(info.bind_target, texture);
5618 if (gl_info->supported[ARB_SHADOW])
5620 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5621 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5624 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5625 gl_info, info.tex_type, &surface->ds_current_size);
5627 glBegin(GL_TRIANGLE_STRIP);
5628 glTexCoord3fv(info.coords[0]);
5629 glVertex2f(-1.0f, -1.0f);
5630 glTexCoord3fv(info.coords[1]);
5631 glVertex2f(1.0f, -1.0f);
5632 glTexCoord3fv(info.coords[2]);
5633 glVertex2f(-1.0f, 1.0f);
5634 glTexCoord3fv(info.coords[3]);
5635 glVertex2f(1.0f, 1.0f);
5636 glEnd();
5638 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5639 glBindTexture(info.bind_target, old_binding);
5641 glPopAttrib();
5643 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5646 void surface_modify_ds_location(struct wined3d_surface *surface,
5647 DWORD location, UINT w, UINT h)
5649 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5651 if (location & ~SFLAG_DS_LOCATIONS)
5652 FIXME("Invalid location (%#x) specified.\n", location);
5654 surface->ds_current_size.cx = w;
5655 surface->ds_current_size.cy = h;
5656 surface->flags &= ~SFLAG_DS_LOCATIONS;
5657 surface->flags |= location;
5660 /* Context activation is done by the caller. */
5661 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5663 struct wined3d_device *device = surface->resource.device;
5664 GLsizei w, h;
5666 TRACE("surface %p, new location %#x.\n", surface, location);
5668 /* TODO: Make this work for modes other than FBO */
5669 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5671 if (!(surface->flags & location))
5673 w = surface->ds_current_size.cx;
5674 h = surface->ds_current_size.cy;
5675 surface->ds_current_size.cx = 0;
5676 surface->ds_current_size.cy = 0;
5678 else
5680 w = surface->resource.width;
5681 h = surface->resource.height;
5684 if (surface->ds_current_size.cx == surface->resource.width
5685 && surface->ds_current_size.cy == surface->resource.height)
5687 TRACE("Location (%#x) is already up to date.\n", location);
5688 return;
5691 if (surface->current_renderbuffer)
5693 FIXME("Not supported with fixed up depth stencil.\n");
5694 return;
5697 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5699 /* This mostly happens when a depth / stencil is used without being
5700 * cleared first. In principle we could upload from sysmem, or
5701 * explicitly clear before first usage. For the moment there don't
5702 * appear to be a lot of applications depending on this, so a FIXME
5703 * should do. */
5704 FIXME("No up to date depth stencil location.\n");
5705 surface->flags |= location;
5706 surface->ds_current_size.cx = surface->resource.width;
5707 surface->ds_current_size.cy = surface->resource.height;
5708 return;
5711 if (location == SFLAG_DS_OFFSCREEN)
5713 GLint old_binding = 0;
5714 GLenum bind_target;
5716 /* The render target is allowed to be smaller than the depth/stencil
5717 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5718 * than the offscreen surface. Don't overwrite the offscreen surface
5719 * with undefined data. */
5720 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5721 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5723 TRACE("Copying onscreen depth buffer to depth texture.\n");
5725 ENTER_GL();
5727 if (!device->depth_blt_texture)
5729 glGenTextures(1, &device->depth_blt_texture);
5732 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5733 * directly on the FBO texture. That's because we need to flip. */
5734 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5735 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5736 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5738 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5739 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5741 else
5743 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5744 bind_target = GL_TEXTURE_2D;
5746 glBindTexture(bind_target, device->depth_blt_texture);
5747 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5748 * internal format, because the internal format might include stencil
5749 * data. In principle we should copy stencil data as well, but unless
5750 * the driver supports stencil export it's hard to do, and doesn't
5751 * seem to be needed in practice. If the hardware doesn't support
5752 * writing stencil data, the glCopyTexImage2D() call might trigger
5753 * software fallbacks. */
5754 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5755 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5756 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5757 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5758 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5759 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5760 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5761 glBindTexture(bind_target, old_binding);
5763 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5764 NULL, surface, SFLAG_INTEXTURE);
5765 context_set_draw_buffer(context, GL_NONE);
5766 glReadBuffer(GL_NONE);
5768 /* Do the actual blit */
5769 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5770 checkGLcall("depth_blt");
5772 context_invalidate_state(context, STATE_FRAMEBUFFER);
5774 LEAVE_GL();
5776 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5778 else if (location == SFLAG_DS_ONSCREEN)
5780 TRACE("Copying depth texture to onscreen depth buffer.\n");
5782 ENTER_GL();
5784 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5785 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5786 surface_depth_blt(surface, context, surface->texture_name,
5787 0, surface->pow2Height - h, w, h, surface->texture_target);
5788 checkGLcall("depth_blt");
5790 context_invalidate_state(context, STATE_FRAMEBUFFER);
5792 LEAVE_GL();
5794 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5796 else
5798 ERR("Invalid location (%#x) specified.\n", location);
5801 surface->flags |= location;
5802 surface->ds_current_size.cx = surface->resource.width;
5803 surface->ds_current_size.cy = surface->resource.height;
5806 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5808 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5809 struct wined3d_surface *overlay;
5811 TRACE("surface %p, location %s, persistent %#x.\n",
5812 surface, debug_surflocation(location), persistent);
5814 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5815 && (location & SFLAG_INDRAWABLE))
5816 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5818 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5819 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5820 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5822 if (persistent)
5824 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5825 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5827 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5829 TRACE("Passing to container.\n");
5830 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5833 surface->flags &= ~SFLAG_LOCATIONS;
5834 surface->flags |= location;
5836 /* Redraw emulated overlays, if any */
5837 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5839 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5841 overlay->surface_ops->surface_draw_overlay(overlay);
5845 else
5847 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5849 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5851 TRACE("Passing to container\n");
5852 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5855 surface->flags &= ~location;
5858 if (!(surface->flags & SFLAG_LOCATIONS))
5860 ERR("Surface %p does not have any up to date location.\n", surface);
5864 static DWORD resource_access_from_location(DWORD location)
5866 switch (location)
5868 case SFLAG_INSYSMEM:
5869 return WINED3D_RESOURCE_ACCESS_CPU;
5871 case SFLAG_INDRAWABLE:
5872 case SFLAG_INSRGBTEX:
5873 case SFLAG_INTEXTURE:
5874 case SFLAG_INRB_MULTISAMPLE:
5875 case SFLAG_INRB_RESOLVED:
5876 return WINED3D_RESOURCE_ACCESS_GPU;
5878 default:
5879 FIXME("Unhandled location %#x.\n", location);
5880 return 0;
5884 static void surface_load_sysmem(struct wined3d_surface *surface,
5885 const struct wined3d_gl_info *gl_info, const RECT *rect)
5887 surface_prepare_system_memory(surface);
5889 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5890 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5892 /* Download the surface to system memory. */
5893 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5895 struct wined3d_device *device = surface->resource.device;
5896 struct wined3d_context *context;
5898 /* TODO: Use already acquired context when possible. */
5899 context = context_acquire(device, NULL);
5901 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5902 surface_download_data(surface, gl_info);
5904 context_release(context);
5906 return;
5909 if (surface->flags & SFLAG_INDRAWABLE)
5911 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5912 wined3d_surface_get_pitch(surface));
5913 return;
5916 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5917 surface, surface->flags & SFLAG_LOCATIONS);
5920 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5921 const struct wined3d_gl_info *gl_info, const RECT *rect)
5923 struct wined3d_device *device = surface->resource.device;
5924 struct wined3d_format format;
5925 CONVERT_TYPES convert;
5926 UINT byte_count;
5927 BYTE *mem;
5929 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5931 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5932 return WINED3DERR_INVALIDCALL;
5935 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5936 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5938 if (surface->flags & SFLAG_INTEXTURE)
5940 RECT r;
5942 surface_get_rect(surface, rect, &r);
5943 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5945 return WINED3D_OK;
5948 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5950 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5951 * path through sysmem. */
5952 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5955 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5957 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5958 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5959 * called. */
5960 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5962 struct wined3d_context *context;
5964 TRACE("Removing the pbo attached to surface %p.\n", surface);
5966 /* TODO: Use already acquired context when possible. */
5967 context = context_acquire(device, NULL);
5969 surface_remove_pbo(surface, gl_info);
5971 context_release(context);
5974 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5976 UINT height = surface->resource.height;
5977 UINT width = surface->resource.width;
5978 UINT src_pitch, dst_pitch;
5980 byte_count = format.conv_byte_count;
5981 src_pitch = wined3d_surface_get_pitch(surface);
5983 /* Stick to the alignment for the converted surface too, makes it
5984 * easier to load the surface. */
5985 dst_pitch = width * byte_count;
5986 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5988 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5990 ERR("Out of memory (%u).\n", dst_pitch * height);
5991 return E_OUTOFMEMORY;
5994 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5995 src_pitch, width, height, dst_pitch, convert, surface);
5997 surface->flags |= SFLAG_CONVERTED;
5999 else
6001 surface->flags &= ~SFLAG_CONVERTED;
6002 mem = surface->resource.allocatedMemory;
6003 byte_count = format.byte_count;
6006 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6008 /* Don't delete PBO memory. */
6009 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6010 HeapFree(GetProcessHeap(), 0, mem);
6012 return WINED3D_OK;
6015 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6016 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6018 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6019 struct wined3d_device *device = surface->resource.device;
6020 struct wined3d_context *context;
6021 UINT width, src_pitch, dst_pitch;
6022 struct wined3d_bo_address data;
6023 struct wined3d_format format;
6024 POINT dst_point = {0, 0};
6025 CONVERT_TYPES convert;
6026 BYTE *mem;
6028 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6029 && surface_is_offscreen(surface)
6030 && (surface->flags & SFLAG_INDRAWABLE))
6032 surface_load_fb_texture(surface, srgb);
6034 return WINED3D_OK;
6037 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6038 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6039 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6040 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6041 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6043 if (srgb)
6044 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6045 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6046 else
6047 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6048 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6050 return WINED3D_OK;
6053 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6054 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6055 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6056 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6057 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6059 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6060 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6061 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6063 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
6064 &rect, surface, dst_location, &rect);
6066 return WINED3D_OK;
6069 /* Upload from system memory */
6071 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6072 TRUE /* We will use textures */, &format, &convert);
6074 if (srgb)
6076 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6078 /* Performance warning... */
6079 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6080 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6083 else
6085 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6087 /* Performance warning... */
6088 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6089 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6093 if (!(surface->flags & SFLAG_INSYSMEM))
6095 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6096 /* Lets hope we get it from somewhere... */
6097 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6100 /* TODO: Use already acquired context when possible. */
6101 context = context_acquire(device, NULL);
6103 surface_prepare_texture(surface, context, srgb);
6104 surface_bind_and_dirtify(surface, context, srgb);
6106 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6108 surface->flags |= SFLAG_GLCKEY;
6109 surface->glCKey = surface->SrcBltCKey;
6111 else surface->flags &= ~SFLAG_GLCKEY;
6113 width = surface->resource.width;
6114 src_pitch = wined3d_surface_get_pitch(surface);
6116 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6117 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6118 * called. */
6119 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6121 TRACE("Removing the pbo attached to surface %p.\n", surface);
6122 surface_remove_pbo(surface, gl_info);
6125 if (format.convert)
6127 /* This code is entered for texture formats which need a fixup. */
6128 UINT height = surface->resource.height;
6130 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6131 dst_pitch = width * format.conv_byte_count;
6132 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6134 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6136 ERR("Out of memory (%u).\n", dst_pitch * height);
6137 context_release(context);
6138 return E_OUTOFMEMORY;
6140 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6142 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6144 /* This code is only entered for color keying fixups */
6145 UINT height = surface->resource.height;
6147 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6148 dst_pitch = width * format.conv_byte_count;
6149 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6151 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6153 ERR("Out of memory (%u).\n", dst_pitch * height);
6154 context_release(context);
6155 return E_OUTOFMEMORY;
6157 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6158 width, height, dst_pitch, convert, surface);
6160 else
6162 mem = surface->resource.allocatedMemory;
6165 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6166 data.addr = mem;
6167 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6169 context_release(context);
6171 /* Don't delete PBO memory. */
6172 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6173 HeapFree(GetProcessHeap(), 0, mem);
6175 return WINED3D_OK;
6178 static void surface_multisample_resolve(struct wined3d_surface *surface)
6180 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6182 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6183 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6185 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6186 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6189 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6191 struct wined3d_device *device = surface->resource.device;
6192 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6193 HRESULT hr;
6195 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6197 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6199 if (location == SFLAG_INTEXTURE)
6201 struct wined3d_context *context = context_acquire(device, NULL);
6202 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6203 context_release(context);
6204 return WINED3D_OK;
6206 else
6208 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6209 return WINED3DERR_INVALIDCALL;
6213 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6214 location = SFLAG_INTEXTURE;
6216 if (surface->flags & location)
6218 TRACE("Location already up to date.\n");
6219 return WINED3D_OK;
6222 if (WARN_ON(d3d_surface))
6224 DWORD required_access = resource_access_from_location(location);
6225 if ((surface->resource.access_flags & required_access) != required_access)
6226 WARN("Operation requires %#x access, but surface only has %#x.\n",
6227 required_access, surface->resource.access_flags);
6230 if (!(surface->flags & SFLAG_LOCATIONS))
6232 ERR("Surface %p does not have any up to date location.\n", surface);
6233 surface->flags |= SFLAG_LOST;
6234 return WINED3DERR_DEVICELOST;
6237 switch (location)
6239 case SFLAG_INSYSMEM:
6240 surface_load_sysmem(surface, gl_info, rect);
6241 break;
6243 case SFLAG_INDRAWABLE:
6244 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6245 return hr;
6246 break;
6248 case SFLAG_INRB_RESOLVED:
6249 surface_multisample_resolve(surface);
6250 break;
6252 case SFLAG_INTEXTURE:
6253 case SFLAG_INSRGBTEX:
6254 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6255 return hr;
6256 break;
6258 default:
6259 ERR("Don't know how to handle location %#x.\n", location);
6260 break;
6263 if (!rect)
6265 surface->flags |= location;
6267 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6268 surface_evict_sysmem(surface);
6271 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6272 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6274 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6277 return WINED3D_OK;
6280 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6282 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6284 /* Not on a swapchain - must be offscreen */
6285 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6287 /* The front buffer is always onscreen */
6288 if (surface == swapchain->front_buffer) return FALSE;
6290 /* If the swapchain is rendered to an FBO, the backbuffer is
6291 * offscreen, otherwise onscreen */
6292 return swapchain->render_to_fbo;
6295 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6296 /* Context activation is done by the caller. */
6297 static void ffp_blit_free(struct wined3d_device *device) { }
6299 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6300 /* Context activation is done by the caller. */
6301 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6303 BYTE table[256][4];
6304 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6306 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6308 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6309 ENTER_GL();
6310 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6311 LEAVE_GL();
6314 /* Context activation is done by the caller. */
6315 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6317 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6319 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6320 * else the surface is converted in software at upload time in LoadLocation.
6322 if(fixup == COMPLEX_FIXUP_P8 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6323 ffp_blit_p8_upload_palette(surface, context->gl_info);
6325 ENTER_GL();
6326 glEnable(surface->texture_target);
6327 checkGLcall("glEnable(surface->texture_target)");
6328 LEAVE_GL();
6329 return WINED3D_OK;
6332 /* Context activation is done by the caller. */
6333 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6335 ENTER_GL();
6336 glDisable(GL_TEXTURE_2D);
6337 checkGLcall("glDisable(GL_TEXTURE_2D)");
6338 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6340 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6341 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6343 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6345 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6346 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6348 LEAVE_GL();
6351 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6352 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6353 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6355 enum complex_fixup src_fixup;
6357 switch (blit_op)
6359 case WINED3D_BLIT_OP_COLOR_BLIT:
6360 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6361 return FALSE;
6363 src_fixup = get_complex_fixup(src_format->color_fixup);
6364 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6366 TRACE("Checking support for fixup:\n");
6367 dump_color_fixup_desc(src_format->color_fixup);
6370 if (!is_identity_fixup(dst_format->color_fixup))
6372 TRACE("Destination fixups are not supported\n");
6373 return FALSE;
6376 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6378 TRACE("P8 fixup supported\n");
6379 return TRUE;
6382 /* We only support identity conversions. */
6383 if (is_identity_fixup(src_format->color_fixup))
6385 TRACE("[OK]\n");
6386 return TRUE;
6389 TRACE("[FAILED]\n");
6390 return FALSE;
6392 case WINED3D_BLIT_OP_COLOR_FILL:
6393 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6394 return FALSE;
6396 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6398 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6399 return FALSE;
6401 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6403 TRACE("Color fill not supported\n");
6404 return FALSE;
6407 /* FIXME: We should reject color fills on formats with fixups,
6408 * but this would break P8 color fills for example. */
6410 return TRUE;
6412 case WINED3D_BLIT_OP_DEPTH_FILL:
6413 return TRUE;
6415 default:
6416 TRACE("Unsupported blit_op=%d\n", blit_op);
6417 return FALSE;
6421 /* Do not call while under the GL lock. */
6422 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6423 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6425 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6426 struct wined3d_fb_state fb = {&dst_surface, NULL};
6428 return device_clear_render_targets(device, 1, &fb,
6429 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6432 /* Do not call while under the GL lock. */
6433 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6434 struct wined3d_surface *surface, const RECT *rect, float depth)
6436 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6437 struct wined3d_fb_state fb = {NULL, surface};
6439 return device_clear_render_targets(device, 0, &fb,
6440 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6443 const struct blit_shader ffp_blit = {
6444 ffp_blit_alloc,
6445 ffp_blit_free,
6446 ffp_blit_set,
6447 ffp_blit_unset,
6448 ffp_blit_supported,
6449 ffp_blit_color_fill,
6450 ffp_blit_depth_fill,
6453 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6455 return WINED3D_OK;
6458 /* Context activation is done by the caller. */
6459 static void cpu_blit_free(struct wined3d_device *device)
6463 /* Context activation is done by the caller. */
6464 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6466 return WINED3D_OK;
6469 /* Context activation is done by the caller. */
6470 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6474 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6475 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6476 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6478 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6480 return TRUE;
6483 return FALSE;
6486 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6487 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6488 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6490 UINT row_block_count;
6491 const BYTE *src_row;
6492 BYTE *dst_row;
6493 UINT x, y;
6495 src_row = src_data;
6496 dst_row = dst_data;
6498 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6500 if (!flags)
6502 for (y = 0; y < update_h; y += format->block_height)
6504 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6505 src_row += src_pitch;
6506 dst_row += dst_pitch;
6509 return WINED3D_OK;
6512 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6514 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6516 switch (format->id)
6518 case WINED3DFMT_DXT1:
6519 for (y = 0; y < update_h; y += format->block_height)
6521 struct block
6523 WORD color[2];
6524 BYTE control_row[4];
6527 const struct block *s = (const struct block *)src_row;
6528 struct block *d = (struct block *)dst_row;
6530 for (x = 0; x < row_block_count; ++x)
6532 d[x].color[0] = s[x].color[0];
6533 d[x].color[1] = s[x].color[1];
6534 d[x].control_row[0] = s[x].control_row[3];
6535 d[x].control_row[1] = s[x].control_row[2];
6536 d[x].control_row[2] = s[x].control_row[1];
6537 d[x].control_row[3] = s[x].control_row[0];
6539 src_row -= src_pitch;
6540 dst_row += dst_pitch;
6542 return WINED3D_OK;
6544 case WINED3DFMT_DXT3:
6545 for (y = 0; y < update_h; y += format->block_height)
6547 struct block
6549 WORD alpha_row[4];
6550 WORD color[2];
6551 BYTE control_row[4];
6554 const struct block *s = (const struct block *)src_row;
6555 struct block *d = (struct block *)dst_row;
6557 for (x = 0; x < row_block_count; ++x)
6559 d[x].alpha_row[0] = s[x].alpha_row[3];
6560 d[x].alpha_row[1] = s[x].alpha_row[2];
6561 d[x].alpha_row[2] = s[x].alpha_row[1];
6562 d[x].alpha_row[3] = s[x].alpha_row[0];
6563 d[x].color[0] = s[x].color[0];
6564 d[x].color[1] = s[x].color[1];
6565 d[x].control_row[0] = s[x].control_row[3];
6566 d[x].control_row[1] = s[x].control_row[2];
6567 d[x].control_row[2] = s[x].control_row[1];
6568 d[x].control_row[3] = s[x].control_row[0];
6570 src_row -= src_pitch;
6571 dst_row += dst_pitch;
6573 return WINED3D_OK;
6575 default:
6576 FIXME("Compressed flip not implemented for format %s.\n",
6577 debug_d3dformat(format->id));
6578 return E_NOTIMPL;
6582 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6583 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6585 return E_NOTIMPL;
6588 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6589 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6590 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6592 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6593 const struct wined3d_format *src_format, *dst_format;
6594 struct wined3d_surface *orig_src = src_surface;
6595 WINED3DLOCKED_RECT dlock, slock;
6596 HRESULT hr = WINED3D_OK;
6597 const BYTE *sbuf;
6598 RECT xdst,xsrc;
6599 BYTE *dbuf;
6600 int x, y;
6602 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6603 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6604 flags, fx, debug_d3dtexturefiltertype(filter));
6606 xsrc = *src_rect;
6608 if (!src_surface)
6610 RECT full_rect;
6612 full_rect.left = 0;
6613 full_rect.top = 0;
6614 full_rect.right = dst_surface->resource.width;
6615 full_rect.bottom = dst_surface->resource.height;
6616 IntersectRect(&xdst, &full_rect, dst_rect);
6618 else
6620 BOOL clip_horiz, clip_vert;
6622 xdst = *dst_rect;
6623 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6624 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6626 if (clip_vert || clip_horiz)
6628 /* Now check if this is a special case or not... */
6629 if ((flags & WINEDDBLT_DDFX)
6630 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6631 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6633 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6634 return WINED3D_OK;
6637 if (clip_horiz)
6639 if (xdst.left < 0)
6641 xsrc.left -= xdst.left;
6642 xdst.left = 0;
6644 if (xdst.right > dst_surface->resource.width)
6646 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6647 xdst.right = (int)dst_surface->resource.width;
6651 if (clip_vert)
6653 if (xdst.top < 0)
6655 xsrc.top -= xdst.top;
6656 xdst.top = 0;
6658 if (xdst.bottom > dst_surface->resource.height)
6660 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6661 xdst.bottom = (int)dst_surface->resource.height;
6665 /* And check if after clipping something is still to be done... */
6666 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6667 || (xdst.left >= (int)dst_surface->resource.width)
6668 || (xdst.top >= (int)dst_surface->resource.height)
6669 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6670 || (xsrc.left >= (int)src_surface->resource.width)
6671 || (xsrc.top >= (int)src_surface->resource.height))
6673 TRACE("Nothing to be done after clipping.\n");
6674 return WINED3D_OK;
6679 if (src_surface == dst_surface)
6681 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6682 slock = dlock;
6683 src_format = dst_surface->resource.format;
6684 dst_format = src_format;
6686 else
6688 dst_format = dst_surface->resource.format;
6689 if (src_surface)
6691 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6693 src_surface = surface_convert_format(src_surface, dst_format->id);
6694 if (!src_surface)
6696 /* The conv function writes a FIXME */
6697 WARN("Cannot convert source surface format to dest format.\n");
6698 goto release;
6701 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6702 src_format = src_surface->resource.format;
6704 else
6706 src_format = dst_format;
6708 if (dst_rect)
6709 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6710 else
6711 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6714 bpp = dst_surface->resource.format->byte_count;
6715 srcheight = xsrc.bottom - xsrc.top;
6716 srcwidth = xsrc.right - xsrc.left;
6717 dstheight = xdst.bottom - xdst.top;
6718 dstwidth = xdst.right - xdst.left;
6719 width = (xdst.right - xdst.left) * bpp;
6721 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6723 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6725 if (src_surface == dst_surface)
6727 FIXME("Only plain blits supported on compressed surfaces.\n");
6728 hr = E_NOTIMPL;
6729 goto release;
6732 if (srcheight != dstheight || srcwidth != dstwidth)
6734 WARN("Stretching not supported on compressed surfaces.\n");
6735 hr = WINED3DERR_INVALIDCALL;
6736 goto release;
6739 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6741 WARN("Rectangle not block-aligned.\n");
6742 hr = WINED3DERR_INVALIDCALL;
6743 goto release;
6746 hr = surface_cpu_blt_compressed(slock.pBits, dlock.pBits,
6747 slock.Pitch, dlock.Pitch, dstwidth, dstheight,
6748 src_format, flags, fx);
6749 goto release;
6752 if (dst_rect && src_surface != dst_surface)
6753 dbuf = dlock.pBits;
6754 else
6755 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6757 /* First, all the 'source-less' blits */
6758 if (flags & WINEDDBLT_COLORFILL)
6760 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6761 flags &= ~WINEDDBLT_COLORFILL;
6764 if (flags & WINEDDBLT_DEPTHFILL)
6766 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6768 if (flags & WINEDDBLT_ROP)
6770 /* Catch some degenerate cases here. */
6771 switch (fx->dwROP)
6773 case BLACKNESS:
6774 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6775 break;
6776 case 0xAA0029: /* No-op */
6777 break;
6778 case WHITENESS:
6779 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6780 break;
6781 case SRCCOPY: /* Well, we do that below? */
6782 break;
6783 default:
6784 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6785 goto error;
6787 flags &= ~WINEDDBLT_ROP;
6789 if (flags & WINEDDBLT_DDROPS)
6791 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6793 /* Now the 'with source' blits. */
6794 if (src_surface)
6796 const BYTE *sbase;
6797 int sx, xinc, sy, yinc;
6799 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6800 goto release;
6802 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6803 && (srcwidth != dstwidth || srcheight != dstheight))
6805 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6806 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6809 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6810 xinc = (srcwidth << 16) / dstwidth;
6811 yinc = (srcheight << 16) / dstheight;
6813 if (!flags)
6815 /* No effects, we can cheat here. */
6816 if (dstwidth == srcwidth)
6818 if (dstheight == srcheight)
6820 /* No stretching in either direction. This needs to be as
6821 * fast as possible. */
6822 sbuf = sbase;
6824 /* Check for overlapping surfaces. */
6825 if (src_surface != dst_surface || xdst.top < xsrc.top
6826 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6828 /* No overlap, or dst above src, so copy from top downwards. */
6829 for (y = 0; y < dstheight; ++y)
6831 memcpy(dbuf, sbuf, width);
6832 sbuf += slock.Pitch;
6833 dbuf += dlock.Pitch;
6836 else if (xdst.top > xsrc.top)
6838 /* Copy from bottom upwards. */
6839 sbuf += (slock.Pitch*dstheight);
6840 dbuf += (dlock.Pitch*dstheight);
6841 for (y = 0; y < dstheight; ++y)
6843 sbuf -= slock.Pitch;
6844 dbuf -= dlock.Pitch;
6845 memcpy(dbuf, sbuf, width);
6848 else
6850 /* Src and dst overlapping on the same line, use memmove. */
6851 for (y = 0; y < dstheight; ++y)
6853 memmove(dbuf, sbuf, width);
6854 sbuf += slock.Pitch;
6855 dbuf += dlock.Pitch;
6859 else
6861 /* Stretching in y direction only. */
6862 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6864 sbuf = sbase + (sy >> 16) * slock.Pitch;
6865 memcpy(dbuf, sbuf, width);
6866 dbuf += dlock.Pitch;
6870 else
6872 /* Stretching in X direction. */
6873 int last_sy = -1;
6874 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6876 sbuf = sbase + (sy >> 16) * slock.Pitch;
6878 if ((sy >> 16) == (last_sy >> 16))
6880 /* This source row is the same as last source row -
6881 * Copy the already stretched row. */
6882 memcpy(dbuf, dbuf - dlock.Pitch, width);
6884 else
6886 #define STRETCH_ROW(type) \
6887 do { \
6888 const type *s = (const type *)sbuf; \
6889 type *d = (type *)dbuf; \
6890 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6891 d[x] = s[sx >> 16]; \
6892 } while(0)
6894 switch(bpp)
6896 case 1:
6897 STRETCH_ROW(BYTE);
6898 break;
6899 case 2:
6900 STRETCH_ROW(WORD);
6901 break;
6902 case 4:
6903 STRETCH_ROW(DWORD);
6904 break;
6905 case 3:
6907 const BYTE *s;
6908 BYTE *d = dbuf;
6909 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6911 DWORD pixel;
6913 s = sbuf + 3 * (sx >> 16);
6914 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6915 d[0] = (pixel ) & 0xff;
6916 d[1] = (pixel >> 8) & 0xff;
6917 d[2] = (pixel >> 16) & 0xff;
6918 d += 3;
6920 break;
6922 default:
6923 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6924 hr = WINED3DERR_NOTAVAILABLE;
6925 goto error;
6927 #undef STRETCH_ROW
6929 dbuf += dlock.Pitch;
6930 last_sy = sy;
6934 else
6936 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6937 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6938 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6939 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6941 /* The color keying flags are checked for correctness in ddraw */
6942 if (flags & WINEDDBLT_KEYSRC)
6944 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6945 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6947 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6949 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6950 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6953 if (flags & WINEDDBLT_KEYDEST)
6955 /* Destination color keys are taken from the source surface! */
6956 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6957 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6959 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6961 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6962 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6965 if (bpp == 1)
6967 keymask = 0xff;
6969 else
6971 keymask = src_format->red_mask
6972 | src_format->green_mask
6973 | src_format->blue_mask;
6975 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6978 if (flags & WINEDDBLT_DDFX)
6980 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6981 LONG tmpxy;
6982 dTopLeft = dbuf;
6983 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6984 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6985 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6987 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6989 /* I don't think we need to do anything about this flag */
6990 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6992 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6994 tmp = dTopRight;
6995 dTopRight = dTopLeft;
6996 dTopLeft = tmp;
6997 tmp = dBottomRight;
6998 dBottomRight = dBottomLeft;
6999 dBottomLeft = tmp;
7000 dstxinc = dstxinc * -1;
7002 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
7004 tmp = dTopLeft;
7005 dTopLeft = dBottomLeft;
7006 dBottomLeft = tmp;
7007 tmp = dTopRight;
7008 dTopRight = dBottomRight;
7009 dBottomRight = tmp;
7010 dstyinc = dstyinc * -1;
7012 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
7014 /* I don't think we need to do anything about this flag */
7015 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
7017 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7019 tmp = dBottomRight;
7020 dBottomRight = dTopLeft;
7021 dTopLeft = tmp;
7022 tmp = dBottomLeft;
7023 dBottomLeft = dTopRight;
7024 dTopRight = tmp;
7025 dstxinc = dstxinc * -1;
7026 dstyinc = dstyinc * -1;
7028 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7030 tmp = dTopLeft;
7031 dTopLeft = dBottomLeft;
7032 dBottomLeft = dBottomRight;
7033 dBottomRight = dTopRight;
7034 dTopRight = tmp;
7035 tmpxy = dstxinc;
7036 dstxinc = dstyinc;
7037 dstyinc = tmpxy;
7038 dstxinc = dstxinc * -1;
7040 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7042 tmp = dTopLeft;
7043 dTopLeft = dTopRight;
7044 dTopRight = dBottomRight;
7045 dBottomRight = dBottomLeft;
7046 dBottomLeft = tmp;
7047 tmpxy = dstxinc;
7048 dstxinc = dstyinc;
7049 dstyinc = tmpxy;
7050 dstyinc = dstyinc * -1;
7052 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7054 /* I don't think we need to do anything about this flag */
7055 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7057 dbuf = dTopLeft;
7058 flags &= ~(WINEDDBLT_DDFX);
7061 #define COPY_COLORKEY_FX(type) \
7062 do { \
7063 const type *s; \
7064 type *d = (type *)dbuf, *dx, tmp; \
7065 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7067 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
7068 dx = d; \
7069 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7071 tmp = s[sx >> 16]; \
7072 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7073 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7075 dx[0] = tmp; \
7077 dx = (type *)(((BYTE *)dx) + dstxinc); \
7079 d = (type *)(((BYTE *)d) + dstyinc); \
7081 } while(0)
7083 switch (bpp)
7085 case 1:
7086 COPY_COLORKEY_FX(BYTE);
7087 break;
7088 case 2:
7089 COPY_COLORKEY_FX(WORD);
7090 break;
7091 case 4:
7092 COPY_COLORKEY_FX(DWORD);
7093 break;
7094 case 3:
7096 const BYTE *s;
7097 BYTE *d = dbuf, *dx;
7098 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7100 sbuf = sbase + (sy >> 16) * slock.Pitch;
7101 dx = d;
7102 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7104 DWORD pixel, dpixel = 0;
7105 s = sbuf + 3 * (sx>>16);
7106 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7107 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7108 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7109 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7111 dx[0] = (pixel ) & 0xff;
7112 dx[1] = (pixel >> 8) & 0xff;
7113 dx[2] = (pixel >> 16) & 0xff;
7115 dx += dstxinc;
7117 d += dstyinc;
7119 break;
7121 default:
7122 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7123 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7124 hr = WINED3DERR_NOTAVAILABLE;
7125 goto error;
7126 #undef COPY_COLORKEY_FX
7131 error:
7132 if (flags && FIXME_ON(d3d_surface))
7134 FIXME("\tUnsupported flags: %#x.\n", flags);
7137 release:
7138 wined3d_surface_unmap(dst_surface);
7139 if (src_surface && src_surface != dst_surface)
7140 wined3d_surface_unmap(src_surface);
7141 /* Release the converted surface, if any. */
7142 if (src_surface && src_surface != orig_src)
7143 wined3d_surface_decref(src_surface);
7145 return hr;
7148 /* Do not call while under the GL lock. */
7149 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7150 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7152 static const RECT src_rect;
7153 WINEDDBLTFX BltFx;
7155 memset(&BltFx, 0, sizeof(BltFx));
7156 BltFx.dwSize = sizeof(BltFx);
7157 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7158 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7159 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7162 /* Do not call while under the GL lock. */
7163 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7164 struct wined3d_surface *surface, const RECT *rect, float depth)
7166 FIXME("Depth filling not implemented by cpu_blit.\n");
7167 return WINED3DERR_INVALIDCALL;
7170 const struct blit_shader cpu_blit = {
7171 cpu_blit_alloc,
7172 cpu_blit_free,
7173 cpu_blit_set,
7174 cpu_blit_unset,
7175 cpu_blit_supported,
7176 cpu_blit_color_fill,
7177 cpu_blit_depth_fill,
7180 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7181 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7182 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7183 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7185 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7186 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7187 unsigned int resource_size;
7188 HRESULT hr;
7190 if (multisample_quality > 0)
7192 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7193 multisample_quality = 0;
7196 /* Quick lockable sanity check.
7197 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7198 * this function is too deep to need to care about things like this.
7199 * Levels need to be checked too, since they all affect what can be done. */
7200 switch (pool)
7202 case WINED3DPOOL_SCRATCH:
7203 if (!lockable)
7205 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7206 "which are mutually exclusive, setting lockable to TRUE.\n");
7207 lockable = TRUE;
7209 break;
7211 case WINED3DPOOL_SYSTEMMEM:
7212 if (!lockable)
7213 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7214 break;
7216 case WINED3DPOOL_MANAGED:
7217 if (usage & WINED3DUSAGE_DYNAMIC)
7218 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7219 break;
7221 case WINED3DPOOL_DEFAULT:
7222 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7223 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7224 break;
7226 default:
7227 FIXME("Unknown pool %#x.\n", pool);
7228 break;
7231 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7232 FIXME("Trying to create a render target that isn't in the default pool.\n");
7234 /* FIXME: Check that the format is supported by the device. */
7236 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7237 if (!resource_size)
7238 return WINED3DERR_INVALIDCALL;
7240 surface->surface_type = surface_type;
7242 switch (surface_type)
7244 case SURFACE_OPENGL:
7245 surface->surface_ops = &surface_ops;
7246 break;
7248 case SURFACE_GDI:
7249 surface->surface_ops = &gdi_surface_ops;
7250 break;
7252 default:
7253 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7254 return WINED3DERR_INVALIDCALL;
7257 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7258 multisample_type, multisample_quality, usage, pool, width, height, 1,
7259 resource_size, parent, parent_ops, &surface_resource_ops);
7260 if (FAILED(hr))
7262 WARN("Failed to initialize resource, returning %#x.\n", hr);
7263 return hr;
7266 /* "Standalone" surface. */
7267 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7269 surface->texture_level = level;
7270 list_init(&surface->overlays);
7272 /* Flags */
7273 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7274 if (discard)
7275 surface->flags |= SFLAG_DISCARD;
7276 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7277 surface->flags |= SFLAG_LOCKABLE;
7278 /* I'm not sure if this qualifies as a hack or as an optimization. It
7279 * seems reasonable to assume that lockable render targets will get
7280 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7281 * creation. However, the other reason we want to do this is that several
7282 * ddraw applications access surface memory while the surface isn't
7283 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7284 * future locks prevents these from crashing. */
7285 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7286 surface->flags |= SFLAG_DYNLOCK;
7288 /* Mark the texture as dirty so that it gets loaded first time around. */
7289 surface_add_dirty_rect(surface, NULL);
7290 list_init(&surface->renderbuffers);
7292 TRACE("surface %p, memory %p, size %u\n",
7293 surface, surface->resource.allocatedMemory, surface->resource.size);
7295 /* Call the private setup routine */
7296 hr = surface->surface_ops->surface_private_setup(surface);
7297 if (FAILED(hr))
7299 ERR("Private setup failed, returning %#x\n", hr);
7300 surface_cleanup(surface);
7301 return hr;
7304 /* Similar to lockable rendertargets above, creating the DIB section
7305 * during surface initialization prevents the sysmem pointer from changing
7306 * after a wined3d_surface_getdc() call. */
7307 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7308 && SUCCEEDED(surface_create_dib_section(surface)))
7310 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7311 surface->resource.heapMemory = NULL;
7312 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7315 return hr;
7318 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7319 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7320 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7321 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7323 struct wined3d_surface *object;
7324 HRESULT hr;
7326 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7327 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7328 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7329 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7330 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7332 if (surface_type == SURFACE_OPENGL && !device->adapter)
7334 ERR("OpenGL surfaces are not available without OpenGL.\n");
7335 return WINED3DERR_NOTAVAILABLE;
7338 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7339 if (!object)
7341 ERR("Failed to allocate surface memory.\n");
7342 return WINED3DERR_OUTOFVIDEOMEMORY;
7345 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7346 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7347 if (FAILED(hr))
7349 WARN("Failed to initialize surface, returning %#x.\n", hr);
7350 HeapFree(GetProcessHeap(), 0, object);
7351 return hr;
7354 TRACE("Created surface %p.\n", object);
7355 *surface = object;
7357 return hr;