wined3d: Use the proper texture target in ffp_blit_p8_upload_palette().
[wine/multimedia.git] / dlls / wined3d / surface.c
blob13c88f94a74487115323ef5f6258b2bf13b2d087
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, enum wined3d_texture_filter_type 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 enum wined3d_texture_filter_type 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 DeleteDC(surface->hDC);
101 DeleteObject(surface->dib.DIBsection);
102 surface->dib.bitmap_data = NULL;
103 surface->resource.allocatedMemory = NULL;
106 if (surface->flags & SFLAG_USERPTR)
107 wined3d_surface_set_mem(surface, NULL);
108 if (surface->overlay_dest)
109 list_remove(&surface->overlay_entry);
111 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
113 list_remove(&overlay->overlay_entry);
114 overlay->overlay_dest = NULL;
117 resource_cleanup(&surface->resource);
120 void surface_update_draw_binding(struct wined3d_surface *surface)
122 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
123 surface->draw_binding = SFLAG_INDRAWABLE;
124 else if (surface->resource.multisample_type)
125 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
126 else
127 surface->draw_binding = SFLAG_INTEXTURE;
130 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
132 TRACE("surface %p, container %p.\n", surface, container);
134 if (!container && type != WINED3D_CONTAINER_NONE)
135 ERR("Setting NULL container of type %#x.\n", type);
137 if (type == WINED3D_CONTAINER_SWAPCHAIN)
139 surface->get_drawable_size = get_drawable_size_swapchain;
141 else
143 switch (wined3d_settings.offscreen_rendering_mode)
145 case ORM_FBO:
146 surface->get_drawable_size = get_drawable_size_fbo;
147 break;
149 case ORM_BACKBUFFER:
150 surface->get_drawable_size = get_drawable_size_backbuffer;
151 break;
153 default:
154 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
155 return;
159 surface->container.type = type;
160 surface->container.u.base = container;
161 surface_update_draw_binding(surface);
164 struct blt_info
166 GLenum binding;
167 GLenum bind_target;
168 enum tex_types tex_type;
169 GLfloat coords[4][3];
172 struct float_rect
174 float l;
175 float t;
176 float r;
177 float b;
180 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
182 f->l = ((r->left * 2.0f) / w) - 1.0f;
183 f->t = ((r->top * 2.0f) / h) - 1.0f;
184 f->r = ((r->right * 2.0f) / w) - 1.0f;
185 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
188 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
190 GLfloat (*coords)[3] = info->coords;
191 struct float_rect f;
193 switch (target)
195 default:
196 FIXME("Unsupported texture target %#x\n", target);
197 /* Fall back to GL_TEXTURE_2D */
198 case GL_TEXTURE_2D:
199 info->binding = GL_TEXTURE_BINDING_2D;
200 info->bind_target = GL_TEXTURE_2D;
201 info->tex_type = tex_2d;
202 coords[0][0] = (float)rect->left / w;
203 coords[0][1] = (float)rect->top / h;
204 coords[0][2] = 0.0f;
206 coords[1][0] = (float)rect->right / w;
207 coords[1][1] = (float)rect->top / h;
208 coords[1][2] = 0.0f;
210 coords[2][0] = (float)rect->left / w;
211 coords[2][1] = (float)rect->bottom / h;
212 coords[2][2] = 0.0f;
214 coords[3][0] = (float)rect->right / w;
215 coords[3][1] = (float)rect->bottom / h;
216 coords[3][2] = 0.0f;
217 break;
219 case GL_TEXTURE_RECTANGLE_ARB:
220 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
221 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
222 info->tex_type = tex_rect;
223 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
224 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
225 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
226 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
227 break;
229 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
230 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
231 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
232 info->tex_type = tex_cube;
233 cube_coords_float(rect, w, h, &f);
235 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
236 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
237 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
238 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
239 break;
241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
242 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
243 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
244 info->tex_type = tex_cube;
245 cube_coords_float(rect, w, h, &f);
247 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
248 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
249 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
250 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
251 break;
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
256 info->tex_type = tex_cube;
257 cube_coords_float(rect, w, h, &f);
259 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
260 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
261 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
262 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
263 break;
265 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
266 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
267 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
268 info->tex_type = tex_cube;
269 cube_coords_float(rect, w, h, &f);
271 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
272 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
273 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
274 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
275 break;
277 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
278 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
279 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
280 info->tex_type = tex_cube;
281 cube_coords_float(rect, w, h, &f);
283 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
284 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
285 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
286 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
287 break;
289 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
292 info->tex_type = tex_cube;
293 cube_coords_float(rect, w, h, &f);
295 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
296 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
297 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
298 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
299 break;
303 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
305 if (rect_in)
306 *rect_out = *rect_in;
307 else
309 rect_out->left = 0;
310 rect_out->top = 0;
311 rect_out->right = surface->resource.width;
312 rect_out->bottom = surface->resource.height;
316 /* GL locking and context activation is done by the caller */
317 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
318 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
320 struct blt_info info;
322 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
324 glEnable(info.bind_target);
325 checkGLcall("glEnable(bind_target)");
327 context_bind_texture(context, info.bind_target, src_surface->texture_name);
329 /* Filtering for StretchRect */
330 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
331 wined3d_gl_mag_filter(magLookup, filter));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
334 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
337 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
338 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
339 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
340 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
341 checkGLcall("glTexEnvi");
343 /* Draw a quad */
344 glBegin(GL_TRIANGLE_STRIP);
345 glTexCoord3fv(info.coords[0]);
346 glVertex2i(dst_rect->left, dst_rect->top);
348 glTexCoord3fv(info.coords[1]);
349 glVertex2i(dst_rect->right, dst_rect->top);
351 glTexCoord3fv(info.coords[2]);
352 glVertex2i(dst_rect->left, dst_rect->bottom);
354 glTexCoord3fv(info.coords[3]);
355 glVertex2i(dst_rect->right, dst_rect->bottom);
356 glEnd();
358 /* Unbind the texture */
359 context_bind_texture(context, info.bind_target, 0);
361 /* We changed the filtering settings on the texture. Inform the
362 * container about this to get the filters reset properly next draw. */
363 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
365 struct wined3d_texture *texture = src_surface->container.u.texture;
366 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
369 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
373 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
375 const struct wined3d_format *format = surface->resource.format;
376 SYSTEM_INFO sysInfo;
377 BITMAPINFO *b_info;
378 int extraline = 0;
379 DWORD *masks;
381 TRACE("surface %p.\n", surface);
383 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
385 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
386 return WINED3DERR_INVALIDCALL;
389 switch (format->byte_count)
391 case 2:
392 case 4:
393 /* Allocate extra space to store the RGB bit masks. */
394 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
395 break;
397 case 3:
398 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
399 break;
401 default:
402 /* Allocate extra space for a palette. */
403 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
404 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
405 break;
408 if (!b_info)
409 return E_OUTOFMEMORY;
411 /* Some applications access the surface in via DWORDs, and do not take
412 * the necessary care at the end of the surface. So we need at least
413 * 4 extra bytes at the end of the surface. Check against the page size,
414 * if the last page used for the surface has at least 4 spare bytes we're
415 * safe, otherwise add an extra line to the DIB section. */
416 GetSystemInfo(&sysInfo);
417 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
419 extraline = 1;
420 TRACE("Adding an extra line to the DIB section.\n");
423 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
424 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
425 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
426 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
427 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
428 * wined3d_surface_get_pitch(surface);
429 b_info->bmiHeader.biPlanes = 1;
430 b_info->bmiHeader.biBitCount = format->byte_count * 8;
432 b_info->bmiHeader.biXPelsPerMeter = 0;
433 b_info->bmiHeader.biYPelsPerMeter = 0;
434 b_info->bmiHeader.biClrUsed = 0;
435 b_info->bmiHeader.biClrImportant = 0;
437 /* Get the bit masks */
438 masks = (DWORD *)b_info->bmiColors;
439 switch (surface->resource.format->id)
441 case WINED3DFMT_B8G8R8_UNORM:
442 b_info->bmiHeader.biCompression = BI_RGB;
443 break;
445 case WINED3DFMT_B5G5R5X1_UNORM:
446 case WINED3DFMT_B5G5R5A1_UNORM:
447 case WINED3DFMT_B4G4R4A4_UNORM:
448 case WINED3DFMT_B4G4R4X4_UNORM:
449 case WINED3DFMT_B2G3R3_UNORM:
450 case WINED3DFMT_B2G3R3A8_UNORM:
451 case WINED3DFMT_R10G10B10A2_UNORM:
452 case WINED3DFMT_R8G8B8A8_UNORM:
453 case WINED3DFMT_R8G8B8X8_UNORM:
454 case WINED3DFMT_B10G10R10A2_UNORM:
455 case WINED3DFMT_B5G6R5_UNORM:
456 case WINED3DFMT_R16G16B16A16_UNORM:
457 b_info->bmiHeader.biCompression = BI_BITFIELDS;
458 masks[0] = format->red_mask;
459 masks[1] = format->green_mask;
460 masks[2] = format->blue_mask;
461 break;
463 default:
464 /* Don't know palette */
465 b_info->bmiHeader.biCompression = BI_RGB;
466 break;
469 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
470 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
471 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
472 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
474 if (!surface->dib.DIBsection)
476 ERR("Failed to create DIB section.\n");
477 HeapFree(GetProcessHeap(), 0, b_info);
478 return HRESULT_FROM_WIN32(GetLastError());
481 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
482 /* Copy the existing surface to the dib section. */
483 if (surface->resource.allocatedMemory)
485 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
486 surface->resource.height * wined3d_surface_get_pitch(surface));
488 else
490 /* This is to make maps read the GL texture although memory is allocated. */
491 surface->flags &= ~SFLAG_INSYSMEM;
493 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
495 HeapFree(GetProcessHeap(), 0, b_info);
497 /* Now allocate a DC. */
498 surface->hDC = CreateCompatibleDC(0);
499 SelectObject(surface->hDC, surface->dib.DIBsection);
500 TRACE("Using wined3d palette %p.\n", surface->palette);
501 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
503 surface->flags |= SFLAG_DIBSECTION;
505 return WINED3D_OK;
508 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
510 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
511 return FALSE;
512 if (!(surface->flags & SFLAG_DYNLOCK))
513 return FALSE;
514 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
515 return FALSE;
516 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
517 return FALSE;
519 return TRUE;
522 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
524 struct wined3d_context *context;
525 GLenum error;
527 context = context_acquire(surface->resource.device, NULL);
528 ENTER_GL();
530 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
531 error = glGetError();
532 if (!surface->pbo || error != GL_NO_ERROR)
533 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
535 TRACE("Binding PBO %u.\n", surface->pbo);
537 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
538 checkGLcall("glBindBufferARB");
540 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
541 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
542 checkGLcall("glBufferDataARB");
544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
545 checkGLcall("glBindBufferARB");
547 /* We don't need the system memory anymore and we can't even use it for PBOs. */
548 if (!(surface->flags & SFLAG_CLIENT))
550 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
551 surface->resource.heapMemory = NULL;
553 surface->resource.allocatedMemory = NULL;
554 surface->flags |= SFLAG_PBO;
555 LEAVE_GL();
556 context_release(context);
559 static void surface_prepare_system_memory(struct wined3d_surface *surface)
561 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
563 TRACE("surface %p.\n", surface);
565 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
566 surface_load_pbo(surface, gl_info);
567 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
569 /* Whatever surface we have, make sure that there is memory allocated
570 * for the downloaded copy, or a PBO to map. */
571 if (!surface->resource.heapMemory)
572 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
574 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
575 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
577 if (surface->flags & SFLAG_INSYSMEM)
578 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
582 static void surface_evict_sysmem(struct wined3d_surface *surface)
584 if (surface->resource.map_count || (surface->flags & SFLAG_DONOTFREE))
585 return;
587 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
588 surface->resource.allocatedMemory = NULL;
589 surface->resource.heapMemory = NULL;
590 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
593 /* Context activation is done by the caller. */
594 static void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
596 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
598 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
600 struct wined3d_texture *texture = surface->container.u.texture;
602 TRACE("Passing to container (%p).\n", texture);
603 texture->texture_ops->texture_bind(texture, context, srgb);
605 else
607 if (surface->texture_level)
609 ERR("Standalone surface %p is non-zero texture level %u.\n",
610 surface, surface->texture_level);
613 if (srgb)
614 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
616 ENTER_GL();
618 if (!surface->texture_name)
620 glGenTextures(1, &surface->texture_name);
621 checkGLcall("glGenTextures");
623 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
625 context_bind_texture(context, surface->texture_target, surface->texture_name);
626 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
627 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
628 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
629 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
630 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
631 checkGLcall("glTexParameteri");
633 else
635 context_bind_texture(context, surface->texture_target, surface->texture_name);
638 LEAVE_GL();
642 /* Context activation is done by the caller. */
643 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
644 struct wined3d_context *context, BOOL srgb)
646 struct wined3d_device *device = surface->resource.device;
647 DWORD active_sampler;
649 /* We don't need a specific texture unit, but after binding the texture
650 * the current unit is dirty. Read the unit back instead of switching to
651 * 0, this avoids messing around with the state manager's GL states. The
652 * current texture unit should always be a valid one.
654 * To be more specific, this is tricky because we can implicitly be
655 * called from sampler() in state.c. This means we can't touch anything
656 * other than whatever happens to be the currently active texture, or we
657 * would risk marking already applied sampler states dirty again. */
658 active_sampler = device->rev_tex_unit_map[context->active_texture];
660 if (active_sampler != WINED3D_UNMAPPED_STAGE)
661 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
662 surface_bind(surface, context, srgb);
665 static void surface_force_reload(struct wined3d_surface *surface)
667 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
670 static void surface_release_client_storage(struct wined3d_surface *surface)
672 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
674 ENTER_GL();
675 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
676 if (surface->texture_name)
678 surface_bind_and_dirtify(surface, context, FALSE);
679 glTexImage2D(surface->texture_target, surface->texture_level,
680 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
682 if (surface->texture_name_srgb)
684 surface_bind_and_dirtify(surface, context, TRUE);
685 glTexImage2D(surface->texture_target, surface->texture_level,
686 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
688 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
689 LEAVE_GL();
691 context_release(context);
693 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
694 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
695 surface_force_reload(surface);
698 static HRESULT surface_private_setup(struct wined3d_surface *surface)
700 /* TODO: Check against the maximum texture sizes supported by the video card. */
701 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
702 unsigned int pow2Width, pow2Height;
704 TRACE("surface %p.\n", surface);
706 surface->texture_name = 0;
707 surface->texture_target = GL_TEXTURE_2D;
709 /* Non-power2 support */
710 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
712 pow2Width = surface->resource.width;
713 pow2Height = surface->resource.height;
715 else
717 /* Find the nearest pow2 match */
718 pow2Width = pow2Height = 1;
719 while (pow2Width < surface->resource.width)
720 pow2Width <<= 1;
721 while (pow2Height < surface->resource.height)
722 pow2Height <<= 1;
724 surface->pow2Width = pow2Width;
725 surface->pow2Height = pow2Height;
727 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
729 /* TODO: Add support for non power two compressed textures. */
730 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
732 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
733 surface, surface->resource.width, surface->resource.height);
734 return WINED3DERR_NOTAVAILABLE;
738 if (pow2Width != surface->resource.width
739 || pow2Height != surface->resource.height)
741 surface->flags |= SFLAG_NONPOW2;
744 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
745 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
747 /* One of three options:
748 * 1: Do the same as we do with NPOT and scale the texture, (any
749 * texture ops would require the texture to be scaled which is
750 * potentially slow)
751 * 2: Set the texture to the maximum size (bad idea).
752 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
753 * 4: Create the surface, but allow it to be used only for DirectDraw
754 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
755 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
756 * the render target. */
757 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
759 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
760 return WINED3DERR_NOTAVAILABLE;
763 /* We should never use this surface in combination with OpenGL! */
764 TRACE("Creating an oversized surface: %ux%u.\n",
765 surface->pow2Width, surface->pow2Height);
767 else
769 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
770 * and EXT_PALETTED_TEXTURE is used in combination with texture
771 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
772 * EXT_PALETTED_TEXTURE doesn't work in combination with
773 * ARB_TEXTURE_RECTANGLE. */
774 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
775 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
776 && gl_info->supported[EXT_PALETTED_TEXTURE]
777 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
779 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
780 surface->pow2Width = surface->resource.width;
781 surface->pow2Height = surface->resource.height;
782 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
786 switch (wined3d_settings.offscreen_rendering_mode)
788 case ORM_FBO:
789 surface->get_drawable_size = get_drawable_size_fbo;
790 break;
792 case ORM_BACKBUFFER:
793 surface->get_drawable_size = get_drawable_size_backbuffer;
794 break;
796 default:
797 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
798 return WINED3DERR_INVALIDCALL;
801 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
802 surface->flags |= SFLAG_DISCARDED;
804 return WINED3D_OK;
807 static void surface_realize_palette(struct wined3d_surface *surface)
809 struct wined3d_palette *palette = surface->palette;
811 TRACE("surface %p.\n", surface);
813 if (!palette) return;
815 if (surface->resource.format->id == WINED3DFMT_P8_UINT
816 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
818 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
820 /* Make sure the texture is up to date. This call doesn't do
821 * anything if the texture is already up to date. */
822 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
824 /* We want to force a palette refresh, so mark the drawable as not being up to date */
825 if (!surface_is_offscreen(surface))
826 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
828 else
830 if (!(surface->flags & SFLAG_INSYSMEM))
832 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
833 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
835 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
839 if (surface->flags & SFLAG_DIBSECTION)
841 RGBQUAD col[256];
842 unsigned int i;
844 TRACE("Updating the DC's palette.\n");
846 for (i = 0; i < 256; ++i)
848 col[i].rgbRed = palette->palents[i].peRed;
849 col[i].rgbGreen = palette->palents[i].peGreen;
850 col[i].rgbBlue = palette->palents[i].peBlue;
851 col[i].rgbReserved = 0;
853 SetDIBColorTable(surface->hDC, 0, 256, col);
856 /* Propagate the changes to the drawable when we have a palette. */
857 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
858 surface_load_location(surface, surface->draw_binding, NULL);
861 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
863 HRESULT hr;
865 /* If there's no destination surface there is nothing to do. */
866 if (!surface->overlay_dest)
867 return WINED3D_OK;
869 /* Blt calls ModifyLocation on the dest surface, which in turn calls
870 * DrawOverlay to update the overlay. Prevent an endless recursion. */
871 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
872 return WINED3D_OK;
874 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
875 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
876 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
877 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
879 return hr;
882 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
884 struct wined3d_device *device = surface->resource.device;
885 const RECT *pass_rect = rect;
887 TRACE("surface %p, rect %s, flags %#x.\n",
888 surface, wine_dbgstr_rect(rect), flags);
890 if (flags & WINED3D_MAP_DISCARD)
892 TRACE("WINED3D_MAP_DISCARD flag passed, marking SYSMEM as up to date.\n");
893 surface_prepare_system_memory(surface);
894 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
896 else
898 /* surface_load_location() does not check if the rectangle specifies
899 * the full surface. Most callers don't need that, so do it here. */
900 if (rect && !rect->top && !rect->left
901 && rect->right == surface->resource.width
902 && rect->bottom == surface->resource.height)
903 pass_rect = NULL;
904 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
907 if (surface->flags & SFLAG_PBO)
909 const struct wined3d_gl_info *gl_info;
910 struct wined3d_context *context;
912 context = context_acquire(device, NULL);
913 gl_info = context->gl_info;
915 ENTER_GL();
916 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
917 checkGLcall("glBindBufferARB");
919 /* This shouldn't happen but could occur if some other function
920 * didn't handle the PBO properly. */
921 if (surface->resource.allocatedMemory)
922 ERR("The surface already has PBO memory allocated.\n");
924 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
925 checkGLcall("glMapBufferARB");
927 /* Make sure the PBO isn't set anymore in order not to break non-PBO
928 * calls. */
929 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
930 checkGLcall("glBindBufferARB");
932 LEAVE_GL();
933 context_release(context);
936 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
938 if (!rect)
939 surface_add_dirty_rect(surface, NULL);
940 else
942 struct wined3d_box b;
944 b.left = rect->left;
945 b.top = rect->top;
946 b.right = rect->right;
947 b.bottom = rect->bottom;
948 b.front = 0;
949 b.back = 1;
950 surface_add_dirty_rect(surface, &b);
955 static void surface_unmap(struct wined3d_surface *surface)
957 struct wined3d_device *device = surface->resource.device;
958 BOOL fullsurface;
960 TRACE("surface %p.\n", surface);
962 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
964 if (surface->flags & SFLAG_PBO)
966 const struct wined3d_gl_info *gl_info;
967 struct wined3d_context *context;
969 TRACE("Freeing PBO memory.\n");
971 context = context_acquire(device, NULL);
972 gl_info = context->gl_info;
974 ENTER_GL();
975 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
976 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
977 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
978 checkGLcall("glUnmapBufferARB");
979 LEAVE_GL();
980 context_release(context);
982 surface->resource.allocatedMemory = NULL;
985 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
987 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
989 TRACE("Not dirtified, nothing to do.\n");
990 goto done;
993 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
994 && surface->container.u.swapchain->front_buffer == surface)
996 if (!surface->dirtyRect.left && !surface->dirtyRect.top
997 && surface->dirtyRect.right == surface->resource.width
998 && surface->dirtyRect.bottom == surface->resource.height)
1000 fullsurface = TRUE;
1002 else
1004 /* TODO: Proper partial rectangle tracking. */
1005 fullsurface = FALSE;
1006 surface->flags |= SFLAG_INSYSMEM;
1009 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
1011 /* Partial rectangle tracking is not commonly implemented, it is only
1012 * done for render targets. INSYSMEM was set before to tell
1013 * surface_load_location() where to read the rectangle from.
1014 * Indrawable is set because all modifications from the partial
1015 * sysmem copy are written back to the drawable, thus the surface is
1016 * merged again in the drawable. The sysmem copy is not fully up to
1017 * date because only a subrectangle was read in Map(). */
1018 if (!fullsurface)
1020 surface_modify_location(surface, surface->draw_binding, TRUE);
1021 surface_evict_sysmem(surface);
1024 surface->dirtyRect.left = surface->resource.width;
1025 surface->dirtyRect.top = surface->resource.height;
1026 surface->dirtyRect.right = 0;
1027 surface->dirtyRect.bottom = 0;
1029 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1031 FIXME("Depth / stencil buffer locking is not implemented.\n");
1034 done:
1035 /* Overlays have to be redrawn manually after changes with the GL implementation */
1036 if (surface->overlay_dest)
1037 surface_draw_overlay(surface);
1040 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1042 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1043 return FALSE;
1044 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1045 return FALSE;
1046 return TRUE;
1049 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1050 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1052 const struct wined3d_gl_info *gl_info;
1053 struct wined3d_context *context;
1054 DWORD src_mask, dst_mask;
1055 GLbitfield gl_mask;
1057 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1058 device, src_surface, wine_dbgstr_rect(src_rect),
1059 dst_surface, wine_dbgstr_rect(dst_rect));
1061 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1062 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1064 if (src_mask != dst_mask)
1066 ERR("Incompatible formats %s and %s.\n",
1067 debug_d3dformat(src_surface->resource.format->id),
1068 debug_d3dformat(dst_surface->resource.format->id));
1069 return;
1072 if (!src_mask)
1074 ERR("Not a depth / stencil format: %s.\n",
1075 debug_d3dformat(src_surface->resource.format->id));
1076 return;
1079 gl_mask = 0;
1080 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1081 gl_mask |= GL_DEPTH_BUFFER_BIT;
1082 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1083 gl_mask |= GL_STENCIL_BUFFER_BIT;
1085 /* Make sure the locations are up-to-date. Loading the destination
1086 * surface isn't required if the entire surface is overwritten. */
1087 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1088 if (!surface_is_full_rect(dst_surface, dst_rect))
1089 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1091 context = context_acquire(device, NULL);
1092 if (!context->valid)
1094 context_release(context);
1095 WARN("Invalid context, skipping blit.\n");
1096 return;
1099 gl_info = context->gl_info;
1101 ENTER_GL();
1103 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1104 glReadBuffer(GL_NONE);
1105 checkGLcall("glReadBuffer()");
1106 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1108 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1109 context_set_draw_buffer(context, GL_NONE);
1110 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1111 context_invalidate_state(context, STATE_FRAMEBUFFER);
1113 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1115 glDepthMask(GL_TRUE);
1116 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1118 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1120 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1122 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1123 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1125 glStencilMask(~0U);
1126 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1129 glDisable(GL_SCISSOR_TEST);
1130 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1132 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1133 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1134 checkGLcall("glBlitFramebuffer()");
1136 LEAVE_GL();
1138 if (wined3d_settings.strict_draw_ordering)
1139 wglFlush(); /* Flush to ensure ordering across contexts. */
1141 context_release(context);
1144 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1145 * Depth / stencil is not supported. */
1146 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1147 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1148 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1150 const struct wined3d_gl_info *gl_info;
1151 struct wined3d_context *context;
1152 RECT src_rect, dst_rect;
1153 GLenum gl_filter;
1154 GLenum buffer;
1156 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1157 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1158 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1159 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1160 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1162 src_rect = *src_rect_in;
1163 dst_rect = *dst_rect_in;
1165 switch (filter)
1167 case WINED3D_TEXF_LINEAR:
1168 gl_filter = GL_LINEAR;
1169 break;
1171 default:
1172 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1173 case WINED3D_TEXF_NONE:
1174 case WINED3D_TEXF_POINT:
1175 gl_filter = GL_NEAREST;
1176 break;
1179 /* Resolve the source surface first if needed. */
1180 if (src_location == SFLAG_INRB_MULTISAMPLE
1181 && (src_surface->resource.format->id != dst_surface->resource.format->id
1182 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1183 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1184 src_location = SFLAG_INRB_RESOLVED;
1186 /* Make sure the locations are up-to-date. Loading the destination
1187 * surface isn't required if the entire surface is overwritten. (And is
1188 * in fact harmful if we're being called by surface_load_location() with
1189 * the purpose of loading the destination surface.) */
1190 surface_load_location(src_surface, src_location, NULL);
1191 if (!surface_is_full_rect(dst_surface, &dst_rect))
1192 surface_load_location(dst_surface, dst_location, NULL);
1194 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1195 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1196 else context = context_acquire(device, NULL);
1198 if (!context->valid)
1200 context_release(context);
1201 WARN("Invalid context, skipping blit.\n");
1202 return;
1205 gl_info = context->gl_info;
1207 if (src_location == SFLAG_INDRAWABLE)
1209 TRACE("Source surface %p is onscreen.\n", src_surface);
1210 buffer = surface_get_gl_buffer(src_surface);
1211 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1213 else
1215 TRACE("Source surface %p is offscreen.\n", src_surface);
1216 buffer = GL_COLOR_ATTACHMENT0;
1219 ENTER_GL();
1220 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1221 glReadBuffer(buffer);
1222 checkGLcall("glReadBuffer()");
1223 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1224 LEAVE_GL();
1226 if (dst_location == SFLAG_INDRAWABLE)
1228 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1229 buffer = surface_get_gl_buffer(dst_surface);
1230 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1232 else
1234 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1235 buffer = GL_COLOR_ATTACHMENT0;
1238 ENTER_GL();
1239 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1240 context_set_draw_buffer(context, buffer);
1241 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1242 context_invalidate_state(context, STATE_FRAMEBUFFER);
1244 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1245 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1246 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1247 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1248 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1250 glDisable(GL_SCISSOR_TEST);
1251 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1253 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1254 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1255 checkGLcall("glBlitFramebuffer()");
1257 LEAVE_GL();
1259 if (wined3d_settings.strict_draw_ordering
1260 || (dst_location == SFLAG_INDRAWABLE
1261 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1262 wglFlush();
1264 context_release(context);
1267 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1268 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1269 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1271 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1272 return FALSE;
1274 /* Source and/or destination need to be on the GL side */
1275 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1276 return FALSE;
1278 switch (blit_op)
1280 case WINED3D_BLIT_OP_COLOR_BLIT:
1281 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1282 return FALSE;
1283 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1284 return FALSE;
1285 break;
1287 case WINED3D_BLIT_OP_DEPTH_BLIT:
1288 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1289 return FALSE;
1290 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1291 return FALSE;
1292 break;
1294 default:
1295 return FALSE;
1298 if (!(src_format->id == dst_format->id
1299 || (is_identity_fixup(src_format->color_fixup)
1300 && is_identity_fixup(dst_format->color_fixup))))
1301 return FALSE;
1303 return TRUE;
1306 /* This function checks if the primary render target uses the 8bit paletted format. */
1307 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1309 if (device->fb.render_targets && device->fb.render_targets[0])
1311 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1312 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1313 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1314 return TRUE;
1316 return FALSE;
1319 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1320 DWORD color, struct wined3d_color *float_color)
1322 const struct wined3d_format *format = surface->resource.format;
1323 const struct wined3d_device *device = surface->resource.device;
1325 switch (format->id)
1327 case WINED3DFMT_P8_UINT:
1328 if (surface->palette)
1330 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1331 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1332 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1334 else
1336 float_color->r = 0.0f;
1337 float_color->g = 0.0f;
1338 float_color->b = 0.0f;
1340 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1341 break;
1343 case WINED3DFMT_B5G6R5_UNORM:
1344 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1345 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1346 float_color->b = (color & 0x1f) / 31.0f;
1347 float_color->a = 1.0f;
1348 break;
1350 case WINED3DFMT_B8G8R8_UNORM:
1351 case WINED3DFMT_B8G8R8X8_UNORM:
1352 float_color->r = D3DCOLOR_R(color);
1353 float_color->g = D3DCOLOR_G(color);
1354 float_color->b = D3DCOLOR_B(color);
1355 float_color->a = 1.0f;
1356 break;
1358 case WINED3DFMT_B8G8R8A8_UNORM:
1359 float_color->r = D3DCOLOR_R(color);
1360 float_color->g = D3DCOLOR_G(color);
1361 float_color->b = D3DCOLOR_B(color);
1362 float_color->a = D3DCOLOR_A(color);
1363 break;
1365 default:
1366 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1367 return FALSE;
1370 return TRUE;
1373 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1375 const struct wined3d_format *format = surface->resource.format;
1377 switch (format->id)
1379 case WINED3DFMT_S1_UINT_D15_UNORM:
1380 *float_depth = depth / (float)0x00007fff;
1381 break;
1383 case WINED3DFMT_D16_UNORM:
1384 *float_depth = depth / (float)0x0000ffff;
1385 break;
1387 case WINED3DFMT_D24_UNORM_S8_UINT:
1388 case WINED3DFMT_X8D24_UNORM:
1389 *float_depth = depth / (float)0x00ffffff;
1390 break;
1392 case WINED3DFMT_D32_UNORM:
1393 *float_depth = depth / (float)0xffffffff;
1394 break;
1396 default:
1397 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1398 return FALSE;
1401 return TRUE;
1404 /* Do not call while under the GL lock. */
1405 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1407 const struct wined3d_resource *resource = &surface->resource;
1408 struct wined3d_device *device = resource->device;
1409 const struct blit_shader *blitter;
1411 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1412 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1413 if (!blitter)
1415 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1416 return WINED3DERR_INVALIDCALL;
1419 return blitter->depth_fill(device, surface, rect, depth);
1422 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1423 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1425 struct wined3d_device *device = src_surface->resource.device;
1427 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1428 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1429 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1430 return WINED3DERR_INVALIDCALL;
1432 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1434 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1435 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1437 return WINED3D_OK;
1440 /* Do not call while under the GL lock. */
1441 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1442 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1443 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1445 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1446 struct wined3d_device *device = dst_surface->resource.device;
1447 DWORD src_ds_flags, dst_ds_flags;
1448 RECT src_rect, dst_rect;
1449 BOOL scale, convert;
1451 static const DWORD simple_blit = WINEDDBLT_ASYNC
1452 | WINEDDBLT_COLORFILL
1453 | WINEDDBLT_WAIT
1454 | WINEDDBLT_DEPTHFILL
1455 | WINEDDBLT_DONOTWAIT;
1457 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1458 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1459 flags, fx, debug_d3dtexturefiltertype(filter));
1460 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1462 if (fx)
1464 TRACE("dwSize %#x.\n", fx->dwSize);
1465 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1466 TRACE("dwROP %#x.\n", fx->dwROP);
1467 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1468 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1469 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1470 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1471 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1472 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1473 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1474 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1475 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1476 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1477 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1478 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1479 TRACE("dwReserved %#x.\n", fx->dwReserved);
1480 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1481 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1482 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1483 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1484 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1485 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1486 fx->ddckDestColorkey.color_space_low_value,
1487 fx->ddckDestColorkey.color_space_high_value);
1488 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1489 fx->ddckSrcColorkey.color_space_low_value,
1490 fx->ddckSrcColorkey.color_space_high_value);
1493 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
1495 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1496 return WINEDDERR_SURFACEBUSY;
1499 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1501 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1502 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1503 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1504 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1505 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1507 WARN("The application gave us a bad destination rectangle.\n");
1508 return WINEDDERR_INVALIDRECT;
1511 if (src_surface)
1513 surface_get_rect(src_surface, src_rect_in, &src_rect);
1515 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1516 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1517 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1518 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1519 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1521 WARN("Application gave us bad source rectangle for Blt.\n");
1522 return WINEDDERR_INVALIDRECT;
1525 else
1527 memset(&src_rect, 0, sizeof(src_rect));
1530 if (!fx || !(fx->dwDDFX))
1531 flags &= ~WINEDDBLT_DDFX;
1533 if (flags & WINEDDBLT_WAIT)
1534 flags &= ~WINEDDBLT_WAIT;
1536 if (flags & WINEDDBLT_ASYNC)
1538 static unsigned int once;
1540 if (!once++)
1541 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1542 flags &= ~WINEDDBLT_ASYNC;
1545 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1546 if (flags & WINEDDBLT_DONOTWAIT)
1548 static unsigned int once;
1550 if (!once++)
1551 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1552 flags &= ~WINEDDBLT_DONOTWAIT;
1555 if (!device->d3d_initialized)
1557 WARN("D3D not initialized, using fallback.\n");
1558 goto cpu;
1561 /* We want to avoid invalidating the sysmem location for converted
1562 * surfaces, since otherwise we'd have to convert the data back when
1563 * locking them. */
1564 if (dst_surface->flags & SFLAG_CONVERTED)
1566 WARN("Converted surface, using CPU blit.\n");
1567 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1570 if (flags & ~simple_blit)
1572 WARN("Using fallback for complex blit (%#x).\n", flags);
1573 goto fallback;
1576 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1577 src_swapchain = src_surface->container.u.swapchain;
1578 else
1579 src_swapchain = NULL;
1581 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1582 dst_swapchain = dst_surface->container.u.swapchain;
1583 else
1584 dst_swapchain = NULL;
1586 /* This isn't strictly needed. FBO blits for example could deal with
1587 * cross-swapchain blits by first downloading the source to a texture
1588 * before switching to the destination context. We just have this here to
1589 * not have to deal with the issue, since cross-swapchain blits should be
1590 * rare. */
1591 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1593 FIXME("Using fallback for cross-swapchain blit.\n");
1594 goto fallback;
1597 scale = src_surface
1598 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1599 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1600 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1602 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1603 if (src_surface)
1604 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1605 else
1606 src_ds_flags = 0;
1608 if (src_ds_flags || dst_ds_flags)
1610 if (flags & WINEDDBLT_DEPTHFILL)
1612 float depth;
1614 TRACE("Depth fill.\n");
1616 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1617 return WINED3DERR_INVALIDCALL;
1619 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1620 return WINED3D_OK;
1622 else
1624 if (src_ds_flags != dst_ds_flags)
1626 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1627 return WINED3DERR_INVALIDCALL;
1630 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1631 return WINED3D_OK;
1634 else
1636 /* In principle this would apply to depth blits as well, but we don't
1637 * implement those in the CPU blitter at the moment. */
1638 if ((dst_surface->flags & SFLAG_INSYSMEM)
1639 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1641 if (scale)
1642 TRACE("Not doing sysmem blit because of scaling.\n");
1643 else if (convert)
1644 TRACE("Not doing sysmem blit because of format conversion.\n");
1645 else
1646 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1649 if (flags & WINEDDBLT_COLORFILL)
1651 struct wined3d_color color;
1653 TRACE("Color fill.\n");
1655 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1656 goto fallback;
1658 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1659 return WINED3D_OK;
1661 else
1663 TRACE("Color blit.\n");
1665 /* Upload */
1666 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1668 if (scale)
1669 TRACE("Not doing upload because of scaling.\n");
1670 else if (convert)
1671 TRACE("Not doing upload because of format conversion.\n");
1672 else
1674 POINT dst_point = {dst_rect.left, dst_rect.top};
1676 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1678 if (!surface_is_offscreen(dst_surface))
1679 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1680 return WINED3D_OK;
1685 /* Use present for back -> front blits. The idea behind this is
1686 * that present is potentially faster than a blit, in particular
1687 * when FBO blits aren't available. Some ddraw applications like
1688 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1689 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1690 * applications can't blit directly to the frontbuffer. */
1691 if (dst_swapchain && dst_swapchain->back_buffers
1692 && dst_surface == dst_swapchain->front_buffer
1693 && src_surface == dst_swapchain->back_buffers[0])
1695 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1697 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1699 /* Set the swap effect to COPY, we don't want the backbuffer
1700 * to become undefined. */
1701 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1702 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1703 dst_swapchain->desc.swap_effect = swap_effect;
1705 return WINED3D_OK;
1708 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1709 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1710 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1712 TRACE("Using FBO blit.\n");
1714 surface_blt_fbo(device, filter,
1715 src_surface, src_surface->draw_binding, &src_rect,
1716 dst_surface, dst_surface->draw_binding, &dst_rect);
1717 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1718 return WINED3D_OK;
1721 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1722 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1723 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1725 TRACE("Using arbfp blit.\n");
1727 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1728 return WINED3D_OK;
1733 fallback:
1735 /* Special cases for render targets. */
1736 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1737 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1739 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1740 src_surface, &src_rect, flags, fx, filter)))
1741 return WINED3D_OK;
1744 cpu:
1746 /* For the rest call the X11 surface implementation. For render targets
1747 * this should be implemented OpenGL accelerated in BltOverride, other
1748 * blits are rather rare. */
1749 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1752 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1753 struct wined3d_surface *render_target)
1755 TRACE("surface %p, render_target %p.\n", surface, render_target);
1757 /* TODO: Check surface sizes, pools, etc. */
1759 if (render_target->resource.multisample_type)
1760 return WINED3DERR_INVALIDCALL;
1762 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1765 /* Context activation is done by the caller. */
1766 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1768 if (surface->flags & SFLAG_DIBSECTION)
1770 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1772 else
1774 if (!surface->resource.heapMemory)
1775 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1776 else if (!(surface->flags & SFLAG_CLIENT))
1777 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1778 surface, surface->resource.heapMemory, surface->flags);
1780 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1781 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1784 ENTER_GL();
1785 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1786 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1787 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1788 surface->resource.size, surface->resource.allocatedMemory));
1789 checkGLcall("glGetBufferSubDataARB");
1790 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1791 checkGLcall("glDeleteBuffersARB");
1792 LEAVE_GL();
1794 surface->pbo = 0;
1795 surface->flags &= ~SFLAG_PBO;
1798 static BOOL surface_init_sysmem(struct wined3d_surface *surface)
1800 if (!surface->resource.allocatedMemory)
1802 if (!surface->resource.heapMemory)
1804 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1805 surface->resource.size + RESOURCE_ALIGNMENT)))
1807 ERR("Failed to allocate memory.\n");
1808 return FALSE;
1811 else if (!(surface->flags & SFLAG_CLIENT))
1813 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1814 surface, surface->resource.heapMemory, surface->flags);
1817 surface->resource.allocatedMemory =
1818 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1820 else
1822 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
1825 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1827 return TRUE;
1830 /* Do not call while under the GL lock. */
1831 static void surface_unload(struct wined3d_resource *resource)
1833 struct wined3d_surface *surface = surface_from_resource(resource);
1834 struct wined3d_renderbuffer_entry *entry, *entry2;
1835 struct wined3d_device *device = resource->device;
1836 const struct wined3d_gl_info *gl_info;
1837 struct wined3d_context *context;
1839 TRACE("surface %p.\n", surface);
1841 if (resource->pool == WINED3D_POOL_DEFAULT)
1843 /* Default pool resources are supposed to be destroyed before Reset is called.
1844 * Implicit resources stay however. So this means we have an implicit render target
1845 * or depth stencil. The content may be destroyed, but we still have to tear down
1846 * opengl resources, so we cannot leave early.
1848 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1849 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1850 * or the depth stencil into an FBO the texture or render buffer will be removed
1851 * and all flags get lost
1853 if (!(surface->flags & SFLAG_PBO))
1854 surface_init_sysmem(surface);
1855 /* We also get here when the ddraw swapchain is destroyed, for example
1856 * for a mode switch. In this case this surface won't necessarily be
1857 * an implicit surface. We have to mark it lost so that the
1858 * application can restore it after the mode switch. */
1859 surface->flags |= SFLAG_LOST;
1861 else
1863 /* Load the surface into system memory */
1864 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1865 surface_modify_location(surface, surface->draw_binding, FALSE);
1867 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1868 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1869 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1871 context = context_acquire(device, NULL);
1872 gl_info = context->gl_info;
1874 /* Destroy PBOs, but load them into real sysmem before */
1875 if (surface->flags & SFLAG_PBO)
1876 surface_remove_pbo(surface, gl_info);
1878 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1879 * all application-created targets the application has to release the surface
1880 * before calling _Reset
1882 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1884 ENTER_GL();
1885 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1886 LEAVE_GL();
1887 list_remove(&entry->entry);
1888 HeapFree(GetProcessHeap(), 0, entry);
1890 list_init(&surface->renderbuffers);
1891 surface->current_renderbuffer = NULL;
1893 ENTER_GL();
1895 /* If we're in a texture, the texture name belongs to the texture.
1896 * Otherwise, destroy it. */
1897 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1899 glDeleteTextures(1, &surface->texture_name);
1900 surface->texture_name = 0;
1901 glDeleteTextures(1, &surface->texture_name_srgb);
1902 surface->texture_name_srgb = 0;
1904 if (surface->rb_multisample)
1906 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1907 surface->rb_multisample = 0;
1909 if (surface->rb_resolved)
1911 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1912 surface->rb_resolved = 0;
1915 LEAVE_GL();
1917 context_release(context);
1919 resource_unload(resource);
1922 static const struct wined3d_resource_ops surface_resource_ops =
1924 surface_unload,
1927 static const struct wined3d_surface_ops surface_ops =
1929 surface_private_setup,
1930 surface_realize_palette,
1931 surface_map,
1932 surface_unmap,
1935 /*****************************************************************************
1936 * Initializes the GDI surface, aka creates the DIB section we render to
1937 * The DIB section creation is done by calling GetDC, which will create the
1938 * section and releasing the dc to allow the app to use it. The dib section
1939 * will stay until the surface is released
1941 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1942 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1943 * avoid confusion in the shared surface code.
1945 * Returns:
1946 * WINED3D_OK on success
1947 * The return values of called methods on failure
1949 *****************************************************************************/
1950 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1952 HRESULT hr;
1954 TRACE("surface %p.\n", surface);
1956 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1958 ERR("Overlays not yet supported by GDI surfaces.\n");
1959 return WINED3DERR_INVALIDCALL;
1962 /* Sysmem textures have memory already allocated - release it,
1963 * this avoids an unnecessary memcpy. */
1964 hr = surface_create_dib_section(surface);
1965 if (SUCCEEDED(hr))
1967 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1968 surface->resource.heapMemory = NULL;
1969 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1972 /* We don't mind the nonpow2 stuff in GDI. */
1973 surface->pow2Width = surface->resource.width;
1974 surface->pow2Height = surface->resource.height;
1976 return WINED3D_OK;
1979 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1981 struct wined3d_palette *palette = surface->palette;
1983 TRACE("surface %p.\n", surface);
1985 if (!palette) return;
1987 if (surface->flags & SFLAG_DIBSECTION)
1989 RGBQUAD col[256];
1990 unsigned int i;
1992 TRACE("Updating the DC's palette.\n");
1994 for (i = 0; i < 256; ++i)
1996 col[i].rgbRed = palette->palents[i].peRed;
1997 col[i].rgbGreen = palette->palents[i].peGreen;
1998 col[i].rgbBlue = palette->palents[i].peBlue;
1999 col[i].rgbReserved = 0;
2001 SetDIBColorTable(surface->hDC, 0, 256, col);
2004 /* Update the image because of the palette change. Some games like e.g.
2005 * Red Alert call SetEntries a lot to implement fading. */
2006 /* Tell the swapchain to update the screen. */
2007 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2009 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2010 if (surface == swapchain->front_buffer)
2012 x11_copy_to_screen(swapchain, NULL);
2017 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2019 TRACE("surface %p, rect %s, flags %#x.\n",
2020 surface, wine_dbgstr_rect(rect), flags);
2022 if (!(surface->flags & SFLAG_DIBSECTION))
2024 HRESULT hr;
2026 /* This happens on gdi surfaces if the application set a user pointer
2027 * and resets it. Recreate the DIB section. */
2028 if (FAILED(hr = surface_create_dib_section(surface)))
2030 ERR("Failed to create dib section, hr %#x.\n", hr);
2031 return;
2033 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
2034 surface->resource.heapMemory = NULL;
2035 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2039 static void gdi_surface_unmap(struct wined3d_surface *surface)
2041 TRACE("surface %p.\n", surface);
2043 /* Tell the swapchain to update the screen. */
2044 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2046 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2047 if (surface == swapchain->front_buffer)
2049 x11_copy_to_screen(swapchain, &surface->lockedRect);
2053 memset(&surface->lockedRect, 0, sizeof(RECT));
2056 static const struct wined3d_surface_ops gdi_surface_ops =
2058 gdi_surface_private_setup,
2059 gdi_surface_realize_palette,
2060 gdi_surface_map,
2061 gdi_surface_unmap,
2064 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2066 GLuint *name;
2067 DWORD flag;
2069 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2071 if(srgb)
2073 name = &surface->texture_name_srgb;
2074 flag = SFLAG_INSRGBTEX;
2076 else
2078 name = &surface->texture_name;
2079 flag = SFLAG_INTEXTURE;
2082 if (!*name && new_name)
2084 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2085 * surface has no texture name yet. See if we can get rid of this. */
2086 if (surface->flags & flag)
2088 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2089 surface_modify_location(surface, flag, FALSE);
2093 *name = new_name;
2094 surface_force_reload(surface);
2097 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2099 TRACE("surface %p, target %#x.\n", surface, target);
2101 if (surface->texture_target != target)
2103 if (target == GL_TEXTURE_RECTANGLE_ARB)
2105 surface->flags &= ~SFLAG_NORMCOORD;
2107 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2109 surface->flags |= SFLAG_NORMCOORD;
2112 surface->texture_target = target;
2113 surface_force_reload(surface);
2116 /* This call just downloads data, the caller is responsible for binding the
2117 * correct texture. */
2118 /* Context activation is done by the caller. */
2119 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2121 const struct wined3d_format *format = surface->resource.format;
2123 /* Only support read back of converted P8 surfaces. */
2124 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2126 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2127 return;
2130 ENTER_GL();
2132 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2134 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2135 surface, surface->texture_level, format->glFormat, format->glType,
2136 surface->resource.allocatedMemory);
2138 if (surface->flags & SFLAG_PBO)
2140 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2141 checkGLcall("glBindBufferARB");
2142 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2143 checkGLcall("glGetCompressedTexImageARB");
2144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2145 checkGLcall("glBindBufferARB");
2147 else
2149 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2150 surface->texture_level, surface->resource.allocatedMemory));
2151 checkGLcall("glGetCompressedTexImageARB");
2154 LEAVE_GL();
2156 else
2158 void *mem;
2159 GLenum gl_format = format->glFormat;
2160 GLenum gl_type = format->glType;
2161 int src_pitch = 0;
2162 int dst_pitch = 0;
2164 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2165 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2167 gl_format = GL_ALPHA;
2168 gl_type = GL_UNSIGNED_BYTE;
2171 if (surface->flags & SFLAG_NONPOW2)
2173 unsigned char alignment = surface->resource.device->surface_alignment;
2174 src_pitch = format->byte_count * surface->pow2Width;
2175 dst_pitch = wined3d_surface_get_pitch(surface);
2176 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2177 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2179 else
2181 mem = surface->resource.allocatedMemory;
2184 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2185 surface, surface->texture_level, gl_format, gl_type, mem);
2187 if (surface->flags & SFLAG_PBO)
2189 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2190 checkGLcall("glBindBufferARB");
2192 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2193 checkGLcall("glGetTexImage");
2195 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2196 checkGLcall("glBindBufferARB");
2198 else
2200 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2201 checkGLcall("glGetTexImage");
2203 LEAVE_GL();
2205 if (surface->flags & SFLAG_NONPOW2)
2207 const BYTE *src_data;
2208 BYTE *dst_data;
2209 UINT y;
2211 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2212 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2213 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2215 * We're doing this...
2217 * instead of boxing the texture :
2218 * |<-texture width ->| -->pow2width| /\
2219 * |111111111111111111| | |
2220 * |222 Texture 222222| boxed empty | texture height
2221 * |3333 Data 33333333| | |
2222 * |444444444444444444| | \/
2223 * ----------------------------------- |
2224 * | boxed empty | boxed empty | pow2height
2225 * | | | \/
2226 * -----------------------------------
2229 * we're repacking the data to the expected texture width
2231 * |<-texture width ->| -->pow2width| /\
2232 * |111111111111111111222222222222222| |
2233 * |222333333333333333333444444444444| texture height
2234 * |444444 | |
2235 * | | \/
2236 * | | |
2237 * | empty | pow2height
2238 * | | \/
2239 * -----------------------------------
2241 * == is the same as
2243 * |<-texture width ->| /\
2244 * |111111111111111111|
2245 * |222222222222222222|texture height
2246 * |333333333333333333|
2247 * |444444444444444444| \/
2248 * --------------------
2250 * this also means that any references to allocatedMemory should work with the data as if were a
2251 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2253 * internally the texture is still stored in a boxed format so any references to textureName will
2254 * get a boxed texture with width pow2width and not a texture of width resource.width.
2256 * Performance should not be an issue, because applications normally do not lock the surfaces when
2257 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2258 * and doesn't have to be re-read. */
2259 src_data = mem;
2260 dst_data = surface->resource.allocatedMemory;
2261 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2262 for (y = 1; y < surface->resource.height; ++y)
2264 /* skip the first row */
2265 src_data += src_pitch;
2266 dst_data += dst_pitch;
2267 memcpy(dst_data, src_data, dst_pitch);
2270 HeapFree(GetProcessHeap(), 0, mem);
2274 /* Surface has now been downloaded */
2275 surface->flags |= SFLAG_INSYSMEM;
2278 /* This call just uploads data, the caller is responsible for binding the
2279 * correct texture. */
2280 /* Context activation is done by the caller. */
2281 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2282 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2283 BOOL srgb, const struct wined3d_bo_address *data)
2285 UINT update_w = src_rect->right - src_rect->left;
2286 UINT update_h = src_rect->bottom - src_rect->top;
2288 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2289 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2290 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2292 if (surface->resource.map_count)
2294 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2295 surface->flags |= SFLAG_PIN_SYSMEM;
2298 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2300 update_h *= format->height_scale.numerator;
2301 update_h /= format->height_scale.denominator;
2304 ENTER_GL();
2306 if (data->buffer_object)
2308 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2309 checkGLcall("glBindBufferARB");
2312 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2314 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2315 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2316 const BYTE *addr = data->addr;
2317 GLenum internal;
2319 addr += (src_rect->top / format->block_height) * src_pitch;
2320 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2322 if (srgb)
2323 internal = format->glGammaInternal;
2324 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2325 internal = format->rtInternal;
2326 else
2327 internal = format->glInternal;
2329 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2330 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2331 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2333 if (row_length == src_pitch)
2335 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2336 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2338 else
2340 UINT row, y;
2342 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2343 * can't use the unpack row length like below. */
2344 for (row = 0, y = dst_point->y; row < row_count; ++row)
2346 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2347 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2348 y += format->block_height;
2349 addr += src_pitch;
2352 checkGLcall("glCompressedTexSubImage2DARB");
2354 else
2356 const BYTE *addr = data->addr;
2358 addr += src_rect->top * src_pitch;
2359 addr += src_rect->left * format->byte_count;
2361 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2362 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2363 update_w, update_h, format->glFormat, format->glType, addr);
2365 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2366 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2367 update_w, update_h, format->glFormat, format->glType, addr);
2368 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2369 checkGLcall("glTexSubImage2D");
2372 if (data->buffer_object)
2374 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2375 checkGLcall("glBindBufferARB");
2378 LEAVE_GL();
2380 if (wined3d_settings.strict_draw_ordering)
2381 wglFlush();
2383 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2385 struct wined3d_device *device = surface->resource.device;
2386 unsigned int i;
2388 for (i = 0; i < device->context_count; ++i)
2390 context_surface_update(device->contexts[i], surface);
2395 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
2396 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
2398 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2399 const struct wined3d_device *device = surface->resource.device;
2400 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2401 BOOL blit_supported = FALSE;
2403 /* Copy the default values from the surface. Below we might perform fixups */
2404 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2405 *format = *surface->resource.format;
2406 *conversion_type = WINED3D_CT_NONE;
2408 /* Ok, now look if we have to do any conversion */
2409 switch (surface->resource.format->id)
2411 case WINED3DFMT_P8_UINT:
2412 /* Below the call to blit_supported is disabled for Wine 1.2
2413 * because the function isn't operating correctly yet. At the
2414 * moment 8-bit blits are handled in software and if certain GL
2415 * extensions are around, surface conversion is performed at
2416 * upload time. The blit_supported call recognizes it as a
2417 * destination fixup. This type of upload 'fixup' and 8-bit to
2418 * 8-bit blits need to be handled by the blit_shader.
2419 * TODO: get rid of this #if 0. */
2420 #if 0
2421 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2422 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
2423 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
2424 #endif
2425 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
2427 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2428 * texturing. Further also use conversion in case of color keying.
2429 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2430 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2431 * conflicts with this.
2433 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
2434 || colorkey_active || !use_texturing)
2436 format->glFormat = GL_RGBA;
2437 format->glInternal = GL_RGBA;
2438 format->glType = GL_UNSIGNED_BYTE;
2439 format->conv_byte_count = 4;
2440 if (colorkey_active)
2441 *conversion_type = WINED3D_CT_PALETTED_CK;
2442 else
2443 *conversion_type = WINED3D_CT_PALETTED;
2445 break;
2447 case WINED3DFMT_B2G3R3_UNORM:
2448 /* **********************
2449 GL_UNSIGNED_BYTE_3_3_2
2450 ********************** */
2451 if (colorkey_active) {
2452 /* This texture format will never be used.. So do not care about color keying
2453 up until the point in time it will be needed :-) */
2454 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2456 break;
2458 case WINED3DFMT_B5G6R5_UNORM:
2459 if (colorkey_active)
2461 *conversion_type = WINED3D_CT_CK_565;
2462 format->glFormat = GL_RGBA;
2463 format->glInternal = GL_RGB5_A1;
2464 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2465 format->conv_byte_count = 2;
2467 break;
2469 case WINED3DFMT_B5G5R5X1_UNORM:
2470 if (colorkey_active)
2472 *conversion_type = WINED3D_CT_CK_5551;
2473 format->glFormat = GL_BGRA;
2474 format->glInternal = GL_RGB5_A1;
2475 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2476 format->conv_byte_count = 2;
2478 break;
2480 case WINED3DFMT_B8G8R8_UNORM:
2481 if (colorkey_active)
2483 *conversion_type = WINED3D_CT_CK_RGB24;
2484 format->glFormat = GL_RGBA;
2485 format->glInternal = GL_RGBA8;
2486 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2487 format->conv_byte_count = 4;
2489 break;
2491 case WINED3DFMT_B8G8R8X8_UNORM:
2492 if (colorkey_active)
2494 *conversion_type = WINED3D_CT_RGB32_888;
2495 format->glFormat = GL_RGBA;
2496 format->glInternal = GL_RGBA8;
2497 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2498 format->conv_byte_count = 4;
2500 break;
2502 case WINED3DFMT_B8G8R8A8_UNORM:
2503 if (colorkey_active)
2505 *conversion_type = WINED3D_CT_CK_ARGB32;
2506 format->conv_byte_count = 4;
2508 break;
2510 default:
2511 break;
2514 if (*conversion_type != WINED3D_CT_NONE)
2516 format->rtInternal = format->glInternal;
2517 format->glGammaInternal = format->glInternal;
2520 return WINED3D_OK;
2523 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
2525 UINT width_mask, height_mask;
2527 if (!rect->left && !rect->top
2528 && rect->right == surface->resource.width
2529 && rect->bottom == surface->resource.height)
2530 return TRUE;
2532 /* This assumes power of two block sizes, but NPOT block sizes would be
2533 * silly anyway. */
2534 width_mask = surface->resource.format->block_width - 1;
2535 height_mask = surface->resource.format->block_height - 1;
2537 if (!(rect->left & width_mask) && !(rect->top & height_mask)
2538 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
2539 return TRUE;
2541 return FALSE;
2544 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2545 struct wined3d_surface *src_surface, const RECT *src_rect)
2547 const struct wined3d_format *src_format;
2548 const struct wined3d_format *dst_format;
2549 const struct wined3d_gl_info *gl_info;
2550 enum wined3d_conversion_type convert;
2551 struct wined3d_context *context;
2552 struct wined3d_bo_address data;
2553 struct wined3d_format format;
2554 UINT update_w, update_h;
2555 UINT dst_w, dst_h;
2556 RECT r, dst_rect;
2557 UINT src_pitch;
2558 POINT p;
2560 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2561 dst_surface, wine_dbgstr_point(dst_point),
2562 src_surface, wine_dbgstr_rect(src_rect));
2564 src_format = src_surface->resource.format;
2565 dst_format = dst_surface->resource.format;
2567 if (src_format->id != dst_format->id)
2569 WARN("Source and destination surfaces should have the same format.\n");
2570 return WINED3DERR_INVALIDCALL;
2573 if (!dst_point)
2575 p.x = 0;
2576 p.y = 0;
2577 dst_point = &p;
2579 else if (dst_point->x < 0 || dst_point->y < 0)
2581 WARN("Invalid destination point.\n");
2582 return WINED3DERR_INVALIDCALL;
2585 if (!src_rect)
2587 r.left = 0;
2588 r.top = 0;
2589 r.right = src_surface->resource.width;
2590 r.bottom = src_surface->resource.height;
2591 src_rect = &r;
2593 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2594 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2596 WARN("Invalid source rectangle.\n");
2597 return WINED3DERR_INVALIDCALL;
2600 dst_w = dst_surface->resource.width;
2601 dst_h = dst_surface->resource.height;
2603 update_w = src_rect->right - src_rect->left;
2604 update_h = src_rect->bottom - src_rect->top;
2606 if (update_w > dst_w || dst_point->x > dst_w - update_w
2607 || update_h > dst_h || dst_point->y > dst_h - update_h)
2609 WARN("Destination out of bounds.\n");
2610 return WINED3DERR_INVALIDCALL;
2613 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
2615 WARN("Source rectangle not block-aligned.\n");
2616 return WINED3DERR_INVALIDCALL;
2619 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
2620 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
2622 WARN("Destination rectangle not block-aligned.\n");
2623 return WINED3DERR_INVALIDCALL;
2626 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2627 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2628 if (convert != WINED3D_CT_NONE || format.convert)
2629 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2631 context = context_acquire(dst_surface->resource.device, NULL);
2632 gl_info = context->gl_info;
2634 /* Only load the surface for partial updates. For newly allocated texture
2635 * the texture wouldn't be the current location, and we'd upload zeroes
2636 * just to overwrite them again. */
2637 if (update_w == dst_w && update_h == dst_h)
2638 surface_prepare_texture(dst_surface, context, FALSE);
2639 else
2640 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2641 surface_bind(dst_surface, context, FALSE);
2643 data.buffer_object = src_surface->pbo;
2644 data.addr = src_surface->resource.allocatedMemory;
2645 src_pitch = wined3d_surface_get_pitch(src_surface);
2647 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2649 invalidate_active_texture(dst_surface->resource.device, context);
2651 context_release(context);
2653 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2654 return WINED3D_OK;
2657 /* This call just allocates the texture, the caller is responsible for binding
2658 * the correct texture. */
2659 /* Context activation is done by the caller. */
2660 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2661 const struct wined3d_format *format, BOOL srgb)
2663 BOOL enable_client_storage = FALSE;
2664 GLsizei width = surface->pow2Width;
2665 GLsizei height = surface->pow2Height;
2666 const BYTE *mem = NULL;
2667 GLenum internal;
2669 if (srgb)
2671 internal = format->glGammaInternal;
2673 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2675 internal = format->rtInternal;
2677 else
2679 internal = format->glInternal;
2682 if (!internal)
2683 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2685 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2687 height *= format->height_scale.numerator;
2688 height /= format->height_scale.denominator;
2691 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",
2692 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2693 internal, width, height, format->glFormat, format->glType);
2695 ENTER_GL();
2697 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2699 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2700 || !surface->resource.allocatedMemory)
2702 /* In some cases we want to disable client storage.
2703 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2704 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2705 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2706 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2708 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2709 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2710 surface->flags &= ~SFLAG_CLIENT;
2711 enable_client_storage = TRUE;
2713 else
2715 surface->flags |= SFLAG_CLIENT;
2717 /* Point OpenGL to our allocated texture memory. Do not use
2718 * resource.allocatedMemory here because it might point into a
2719 * PBO. Instead use heapMemory, but get the alignment right. */
2720 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2721 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2725 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2727 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2728 internal, width, height, 0, surface->resource.size, mem));
2729 checkGLcall("glCompressedTexImage2DARB");
2731 else
2733 glTexImage2D(surface->texture_target, surface->texture_level,
2734 internal, width, height, 0, format->glFormat, format->glType, mem);
2735 checkGLcall("glTexImage2D");
2738 if(enable_client_storage) {
2739 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2740 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2742 LEAVE_GL();
2745 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2746 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2747 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2748 /* GL locking is done by the caller */
2749 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2751 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2752 struct wined3d_renderbuffer_entry *entry;
2753 GLuint renderbuffer = 0;
2754 unsigned int src_width, src_height;
2755 unsigned int width, height;
2757 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2759 width = rt->pow2Width;
2760 height = rt->pow2Height;
2762 else
2764 width = surface->pow2Width;
2765 height = surface->pow2Height;
2768 src_width = surface->pow2Width;
2769 src_height = surface->pow2Height;
2771 /* A depth stencil smaller than the render target is not valid */
2772 if (width > src_width || height > src_height) return;
2774 /* Remove any renderbuffer set if the sizes match */
2775 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2776 || (width == src_width && height == src_height))
2778 surface->current_renderbuffer = NULL;
2779 return;
2782 /* Look if we've already got a renderbuffer of the correct dimensions */
2783 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2785 if (entry->width == width && entry->height == height)
2787 renderbuffer = entry->id;
2788 surface->current_renderbuffer = entry;
2789 break;
2793 if (!renderbuffer)
2795 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2796 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2797 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2798 surface->resource.format->glInternal, width, height);
2800 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2801 entry->width = width;
2802 entry->height = height;
2803 entry->id = renderbuffer;
2804 list_add_head(&surface->renderbuffers, &entry->entry);
2806 surface->current_renderbuffer = entry;
2809 checkGLcall("set_compatible_renderbuffer");
2812 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2814 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2816 TRACE("surface %p.\n", surface);
2818 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2820 ERR("Surface %p is not on a swapchain.\n", surface);
2821 return GL_NONE;
2824 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2826 if (swapchain->render_to_fbo)
2828 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2829 return GL_COLOR_ATTACHMENT0;
2831 TRACE("Returning GL_BACK\n");
2832 return GL_BACK;
2834 else if (surface == swapchain->front_buffer)
2836 TRACE("Returning GL_FRONT\n");
2837 return GL_FRONT;
2840 FIXME("Higher back buffer, returning GL_BACK\n");
2841 return GL_BACK;
2844 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2845 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2847 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2849 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2850 /* No partial locking for textures yet. */
2851 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2853 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2854 if (dirty_rect)
2856 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2857 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2858 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2859 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2861 else
2863 surface->dirtyRect.left = 0;
2864 surface->dirtyRect.top = 0;
2865 surface->dirtyRect.right = surface->resource.width;
2866 surface->dirtyRect.bottom = surface->resource.height;
2869 /* if the container is a texture then mark it dirty. */
2870 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2872 TRACE("Passing to container.\n");
2873 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2877 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2879 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2880 BOOL ck_changed;
2882 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2884 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2886 ERR("Not supported on scratch surfaces.\n");
2887 return WINED3DERR_INVALIDCALL;
2890 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2892 /* Reload if either the texture and sysmem have different ideas about the
2893 * color key, or the actual key values changed. */
2894 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2895 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2896 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2898 TRACE("Reloading because of color keying\n");
2899 /* To perform the color key conversion we need a sysmem copy of
2900 * the surface. Make sure we have it. */
2902 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2903 /* Make sure the texture is reloaded because of the color key change,
2904 * this kills performance though :( */
2905 /* TODO: This is not necessarily needed with hw palettized texture support. */
2906 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2907 /* Switching color keying on / off may change the internal format. */
2908 if (ck_changed)
2909 surface_force_reload(surface);
2911 else if (!(surface->flags & flag))
2913 TRACE("Reloading because surface is dirty.\n");
2915 else
2917 TRACE("surface is already in texture\n");
2918 return WINED3D_OK;
2921 /* No partial locking for textures yet. */
2922 surface_load_location(surface, flag, NULL);
2923 surface_evict_sysmem(surface);
2925 return WINED3D_OK;
2928 /* See also float_16_to_32() in wined3d_private.h */
2929 static inline unsigned short float_32_to_16(const float *in)
2931 int exp = 0;
2932 float tmp = fabsf(*in);
2933 unsigned int mantissa;
2934 unsigned short ret;
2936 /* Deal with special numbers */
2937 if (*in == 0.0f)
2938 return 0x0000;
2939 if (isnan(*in))
2940 return 0x7c01;
2941 if (isinf(*in))
2942 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2944 if (tmp < powf(2, 10))
2948 tmp = tmp * 2.0f;
2949 exp--;
2950 } while (tmp < powf(2, 10));
2952 else if (tmp >= powf(2, 11))
2956 tmp /= 2.0f;
2957 exp++;
2958 } while (tmp >= powf(2, 11));
2961 mantissa = (unsigned int)tmp;
2962 if (tmp - mantissa >= 0.5f)
2963 ++mantissa; /* Round to nearest, away from zero. */
2965 exp += 10; /* Normalize the mantissa. */
2966 exp += 15; /* Exponent is encoded with excess 15. */
2968 if (exp > 30) /* too big */
2970 ret = 0x7c00; /* INF */
2972 else if (exp <= 0)
2974 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2975 while (exp <= 0)
2977 mantissa = mantissa >> 1;
2978 ++exp;
2980 ret = mantissa & 0x3ff;
2982 else
2984 ret = (exp << 10) | (mantissa & 0x3ff);
2987 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2988 return ret;
2991 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2993 ULONG refcount;
2995 TRACE("Surface %p, container %p of type %#x.\n",
2996 surface, surface->container.u.base, surface->container.type);
2998 switch (surface->container.type)
3000 case WINED3D_CONTAINER_TEXTURE:
3001 return wined3d_texture_incref(surface->container.u.texture);
3003 case WINED3D_CONTAINER_SWAPCHAIN:
3004 return wined3d_swapchain_incref(surface->container.u.swapchain);
3006 default:
3007 ERR("Unhandled container type %#x.\n", surface->container.type);
3008 case WINED3D_CONTAINER_NONE:
3009 break;
3012 refcount = InterlockedIncrement(&surface->resource.ref);
3013 TRACE("%p increasing refcount to %u.\n", surface, refcount);
3015 return refcount;
3018 /* Do not call while under the GL lock. */
3019 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
3021 ULONG refcount;
3023 TRACE("Surface %p, container %p of type %#x.\n",
3024 surface, surface->container.u.base, surface->container.type);
3026 switch (surface->container.type)
3028 case WINED3D_CONTAINER_TEXTURE:
3029 return wined3d_texture_decref(surface->container.u.texture);
3031 case WINED3D_CONTAINER_SWAPCHAIN:
3032 return wined3d_swapchain_decref(surface->container.u.swapchain);
3034 default:
3035 ERR("Unhandled container type %#x.\n", surface->container.type);
3036 case WINED3D_CONTAINER_NONE:
3037 break;
3040 refcount = InterlockedDecrement(&surface->resource.ref);
3041 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
3043 if (!refcount)
3045 surface_cleanup(surface);
3046 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
3048 TRACE("Destroyed surface %p.\n", surface);
3049 HeapFree(GetProcessHeap(), 0, surface);
3052 return refcount;
3055 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
3057 return resource_set_priority(&surface->resource, priority);
3060 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
3062 return resource_get_priority(&surface->resource);
3065 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
3067 TRACE("surface %p.\n", surface);
3069 if (!surface->resource.device->d3d_initialized)
3071 ERR("D3D not initialized.\n");
3072 return;
3075 surface_internal_preload(surface, SRGB_ANY);
3078 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
3080 TRACE("surface %p.\n", surface);
3082 return surface->resource.parent;
3085 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
3087 TRACE("surface %p.\n", surface);
3089 return &surface->resource;
3092 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
3094 TRACE("surface %p, flags %#x.\n", surface, flags);
3096 switch (flags)
3098 case WINEDDGBS_CANBLT:
3099 case WINEDDGBS_ISBLTDONE:
3100 return WINED3D_OK;
3102 default:
3103 return WINED3DERR_INVALIDCALL;
3107 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
3109 TRACE("surface %p, flags %#x.\n", surface, flags);
3111 /* XXX: DDERR_INVALIDSURFACETYPE */
3113 switch (flags)
3115 case WINEDDGFS_CANFLIP:
3116 case WINEDDGFS_ISFLIPDONE:
3117 return WINED3D_OK;
3119 default:
3120 return WINED3DERR_INVALIDCALL;
3124 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
3126 TRACE("surface %p.\n", surface);
3128 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
3129 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3132 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
3134 TRACE("surface %p.\n", surface);
3136 surface->flags &= ~SFLAG_LOST;
3137 return WINED3D_OK;
3140 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3142 TRACE("surface %p, palette %p.\n", surface, palette);
3144 if (surface->palette == palette)
3146 TRACE("Nop palette change.\n");
3147 return WINED3D_OK;
3150 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3151 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3153 surface->palette = palette;
3155 if (palette)
3157 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3158 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3160 surface->surface_ops->surface_realize_palette(surface);
3163 return WINED3D_OK;
3166 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3167 DWORD flags, const struct wined3d_color_key *color_key)
3169 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3171 if (flags & WINEDDCKEY_COLORSPACE)
3173 FIXME(" colorkey value not supported (%08x) !\n", flags);
3174 return WINED3DERR_INVALIDCALL;
3177 /* Dirtify the surface, but only if a key was changed. */
3178 if (color_key)
3180 switch (flags & ~WINEDDCKEY_COLORSPACE)
3182 case WINEDDCKEY_DESTBLT:
3183 surface->dst_blt_color_key = *color_key;
3184 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3185 break;
3187 case WINEDDCKEY_DESTOVERLAY:
3188 surface->dst_overlay_color_key = *color_key;
3189 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3190 break;
3192 case WINEDDCKEY_SRCOVERLAY:
3193 surface->src_overlay_color_key = *color_key;
3194 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3195 break;
3197 case WINEDDCKEY_SRCBLT:
3198 surface->src_blt_color_key = *color_key;
3199 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3200 break;
3203 else
3205 switch (flags & ~WINEDDCKEY_COLORSPACE)
3207 case WINEDDCKEY_DESTBLT:
3208 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3209 break;
3211 case WINEDDCKEY_DESTOVERLAY:
3212 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3213 break;
3215 case WINEDDCKEY_SRCOVERLAY:
3216 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3217 break;
3219 case WINEDDCKEY_SRCBLT:
3220 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3221 break;
3225 return WINED3D_OK;
3228 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3230 TRACE("surface %p.\n", surface);
3232 return surface->palette;
3235 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3237 const struct wined3d_format *format = surface->resource.format;
3238 DWORD pitch;
3240 TRACE("surface %p.\n", surface);
3242 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3244 /* Since compressed formats are block based, pitch means the amount of
3245 * bytes to the next row of block rather than the next row of pixels. */
3246 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3247 pitch = row_block_count * format->block_byte_count;
3249 else
3251 unsigned char alignment = surface->resource.device->surface_alignment;
3252 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3253 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3256 TRACE("Returning %u.\n", pitch);
3258 return pitch;
3261 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3263 TRACE("surface %p, mem %p.\n", surface, mem);
3265 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
3267 WARN("Surface is mapped or the DC is in use.\n");
3268 return WINED3DERR_INVALIDCALL;
3271 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3272 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3274 ERR("Not supported on render targets.\n");
3275 return WINED3DERR_INVALIDCALL;
3278 if (mem && mem != surface->resource.allocatedMemory)
3280 void *release = NULL;
3282 /* Do I have to copy the old surface content? */
3283 if (surface->flags & SFLAG_DIBSECTION)
3285 DeleteDC(surface->hDC);
3286 DeleteObject(surface->dib.DIBsection);
3287 surface->dib.bitmap_data = NULL;
3288 surface->resource.allocatedMemory = NULL;
3289 surface->hDC = NULL;
3290 surface->flags &= ~SFLAG_DIBSECTION;
3292 else if (!(surface->flags & SFLAG_USERPTR))
3294 release = surface->resource.heapMemory;
3295 surface->resource.heapMemory = NULL;
3297 surface->resource.allocatedMemory = mem;
3298 surface->flags |= SFLAG_USERPTR;
3300 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3301 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3303 /* For client textures OpenGL has to be notified. */
3304 if (surface->flags & SFLAG_CLIENT)
3305 surface_release_client_storage(surface);
3307 /* Now free the old memory if any. */
3308 HeapFree(GetProcessHeap(), 0, release);
3310 else if (surface->flags & SFLAG_USERPTR)
3312 /* HeapMemory should be NULL already. */
3313 if (surface->resource.heapMemory)
3314 ERR("User pointer surface has heap memory allocated.\n");
3316 if (!mem)
3318 surface->resource.allocatedMemory = NULL;
3319 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3321 if (surface->flags & SFLAG_CLIENT)
3322 surface_release_client_storage(surface);
3324 surface_prepare_system_memory(surface);
3327 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3330 return WINED3D_OK;
3333 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3335 LONG w, h;
3337 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3339 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3341 WARN("Not an overlay surface.\n");
3342 return WINEDDERR_NOTAOVERLAYSURFACE;
3345 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3346 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3347 surface->overlay_destrect.left = x;
3348 surface->overlay_destrect.top = y;
3349 surface->overlay_destrect.right = x + w;
3350 surface->overlay_destrect.bottom = y + h;
3352 surface_draw_overlay(surface);
3354 return WINED3D_OK;
3357 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3359 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3361 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3363 TRACE("Not an overlay surface.\n");
3364 return WINEDDERR_NOTAOVERLAYSURFACE;
3367 if (!surface->overlay_dest)
3369 TRACE("Overlay not visible.\n");
3370 *x = 0;
3371 *y = 0;
3372 return WINEDDERR_OVERLAYNOTVISIBLE;
3375 *x = surface->overlay_destrect.left;
3376 *y = surface->overlay_destrect.top;
3378 TRACE("Returning position %d, %d.\n", *x, *y);
3380 return WINED3D_OK;
3383 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3384 DWORD flags, struct wined3d_surface *ref)
3386 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3388 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3390 TRACE("Not an overlay surface.\n");
3391 return WINEDDERR_NOTAOVERLAYSURFACE;
3394 return WINED3D_OK;
3397 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3398 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3400 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3401 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3403 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3405 WARN("Not an overlay surface.\n");
3406 return WINEDDERR_NOTAOVERLAYSURFACE;
3408 else if (!dst_surface)
3410 WARN("Dest surface is NULL.\n");
3411 return WINED3DERR_INVALIDCALL;
3414 if (src_rect)
3416 surface->overlay_srcrect = *src_rect;
3418 else
3420 surface->overlay_srcrect.left = 0;
3421 surface->overlay_srcrect.top = 0;
3422 surface->overlay_srcrect.right = surface->resource.width;
3423 surface->overlay_srcrect.bottom = surface->resource.height;
3426 if (dst_rect)
3428 surface->overlay_destrect = *dst_rect;
3430 else
3432 surface->overlay_destrect.left = 0;
3433 surface->overlay_destrect.top = 0;
3434 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3435 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3438 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3440 surface->overlay_dest = NULL;
3441 list_remove(&surface->overlay_entry);
3444 if (flags & WINEDDOVER_SHOW)
3446 if (surface->overlay_dest != dst_surface)
3448 surface->overlay_dest = dst_surface;
3449 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3452 else if (flags & WINEDDOVER_HIDE)
3454 /* tests show that the rectangles are erased on hide */
3455 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3456 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3457 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3458 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3459 surface->overlay_dest = NULL;
3462 surface_draw_overlay(surface);
3464 return WINED3D_OK;
3467 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3468 UINT width, UINT height, enum wined3d_format_id format_id,
3469 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3471 struct wined3d_device *device = surface->resource.device;
3472 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3473 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3474 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3476 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3477 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3479 if (!resource_size)
3480 return WINED3DERR_INVALIDCALL;
3482 if (device->d3d_initialized)
3483 surface->resource.resource_ops->resource_unload(&surface->resource);
3485 if (surface->flags & SFLAG_DIBSECTION)
3487 DeleteDC(surface->hDC);
3488 DeleteObject(surface->dib.DIBsection);
3489 surface->dib.bitmap_data = NULL;
3490 surface->flags &= ~SFLAG_DIBSECTION;
3493 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3494 surface->resource.allocatedMemory = NULL;
3495 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3496 surface->resource.heapMemory = NULL;
3498 surface->resource.width = width;
3499 surface->resource.height = height;
3500 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3501 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3503 surface->pow2Width = width;
3504 surface->pow2Height = height;
3506 else
3508 surface->pow2Width = surface->pow2Height = 1;
3509 while (surface->pow2Width < width)
3510 surface->pow2Width <<= 1;
3511 while (surface->pow2Height < height)
3512 surface->pow2Height <<= 1;
3515 if (surface->pow2Width != width || surface->pow2Height != height)
3516 surface->flags |= SFLAG_NONPOW2;
3517 else
3518 surface->flags &= ~SFLAG_NONPOW2;
3520 surface->resource.format = format;
3521 surface->resource.multisample_type = multisample_type;
3522 surface->resource.multisample_quality = multisample_quality;
3523 surface->resource.size = resource_size;
3525 if (!surface_init_sysmem(surface))
3526 return E_OUTOFMEMORY;
3528 return WINED3D_OK;
3531 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3532 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3534 unsigned short *dst_s;
3535 const float *src_f;
3536 unsigned int x, y;
3538 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3540 for (y = 0; y < h; ++y)
3542 src_f = (const float *)(src + y * pitch_in);
3543 dst_s = (unsigned short *) (dst + y * pitch_out);
3544 for (x = 0; x < w; ++x)
3546 dst_s[x] = float_32_to_16(src_f + x);
3551 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3552 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3554 static const unsigned char convert_5to8[] =
3556 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3557 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3558 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3559 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3561 static const unsigned char convert_6to8[] =
3563 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3564 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3565 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3566 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3567 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3568 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3569 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3570 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3572 unsigned int x, y;
3574 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3576 for (y = 0; y < h; ++y)
3578 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3579 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3580 for (x = 0; x < w; ++x)
3582 WORD pixel = src_line[x];
3583 dst_line[x] = 0xff000000
3584 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3585 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3586 | convert_5to8[(pixel & 0x001f)];
3591 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3592 * in both cases we're just setting the X / Alpha channel to 0xff. */
3593 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3594 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3596 unsigned int x, y;
3598 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3600 for (y = 0; y < h; ++y)
3602 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3603 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3605 for (x = 0; x < w; ++x)
3607 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3612 static inline BYTE cliptobyte(int x)
3614 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3617 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3618 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3620 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3621 unsigned int x, y;
3623 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3625 for (y = 0; y < h; ++y)
3627 const BYTE *src_line = src + y * pitch_in;
3628 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3629 for (x = 0; x < w; ++x)
3631 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3632 * C = Y - 16; D = U - 128; E = V - 128;
3633 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3634 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3635 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3636 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3637 * U and V are shared between the pixels. */
3638 if (!(x & 1)) /* For every even pixel, read new U and V. */
3640 d = (int) src_line[1] - 128;
3641 e = (int) src_line[3] - 128;
3642 r2 = 409 * e + 128;
3643 g2 = - 100 * d - 208 * e + 128;
3644 b2 = 516 * d + 128;
3646 c2 = 298 * ((int) src_line[0] - 16);
3647 dst_line[x] = 0xff000000
3648 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3649 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3650 | cliptobyte((c2 + b2) >> 8); /* blue */
3651 /* Scale RGB values to 0..255 range,
3652 * then clip them if still not in range (may be negative),
3653 * then shift them within DWORD if necessary. */
3654 src_line += 2;
3659 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3660 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3662 unsigned int x, y;
3663 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3665 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3667 for (y = 0; y < h; ++y)
3669 const BYTE *src_line = src + y * pitch_in;
3670 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3671 for (x = 0; x < w; ++x)
3673 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3674 * C = Y - 16; D = U - 128; E = V - 128;
3675 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3676 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3677 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3678 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3679 * U and V are shared between the pixels. */
3680 if (!(x & 1)) /* For every even pixel, read new U and V. */
3682 d = (int) src_line[1] - 128;
3683 e = (int) src_line[3] - 128;
3684 r2 = 409 * e + 128;
3685 g2 = - 100 * d - 208 * e + 128;
3686 b2 = 516 * d + 128;
3688 c2 = 298 * ((int) src_line[0] - 16);
3689 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3690 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3691 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3692 /* Scale RGB values to 0..255 range,
3693 * then clip them if still not in range (may be negative),
3694 * then shift them within DWORD if necessary. */
3695 src_line += 2;
3700 struct d3dfmt_convertor_desc
3702 enum wined3d_format_id from, to;
3703 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3706 static const struct d3dfmt_convertor_desc convertors[] =
3708 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3709 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3710 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3711 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3712 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3713 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3716 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3717 enum wined3d_format_id to)
3719 unsigned int i;
3721 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3723 if (convertors[i].from == from && convertors[i].to == to)
3724 return &convertors[i];
3727 return NULL;
3730 /*****************************************************************************
3731 * surface_convert_format
3733 * Creates a duplicate of a surface in a different format. Is used by Blt to
3734 * blit between surfaces with different formats.
3736 * Parameters
3737 * source: Source surface
3738 * fmt: Requested destination format
3740 *****************************************************************************/
3741 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3743 struct wined3d_map_desc src_map, dst_map;
3744 const struct d3dfmt_convertor_desc *conv;
3745 struct wined3d_surface *ret = NULL;
3746 HRESULT hr;
3748 conv = find_convertor(source->resource.format->id, to_fmt);
3749 if (!conv)
3751 FIXME("Cannot find a conversion function from format %s to %s.\n",
3752 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3753 return NULL;
3756 wined3d_surface_create(source->resource.device, source->resource.width,
3757 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3D_POOL_SCRATCH,
3758 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3759 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3760 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3761 if (!ret)
3763 ERR("Failed to create a destination surface for conversion.\n");
3764 return NULL;
3767 memset(&src_map, 0, sizeof(src_map));
3768 memset(&dst_map, 0, sizeof(dst_map));
3770 if (FAILED(hr = wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3772 ERR("Failed to lock the source surface.\n");
3773 wined3d_surface_decref(ret);
3774 return NULL;
3776 if (FAILED(hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3D_MAP_READONLY)))
3778 ERR("Failed to lock the destination surface.\n");
3779 wined3d_surface_unmap(source);
3780 wined3d_surface_decref(ret);
3781 return NULL;
3784 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3785 source->resource.width, source->resource.height);
3787 wined3d_surface_unmap(ret);
3788 wined3d_surface_unmap(source);
3790 return ret;
3793 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3794 unsigned int bpp, UINT pitch, DWORD color)
3796 BYTE *first;
3797 int x, y;
3799 /* Do first row */
3801 #define COLORFILL_ROW(type) \
3802 do { \
3803 type *d = (type *)buf; \
3804 for (x = 0; x < width; ++x) \
3805 d[x] = (type)color; \
3806 } while(0)
3808 switch (bpp)
3810 case 1:
3811 COLORFILL_ROW(BYTE);
3812 break;
3814 case 2:
3815 COLORFILL_ROW(WORD);
3816 break;
3818 case 3:
3820 BYTE *d = buf;
3821 for (x = 0; x < width; ++x, d += 3)
3823 d[0] = (color ) & 0xFF;
3824 d[1] = (color >> 8) & 0xFF;
3825 d[2] = (color >> 16) & 0xFF;
3827 break;
3829 case 4:
3830 COLORFILL_ROW(DWORD);
3831 break;
3833 default:
3834 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3835 return WINED3DERR_NOTAVAILABLE;
3838 #undef COLORFILL_ROW
3840 /* Now copy first row. */
3841 first = buf;
3842 for (y = 1; y < height; ++y)
3844 buf += pitch;
3845 memcpy(buf, first, width * bpp);
3848 return WINED3D_OK;
3851 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3853 return surface_from_resource(resource);
3856 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3858 TRACE("surface %p.\n", surface);
3860 if (!surface->resource.map_count)
3862 WARN("Trying to unmap unmapped surface.\n");
3863 return WINEDDERR_NOTLOCKED;
3865 --surface->resource.map_count;
3867 surface->surface_ops->surface_unmap(surface);
3869 return WINED3D_OK;
3872 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3873 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3875 const struct wined3d_format *format = surface->resource.format;
3877 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3878 surface, map_desc, wine_dbgstr_rect(rect), flags);
3880 if (surface->resource.map_count)
3882 WARN("Surface is already mapped.\n");
3883 return WINED3DERR_INVALIDCALL;
3886 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3887 && !surface_check_block_align(surface, rect))
3889 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3890 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3892 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3893 return WINED3DERR_INVALIDCALL;
3896 ++surface->resource.map_count;
3898 if (!(surface->flags & SFLAG_LOCKABLE))
3899 WARN("Trying to lock unlockable surface.\n");
3901 /* Performance optimization: Count how often a surface is mapped, if it is
3902 * mapped regularly do not throw away the system memory copy. This avoids
3903 * the need to download the surface from OpenGL all the time. The surface
3904 * is still downloaded if the OpenGL texture is changed. */
3905 if (!(surface->flags & SFLAG_DYNLOCK))
3907 if (++surface->lockCount > MAXLOCKCOUNT)
3909 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3910 surface->flags |= SFLAG_DYNLOCK;
3914 surface->surface_ops->surface_map(surface, rect, flags);
3916 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3917 map_desc->row_pitch = surface->resource.width * format->byte_count;
3918 else
3919 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3920 map_desc->slice_pitch = 0;
3922 if (!rect)
3924 map_desc->data = surface->resource.allocatedMemory;
3925 surface->lockedRect.left = 0;
3926 surface->lockedRect.top = 0;
3927 surface->lockedRect.right = surface->resource.width;
3928 surface->lockedRect.bottom = surface->resource.height;
3930 else
3932 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3934 /* Compressed textures are block based, so calculate the offset of
3935 * the block that contains the top-left pixel of the locked rectangle. */
3936 map_desc->data = surface->resource.allocatedMemory
3937 + ((rect->top / format->block_height) * map_desc->row_pitch)
3938 + ((rect->left / format->block_width) * format->block_byte_count);
3940 else
3942 map_desc->data = surface->resource.allocatedMemory
3943 + (map_desc->row_pitch * rect->top)
3944 + (rect->left * format->byte_count);
3946 surface->lockedRect.left = rect->left;
3947 surface->lockedRect.top = rect->top;
3948 surface->lockedRect.right = rect->right;
3949 surface->lockedRect.bottom = rect->bottom;
3952 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3953 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3955 return WINED3D_OK;
3958 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3960 struct wined3d_map_desc map;
3961 HRESULT hr;
3963 TRACE("surface %p, dc %p.\n", surface, dc);
3965 if (surface->flags & SFLAG_USERPTR)
3967 ERR("Not supported on surfaces with application-provided memory.\n");
3968 return WINEDDERR_NODC;
3971 /* Give more detailed info for ddraw. */
3972 if (surface->flags & SFLAG_DCINUSE)
3973 return WINEDDERR_DCALREADYCREATED;
3975 /* Can't GetDC if the surface is locked. */
3976 if (surface->resource.map_count)
3977 return WINED3DERR_INVALIDCALL;
3979 /* Create a DIB section if there isn't a dc yet. */
3980 if (!surface->hDC)
3982 if (surface->flags & SFLAG_CLIENT)
3984 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3985 surface_release_client_storage(surface);
3987 hr = surface_create_dib_section(surface);
3988 if (FAILED(hr))
3989 return WINED3DERR_INVALIDCALL;
3991 /* Use the DIB section from now on if we are not using a PBO. */
3992 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3994 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3995 surface->resource.heapMemory = NULL;
3996 surface->resource.allocatedMemory = surface->dib.bitmap_data;
4000 /* Map the surface. */
4001 hr = wined3d_surface_map(surface, &map, NULL, 0);
4002 if (FAILED(hr))
4004 ERR("Map failed, hr %#x.\n", hr);
4005 return hr;
4008 /* Sync the DIB with the PBO. This can't be done earlier because Map()
4009 * activates the allocatedMemory. */
4010 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
4011 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
4013 if (surface->resource.format->id == WINED3DFMT_P8_UINT
4014 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
4016 /* GetDC on palettized formats is unsupported in D3D9, and the method
4017 * is missing in D3D8, so this should only be used for DX <=7
4018 * surfaces (with non-device palettes). */
4019 const PALETTEENTRY *pal = NULL;
4021 if (surface->palette)
4023 pal = surface->palette->palents;
4025 else
4027 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
4028 struct wined3d_surface *dds_primary = swapchain->front_buffer;
4030 if (dds_primary && dds_primary->palette)
4031 pal = dds_primary->palette->palents;
4034 if (pal)
4036 RGBQUAD col[256];
4037 unsigned int i;
4039 for (i = 0; i < 256; ++i)
4041 col[i].rgbRed = pal[i].peRed;
4042 col[i].rgbGreen = pal[i].peGreen;
4043 col[i].rgbBlue = pal[i].peBlue;
4044 col[i].rgbReserved = 0;
4046 SetDIBColorTable(surface->hDC, 0, 256, col);
4050 surface->flags |= SFLAG_DCINUSE;
4052 *dc = surface->hDC;
4053 TRACE("Returning dc %p.\n", *dc);
4055 return WINED3D_OK;
4058 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
4060 TRACE("surface %p, dc %p.\n", surface, dc);
4062 if (!(surface->flags & SFLAG_DCINUSE))
4063 return WINEDDERR_NODC;
4065 if (surface->hDC != dc)
4067 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
4068 dc, surface->hDC);
4069 return WINEDDERR_NODC;
4072 /* Copy the contents of the DIB over to the PBO. */
4073 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
4074 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
4076 /* We locked first, so unlock now. */
4077 wined3d_surface_unmap(surface);
4079 surface->flags &= ~SFLAG_DCINUSE;
4081 return WINED3D_OK;
4084 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
4086 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
4088 if (flags)
4090 static UINT once;
4091 if (!once++)
4092 FIXME("Ignoring flags %#x.\n", flags);
4093 else
4094 WARN("Ignoring flags %#x.\n", flags);
4097 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4099 ERR("Not supported on swapchain surfaces.\n");
4100 return WINEDDERR_NOTFLIPPABLE;
4103 /* Flipping is only supported on render targets and overlays. */
4104 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
4106 WARN("Tried to flip a non-render target, non-overlay surface.\n");
4107 return WINEDDERR_NOTFLIPPABLE;
4110 flip_surface(surface, override);
4112 /* Update overlays if they're visible. */
4113 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
4114 return surface_draw_overlay(surface);
4116 return WINED3D_OK;
4119 /* Do not call while under the GL lock. */
4120 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
4122 struct wined3d_device *device = surface->resource.device;
4124 TRACE("iface %p, srgb %#x.\n", surface, srgb);
4126 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4128 struct wined3d_texture *texture = surface->container.u.texture;
4130 TRACE("Passing to container (%p).\n", texture);
4131 texture->texture_ops->texture_preload(texture, srgb);
4133 else
4135 struct wined3d_context *context;
4137 TRACE("(%p) : About to load surface\n", surface);
4139 /* TODO: Use already acquired context when possible. */
4140 context = context_acquire(device, NULL);
4142 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
4144 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
4146 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4147 GLclampf tmp;
4148 tmp = 0.9f;
4149 ENTER_GL();
4150 glPrioritizeTextures(1, &surface->texture_name, &tmp);
4151 LEAVE_GL();
4154 context_release(context);
4158 /* Read the framebuffer back into the surface */
4159 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4161 struct wined3d_device *device = surface->resource.device;
4162 const struct wined3d_gl_info *gl_info;
4163 struct wined3d_context *context;
4164 BYTE *mem;
4165 GLint fmt;
4166 GLint type;
4167 BYTE *row, *top, *bottom;
4168 int i;
4169 BOOL bpp;
4170 RECT local_rect;
4171 BOOL srcIsUpsideDown;
4172 GLint rowLen = 0;
4173 GLint skipPix = 0;
4174 GLint skipRow = 0;
4176 context = context_acquire(device, surface);
4177 context_apply_blit_state(context, device);
4178 gl_info = context->gl_info;
4180 ENTER_GL();
4182 /* Select the correct read buffer, and give some debug output.
4183 * There is no need to keep track of the current read buffer or reset it, every part of the code
4184 * that reads sets the read buffer as desired.
4186 if (surface_is_offscreen(surface))
4188 /* Mapping the primary render target which is not on a swapchain.
4189 * Read from the back buffer. */
4190 TRACE("Mapping offscreen render target.\n");
4191 glReadBuffer(device->offscreenBuffer);
4192 srcIsUpsideDown = TRUE;
4194 else
4196 /* Onscreen surfaces are always part of a swapchain */
4197 GLenum buffer = surface_get_gl_buffer(surface);
4198 TRACE("Mapping %#x buffer.\n", buffer);
4199 glReadBuffer(buffer);
4200 checkGLcall("glReadBuffer");
4201 srcIsUpsideDown = FALSE;
4204 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4205 if (!rect)
4207 local_rect.left = 0;
4208 local_rect.top = 0;
4209 local_rect.right = surface->resource.width;
4210 local_rect.bottom = surface->resource.height;
4212 else
4214 local_rect = *rect;
4216 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4218 switch (surface->resource.format->id)
4220 case WINED3DFMT_P8_UINT:
4222 if (primary_render_target_is_p8(device))
4224 /* In case of P8 render targets the index is stored in the alpha component */
4225 fmt = GL_ALPHA;
4226 type = GL_UNSIGNED_BYTE;
4227 mem = dest;
4228 bpp = surface->resource.format->byte_count;
4230 else
4232 /* GL can't return palettized data, so read ARGB pixels into a
4233 * separate block of memory and convert them into palettized format
4234 * in software. Slow, but if the app means to use palettized render
4235 * targets and locks it...
4237 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4238 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4239 * for the color channels when palettizing the colors.
4241 fmt = GL_RGB;
4242 type = GL_UNSIGNED_BYTE;
4243 pitch *= 3;
4244 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4245 if (!mem)
4247 ERR("Out of memory\n");
4248 LEAVE_GL();
4249 return;
4251 bpp = surface->resource.format->byte_count * 3;
4254 break;
4256 default:
4257 mem = dest;
4258 fmt = surface->resource.format->glFormat;
4259 type = surface->resource.format->glType;
4260 bpp = surface->resource.format->byte_count;
4263 if (surface->flags & SFLAG_PBO)
4265 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4266 checkGLcall("glBindBufferARB");
4267 if (mem)
4269 ERR("mem not null for pbo -- unexpected\n");
4270 mem = NULL;
4274 /* Save old pixel store pack state */
4275 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4276 checkGLcall("glGetIntegerv");
4277 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4278 checkGLcall("glGetIntegerv");
4279 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4280 checkGLcall("glGetIntegerv");
4282 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4283 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4284 checkGLcall("glPixelStorei");
4285 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4286 checkGLcall("glPixelStorei");
4287 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4288 checkGLcall("glPixelStorei");
4290 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4291 local_rect.right - local_rect.left,
4292 local_rect.bottom - local_rect.top,
4293 fmt, type, mem);
4294 checkGLcall("glReadPixels");
4296 /* Reset previous pixel store pack state */
4297 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4298 checkGLcall("glPixelStorei");
4299 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4300 checkGLcall("glPixelStorei");
4301 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4302 checkGLcall("glPixelStorei");
4304 if (surface->flags & SFLAG_PBO)
4306 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4307 checkGLcall("glBindBufferARB");
4309 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4310 * to get a pointer to it and perform the flipping in software. This is a lot
4311 * faster than calling glReadPixels for each line. In case we want more speed
4312 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4313 if (!srcIsUpsideDown)
4315 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4316 checkGLcall("glBindBufferARB");
4318 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4319 checkGLcall("glMapBufferARB");
4323 /* TODO: Merge this with the palettization loop below for P8 targets */
4324 if(!srcIsUpsideDown) {
4325 UINT len, off;
4326 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4327 Flip the lines in software */
4328 len = (local_rect.right - local_rect.left) * bpp;
4329 off = local_rect.left * bpp;
4331 row = HeapAlloc(GetProcessHeap(), 0, len);
4332 if(!row) {
4333 ERR("Out of memory\n");
4334 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4335 HeapFree(GetProcessHeap(), 0, mem);
4336 LEAVE_GL();
4337 return;
4340 top = mem + pitch * local_rect.top;
4341 bottom = mem + pitch * (local_rect.bottom - 1);
4342 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4343 memcpy(row, top + off, len);
4344 memcpy(top + off, bottom + off, len);
4345 memcpy(bottom + off, row, len);
4346 top += pitch;
4347 bottom -= pitch;
4349 HeapFree(GetProcessHeap(), 0, row);
4351 /* Unmap the temp PBO buffer */
4352 if (surface->flags & SFLAG_PBO)
4354 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4355 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4359 LEAVE_GL();
4360 context_release(context);
4362 /* For P8 textures we need to perform an inverse palette lookup. This is
4363 * done by searching for a palette index which matches the RGB value.
4364 * Note this isn't guaranteed to work when there are multiple entries for
4365 * the same color but we have no choice. In case of P8 render targets,
4366 * the index is stored in the alpha component so no conversion is needed. */
4367 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4369 const PALETTEENTRY *pal = NULL;
4370 DWORD width = pitch / 3;
4371 int x, y, c;
4373 if (surface->palette)
4375 pal = surface->palette->palents;
4377 else
4379 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4380 HeapFree(GetProcessHeap(), 0, mem);
4381 return;
4384 for(y = local_rect.top; y < local_rect.bottom; y++) {
4385 for(x = local_rect.left; x < local_rect.right; x++) {
4386 /* start lines pixels */
4387 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4388 const BYTE *green = blue + 1;
4389 const BYTE *red = green + 1;
4391 for(c = 0; c < 256; c++) {
4392 if(*red == pal[c].peRed &&
4393 *green == pal[c].peGreen &&
4394 *blue == pal[c].peBlue)
4396 *((BYTE *) dest + y * width + x) = c;
4397 break;
4402 HeapFree(GetProcessHeap(), 0, mem);
4406 /* Read the framebuffer contents into a texture. Note that this function
4407 * doesn't do any kind of flipping. Using this on an onscreen surface will
4408 * result in a flipped D3D texture. */
4409 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4411 struct wined3d_device *device = surface->resource.device;
4412 struct wined3d_context *context;
4414 context = context_acquire(device, surface);
4415 device_invalidate_state(device, STATE_FRAMEBUFFER);
4417 surface_prepare_texture(surface, context, srgb);
4418 surface_bind_and_dirtify(surface, context, srgb);
4420 TRACE("Reading back offscreen render target %p.\n", surface);
4422 ENTER_GL();
4424 if (surface_is_offscreen(surface))
4425 glReadBuffer(device->offscreenBuffer);
4426 else
4427 glReadBuffer(surface_get_gl_buffer(surface));
4428 checkGLcall("glReadBuffer");
4430 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4431 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4432 checkGLcall("glCopyTexSubImage2D");
4434 LEAVE_GL();
4436 context_release(context);
4439 /* Context activation is done by the caller. */
4440 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4441 struct wined3d_context *context, BOOL srgb)
4443 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4444 enum wined3d_conversion_type convert;
4445 struct wined3d_format format;
4447 if (surface->flags & alloc_flag) return;
4449 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4450 if (convert != WINED3D_CT_NONE || format.convert)
4451 surface->flags |= SFLAG_CONVERTED;
4452 else surface->flags &= ~SFLAG_CONVERTED;
4454 surface_bind_and_dirtify(surface, context, srgb);
4455 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4456 surface->flags |= alloc_flag;
4459 /* Context activation is done by the caller. */
4460 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4462 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4464 struct wined3d_texture *texture = surface->container.u.texture;
4465 UINT sub_count = texture->level_count * texture->layer_count;
4466 UINT i;
4468 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4470 for (i = 0; i < sub_count; ++i)
4472 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4473 surface_prepare_texture_internal(s, context, srgb);
4476 return;
4479 surface_prepare_texture_internal(surface, context, srgb);
4482 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4484 if (multisample)
4486 if (surface->rb_multisample)
4487 return;
4489 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4490 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4491 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4492 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4493 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4495 else
4497 if (surface->rb_resolved)
4498 return;
4500 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4501 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4502 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4503 surface->pow2Width, surface->pow2Height);
4504 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4508 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4509 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4511 struct wined3d_device *device = surface->resource.device;
4512 UINT pitch = wined3d_surface_get_pitch(surface);
4513 const struct wined3d_gl_info *gl_info;
4514 struct wined3d_context *context;
4515 RECT local_rect;
4516 UINT w, h;
4518 surface_get_rect(surface, rect, &local_rect);
4520 mem += local_rect.top * pitch + local_rect.left * bpp;
4521 w = local_rect.right - local_rect.left;
4522 h = local_rect.bottom - local_rect.top;
4524 /* Activate the correct context for the render target */
4525 context = context_acquire(device, surface);
4526 context_apply_blit_state(context, device);
4527 gl_info = context->gl_info;
4529 ENTER_GL();
4531 if (!surface_is_offscreen(surface))
4533 GLenum buffer = surface_get_gl_buffer(surface);
4534 TRACE("Unlocking %#x buffer.\n", buffer);
4535 context_set_draw_buffer(context, buffer);
4537 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4538 glPixelZoom(1.0f, -1.0f);
4540 else
4542 /* Primary offscreen render target */
4543 TRACE("Offscreen render target.\n");
4544 context_set_draw_buffer(context, device->offscreenBuffer);
4546 glPixelZoom(1.0f, 1.0f);
4549 glRasterPos3i(local_rect.left, local_rect.top, 1);
4550 checkGLcall("glRasterPos3i");
4552 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4553 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4555 if (surface->flags & SFLAG_PBO)
4557 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4558 checkGLcall("glBindBufferARB");
4561 glDrawPixels(w, h, fmt, type, mem);
4562 checkGLcall("glDrawPixels");
4564 if (surface->flags & SFLAG_PBO)
4566 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4567 checkGLcall("glBindBufferARB");
4570 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4571 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4573 LEAVE_GL();
4575 if (wined3d_settings.strict_draw_ordering
4576 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4577 && surface->container.u.swapchain->front_buffer == surface))
4578 wglFlush();
4580 context_release(context);
4583 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4585 /* FIXME: Is this really how color keys are supposed to work? I think it
4586 * makes more sense to compare the individual channels. */
4587 return color >= color_key->color_space_low_value
4588 && color <= color_key->color_space_high_value;
4591 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4593 const struct wined3d_device *device = surface->resource.device;
4594 const struct wined3d_palette *pal = surface->palette;
4595 BOOL index_in_alpha = FALSE;
4596 unsigned int i;
4598 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4599 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4600 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4601 * duplicate entries. Store the color key in the unused alpha component to speed the
4602 * download up and to make conversion unneeded. */
4603 index_in_alpha = primary_render_target_is_p8(device);
4605 if (!pal)
4607 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4608 if (index_in_alpha)
4610 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4611 * there's no palette at this time. */
4612 for (i = 0; i < 256; i++) table[i][3] = i;
4615 else
4617 TRACE("Using surface palette %p\n", pal);
4618 /* Get the surface's palette */
4619 for (i = 0; i < 256; ++i)
4621 table[i][0] = pal->palents[i].peRed;
4622 table[i][1] = pal->palents[i].peGreen;
4623 table[i][2] = pal->palents[i].peBlue;
4625 /* When index_in_alpha is set the palette index is stored in the
4626 * alpha component. In case of a readback we can then read
4627 * GL_ALPHA. Color keying is handled in BltOverride using a
4628 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4629 * color key itself is passed to glAlphaFunc in other cases the
4630 * alpha component of pixels that should be masked away is set to 0. */
4631 if (index_in_alpha)
4632 table[i][3] = i;
4633 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4634 table[i][3] = 0x00;
4635 else if (pal->flags & WINEDDPCAPS_ALPHA)
4636 table[i][3] = pal->palents[i].peFlags;
4637 else
4638 table[i][3] = 0xFF;
4643 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4644 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4646 const BYTE *source;
4647 BYTE *dest;
4649 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4650 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4652 switch (conversion_type)
4654 case WINED3D_CT_NONE:
4656 memcpy(dst, src, pitch * height);
4657 break;
4660 case WINED3D_CT_PALETTED:
4661 case WINED3D_CT_PALETTED_CK:
4663 BYTE table[256][4];
4664 unsigned int x, y;
4666 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4668 for (y = 0; y < height; y++)
4670 source = src + pitch * y;
4671 dest = dst + outpitch * y;
4672 /* This is an 1 bpp format, using the width here is fine */
4673 for (x = 0; x < width; x++) {
4674 BYTE color = *source++;
4675 *dest++ = table[color][0];
4676 *dest++ = table[color][1];
4677 *dest++ = table[color][2];
4678 *dest++ = table[color][3];
4682 break;
4684 case WINED3D_CT_CK_565:
4686 /* Converting the 565 format in 5551 packed to emulate color-keying.
4688 Note : in all these conversion, it would be best to average the averaging
4689 pixels to get the color of the pixel that will be color-keyed to
4690 prevent 'color bleeding'. This will be done later on if ever it is
4691 too visible.
4693 Note2: Nvidia documents say that their driver does not support alpha + color keying
4694 on the same surface and disables color keying in such a case
4696 unsigned int x, y;
4697 const WORD *Source;
4698 WORD *Dest;
4700 TRACE("Color keyed 565\n");
4702 for (y = 0; y < height; y++) {
4703 Source = (const WORD *)(src + y * pitch);
4704 Dest = (WORD *) (dst + y * outpitch);
4705 for (x = 0; x < width; x++ ) {
4706 WORD color = *Source++;
4707 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4708 if (!color_in_range(&surface->src_blt_color_key, color))
4709 *Dest |= 0x0001;
4710 Dest++;
4714 break;
4716 case WINED3D_CT_CK_5551:
4718 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4719 unsigned int x, y;
4720 const WORD *Source;
4721 WORD *Dest;
4722 TRACE("Color keyed 5551\n");
4723 for (y = 0; y < height; y++) {
4724 Source = (const WORD *)(src + y * pitch);
4725 Dest = (WORD *) (dst + y * outpitch);
4726 for (x = 0; x < width; x++ ) {
4727 WORD color = *Source++;
4728 *Dest = color;
4729 if (!color_in_range(&surface->src_blt_color_key, color))
4730 *Dest |= (1 << 15);
4731 else
4732 *Dest &= ~(1 << 15);
4733 Dest++;
4737 break;
4739 case WINED3D_CT_CK_RGB24:
4741 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4742 unsigned int x, y;
4743 for (y = 0; y < height; y++)
4745 source = src + pitch * y;
4746 dest = dst + outpitch * y;
4747 for (x = 0; x < width; x++) {
4748 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4749 DWORD dstcolor = color << 8;
4750 if (!color_in_range(&surface->src_blt_color_key, color))
4751 dstcolor |= 0xff;
4752 *(DWORD*)dest = dstcolor;
4753 source += 3;
4754 dest += 4;
4758 break;
4760 case WINED3D_CT_RGB32_888:
4762 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4763 unsigned int x, y;
4764 for (y = 0; y < height; y++)
4766 source = src + pitch * y;
4767 dest = dst + outpitch * y;
4768 for (x = 0; x < width; x++) {
4769 DWORD color = 0xffffff & *(const DWORD*)source;
4770 DWORD dstcolor = color << 8;
4771 if (!color_in_range(&surface->src_blt_color_key, color))
4772 dstcolor |= 0xff;
4773 *(DWORD*)dest = dstcolor;
4774 source += 4;
4775 dest += 4;
4779 break;
4781 case WINED3D_CT_CK_ARGB32:
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)
4790 DWORD color = *(const DWORD *)source;
4791 if (color_in_range(&surface->src_blt_color_key, color))
4792 color &= ~0xff000000;
4793 *(DWORD*)dest = color;
4794 source += 4;
4795 dest += 4;
4799 break;
4801 default:
4802 ERR("Unsupported conversion type %#x.\n", conversion_type);
4804 return WINED3D_OK;
4807 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4809 /* Flip the surface contents */
4810 /* Flip the DC */
4812 HDC tmp;
4813 tmp = front->hDC;
4814 front->hDC = back->hDC;
4815 back->hDC = tmp;
4818 /* Flip the DIBsection */
4820 HBITMAP tmp = front->dib.DIBsection;
4821 front->dib.DIBsection = back->dib.DIBsection;
4822 back->dib.DIBsection = tmp;
4825 /* Flip the surface data */
4827 void* tmp;
4829 tmp = front->dib.bitmap_data;
4830 front->dib.bitmap_data = back->dib.bitmap_data;
4831 back->dib.bitmap_data = tmp;
4833 tmp = front->resource.allocatedMemory;
4834 front->resource.allocatedMemory = back->resource.allocatedMemory;
4835 back->resource.allocatedMemory = tmp;
4837 tmp = front->resource.heapMemory;
4838 front->resource.heapMemory = back->resource.heapMemory;
4839 back->resource.heapMemory = tmp;
4842 /* Flip the PBO */
4844 GLuint tmp_pbo = front->pbo;
4845 front->pbo = back->pbo;
4846 back->pbo = tmp_pbo;
4849 /* Flip the opengl texture */
4851 GLuint tmp;
4853 tmp = back->texture_name;
4854 back->texture_name = front->texture_name;
4855 front->texture_name = tmp;
4857 tmp = back->texture_name_srgb;
4858 back->texture_name_srgb = front->texture_name_srgb;
4859 front->texture_name_srgb = tmp;
4861 tmp = back->rb_multisample;
4862 back->rb_multisample = front->rb_multisample;
4863 front->rb_multisample = tmp;
4865 tmp = back->rb_resolved;
4866 back->rb_resolved = front->rb_resolved;
4867 front->rb_resolved = tmp;
4869 resource_unload(&back->resource);
4870 resource_unload(&front->resource);
4874 DWORD tmp_flags = back->flags;
4875 back->flags = front->flags;
4876 front->flags = tmp_flags;
4880 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4881 * pixel copy calls. */
4882 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4883 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4885 struct wined3d_device *device = dst_surface->resource.device;
4886 float xrel, yrel;
4887 UINT row;
4888 struct wined3d_context *context;
4889 BOOL upsidedown = FALSE;
4890 RECT dst_rect = *dst_rect_in;
4891 GLenum dst_target;
4893 if (dst_surface->container.type == WINED3D_CONTAINER_TEXTURE)
4894 dst_target = dst_surface->container.u.texture->target;
4895 else
4896 dst_target = dst_surface->texture_target;
4898 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4899 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4901 if(dst_rect.top > dst_rect.bottom) {
4902 UINT tmp = dst_rect.bottom;
4903 dst_rect.bottom = dst_rect.top;
4904 dst_rect.top = tmp;
4905 upsidedown = TRUE;
4908 context = context_acquire(device, src_surface);
4909 context_apply_blit_state(context, device);
4910 surface_internal_preload(dst_surface, SRGB_RGB);
4911 ENTER_GL();
4913 /* Bind the target texture */
4914 context_bind_texture(context, dst_target, dst_surface->texture_name);
4915 if (surface_is_offscreen(src_surface))
4917 TRACE("Reading from an offscreen target\n");
4918 upsidedown = !upsidedown;
4919 glReadBuffer(device->offscreenBuffer);
4921 else
4923 glReadBuffer(surface_get_gl_buffer(src_surface));
4925 checkGLcall("glReadBuffer");
4927 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4928 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4930 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4932 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4934 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4935 ERR("Texture filtering not supported in direct blit.\n");
4937 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4938 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4940 ERR("Texture filtering not supported in direct blit\n");
4943 if (upsidedown
4944 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4945 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4947 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4949 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4950 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4951 src_rect->left, src_surface->resource.height - src_rect->bottom,
4952 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4954 else
4956 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4957 /* I have to process this row by row to swap the image,
4958 * otherwise it would be upside down, so stretching in y direction
4959 * doesn't cost extra time
4961 * However, stretching in x direction can be avoided if not necessary
4963 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4964 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4966 /* Well, that stuff works, but it's very slow.
4967 * find a better way instead
4969 UINT col;
4971 for (col = dst_rect.left; col < dst_rect.right; ++col)
4973 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4974 dst_rect.left + col /* x offset */, row /* y offset */,
4975 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4978 else
4980 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4981 dst_rect.left /* x offset */, row /* y offset */,
4982 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4986 checkGLcall("glCopyTexSubImage2D");
4988 LEAVE_GL();
4989 context_release(context);
4991 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4992 * path is never entered
4994 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4997 /* Uses the hardware to stretch and flip the image */
4998 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4999 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
5001 struct wined3d_device *device = dst_surface->resource.device;
5002 struct wined3d_swapchain *src_swapchain = NULL;
5003 GLuint src, backup = 0;
5004 float left, right, top, bottom; /* Texture coordinates */
5005 UINT fbwidth = src_surface->resource.width;
5006 UINT fbheight = src_surface->resource.height;
5007 struct wined3d_context *context;
5008 GLenum drawBuffer = GL_BACK;
5009 GLenum texture_target;
5010 BOOL noBackBufferBackup;
5011 BOOL src_offscreen;
5012 BOOL upsidedown = FALSE;
5013 RECT dst_rect = *dst_rect_in;
5015 TRACE("Using hwstretch blit\n");
5016 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5017 context = context_acquire(device, src_surface);
5018 context_apply_blit_state(context, device);
5019 surface_internal_preload(dst_surface, SRGB_RGB);
5021 src_offscreen = surface_is_offscreen(src_surface);
5022 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5023 if (!noBackBufferBackup && !src_surface->texture_name)
5025 /* Get it a description */
5026 surface_internal_preload(src_surface, SRGB_RGB);
5028 ENTER_GL();
5030 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5031 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5033 if (context->aux_buffers >= 2)
5035 /* Got more than one aux buffer? Use the 2nd aux buffer */
5036 drawBuffer = GL_AUX1;
5038 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5040 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5041 drawBuffer = GL_AUX0;
5044 if(noBackBufferBackup) {
5045 glGenTextures(1, &backup);
5046 checkGLcall("glGenTextures");
5047 context_bind_texture(context, GL_TEXTURE_2D, backup);
5048 texture_target = GL_TEXTURE_2D;
5049 } else {
5050 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5051 * we are reading from the back buffer, the backup can be used as source texture
5053 texture_target = src_surface->texture_target;
5054 context_bind_texture(context, texture_target, src_surface->texture_name);
5055 glEnable(texture_target);
5056 checkGLcall("glEnable(texture_target)");
5058 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5059 src_surface->flags &= ~SFLAG_INTEXTURE;
5062 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5063 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5065 if(dst_rect.top > dst_rect.bottom) {
5066 UINT tmp = dst_rect.bottom;
5067 dst_rect.bottom = dst_rect.top;
5068 dst_rect.top = tmp;
5069 upsidedown = TRUE;
5072 if (src_offscreen)
5074 TRACE("Reading from an offscreen target\n");
5075 upsidedown = !upsidedown;
5076 glReadBuffer(device->offscreenBuffer);
5078 else
5080 glReadBuffer(surface_get_gl_buffer(src_surface));
5083 /* TODO: Only back up the part that will be overwritten */
5084 glCopyTexSubImage2D(texture_target, 0,
5085 0, 0 /* read offsets */,
5086 0, 0,
5087 fbwidth,
5088 fbheight);
5090 checkGLcall("glCopyTexSubImage2D");
5092 /* No issue with overriding these - the sampler is dirty due to blit usage */
5093 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5094 wined3d_gl_mag_filter(magLookup, filter));
5095 checkGLcall("glTexParameteri");
5096 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5097 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5098 checkGLcall("glTexParameteri");
5100 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5101 src_swapchain = src_surface->container.u.swapchain;
5102 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5104 src = backup ? backup : src_surface->texture_name;
5106 else
5108 glReadBuffer(GL_FRONT);
5109 checkGLcall("glReadBuffer(GL_FRONT)");
5111 glGenTextures(1, &src);
5112 checkGLcall("glGenTextures(1, &src)");
5113 context_bind_texture(context, GL_TEXTURE_2D, src);
5115 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5116 * out for power of 2 sizes
5118 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5119 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5120 checkGLcall("glTexImage2D");
5121 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5122 0, 0 /* read offsets */,
5123 0, 0,
5124 fbwidth,
5125 fbheight);
5127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5128 checkGLcall("glTexParameteri");
5129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5130 checkGLcall("glTexParameteri");
5132 glReadBuffer(GL_BACK);
5133 checkGLcall("glReadBuffer(GL_BACK)");
5135 if(texture_target != GL_TEXTURE_2D) {
5136 glDisable(texture_target);
5137 glEnable(GL_TEXTURE_2D);
5138 texture_target = GL_TEXTURE_2D;
5141 checkGLcall("glEnd and previous");
5143 left = src_rect->left;
5144 right = src_rect->right;
5146 if (!upsidedown)
5148 top = src_surface->resource.height - src_rect->top;
5149 bottom = src_surface->resource.height - src_rect->bottom;
5151 else
5153 top = src_surface->resource.height - src_rect->bottom;
5154 bottom = src_surface->resource.height - src_rect->top;
5157 if (src_surface->flags & SFLAG_NORMCOORD)
5159 left /= src_surface->pow2Width;
5160 right /= src_surface->pow2Width;
5161 top /= src_surface->pow2Height;
5162 bottom /= src_surface->pow2Height;
5165 /* draw the source texture stretched and upside down. The correct surface is bound already */
5166 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5167 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5169 context_set_draw_buffer(context, drawBuffer);
5170 glReadBuffer(drawBuffer);
5172 glBegin(GL_QUADS);
5173 /* bottom left */
5174 glTexCoord2f(left, bottom);
5175 glVertex2i(0, 0);
5177 /* top left */
5178 glTexCoord2f(left, top);
5179 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5181 /* top right */
5182 glTexCoord2f(right, top);
5183 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5185 /* bottom right */
5186 glTexCoord2f(right, bottom);
5187 glVertex2i(dst_rect.right - dst_rect.left, 0);
5188 glEnd();
5189 checkGLcall("glEnd and previous");
5191 if (texture_target != dst_surface->texture_target)
5193 glDisable(texture_target);
5194 glEnable(dst_surface->texture_target);
5195 texture_target = dst_surface->texture_target;
5198 /* Now read the stretched and upside down image into the destination texture */
5199 context_bind_texture(context, texture_target, dst_surface->texture_name);
5200 glCopyTexSubImage2D(texture_target,
5202 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5203 0, 0, /* We blitted the image to the origin */
5204 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5205 checkGLcall("glCopyTexSubImage2D");
5207 if(drawBuffer == GL_BACK) {
5208 /* Write the back buffer backup back */
5209 if(backup) {
5210 if(texture_target != GL_TEXTURE_2D) {
5211 glDisable(texture_target);
5212 glEnable(GL_TEXTURE_2D);
5213 texture_target = GL_TEXTURE_2D;
5215 context_bind_texture(context, GL_TEXTURE_2D, backup);
5217 else
5219 if (texture_target != src_surface->texture_target)
5221 glDisable(texture_target);
5222 glEnable(src_surface->texture_target);
5223 texture_target = src_surface->texture_target;
5225 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5228 glBegin(GL_QUADS);
5229 /* top left */
5230 glTexCoord2f(0.0f, 0.0f);
5231 glVertex2i(0, fbheight);
5233 /* bottom left */
5234 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5235 glVertex2i(0, 0);
5237 /* bottom right */
5238 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5239 (float)fbheight / (float)src_surface->pow2Height);
5240 glVertex2i(fbwidth, 0);
5242 /* top right */
5243 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5244 glVertex2i(fbwidth, fbheight);
5245 glEnd();
5247 glDisable(texture_target);
5248 checkGLcall("glDisable(texture_target)");
5250 /* Cleanup */
5251 if (src != src_surface->texture_name && src != backup)
5253 glDeleteTextures(1, &src);
5254 checkGLcall("glDeleteTextures(1, &src)");
5256 if(backup) {
5257 glDeleteTextures(1, &backup);
5258 checkGLcall("glDeleteTextures(1, &backup)");
5261 LEAVE_GL();
5263 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5265 context_release(context);
5267 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5268 * path is never entered
5270 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5273 /* Front buffer coordinates are always full screen coordinates, but our GL
5274 * drawable is limited to the window's client area. The sysmem and texture
5275 * copies do have the full screen size. Note that GL has a bottom-left
5276 * origin, while D3D has a top-left origin. */
5277 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5279 UINT drawable_height;
5281 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5282 && surface == surface->container.u.swapchain->front_buffer)
5284 POINT offset = {0, 0};
5285 RECT windowsize;
5287 ScreenToClient(window, &offset);
5288 OffsetRect(rect, offset.x, offset.y);
5290 GetClientRect(window, &windowsize);
5291 drawable_height = windowsize.bottom - windowsize.top;
5293 else
5295 drawable_height = surface->resource.height;
5298 rect->top = drawable_height - rect->top;
5299 rect->bottom = drawable_height - rect->bottom;
5302 static void surface_blt_to_drawable(const struct wined3d_device *device,
5303 enum wined3d_texture_filter_type filter, BOOL color_key,
5304 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5305 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5307 struct wined3d_context *context;
5308 RECT src_rect, dst_rect;
5310 src_rect = *src_rect_in;
5311 dst_rect = *dst_rect_in;
5313 /* Make sure the surface is up-to-date. This should probably use
5314 * surface_load_location() and worry about the destination surface too,
5315 * unless we're overwriting it completely. */
5316 surface_internal_preload(src_surface, SRGB_RGB);
5318 /* Activate the destination context, set it up for blitting */
5319 context = context_acquire(device, dst_surface);
5320 context_apply_blit_state(context, device);
5322 if (!surface_is_offscreen(dst_surface))
5323 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5325 device->blitter->set_shader(device->blit_priv, context, src_surface);
5327 ENTER_GL();
5329 if (color_key)
5331 glEnable(GL_ALPHA_TEST);
5332 checkGLcall("glEnable(GL_ALPHA_TEST)");
5334 /* When the primary render target uses P8, the alpha component
5335 * contains the palette index. Which means that the colorkey is one of
5336 * the palette entries. In other cases pixels that should be masked
5337 * away have alpha set to 0. */
5338 if (primary_render_target_is_p8(device))
5339 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5340 else
5341 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5342 checkGLcall("glAlphaFunc");
5344 else
5346 glDisable(GL_ALPHA_TEST);
5347 checkGLcall("glDisable(GL_ALPHA_TEST)");
5350 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5352 if (color_key)
5354 glDisable(GL_ALPHA_TEST);
5355 checkGLcall("glDisable(GL_ALPHA_TEST)");
5358 LEAVE_GL();
5360 /* Leave the opengl state valid for blitting */
5361 device->blitter->unset_shader(context->gl_info);
5363 if (wined3d_settings.strict_draw_ordering
5364 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5365 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5366 wglFlush(); /* Flush to ensure ordering across contexts. */
5368 context_release(context);
5371 /* Do not call while under the GL lock. */
5372 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5374 struct wined3d_device *device = s->resource.device;
5375 const struct blit_shader *blitter;
5377 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5378 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5379 if (!blitter)
5381 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5382 return WINED3DERR_INVALIDCALL;
5385 return blitter->color_fill(device, s, rect, color);
5388 /* Do not call while under the GL lock. */
5389 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5390 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5391 enum wined3d_texture_filter_type filter)
5393 struct wined3d_device *device = dst_surface->resource.device;
5394 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5395 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5397 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5398 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5399 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5401 /* Get the swapchain. One of the surfaces has to be a primary surface */
5402 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5404 WARN("Destination is in sysmem, rejecting gl blt\n");
5405 return WINED3DERR_INVALIDCALL;
5408 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5409 dstSwapchain = dst_surface->container.u.swapchain;
5411 if (src_surface)
5413 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5415 WARN("Src is in sysmem, rejecting gl blt\n");
5416 return WINED3DERR_INVALIDCALL;
5419 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5420 srcSwapchain = src_surface->container.u.swapchain;
5423 /* Early sort out of cases where no render target is used */
5424 if (!dstSwapchain && !srcSwapchain
5425 && src_surface != device->fb.render_targets[0]
5426 && dst_surface != device->fb.render_targets[0])
5428 TRACE("No surface is render target, not using hardware blit.\n");
5429 return WINED3DERR_INVALIDCALL;
5432 /* No destination color keying supported */
5433 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5435 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5436 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5437 return WINED3DERR_INVALIDCALL;
5440 if (dstSwapchain && dstSwapchain == srcSwapchain)
5442 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5443 return WINED3DERR_INVALIDCALL;
5446 if (dstSwapchain && srcSwapchain)
5448 FIXME("Implement hardware blit between two different swapchains\n");
5449 return WINED3DERR_INVALIDCALL;
5452 if (dstSwapchain)
5454 /* Handled with regular texture -> swapchain blit */
5455 if (src_surface == device->fb.render_targets[0])
5456 TRACE("Blit from active render target to a swapchain\n");
5458 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5460 FIXME("Implement blit from a swapchain to the active render target\n");
5461 return WINED3DERR_INVALIDCALL;
5464 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5466 /* Blit from render target to texture */
5467 BOOL stretchx;
5469 /* P8 read back is not implemented */
5470 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5471 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5473 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5474 return WINED3DERR_INVALIDCALL;
5477 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5479 TRACE("Color keying not supported by frame buffer to texture blit\n");
5480 return WINED3DERR_INVALIDCALL;
5481 /* Destination color key is checked above */
5484 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5485 stretchx = TRUE;
5486 else
5487 stretchx = FALSE;
5489 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5490 * flip the image nor scale it.
5492 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5493 * -> If the app wants a image width an unscaled width, copy it line per line
5494 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5495 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5496 * back buffer. This is slower than reading line per line, thus not used for flipping
5497 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5498 * pixel by pixel. */
5499 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5500 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5502 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5503 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5505 else
5507 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5508 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5511 if (!dst_surface->resource.map_count && !(dst_surface->flags & SFLAG_DONOTFREE))
5513 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5514 dst_surface->resource.allocatedMemory = NULL;
5515 dst_surface->resource.heapMemory = NULL;
5517 else
5519 dst_surface->flags &= ~SFLAG_INSYSMEM;
5522 return WINED3D_OK;
5524 else if (src_surface)
5526 /* Blit from offscreen surface to render target */
5527 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5528 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5530 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5532 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5533 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5534 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5536 FIXME("Unsupported blit operation falling back to software\n");
5537 return WINED3DERR_INVALIDCALL;
5540 /* Color keying: Check if we have to do a color keyed blt,
5541 * and if not check if a color key is activated.
5543 * Just modify the color keying parameters in the surface and restore them afterwards
5544 * The surface keeps track of the color key last used to load the opengl surface.
5545 * PreLoad will catch the change to the flags and color key and reload if necessary.
5547 if (flags & WINEDDBLT_KEYSRC)
5549 /* Use color key from surface */
5551 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5553 /* Use color key from DDBltFx */
5554 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5555 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5557 else
5559 /* Do not use color key */
5560 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5563 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5564 src_surface, src_rect, dst_surface, dst_rect);
5566 /* Restore the color key parameters */
5567 src_surface->CKeyFlags = oldCKeyFlags;
5568 src_surface->src_blt_color_key = old_blt_key;
5570 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5572 return WINED3D_OK;
5575 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5576 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5577 return WINED3DERR_INVALIDCALL;
5580 /* GL locking is done by the caller */
5581 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5582 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5584 struct wined3d_device *device = surface->resource.device;
5585 const struct wined3d_gl_info *gl_info = context->gl_info;
5586 GLint compare_mode = GL_NONE;
5587 struct blt_info info;
5588 GLint old_binding = 0;
5589 RECT rect;
5591 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5593 glDisable(GL_CULL_FACE);
5594 glDisable(GL_BLEND);
5595 glDisable(GL_ALPHA_TEST);
5596 glDisable(GL_SCISSOR_TEST);
5597 glDisable(GL_STENCIL_TEST);
5598 glEnable(GL_DEPTH_TEST);
5599 glDepthFunc(GL_ALWAYS);
5600 glDepthMask(GL_TRUE);
5601 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5602 glViewport(x, y, w, h);
5603 glDepthRange(0.0, 1.0);
5605 SetRect(&rect, 0, h, w, 0);
5606 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5607 context_active_texture(context, context->gl_info, 0);
5608 glGetIntegerv(info.binding, &old_binding);
5609 glBindTexture(info.bind_target, texture);
5610 if (gl_info->supported[ARB_SHADOW])
5612 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5613 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5616 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5617 gl_info, info.tex_type, &surface->ds_current_size);
5619 glBegin(GL_TRIANGLE_STRIP);
5620 glTexCoord3fv(info.coords[0]);
5621 glVertex2f(-1.0f, -1.0f);
5622 glTexCoord3fv(info.coords[1]);
5623 glVertex2f(1.0f, -1.0f);
5624 glTexCoord3fv(info.coords[2]);
5625 glVertex2f(-1.0f, 1.0f);
5626 glTexCoord3fv(info.coords[3]);
5627 glVertex2f(1.0f, 1.0f);
5628 glEnd();
5630 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5631 glBindTexture(info.bind_target, old_binding);
5633 glPopAttrib();
5635 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5638 void surface_modify_ds_location(struct wined3d_surface *surface,
5639 DWORD location, UINT w, UINT h)
5641 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5643 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5644 FIXME("Invalid location (%#x) specified.\n", location);
5646 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5647 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5649 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5651 TRACE("Passing to container.\n");
5652 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5656 surface->ds_current_size.cx = w;
5657 surface->ds_current_size.cy = h;
5658 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5659 surface->flags |= location;
5662 /* Context activation is done by the caller. */
5663 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5665 struct wined3d_device *device = surface->resource.device;
5666 GLsizei w, h;
5668 TRACE("surface %p, new location %#x.\n", surface, location);
5670 /* TODO: Make this work for modes other than FBO */
5671 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5673 if (!(surface->flags & location))
5675 w = surface->ds_current_size.cx;
5676 h = surface->ds_current_size.cy;
5677 surface->ds_current_size.cx = 0;
5678 surface->ds_current_size.cy = 0;
5680 else
5682 w = surface->resource.width;
5683 h = surface->resource.height;
5686 if (surface->ds_current_size.cx == surface->resource.width
5687 && surface->ds_current_size.cy == surface->resource.height)
5689 TRACE("Location (%#x) is already up to date.\n", location);
5690 return;
5693 if (surface->current_renderbuffer)
5695 FIXME("Not supported with fixed up depth stencil.\n");
5696 return;
5699 if (surface->flags & SFLAG_DISCARDED)
5701 TRACE("Surface was discarded, no need copy data.\n");
5702 switch (location)
5704 case SFLAG_INTEXTURE:
5705 surface_prepare_texture(surface, context, FALSE);
5706 break;
5707 case SFLAG_INRB_MULTISAMPLE:
5708 surface_prepare_rb(surface, context->gl_info, TRUE);
5709 break;
5710 case SFLAG_INDRAWABLE:
5711 /* Nothing to do */
5712 break;
5713 default:
5714 FIXME("Unhandled location %#x\n", location);
5716 surface->flags &= ~SFLAG_DISCARDED;
5717 surface->flags |= location;
5718 surface->ds_current_size.cx = surface->resource.width;
5719 surface->ds_current_size.cy = surface->resource.height;
5720 return;
5723 if (!(surface->flags & SFLAG_LOCATIONS))
5725 FIXME("No up to date depth stencil location.\n");
5726 surface->flags |= location;
5727 surface->ds_current_size.cx = surface->resource.width;
5728 surface->ds_current_size.cy = surface->resource.height;
5729 return;
5732 if (location == SFLAG_INTEXTURE)
5734 GLint old_binding = 0;
5735 GLenum bind_target;
5737 /* The render target is allowed to be smaller than the depth/stencil
5738 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5739 * than the offscreen surface. Don't overwrite the offscreen surface
5740 * with undefined data. */
5741 w = min(w, context->swapchain->desc.backbuffer_width);
5742 h = min(h, context->swapchain->desc.backbuffer_height);
5744 TRACE("Copying onscreen depth buffer to depth texture.\n");
5746 ENTER_GL();
5748 if (!device->depth_blt_texture)
5750 glGenTextures(1, &device->depth_blt_texture);
5753 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5754 * directly on the FBO texture. That's because we need to flip. */
5755 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5756 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5757 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5759 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5760 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5762 else
5764 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5765 bind_target = GL_TEXTURE_2D;
5767 glBindTexture(bind_target, device->depth_blt_texture);
5768 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5769 * internal format, because the internal format might include stencil
5770 * data. In principle we should copy stencil data as well, but unless
5771 * the driver supports stencil export it's hard to do, and doesn't
5772 * seem to be needed in practice. If the hardware doesn't support
5773 * writing stencil data, the glCopyTexImage2D() call might trigger
5774 * software fallbacks. */
5775 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5776 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5777 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5778 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5779 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5780 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5781 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5782 glBindTexture(bind_target, old_binding);
5784 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5785 NULL, surface, SFLAG_INTEXTURE);
5786 context_set_draw_buffer(context, GL_NONE);
5787 glReadBuffer(GL_NONE);
5789 /* Do the actual blit */
5790 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5791 checkGLcall("depth_blt");
5793 context_invalidate_state(context, STATE_FRAMEBUFFER);
5795 LEAVE_GL();
5797 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5799 else if (location == SFLAG_INDRAWABLE)
5801 TRACE("Copying depth texture to onscreen depth buffer.\n");
5803 ENTER_GL();
5805 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5806 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5807 surface_depth_blt(surface, context, surface->texture_name,
5808 0, surface->pow2Height - h, w, h, surface->texture_target);
5809 checkGLcall("depth_blt");
5811 context_invalidate_state(context, STATE_FRAMEBUFFER);
5813 LEAVE_GL();
5815 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5817 else
5819 ERR("Invalid location (%#x) specified.\n", location);
5822 surface->flags |= location;
5823 surface->ds_current_size.cx = surface->resource.width;
5824 surface->ds_current_size.cy = surface->resource.height;
5827 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5829 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5830 struct wined3d_surface *overlay;
5832 TRACE("surface %p, location %s, persistent %#x.\n",
5833 surface, debug_surflocation(location), persistent);
5835 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5836 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5837 && (location & SFLAG_INDRAWABLE))
5838 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5840 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5841 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5842 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5844 if (persistent)
5846 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5847 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & 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 &= ~SFLAG_LOCATIONS;
5856 surface->flags |= location;
5858 /* Redraw emulated overlays, if any */
5859 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5861 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5863 surface_draw_overlay(overlay);
5867 else
5869 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5871 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5873 TRACE("Passing to container\n");
5874 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5877 surface->flags &= ~location;
5880 if (!(surface->flags & SFLAG_LOCATIONS))
5882 ERR("Surface %p does not have any up to date location.\n", surface);
5886 static DWORD resource_access_from_location(DWORD location)
5888 switch (location)
5890 case SFLAG_INSYSMEM:
5891 return WINED3D_RESOURCE_ACCESS_CPU;
5893 case SFLAG_INDRAWABLE:
5894 case SFLAG_INSRGBTEX:
5895 case SFLAG_INTEXTURE:
5896 case SFLAG_INRB_MULTISAMPLE:
5897 case SFLAG_INRB_RESOLVED:
5898 return WINED3D_RESOURCE_ACCESS_GPU;
5900 default:
5901 FIXME("Unhandled location %#x.\n", location);
5902 return 0;
5906 static void surface_load_sysmem(struct wined3d_surface *surface,
5907 const struct wined3d_gl_info *gl_info, const RECT *rect)
5909 surface_prepare_system_memory(surface);
5911 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5912 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5914 /* Download the surface to system memory. */
5915 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5917 struct wined3d_device *device = surface->resource.device;
5918 struct wined3d_context *context;
5920 /* TODO: Use already acquired context when possible. */
5921 context = context_acquire(device, NULL);
5923 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5924 surface_download_data(surface, gl_info);
5926 context_release(context);
5928 return;
5931 if (surface->flags & SFLAG_INDRAWABLE)
5933 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5934 wined3d_surface_get_pitch(surface));
5935 return;
5938 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5939 surface, surface->flags & SFLAG_LOCATIONS);
5942 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5943 const struct wined3d_gl_info *gl_info, const RECT *rect)
5945 struct wined3d_device *device = surface->resource.device;
5946 enum wined3d_conversion_type convert;
5947 struct wined3d_format format;
5948 UINT byte_count;
5949 BYTE *mem;
5951 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5953 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5954 return WINED3DERR_INVALIDCALL;
5957 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5958 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5960 if (surface->flags & SFLAG_INTEXTURE)
5962 RECT r;
5964 surface_get_rect(surface, rect, &r);
5965 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5967 return WINED3D_OK;
5970 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5972 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5973 * path through sysmem. */
5974 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5977 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5979 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5980 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5981 * called. */
5982 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5984 struct wined3d_context *context;
5986 TRACE("Removing the pbo attached to surface %p.\n", surface);
5988 /* TODO: Use already acquired context when possible. */
5989 context = context_acquire(device, NULL);
5991 surface_remove_pbo(surface, gl_info);
5993 context_release(context);
5996 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5998 UINT height = surface->resource.height;
5999 UINT width = surface->resource.width;
6000 UINT src_pitch, dst_pitch;
6002 byte_count = format.conv_byte_count;
6003 src_pitch = wined3d_surface_get_pitch(surface);
6005 /* Stick to the alignment for the converted surface too, makes it
6006 * easier to load the surface. */
6007 dst_pitch = width * byte_count;
6008 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6010 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6012 ERR("Out of memory (%u).\n", dst_pitch * height);
6013 return E_OUTOFMEMORY;
6016 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6017 src_pitch, width, height, dst_pitch, convert, surface);
6019 surface->flags |= SFLAG_CONVERTED;
6021 else
6023 surface->flags &= ~SFLAG_CONVERTED;
6024 mem = surface->resource.allocatedMemory;
6025 byte_count = format.byte_count;
6028 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6030 /* Don't delete PBO memory. */
6031 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6032 HeapFree(GetProcessHeap(), 0, mem);
6034 return WINED3D_OK;
6037 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6038 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6040 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6041 struct wined3d_device *device = surface->resource.device;
6042 enum wined3d_conversion_type convert;
6043 struct wined3d_context *context;
6044 UINT width, src_pitch, dst_pitch;
6045 struct wined3d_bo_address data;
6046 struct wined3d_format format;
6047 POINT dst_point = {0, 0};
6048 BYTE *mem;
6050 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6051 && surface_is_offscreen(surface)
6052 && (surface->flags & SFLAG_INDRAWABLE))
6054 surface_load_fb_texture(surface, srgb);
6056 return WINED3D_OK;
6059 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6060 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6061 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6062 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6063 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6065 if (srgb)
6066 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6067 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6068 else
6069 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6070 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6072 return WINED3D_OK;
6075 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6076 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6077 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6078 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6079 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6081 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6082 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6083 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6085 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6086 &rect, surface, dst_location, &rect);
6088 return WINED3D_OK;
6091 /* Upload from system memory */
6093 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6094 TRUE /* We will use textures */, &format, &convert);
6096 if (srgb)
6098 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6100 /* Performance warning... */
6101 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6102 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6105 else
6107 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6109 /* Performance warning... */
6110 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6111 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6115 if (!(surface->flags & SFLAG_INSYSMEM))
6117 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6118 /* Lets hope we get it from somewhere... */
6119 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6122 /* TODO: Use already acquired context when possible. */
6123 context = context_acquire(device, NULL);
6125 surface_prepare_texture(surface, context, srgb);
6126 surface_bind_and_dirtify(surface, context, srgb);
6128 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6130 surface->flags |= SFLAG_GLCKEY;
6131 surface->gl_color_key = surface->src_blt_color_key;
6133 else surface->flags &= ~SFLAG_GLCKEY;
6135 width = surface->resource.width;
6136 src_pitch = wined3d_surface_get_pitch(surface);
6138 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6139 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6140 * called. */
6141 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6143 TRACE("Removing the pbo attached to surface %p.\n", surface);
6144 surface_remove_pbo(surface, gl_info);
6147 if (format.convert)
6149 /* This code is entered for texture formats which need a fixup. */
6150 UINT height = surface->resource.height;
6152 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6153 dst_pitch = width * format.conv_byte_count;
6154 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6156 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6158 ERR("Out of memory (%u).\n", dst_pitch * height);
6159 context_release(context);
6160 return E_OUTOFMEMORY;
6162 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6163 format.byte_count = format.conv_byte_count;
6164 src_pitch = dst_pitch;
6166 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6168 /* This code is only entered for color keying fixups */
6169 UINT height = surface->resource.height;
6171 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6172 dst_pitch = width * format.conv_byte_count;
6173 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6175 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6177 ERR("Out of memory (%u).\n", dst_pitch * height);
6178 context_release(context);
6179 return E_OUTOFMEMORY;
6181 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6182 width, height, dst_pitch, convert, surface);
6183 format.byte_count = format.conv_byte_count;
6184 src_pitch = dst_pitch;
6186 else
6188 mem = surface->resource.allocatedMemory;
6191 data.buffer_object = surface->pbo;
6192 data.addr = mem;
6193 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6195 context_release(context);
6197 /* Don't delete PBO memory. */
6198 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6199 HeapFree(GetProcessHeap(), 0, mem);
6201 return WINED3D_OK;
6204 static void surface_multisample_resolve(struct wined3d_surface *surface)
6206 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6208 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6209 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6211 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6212 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6215 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6217 struct wined3d_device *device = surface->resource.device;
6218 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6219 HRESULT hr;
6221 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6223 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6225 if (location == SFLAG_INTEXTURE)
6227 struct wined3d_context *context = context_acquire(device, NULL);
6228 surface_load_ds_location(surface, context, location);
6229 context_release(context);
6230 return WINED3D_OK;
6232 else
6234 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6235 return WINED3DERR_INVALIDCALL;
6239 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6240 location = SFLAG_INTEXTURE;
6242 if (surface->flags & location)
6244 TRACE("Location already up to date.\n");
6246 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6247 && surface_need_pbo(surface, gl_info))
6248 surface_load_pbo(surface, gl_info);
6250 return WINED3D_OK;
6253 if (WARN_ON(d3d_surface))
6255 DWORD required_access = resource_access_from_location(location);
6256 if ((surface->resource.access_flags & required_access) != required_access)
6257 WARN("Operation requires %#x access, but surface only has %#x.\n",
6258 required_access, surface->resource.access_flags);
6261 if (!(surface->flags & SFLAG_LOCATIONS))
6263 ERR("Surface %p does not have any up to date location.\n", surface);
6264 surface->flags |= SFLAG_LOST;
6265 return WINED3DERR_DEVICELOST;
6268 switch (location)
6270 case SFLAG_INSYSMEM:
6271 surface_load_sysmem(surface, gl_info, rect);
6272 break;
6274 case SFLAG_INDRAWABLE:
6275 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6276 return hr;
6277 break;
6279 case SFLAG_INRB_RESOLVED:
6280 surface_multisample_resolve(surface);
6281 break;
6283 case SFLAG_INTEXTURE:
6284 case SFLAG_INSRGBTEX:
6285 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6286 return hr;
6287 break;
6289 default:
6290 ERR("Don't know how to handle location %#x.\n", location);
6291 break;
6294 if (!rect)
6296 surface->flags |= location;
6298 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6299 surface_evict_sysmem(surface);
6302 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6303 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6305 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6308 return WINED3D_OK;
6311 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6313 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6315 /* Not on a swapchain - must be offscreen */
6316 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6318 /* The front buffer is always onscreen */
6319 if (surface == swapchain->front_buffer) return FALSE;
6321 /* If the swapchain is rendered to an FBO, the backbuffer is
6322 * offscreen, otherwise onscreen */
6323 return swapchain->render_to_fbo;
6326 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6327 /* Context activation is done by the caller. */
6328 static void ffp_blit_free(struct wined3d_device *device) { }
6330 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6331 /* Context activation is done by the caller. */
6332 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6334 BYTE table[256][4];
6335 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6336 GLenum target;
6338 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
6339 target = surface->container.u.texture->target;
6340 else
6341 target = surface->texture_target;
6343 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6345 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6346 ENTER_GL();
6347 GL_EXTCALL(glColorTableEXT(target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6348 LEAVE_GL();
6351 /* Context activation is done by the caller. */
6352 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6354 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6355 GLenum target;
6357 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
6358 target = surface->container.u.texture->target;
6359 else
6360 target = surface->texture_target;
6362 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6363 * else the surface is converted in software at upload time in LoadLocation.
6365 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6366 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6367 ffp_blit_p8_upload_palette(surface, context->gl_info);
6369 ENTER_GL();
6370 glEnable(target);
6371 checkGLcall("glEnable(target)");
6372 LEAVE_GL();
6373 return WINED3D_OK;
6376 /* Context activation is done by the caller. */
6377 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6379 ENTER_GL();
6380 glDisable(GL_TEXTURE_2D);
6381 checkGLcall("glDisable(GL_TEXTURE_2D)");
6382 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6384 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6385 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6387 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6389 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6390 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6392 LEAVE_GL();
6395 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6396 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6397 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6399 enum complex_fixup src_fixup;
6401 switch (blit_op)
6403 case WINED3D_BLIT_OP_COLOR_BLIT:
6404 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6405 return FALSE;
6407 src_fixup = get_complex_fixup(src_format->color_fixup);
6408 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6410 TRACE("Checking support for fixup:\n");
6411 dump_color_fixup_desc(src_format->color_fixup);
6414 if (!is_identity_fixup(dst_format->color_fixup))
6416 TRACE("Destination fixups are not supported\n");
6417 return FALSE;
6420 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6422 TRACE("P8 fixup supported\n");
6423 return TRUE;
6426 /* We only support identity conversions. */
6427 if (is_identity_fixup(src_format->color_fixup))
6429 TRACE("[OK]\n");
6430 return TRUE;
6433 TRACE("[FAILED]\n");
6434 return FALSE;
6436 case WINED3D_BLIT_OP_COLOR_FILL:
6437 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6438 return FALSE;
6440 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6442 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6443 return FALSE;
6445 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6447 TRACE("Color fill not supported\n");
6448 return FALSE;
6451 /* FIXME: We should reject color fills on formats with fixups,
6452 * but this would break P8 color fills for example. */
6454 return TRUE;
6456 case WINED3D_BLIT_OP_DEPTH_FILL:
6457 return TRUE;
6459 default:
6460 TRACE("Unsupported blit_op=%d\n", blit_op);
6461 return FALSE;
6465 /* Do not call while under the GL lock. */
6466 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6467 const RECT *dst_rect, const struct wined3d_color *color)
6469 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6470 struct wined3d_fb_state fb = {&dst_surface, NULL};
6472 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6474 return WINED3D_OK;
6477 /* Do not call while under the GL lock. */
6478 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6479 struct wined3d_surface *surface, const RECT *rect, float depth)
6481 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6482 struct wined3d_fb_state fb = {NULL, surface};
6484 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6486 return WINED3D_OK;
6489 const struct blit_shader ffp_blit = {
6490 ffp_blit_alloc,
6491 ffp_blit_free,
6492 ffp_blit_set,
6493 ffp_blit_unset,
6494 ffp_blit_supported,
6495 ffp_blit_color_fill,
6496 ffp_blit_depth_fill,
6499 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6501 return WINED3D_OK;
6504 /* Context activation is done by the caller. */
6505 static void cpu_blit_free(struct wined3d_device *device)
6509 /* Context activation is done by the caller. */
6510 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6512 return WINED3D_OK;
6515 /* Context activation is done by the caller. */
6516 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6520 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6521 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6522 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6524 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6526 return TRUE;
6529 return FALSE;
6532 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6533 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6534 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6536 UINT row_block_count;
6537 const BYTE *src_row;
6538 BYTE *dst_row;
6539 UINT x, y;
6541 src_row = src_data;
6542 dst_row = dst_data;
6544 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6546 if (!flags)
6548 for (y = 0; y < update_h; y += format->block_height)
6550 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6551 src_row += src_pitch;
6552 dst_row += dst_pitch;
6555 return WINED3D_OK;
6558 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6560 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6562 switch (format->id)
6564 case WINED3DFMT_DXT1:
6565 for (y = 0; y < update_h; y += format->block_height)
6567 struct block
6569 WORD color[2];
6570 BYTE control_row[4];
6573 const struct block *s = (const struct block *)src_row;
6574 struct block *d = (struct block *)dst_row;
6576 for (x = 0; x < row_block_count; ++x)
6578 d[x].color[0] = s[x].color[0];
6579 d[x].color[1] = s[x].color[1];
6580 d[x].control_row[0] = s[x].control_row[3];
6581 d[x].control_row[1] = s[x].control_row[2];
6582 d[x].control_row[2] = s[x].control_row[1];
6583 d[x].control_row[3] = s[x].control_row[0];
6585 src_row -= src_pitch;
6586 dst_row += dst_pitch;
6588 return WINED3D_OK;
6590 case WINED3DFMT_DXT3:
6591 for (y = 0; y < update_h; y += format->block_height)
6593 struct block
6595 WORD alpha_row[4];
6596 WORD color[2];
6597 BYTE control_row[4];
6600 const struct block *s = (const struct block *)src_row;
6601 struct block *d = (struct block *)dst_row;
6603 for (x = 0; x < row_block_count; ++x)
6605 d[x].alpha_row[0] = s[x].alpha_row[3];
6606 d[x].alpha_row[1] = s[x].alpha_row[2];
6607 d[x].alpha_row[2] = s[x].alpha_row[1];
6608 d[x].alpha_row[3] = s[x].alpha_row[0];
6609 d[x].color[0] = s[x].color[0];
6610 d[x].color[1] = s[x].color[1];
6611 d[x].control_row[0] = s[x].control_row[3];
6612 d[x].control_row[1] = s[x].control_row[2];
6613 d[x].control_row[2] = s[x].control_row[1];
6614 d[x].control_row[3] = s[x].control_row[0];
6616 src_row -= src_pitch;
6617 dst_row += dst_pitch;
6619 return WINED3D_OK;
6621 default:
6622 FIXME("Compressed flip not implemented for format %s.\n",
6623 debug_d3dformat(format->id));
6624 return E_NOTIMPL;
6628 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6629 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6631 return E_NOTIMPL;
6634 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6635 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6636 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6638 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6639 const struct wined3d_format *src_format, *dst_format;
6640 struct wined3d_surface *orig_src = src_surface;
6641 struct wined3d_map_desc dst_map, src_map;
6642 const BYTE *sbase = NULL;
6643 HRESULT hr = WINED3D_OK;
6644 const BYTE *sbuf;
6645 BYTE *dbuf;
6646 int x, y;
6648 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6649 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6650 flags, fx, debug_d3dtexturefiltertype(filter));
6652 if (src_surface == dst_surface)
6654 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6655 src_map = dst_map;
6656 src_format = dst_surface->resource.format;
6657 dst_format = src_format;
6659 else
6661 dst_format = dst_surface->resource.format;
6662 if (src_surface)
6664 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6666 src_surface = surface_convert_format(src_surface, dst_format->id);
6667 if (!src_surface)
6669 /* The conv function writes a FIXME */
6670 WARN("Cannot convert source surface format to dest format.\n");
6671 goto release;
6674 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
6675 src_format = src_surface->resource.format;
6677 else
6679 src_format = dst_format;
6682 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
6685 bpp = dst_surface->resource.format->byte_count;
6686 srcheight = src_rect->bottom - src_rect->top;
6687 srcwidth = src_rect->right - src_rect->left;
6688 dstheight = dst_rect->bottom - dst_rect->top;
6689 dstwidth = dst_rect->right - dst_rect->left;
6690 width = (dst_rect->right - dst_rect->left) * bpp;
6692 if (src_surface)
6693 sbase = (BYTE *)src_map.data
6694 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6695 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6696 if (src_surface != dst_surface)
6697 dbuf = dst_map.data;
6698 else
6699 dbuf = (BYTE *)dst_map.data
6700 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6701 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6703 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6705 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6707 if (src_surface == dst_surface)
6709 FIXME("Only plain blits supported on compressed surfaces.\n");
6710 hr = E_NOTIMPL;
6711 goto release;
6714 if (srcheight != dstheight || srcwidth != dstwidth)
6716 WARN("Stretching not supported on compressed surfaces.\n");
6717 hr = WINED3DERR_INVALIDCALL;
6718 goto release;
6721 if (!surface_check_block_align(src_surface, src_rect))
6723 WARN("Source rectangle not block-aligned.\n");
6724 hr = WINED3DERR_INVALIDCALL;
6725 goto release;
6728 if (!surface_check_block_align(dst_surface, dst_rect))
6730 WARN("Destination rectangle not block-aligned.\n");
6731 hr = WINED3DERR_INVALIDCALL;
6732 goto release;
6735 hr = surface_cpu_blt_compressed(sbase, dbuf,
6736 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6737 src_format, flags, fx);
6738 goto release;
6741 /* First, all the 'source-less' blits */
6742 if (flags & WINEDDBLT_COLORFILL)
6744 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6745 flags &= ~WINEDDBLT_COLORFILL;
6748 if (flags & WINEDDBLT_DEPTHFILL)
6750 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6752 if (flags & WINEDDBLT_ROP)
6754 /* Catch some degenerate cases here. */
6755 switch (fx->dwROP)
6757 case BLACKNESS:
6758 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6759 break;
6760 case 0xAA0029: /* No-op */
6761 break;
6762 case WHITENESS:
6763 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6764 break;
6765 case SRCCOPY: /* Well, we do that below? */
6766 break;
6767 default:
6768 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6769 goto error;
6771 flags &= ~WINEDDBLT_ROP;
6773 if (flags & WINEDDBLT_DDROPS)
6775 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6777 /* Now the 'with source' blits. */
6778 if (src_surface)
6780 int sx, xinc, sy, yinc;
6782 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6783 goto release;
6785 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6786 && (srcwidth != dstwidth || srcheight != dstheight))
6788 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6789 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6792 xinc = (srcwidth << 16) / dstwidth;
6793 yinc = (srcheight << 16) / dstheight;
6795 if (!flags)
6797 /* No effects, we can cheat here. */
6798 if (dstwidth == srcwidth)
6800 if (dstheight == srcheight)
6802 /* No stretching in either direction. This needs to be as
6803 * fast as possible. */
6804 sbuf = sbase;
6806 /* Check for overlapping surfaces. */
6807 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6808 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6810 /* No overlap, or dst above src, so copy from top downwards. */
6811 for (y = 0; y < dstheight; ++y)
6813 memcpy(dbuf, sbuf, width);
6814 sbuf += src_map.row_pitch;
6815 dbuf += dst_map.row_pitch;
6818 else if (dst_rect->top > src_rect->top)
6820 /* Copy from bottom upwards. */
6821 sbuf += src_map.row_pitch * dstheight;
6822 dbuf += dst_map.row_pitch * dstheight;
6823 for (y = 0; y < dstheight; ++y)
6825 sbuf -= src_map.row_pitch;
6826 dbuf -= dst_map.row_pitch;
6827 memcpy(dbuf, sbuf, width);
6830 else
6832 /* Src and dst overlapping on the same line, use memmove. */
6833 for (y = 0; y < dstheight; ++y)
6835 memmove(dbuf, sbuf, width);
6836 sbuf += src_map.row_pitch;
6837 dbuf += dst_map.row_pitch;
6841 else
6843 /* Stretching in y direction only. */
6844 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6846 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6847 memcpy(dbuf, sbuf, width);
6848 dbuf += dst_map.row_pitch;
6852 else
6854 /* Stretching in X direction. */
6855 int last_sy = -1;
6856 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6858 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6860 if ((sy >> 16) == (last_sy >> 16))
6862 /* This source row is the same as last source row -
6863 * Copy the already stretched row. */
6864 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6866 else
6868 #define STRETCH_ROW(type) \
6869 do { \
6870 const type *s = (const type *)sbuf; \
6871 type *d = (type *)dbuf; \
6872 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6873 d[x] = s[sx >> 16]; \
6874 } while(0)
6876 switch(bpp)
6878 case 1:
6879 STRETCH_ROW(BYTE);
6880 break;
6881 case 2:
6882 STRETCH_ROW(WORD);
6883 break;
6884 case 4:
6885 STRETCH_ROW(DWORD);
6886 break;
6887 case 3:
6889 const BYTE *s;
6890 BYTE *d = dbuf;
6891 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6893 DWORD pixel;
6895 s = sbuf + 3 * (sx >> 16);
6896 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6897 d[0] = (pixel ) & 0xff;
6898 d[1] = (pixel >> 8) & 0xff;
6899 d[2] = (pixel >> 16) & 0xff;
6900 d += 3;
6902 break;
6904 default:
6905 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6906 hr = WINED3DERR_NOTAVAILABLE;
6907 goto error;
6909 #undef STRETCH_ROW
6911 dbuf += dst_map.row_pitch;
6912 last_sy = sy;
6916 else
6918 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6919 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6920 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6921 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6923 /* The color keying flags are checked for correctness in ddraw */
6924 if (flags & WINEDDBLT_KEYSRC)
6926 keylow = src_surface->src_blt_color_key.color_space_low_value;
6927 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6929 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6931 keylow = fx->ddckSrcColorkey.color_space_low_value;
6932 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6935 if (flags & WINEDDBLT_KEYDEST)
6937 /* Destination color keys are taken from the source surface! */
6938 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6939 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6941 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6943 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6944 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6947 if (bpp == 1)
6949 keymask = 0xff;
6951 else
6953 keymask = src_format->red_mask
6954 | src_format->green_mask
6955 | src_format->blue_mask;
6957 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6960 if (flags & WINEDDBLT_DDFX)
6962 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6963 LONG tmpxy;
6964 dTopLeft = dbuf;
6965 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6966 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6967 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6969 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6971 /* I don't think we need to do anything about this flag */
6972 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6974 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6976 tmp = dTopRight;
6977 dTopRight = dTopLeft;
6978 dTopLeft = tmp;
6979 tmp = dBottomRight;
6980 dBottomRight = dBottomLeft;
6981 dBottomLeft = tmp;
6982 dstxinc = dstxinc * -1;
6984 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6986 tmp = dTopLeft;
6987 dTopLeft = dBottomLeft;
6988 dBottomLeft = tmp;
6989 tmp = dTopRight;
6990 dTopRight = dBottomRight;
6991 dBottomRight = tmp;
6992 dstyinc = dstyinc * -1;
6994 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6996 /* I don't think we need to do anything about this flag */
6997 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6999 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7001 tmp = dBottomRight;
7002 dBottomRight = dTopLeft;
7003 dTopLeft = tmp;
7004 tmp = dBottomLeft;
7005 dBottomLeft = dTopRight;
7006 dTopRight = tmp;
7007 dstxinc = dstxinc * -1;
7008 dstyinc = dstyinc * -1;
7010 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7012 tmp = dTopLeft;
7013 dTopLeft = dBottomLeft;
7014 dBottomLeft = dBottomRight;
7015 dBottomRight = dTopRight;
7016 dTopRight = tmp;
7017 tmpxy = dstxinc;
7018 dstxinc = dstyinc;
7019 dstyinc = tmpxy;
7020 dstxinc = dstxinc * -1;
7022 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7024 tmp = dTopLeft;
7025 dTopLeft = dTopRight;
7026 dTopRight = dBottomRight;
7027 dBottomRight = dBottomLeft;
7028 dBottomLeft = tmp;
7029 tmpxy = dstxinc;
7030 dstxinc = dstyinc;
7031 dstyinc = tmpxy;
7032 dstyinc = dstyinc * -1;
7034 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7036 /* I don't think we need to do anything about this flag */
7037 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7039 dbuf = dTopLeft;
7040 flags &= ~(WINEDDBLT_DDFX);
7043 #define COPY_COLORKEY_FX(type) \
7044 do { \
7045 const type *s; \
7046 type *d = (type *)dbuf, *dx, tmp; \
7047 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7049 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7050 dx = d; \
7051 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7053 tmp = s[sx >> 16]; \
7054 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7055 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7057 dx[0] = tmp; \
7059 dx = (type *)(((BYTE *)dx) + dstxinc); \
7061 d = (type *)(((BYTE *)d) + dstyinc); \
7063 } while(0)
7065 switch (bpp)
7067 case 1:
7068 COPY_COLORKEY_FX(BYTE);
7069 break;
7070 case 2:
7071 COPY_COLORKEY_FX(WORD);
7072 break;
7073 case 4:
7074 COPY_COLORKEY_FX(DWORD);
7075 break;
7076 case 3:
7078 const BYTE *s;
7079 BYTE *d = dbuf, *dx;
7080 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7082 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7083 dx = d;
7084 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7086 DWORD pixel, dpixel = 0;
7087 s = sbuf + 3 * (sx>>16);
7088 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7089 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7090 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7091 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7093 dx[0] = (pixel ) & 0xff;
7094 dx[1] = (pixel >> 8) & 0xff;
7095 dx[2] = (pixel >> 16) & 0xff;
7097 dx += dstxinc;
7099 d += dstyinc;
7101 break;
7103 default:
7104 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7105 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7106 hr = WINED3DERR_NOTAVAILABLE;
7107 goto error;
7108 #undef COPY_COLORKEY_FX
7113 error:
7114 if (flags && FIXME_ON(d3d_surface))
7116 FIXME("\tUnsupported flags: %#x.\n", flags);
7119 release:
7120 wined3d_surface_unmap(dst_surface);
7121 if (src_surface && src_surface != dst_surface)
7122 wined3d_surface_unmap(src_surface);
7123 /* Release the converted surface, if any. */
7124 if (src_surface && src_surface != orig_src)
7125 wined3d_surface_decref(src_surface);
7127 return hr;
7130 /* Do not call while under the GL lock. */
7131 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7132 const RECT *dst_rect, const struct wined3d_color *color)
7134 static const RECT src_rect;
7135 WINEDDBLTFX BltFx;
7137 memset(&BltFx, 0, sizeof(BltFx));
7138 BltFx.dwSize = sizeof(BltFx);
7139 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7140 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7141 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7144 /* Do not call while under the GL lock. */
7145 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7146 struct wined3d_surface *surface, const RECT *rect, float depth)
7148 FIXME("Depth filling not implemented by cpu_blit.\n");
7149 return WINED3DERR_INVALIDCALL;
7152 const struct blit_shader cpu_blit = {
7153 cpu_blit_alloc,
7154 cpu_blit_free,
7155 cpu_blit_set,
7156 cpu_blit_unset,
7157 cpu_blit_supported,
7158 cpu_blit_color_fill,
7159 cpu_blit_depth_fill,
7162 static HRESULT surface_init(struct wined3d_surface *surface, enum wined3d_surface_type surface_type, UINT alignment,
7163 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7164 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7165 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7167 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7168 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7169 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7170 unsigned int resource_size;
7171 HRESULT hr;
7173 if (multisample_quality > 0)
7175 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7176 multisample_quality = 0;
7179 /* Quick lockable sanity check.
7180 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7181 * this function is too deep to need to care about things like this.
7182 * Levels need to be checked too, since they all affect what can be done. */
7183 switch (pool)
7185 case WINED3D_POOL_SCRATCH:
7186 if (!lockable)
7188 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7189 "which are mutually exclusive, setting lockable to TRUE.\n");
7190 lockable = TRUE;
7192 break;
7194 case WINED3D_POOL_SYSTEM_MEM:
7195 if (!lockable)
7196 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7197 break;
7199 case WINED3D_POOL_MANAGED:
7200 if (usage & WINED3DUSAGE_DYNAMIC)
7201 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7202 break;
7204 case WINED3D_POOL_DEFAULT:
7205 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7206 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7207 break;
7209 default:
7210 FIXME("Unknown pool %#x.\n", pool);
7211 break;
7214 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7215 FIXME("Trying to create a render target that isn't in the default pool.\n");
7217 /* FIXME: Check that the format is supported by the device. */
7219 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7220 if (!resource_size)
7221 return WINED3DERR_INVALIDCALL;
7223 surface->surface_type = surface_type;
7225 switch (surface_type)
7227 case WINED3D_SURFACE_TYPE_OPENGL:
7228 surface->surface_ops = &surface_ops;
7229 break;
7231 case WINED3D_SURFACE_TYPE_GDI:
7232 surface->surface_ops = &gdi_surface_ops;
7233 break;
7235 default:
7236 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7237 return WINED3DERR_INVALIDCALL;
7240 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7241 multisample_type, multisample_quality, usage, pool, width, height, 1,
7242 resource_size, parent, parent_ops, &surface_resource_ops);
7243 if (FAILED(hr))
7245 WARN("Failed to initialize resource, returning %#x.\n", hr);
7246 return hr;
7249 /* "Standalone" surface. */
7250 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7252 surface->texture_level = level;
7253 list_init(&surface->overlays);
7255 /* Flags */
7256 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7257 if (flags & WINED3D_SURFACE_DISCARD)
7258 surface->flags |= SFLAG_DISCARD;
7259 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7260 surface->flags |= SFLAG_PIN_SYSMEM;
7261 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7262 surface->flags |= SFLAG_LOCKABLE;
7263 /* I'm not sure if this qualifies as a hack or as an optimization. It
7264 * seems reasonable to assume that lockable render targets will get
7265 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7266 * creation. However, the other reason we want to do this is that several
7267 * ddraw applications access surface memory while the surface isn't
7268 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7269 * future locks prevents these from crashing. */
7270 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7271 surface->flags |= SFLAG_DYNLOCK;
7273 /* Mark the texture as dirty so that it gets loaded first time around. */
7274 surface_add_dirty_rect(surface, NULL);
7275 list_init(&surface->renderbuffers);
7277 TRACE("surface %p, memory %p, size %u\n",
7278 surface, surface->resource.allocatedMemory, surface->resource.size);
7280 /* Call the private setup routine */
7281 hr = surface->surface_ops->surface_private_setup(surface);
7282 if (FAILED(hr))
7284 ERR("Private setup failed, returning %#x\n", hr);
7285 surface_cleanup(surface);
7286 return hr;
7289 /* Similar to lockable rendertargets above, creating the DIB section
7290 * during surface initialization prevents the sysmem pointer from changing
7291 * after a wined3d_surface_getdc() call. */
7292 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7293 && SUCCEEDED(surface_create_dib_section(surface)))
7295 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7296 surface->resource.heapMemory = NULL;
7297 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7300 return hr;
7303 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7304 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7305 enum wined3d_multisample_type multisample_type, DWORD multisample_quality,
7306 enum wined3d_surface_type surface_type, DWORD flags, void *parent,
7307 const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7309 struct wined3d_surface *object;
7310 HRESULT hr;
7312 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7313 device, width, height, debug_d3dformat(format_id), level);
7314 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7315 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7316 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7318 if (surface_type == WINED3D_SURFACE_TYPE_OPENGL && !device->adapter)
7320 ERR("OpenGL surfaces are not available without OpenGL.\n");
7321 return WINED3DERR_NOTAVAILABLE;
7324 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7325 if (!object)
7327 ERR("Failed to allocate surface memory.\n");
7328 return WINED3DERR_OUTOFVIDEOMEMORY;
7331 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7332 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7333 if (FAILED(hr))
7335 WARN("Failed to initialize surface, returning %#x.\n", hr);
7336 HeapFree(GetProcessHeap(), 0, object);
7337 return hr;
7340 TRACE("Created surface %p.\n", object);
7341 *surface = object;
7343 return hr;