wined3d: Use draw_binding in surface_realize_palette().
[wine.git] / dlls / wined3d / surface.c
blob3edb48bf917edc1d19052d9047086ec0ceb18a35
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 TRACE("surface %p.\n", surface);
47 if (surface->texture_name || (surface->flags & SFLAG_PBO) || !list_empty(&surface->renderbuffers))
49 struct wined3d_renderbuffer_entry *entry, *entry2;
50 const struct wined3d_gl_info *gl_info;
51 struct wined3d_context *context;
53 context = context_acquire(surface->resource.device, NULL);
54 gl_info = context->gl_info;
56 ENTER_GL();
58 if (surface->texture_name)
60 TRACE("Deleting texture %u.\n", surface->texture_name);
61 glDeleteTextures(1, &surface->texture_name);
64 if (surface->flags & SFLAG_PBO)
66 TRACE("Deleting PBO %u.\n", surface->pbo);
67 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
72 TRACE("Deleting renderbuffer %u.\n", entry->id);
73 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
74 HeapFree(GetProcessHeap(), 0, entry);
77 LEAVE_GL();
79 context_release(context);
82 if (surface->flags & SFLAG_DIBSECTION)
84 /* Release the DC. */
85 SelectObject(surface->hDC, surface->dib.holdbitmap);
86 DeleteDC(surface->hDC);
87 /* Release the DIB section. */
88 DeleteObject(surface->dib.DIBsection);
89 surface->dib.bitmap_data = NULL;
90 surface->resource.allocatedMemory = NULL;
93 if (surface->flags & SFLAG_USERPTR)
94 wined3d_surface_set_mem(surface, NULL);
95 if (surface->overlay_dest)
96 list_remove(&surface->overlay_entry);
98 HeapFree(GetProcessHeap(), 0, surface->palette9);
100 resource_cleanup(&surface->resource);
103 void surface_update_draw_binding(struct wined3d_surface *surface)
105 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
106 surface->draw_binding = SFLAG_INDRAWABLE;
107 else
108 surface->draw_binding = SFLAG_INTEXTURE;
111 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
113 TRACE("surface %p, container %p.\n", surface, container);
115 if (!container && type != WINED3D_CONTAINER_NONE)
116 ERR("Setting NULL container of type %#x.\n", type);
118 if (type == WINED3D_CONTAINER_SWAPCHAIN)
120 surface->get_drawable_size = get_drawable_size_swapchain;
122 else
124 switch (wined3d_settings.offscreen_rendering_mode)
126 case ORM_FBO:
127 surface->get_drawable_size = get_drawable_size_fbo;
128 break;
130 case ORM_BACKBUFFER:
131 surface->get_drawable_size = get_drawable_size_backbuffer;
132 break;
134 default:
135 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
136 return;
140 surface->container.type = type;
141 surface->container.u.base = container;
142 surface_update_draw_binding(surface);
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* GL locking and context activation is done by the caller */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, const RECT *src_rect,
299 const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
301 struct blt_info info;
303 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
305 glEnable(info.bind_target);
306 checkGLcall("glEnable(bind_target)");
308 /* Bind the texture */
309 glBindTexture(info.bind_target, src_surface->texture_name);
310 checkGLcall("glBindTexture");
312 /* Filtering for StretchRect */
313 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
314 wined3d_gl_mag_filter(magLookup, Filter));
315 checkGLcall("glTexParameteri");
316 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
318 checkGLcall("glTexParameteri");
319 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
322 checkGLcall("glTexEnvi");
324 /* Draw a quad */
325 glBegin(GL_TRIANGLE_STRIP);
326 glTexCoord3fv(info.coords[0]);
327 glVertex2i(dst_rect->left, dst_rect->top);
329 glTexCoord3fv(info.coords[1]);
330 glVertex2i(dst_rect->right, dst_rect->top);
332 glTexCoord3fv(info.coords[2]);
333 glVertex2i(dst_rect->left, dst_rect->bottom);
335 glTexCoord3fv(info.coords[3]);
336 glVertex2i(dst_rect->right, dst_rect->bottom);
337 glEnd();
339 /* Unbind the texture */
340 glBindTexture(info.bind_target, 0);
341 checkGLcall("glBindTexture(info->bind_target, 0)");
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
347 struct wined3d_texture *texture = src_surface->container.u.texture;
348 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
349 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
350 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
354 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
356 const struct wined3d_format *format = surface->resource.format;
357 SYSTEM_INFO sysInfo;
358 BITMAPINFO *b_info;
359 int extraline = 0;
360 DWORD *masks;
361 UINT usage;
362 HDC dc;
364 TRACE("surface %p.\n", surface);
366 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
368 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
369 return WINED3DERR_INVALIDCALL;
372 switch (format->byte_count)
374 case 2:
375 case 4:
376 /* Allocate extra space to store the RGB bit masks. */
377 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
378 break;
380 case 3:
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
382 break;
384 default:
385 /* Allocate extra space for a palette. */
386 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
387 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
388 break;
391 if (!b_info)
392 return E_OUTOFMEMORY;
394 /* Some applications access the surface in via DWORDs, and do not take
395 * the necessary care at the end of the surface. So we need at least
396 * 4 extra bytes at the end of the surface. Check against the page size,
397 * if the last page used for the surface has at least 4 spare bytes we're
398 * safe, otherwise add an extra line to the DIB section. */
399 GetSystemInfo(&sysInfo);
400 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
402 extraline = 1;
403 TRACE("Adding an extra line to the DIB section.\n");
406 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
407 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
408 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
409 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
410 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
411 * wined3d_surface_get_pitch(surface);
412 b_info->bmiHeader.biPlanes = 1;
413 b_info->bmiHeader.biBitCount = format->byte_count * 8;
415 b_info->bmiHeader.biXPelsPerMeter = 0;
416 b_info->bmiHeader.biYPelsPerMeter = 0;
417 b_info->bmiHeader.biClrUsed = 0;
418 b_info->bmiHeader.biClrImportant = 0;
420 /* Get the bit masks */
421 masks = (DWORD *)b_info->bmiColors;
422 switch (surface->resource.format->id)
424 case WINED3DFMT_B8G8R8_UNORM:
425 usage = DIB_RGB_COLORS;
426 b_info->bmiHeader.biCompression = BI_RGB;
427 break;
429 case WINED3DFMT_B5G5R5X1_UNORM:
430 case WINED3DFMT_B5G5R5A1_UNORM:
431 case WINED3DFMT_B4G4R4A4_UNORM:
432 case WINED3DFMT_B4G4R4X4_UNORM:
433 case WINED3DFMT_B2G3R3_UNORM:
434 case WINED3DFMT_B2G3R3A8_UNORM:
435 case WINED3DFMT_R10G10B10A2_UNORM:
436 case WINED3DFMT_R8G8B8A8_UNORM:
437 case WINED3DFMT_R8G8B8X8_UNORM:
438 case WINED3DFMT_B10G10R10A2_UNORM:
439 case WINED3DFMT_B5G6R5_UNORM:
440 case WINED3DFMT_R16G16B16A16_UNORM:
441 usage = 0;
442 b_info->bmiHeader.biCompression = BI_BITFIELDS;
443 masks[0] = format->red_mask;
444 masks[1] = format->green_mask;
445 masks[2] = format->blue_mask;
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 usage = 0;
452 break;
455 if (!(dc = GetDC(0)))
457 HeapFree(GetProcessHeap(), 0, b_info);
458 return HRESULT_FROM_WIN32(GetLastError());
461 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
462 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
463 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
464 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
465 ReleaseDC(0, dc);
467 if (!surface->dib.DIBsection)
469 ERR("Failed to create DIB section.\n");
470 HeapFree(GetProcessHeap(), 0, b_info);
471 return HRESULT_FROM_WIN32(GetLastError());
474 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
475 /* Copy the existing surface to the dib section. */
476 if (surface->resource.allocatedMemory)
478 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
479 surface->resource.height * wined3d_surface_get_pitch(surface));
481 else
483 /* This is to make maps read the GL texture although memory is allocated. */
484 surface->flags &= ~SFLAG_INSYSMEM;
486 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
488 HeapFree(GetProcessHeap(), 0, b_info);
490 /* Now allocate a DC. */
491 surface->hDC = CreateCompatibleDC(0);
492 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
493 TRACE("Using wined3d palette %p.\n", surface->palette);
494 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
496 surface->flags |= SFLAG_DIBSECTION;
498 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
499 surface->resource.heapMemory = NULL;
501 return WINED3D_OK;
504 static void surface_prepare_system_memory(struct wined3d_surface *surface)
506 struct wined3d_device *device = surface->resource.device;
507 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
509 TRACE("surface %p.\n", surface);
511 /* Performance optimization: Count how often a surface is locked, if it is
512 * locked regularly do not throw away the system memory copy. This avoids
513 * the need to download the surface from OpenGL all the time. The surface
514 * is still downloaded if the OpenGL texture is changed. */
515 if (!(surface->flags & SFLAG_DYNLOCK))
517 if (++surface->lockCount > MAXLOCKCOUNT)
519 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
520 surface->flags |= SFLAG_DYNLOCK;
524 /* Create a PBO for dynamically locked surfaces but don't do it for
525 * converted or NPOT surfaces. Also don't create a PBO for systemmem
526 * surfaces. */
527 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
528 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
529 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
531 struct wined3d_context *context;
532 GLenum error;
534 context = context_acquire(device, NULL);
535 ENTER_GL();
537 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
538 error = glGetError();
539 if (!surface->pbo || error != GL_NO_ERROR)
540 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
542 TRACE("Binding PBO %u.\n", surface->pbo);
544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
545 checkGLcall("glBindBufferARB");
547 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
548 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
549 checkGLcall("glBufferDataARB");
551 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
552 checkGLcall("glBindBufferARB");
554 /* We don't need the system memory anymore and we can't even use it for PBOs. */
555 if (!(surface->flags & SFLAG_CLIENT))
557 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
558 surface->resource.heapMemory = NULL;
560 surface->resource.allocatedMemory = NULL;
561 surface->flags |= SFLAG_PBO;
562 LEAVE_GL();
563 context_release(context);
565 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
567 /* Whatever surface we have, make sure that there is memory allocated
568 * for the downloaded copy, or a PBO to map. */
569 if (!surface->resource.heapMemory)
570 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
572 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
573 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
575 if (surface->flags & SFLAG_INSYSMEM)
576 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
580 static void surface_evict_sysmem(struct wined3d_surface *surface)
582 if (surface->flags & SFLAG_DONOTFREE)
583 return;
585 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
586 surface->resource.allocatedMemory = NULL;
587 surface->resource.heapMemory = NULL;
588 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
591 /* Context activation is done by the caller. */
592 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
593 const struct wined3d_gl_info *gl_info, BOOL srgb)
595 struct wined3d_device *device = surface->resource.device;
596 DWORD active_sampler;
597 GLint active_texture;
599 /* We don't need a specific texture unit, but after binding the texture
600 * the current unit is dirty. Read the unit back instead of switching to
601 * 0, this avoids messing around with the state manager's GL states. The
602 * current texture unit should always be a valid one.
604 * To be more specific, this is tricky because we can implicitly be
605 * called from sampler() in state.c. This means we can't touch anything
606 * other than whatever happens to be the currently active texture, or we
607 * would risk marking already applied sampler states dirty again.
609 * TODO: Track the current active texture per GL context instead of using
610 * glGet(). */
612 ENTER_GL();
613 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
614 LEAVE_GL();
615 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
617 if (active_sampler != WINED3D_UNMAPPED_STAGE)
618 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
619 surface_bind(surface, gl_info, srgb);
622 static void surface_force_reload(struct wined3d_surface *surface)
624 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
627 static void surface_release_client_storage(struct wined3d_surface *surface)
629 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
631 ENTER_GL();
632 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
633 if (surface->texture_name)
635 surface_bind_and_dirtify(surface, context->gl_info, FALSE);
636 glTexImage2D(surface->texture_target, surface->texture_level,
637 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
639 if (surface->texture_name_srgb)
641 surface_bind_and_dirtify(surface, context->gl_info, TRUE);
642 glTexImage2D(surface->texture_target, surface->texture_level,
643 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
645 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
646 LEAVE_GL();
648 context_release(context);
650 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
651 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
652 surface_force_reload(surface);
655 static HRESULT surface_private_setup(struct wined3d_surface *surface)
657 /* TODO: Check against the maximum texture sizes supported by the video card. */
658 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
659 unsigned int pow2Width, pow2Height;
661 TRACE("surface %p.\n", surface);
663 surface->texture_name = 0;
664 surface->texture_target = GL_TEXTURE_2D;
666 /* Non-power2 support */
667 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
669 pow2Width = surface->resource.width;
670 pow2Height = surface->resource.height;
672 else
674 /* Find the nearest pow2 match */
675 pow2Width = pow2Height = 1;
676 while (pow2Width < surface->resource.width)
677 pow2Width <<= 1;
678 while (pow2Height < surface->resource.height)
679 pow2Height <<= 1;
681 surface->pow2Width = pow2Width;
682 surface->pow2Height = pow2Height;
684 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
686 /* TODO: Add support for non power two compressed textures. */
687 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
689 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
690 surface, surface->resource.width, surface->resource.height);
691 return WINED3DERR_NOTAVAILABLE;
695 if (pow2Width != surface->resource.width
696 || pow2Height != surface->resource.height)
698 surface->flags |= SFLAG_NONPOW2;
701 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
702 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
704 /* One of three options:
705 * 1: Do the same as we do with NPOT and scale the texture, (any
706 * texture ops would require the texture to be scaled which is
707 * potentially slow)
708 * 2: Set the texture to the maximum size (bad idea).
709 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
710 * 4: Create the surface, but allow it to be used only for DirectDraw
711 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
712 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
713 * the render target. */
714 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
716 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
717 return WINED3DERR_NOTAVAILABLE;
720 /* We should never use this surface in combination with OpenGL! */
721 TRACE("Creating an oversized surface: %ux%u.\n",
722 surface->pow2Width, surface->pow2Height);
724 else
726 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
727 * and EXT_PALETTED_TEXTURE is used in combination with texture
728 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
729 * EXT_PALETTED_TEXTURE doesn't work in combination with
730 * ARB_TEXTURE_RECTANGLE. */
731 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
732 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
733 && gl_info->supported[EXT_PALETTED_TEXTURE]
734 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
736 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
737 surface->pow2Width = surface->resource.width;
738 surface->pow2Height = surface->resource.height;
739 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
743 switch (wined3d_settings.offscreen_rendering_mode)
745 case ORM_FBO:
746 surface->get_drawable_size = get_drawable_size_fbo;
747 break;
749 case ORM_BACKBUFFER:
750 surface->get_drawable_size = get_drawable_size_backbuffer;
751 break;
753 default:
754 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
755 return WINED3DERR_INVALIDCALL;
758 surface->flags |= SFLAG_INSYSMEM;
760 return WINED3D_OK;
763 static void surface_realize_palette(struct wined3d_surface *surface)
765 struct wined3d_palette *palette = surface->palette;
767 TRACE("surface %p.\n", surface);
769 if (!palette) return;
771 if (surface->resource.format->id == WINED3DFMT_P8_UINT
772 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
774 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
776 /* Make sure the texture is up to date. This call doesn't do
777 * anything if the texture is already up to date. */
778 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
780 /* We want to force a palette refresh, so mark the drawable as not being up to date */
781 if (!surface_is_offscreen(surface))
782 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
784 else
786 if (!(surface->flags & SFLAG_INSYSMEM))
788 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
789 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
791 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
795 if (surface->flags & SFLAG_DIBSECTION)
797 RGBQUAD col[256];
798 unsigned int i;
800 TRACE("Updating the DC's palette.\n");
802 for (i = 0; i < 256; ++i)
804 col[i].rgbRed = palette->palents[i].peRed;
805 col[i].rgbGreen = palette->palents[i].peGreen;
806 col[i].rgbBlue = palette->palents[i].peBlue;
807 col[i].rgbReserved = 0;
809 SetDIBColorTable(surface->hDC, 0, 256, col);
812 /* Propagate the changes to the drawable when we have a palette. */
813 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
814 surface_load_location(surface, surface->draw_binding, NULL);
817 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
819 HRESULT hr;
821 /* If there's no destination surface there is nothing to do. */
822 if (!surface->overlay_dest)
823 return WINED3D_OK;
825 /* Blt calls ModifyLocation on the dest surface, which in turn calls
826 * DrawOverlay to update the overlay. Prevent an endless recursion. */
827 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
828 return WINED3D_OK;
830 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
831 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
832 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
833 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
835 return hr;
838 static void surface_preload(struct wined3d_surface *surface)
840 TRACE("surface %p.\n", surface);
842 surface_internal_preload(surface, SRGB_ANY);
845 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
847 struct wined3d_device *device = surface->resource.device;
848 const RECT *pass_rect = rect;
850 TRACE("surface %p, rect %s, flags %#x.\n",
851 surface, wine_dbgstr_rect(rect), flags);
853 if (flags & WINED3DLOCK_DISCARD)
855 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
856 surface_prepare_system_memory(surface);
857 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
859 else
861 /* surface_load_location() does not check if the rectangle specifies
862 * the full surface. Most callers don't need that, so do it here. */
863 if (rect && !rect->top && !rect->left
864 && rect->right == surface->resource.width
865 && rect->bottom == surface->resource.height)
866 pass_rect = NULL;
868 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
869 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
870 || surface == device->fb.render_targets[0])))
871 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
874 if (surface->flags & SFLAG_PBO)
876 const struct wined3d_gl_info *gl_info;
877 struct wined3d_context *context;
879 context = context_acquire(device, NULL);
880 gl_info = context->gl_info;
882 ENTER_GL();
883 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
884 checkGLcall("glBindBufferARB");
886 /* This shouldn't happen but could occur if some other function
887 * didn't handle the PBO properly. */
888 if (surface->resource.allocatedMemory)
889 ERR("The surface already has PBO memory allocated.\n");
891 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
892 checkGLcall("glMapBufferARB");
894 /* Make sure the PBO isn't set anymore in order not to break non-PBO
895 * calls. */
896 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
897 checkGLcall("glBindBufferARB");
899 LEAVE_GL();
900 context_release(context);
903 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
905 if (!rect)
906 surface_add_dirty_rect(surface, NULL);
907 else
909 WINED3DBOX b;
911 b.Left = rect->left;
912 b.Top = rect->top;
913 b.Right = rect->right;
914 b.Bottom = rect->bottom;
915 b.Front = 0;
916 b.Back = 1;
917 surface_add_dirty_rect(surface, &b);
922 static void surface_unmap(struct wined3d_surface *surface)
924 struct wined3d_device *device = surface->resource.device;
925 BOOL fullsurface;
927 TRACE("surface %p.\n", surface);
929 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
931 if (surface->flags & SFLAG_PBO)
933 const struct wined3d_gl_info *gl_info;
934 struct wined3d_context *context;
936 TRACE("Freeing PBO memory.\n");
938 context = context_acquire(device, NULL);
939 gl_info = context->gl_info;
941 ENTER_GL();
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
943 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
944 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
945 checkGLcall("glUnmapBufferARB");
946 LEAVE_GL();
947 context_release(context);
949 surface->resource.allocatedMemory = NULL;
952 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
954 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
956 TRACE("Not dirtified, nothing to do.\n");
957 goto done;
960 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
961 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
963 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
965 static BOOL warned = FALSE;
966 if (!warned)
968 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
969 warned = TRUE;
971 goto done;
974 if (!surface->dirtyRect.left && !surface->dirtyRect.top
975 && surface->dirtyRect.right == surface->resource.width
976 && surface->dirtyRect.bottom == surface->resource.height)
978 fullsurface = TRUE;
980 else
982 /* TODO: Proper partial rectangle tracking. */
983 fullsurface = FALSE;
984 surface->flags |= SFLAG_INSYSMEM;
987 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
989 /* Partial rectangle tracking is not commonly implemented, it is only
990 * done for render targets. INSYSMEM was set before to tell
991 * surface_load_location() where to read the rectangle from.
992 * Indrawable is set because all modifications from the partial
993 * sysmem copy are written back to the drawable, thus the surface is
994 * merged again in the drawable. The sysmem copy is not fully up to
995 * date because only a subrectangle was read in Map(). */
996 if (!fullsurface)
998 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
999 surface_evict_sysmem(surface);
1002 surface->dirtyRect.left = surface->resource.width;
1003 surface->dirtyRect.top = surface->resource.height;
1004 surface->dirtyRect.right = 0;
1005 surface->dirtyRect.bottom = 0;
1007 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1009 FIXME("Depth / stencil buffer locking is not implemented.\n");
1012 done:
1013 /* Overlays have to be redrawn manually after changes with the GL implementation */
1014 if (surface->overlay_dest)
1015 surface->surface_ops->surface_draw_overlay(surface);
1018 static HRESULT surface_getdc(struct wined3d_surface *surface)
1020 WINED3DLOCKED_RECT lock;
1021 HRESULT hr;
1023 TRACE("surface %p.\n", surface);
1025 /* Create a DIB section if there isn't a dc yet. */
1026 if (!surface->hDC)
1028 if (surface->flags & SFLAG_CLIENT)
1030 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1031 surface_release_client_storage(surface);
1033 hr = surface_create_dib_section(surface);
1034 if (FAILED(hr))
1035 return WINED3DERR_INVALIDCALL;
1037 /* Use the DIB section from now on if we are not using a PBO. */
1038 if (!(surface->flags & SFLAG_PBO))
1039 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1042 /* Map the surface. */
1043 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1044 if (FAILED(hr))
1045 ERR("Map failed, hr %#x.\n", hr);
1047 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1048 * activates the allocatedMemory. */
1049 if (surface->flags & SFLAG_PBO)
1050 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1052 return hr;
1055 static HRESULT surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1057 TRACE("surface %p, override %p.\n", surface, override);
1059 /* Flipping is only supported on render targets and overlays. */
1060 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1062 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1063 return WINEDDERR_NOTFLIPPABLE;
1066 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1068 flip_surface(surface, override);
1070 /* Update the overlay if it is visible */
1071 if (surface->overlay_dest)
1072 return surface->surface_ops->surface_draw_overlay(surface);
1073 else
1074 return WINED3D_OK;
1077 return WINED3D_OK;
1080 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1082 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1083 return FALSE;
1084 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1085 return FALSE;
1086 return TRUE;
1089 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1090 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1092 const struct wined3d_gl_info *gl_info;
1093 struct wined3d_context *context;
1094 DWORD src_mask, dst_mask;
1095 GLbitfield gl_mask;
1097 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1098 device, src_surface, wine_dbgstr_rect(src_rect),
1099 dst_surface, wine_dbgstr_rect(dst_rect));
1101 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1102 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1104 if (src_mask != dst_mask)
1106 ERR("Incompatible formats %s and %s.\n",
1107 debug_d3dformat(src_surface->resource.format->id),
1108 debug_d3dformat(dst_surface->resource.format->id));
1109 return;
1112 if (!src_mask)
1114 ERR("Not a depth / stencil format: %s.\n",
1115 debug_d3dformat(src_surface->resource.format->id));
1116 return;
1119 gl_mask = 0;
1120 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1121 gl_mask |= GL_DEPTH_BUFFER_BIT;
1122 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1123 gl_mask |= GL_STENCIL_BUFFER_BIT;
1125 /* Make sure the locations are up-to-date. Loading the destination
1126 * surface isn't required if the entire surface is overwritten. */
1127 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1128 if (!surface_is_full_rect(dst_surface, dst_rect))
1129 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1131 context = context_acquire(device, NULL);
1132 if (!context->valid)
1134 context_release(context);
1135 WARN("Invalid context, skipping blit.\n");
1136 return;
1139 gl_info = context->gl_info;
1141 ENTER_GL();
1143 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1144 glReadBuffer(GL_NONE);
1145 checkGLcall("glReadBuffer()");
1146 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1148 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1149 context_set_draw_buffer(context, GL_NONE);
1150 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1152 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1154 glDepthMask(GL_TRUE);
1155 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1157 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1159 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1161 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1162 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1164 glStencilMask(~0U);
1165 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1168 glDisable(GL_SCISSOR_TEST);
1169 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1171 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1172 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1173 checkGLcall("glBlitFramebuffer()");
1175 LEAVE_GL();
1177 if (wined3d_settings.strict_draw_ordering)
1178 wglFlush(); /* Flush to ensure ordering across contexts. */
1180 context_release(context);
1183 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1184 * Depth / stencil is not supported. */
1185 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1186 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1187 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1189 const struct wined3d_gl_info *gl_info;
1190 struct wined3d_context *context;
1191 RECT src_rect, dst_rect;
1192 GLenum gl_filter;
1193 GLenum buffer;
1195 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1196 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1197 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1198 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1199 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1201 src_rect = *src_rect_in;
1202 dst_rect = *dst_rect_in;
1204 switch (filter)
1206 case WINED3DTEXF_LINEAR:
1207 gl_filter = GL_LINEAR;
1208 break;
1210 default:
1211 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1212 case WINED3DTEXF_NONE:
1213 case WINED3DTEXF_POINT:
1214 gl_filter = GL_NEAREST;
1215 break;
1218 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
1219 src_location = SFLAG_INTEXTURE;
1220 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
1221 dst_location = SFLAG_INTEXTURE;
1223 /* Make sure the locations are up-to-date. Loading the destination
1224 * surface isn't required if the entire surface is overwritten. (And is
1225 * in fact harmful if we're being called by surface_load_location() with
1226 * the purpose of loading the destination surface.) */
1227 surface_load_location(src_surface, src_location, NULL);
1228 if (!surface_is_full_rect(dst_surface, &dst_rect))
1229 surface_load_location(dst_surface, dst_location, NULL);
1231 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1232 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1233 else context = context_acquire(device, NULL);
1235 if (!context->valid)
1237 context_release(context);
1238 WARN("Invalid context, skipping blit.\n");
1239 return;
1242 gl_info = context->gl_info;
1244 if (src_location == SFLAG_INDRAWABLE)
1246 TRACE("Source surface %p is onscreen.\n", src_surface);
1247 buffer = surface_get_gl_buffer(src_surface);
1248 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1250 else
1252 TRACE("Source surface %p is offscreen.\n", src_surface);
1253 buffer = GL_COLOR_ATTACHMENT0;
1256 ENTER_GL();
1257 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1258 glReadBuffer(buffer);
1259 checkGLcall("glReadBuffer()");
1260 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1261 LEAVE_GL();
1263 if (dst_location == SFLAG_INDRAWABLE)
1265 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1266 buffer = surface_get_gl_buffer(dst_surface);
1267 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1269 else
1271 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1272 buffer = GL_COLOR_ATTACHMENT0;
1275 ENTER_GL();
1276 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1277 context_set_draw_buffer(context, buffer);
1278 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1279 context_invalidate_state(context, STATE_FRAMEBUFFER);
1281 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1282 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1283 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1284 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1285 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1287 glDisable(GL_SCISSOR_TEST);
1288 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1290 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1291 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1292 checkGLcall("glBlitFramebuffer()");
1294 LEAVE_GL();
1296 if (wined3d_settings.strict_draw_ordering
1297 || (dst_location == SFLAG_INDRAWABLE
1298 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1299 wglFlush();
1301 context_release(context);
1304 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1305 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1306 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1308 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1309 return FALSE;
1311 /* Source and/or destination need to be on the GL side */
1312 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1313 return FALSE;
1315 switch (blit_op)
1317 case WINED3D_BLIT_OP_COLOR_BLIT:
1318 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1319 return FALSE;
1320 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1321 return FALSE;
1322 break;
1324 case WINED3D_BLIT_OP_DEPTH_BLIT:
1325 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1326 return FALSE;
1327 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1328 return FALSE;
1329 break;
1331 default:
1332 return FALSE;
1335 if (!(src_format->id == dst_format->id
1336 || (is_identity_fixup(src_format->color_fixup)
1337 && is_identity_fixup(dst_format->color_fixup))))
1338 return FALSE;
1340 return TRUE;
1343 /* This function checks if the primary render target uses the 8bit paletted format. */
1344 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1346 if (device->fb.render_targets && device->fb.render_targets[0])
1348 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1349 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1350 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1351 return TRUE;
1353 return FALSE;
1356 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1357 DWORD color, WINED3DCOLORVALUE *float_color)
1359 const struct wined3d_format *format = surface->resource.format;
1360 const struct wined3d_device *device = surface->resource.device;
1362 switch (format->id)
1364 case WINED3DFMT_P8_UINT:
1365 if (surface->palette)
1367 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1368 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1369 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1371 else
1373 float_color->r = 0.0f;
1374 float_color->g = 0.0f;
1375 float_color->b = 0.0f;
1377 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1378 break;
1380 case WINED3DFMT_B5G6R5_UNORM:
1381 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1382 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1383 float_color->b = (color & 0x1f) / 31.0f;
1384 float_color->a = 1.0f;
1385 break;
1387 case WINED3DFMT_B8G8R8_UNORM:
1388 case WINED3DFMT_B8G8R8X8_UNORM:
1389 float_color->r = D3DCOLOR_R(color);
1390 float_color->g = D3DCOLOR_G(color);
1391 float_color->b = D3DCOLOR_B(color);
1392 float_color->a = 1.0f;
1393 break;
1395 case WINED3DFMT_B8G8R8A8_UNORM:
1396 float_color->r = D3DCOLOR_R(color);
1397 float_color->g = D3DCOLOR_G(color);
1398 float_color->b = D3DCOLOR_B(color);
1399 float_color->a = D3DCOLOR_A(color);
1400 break;
1402 default:
1403 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1404 return FALSE;
1407 return TRUE;
1410 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1412 const struct wined3d_format *format = surface->resource.format;
1414 switch (format->id)
1416 case WINED3DFMT_S1_UINT_D15_UNORM:
1417 *float_depth = depth / (float)0x00007fff;
1418 break;
1420 case WINED3DFMT_D16_UNORM:
1421 *float_depth = depth / (float)0x0000ffff;
1422 break;
1424 case WINED3DFMT_D24_UNORM_S8_UINT:
1425 case WINED3DFMT_X8D24_UNORM:
1426 *float_depth = depth / (float)0x00ffffff;
1427 break;
1429 case WINED3DFMT_D32_UNORM:
1430 *float_depth = depth / (float)0xffffffff;
1431 break;
1433 default:
1434 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1435 return FALSE;
1438 return TRUE;
1441 /* Do not call while under the GL lock. */
1442 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1444 const struct wined3d_resource *resource = &surface->resource;
1445 struct wined3d_device *device = resource->device;
1446 const struct blit_shader *blitter;
1448 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1449 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1450 if (!blitter)
1452 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1453 return WINED3DERR_INVALIDCALL;
1456 return blitter->depth_fill(device, surface, rect, depth);
1459 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1460 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1462 struct wined3d_device *device = src_surface->resource.device;
1464 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1465 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1466 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1467 return WINED3DERR_INVALIDCALL;
1469 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1471 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1472 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1473 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1475 return WINED3D_OK;
1478 /* Do not call while under the GL lock. */
1479 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1480 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1481 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1483 const struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1484 struct wined3d_device *device = dst_surface->resource.device;
1485 DWORD src_ds_flags, dst_ds_flags;
1486 RECT src_rect, dst_rect;
1488 static const DWORD simple_blit = WINEDDBLT_ASYNC
1489 | WINEDDBLT_COLORFILL
1490 | WINEDDBLT_WAIT
1491 | WINEDDBLT_DEPTHFILL
1492 | WINEDDBLT_DONOTWAIT;
1494 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1495 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1496 flags, fx, debug_d3dtexturefiltertype(filter));
1497 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1499 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1501 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1502 return WINEDDERR_SURFACEBUSY;
1505 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1507 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1508 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1509 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1510 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1511 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1513 /* The destination rect can be out of bounds on the condition
1514 * that a clipper is set for the surface. */
1515 if (dst_surface->clipper)
1516 FIXME("Blit clipping not implemented.\n");
1517 else
1518 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1519 return WINEDDERR_INVALIDRECT;
1522 if (src_surface)
1524 surface_get_rect(src_surface, src_rect_in, &src_rect);
1526 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1527 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1528 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1529 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1530 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1532 WARN("Application gave us bad source rectangle for Blt.\n");
1533 return WINEDDERR_INVALIDRECT;
1536 else
1538 memset(&src_rect, 0, sizeof(src_rect));
1541 if (!fx || !(fx->dwDDFX))
1542 flags &= ~WINEDDBLT_DDFX;
1544 if (flags & WINEDDBLT_WAIT)
1545 flags &= ~WINEDDBLT_WAIT;
1547 if (flags & WINEDDBLT_ASYNC)
1549 static unsigned int once;
1551 if (!once++)
1552 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1553 flags &= ~WINEDDBLT_ASYNC;
1556 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1557 if (flags & WINEDDBLT_DONOTWAIT)
1559 static unsigned int once;
1561 if (!once++)
1562 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1563 flags &= ~WINEDDBLT_DONOTWAIT;
1566 if (!device->d3d_initialized)
1568 WARN("D3D not initialized, using fallback.\n");
1569 goto cpu;
1572 if (flags & ~simple_blit)
1574 WARN("Using fallback for complex blit (%#x).\n", flags);
1575 goto fallback;
1578 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1579 src_swapchain = src_surface->container.u.swapchain;
1580 else
1581 src_swapchain = NULL;
1583 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1584 dst_swapchain = dst_surface->container.u.swapchain;
1585 else
1586 dst_swapchain = NULL;
1588 /* This isn't strictly needed. FBO blits for example could deal with
1589 * cross-swapchain blits by first downloading the source to a texture
1590 * before switching to the destination context. We just have this here to
1591 * not have to deal with the issue, since cross-swapchain blits should be
1592 * rare. */
1593 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1595 FIXME("Using fallback for cross-swapchain blit.\n");
1596 goto fallback;
1599 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1600 if (src_surface)
1601 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1602 else
1603 src_ds_flags = 0;
1605 if (src_ds_flags || dst_ds_flags)
1607 if (flags & WINEDDBLT_DEPTHFILL)
1609 float depth;
1611 TRACE("Depth fill.\n");
1613 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1614 return WINED3DERR_INVALIDCALL;
1616 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1617 return WINED3D_OK;
1619 else
1621 /* Accessing depth / stencil surfaces is supposed to fail while in
1622 * a scene, except for fills, which seem to work. */
1623 if (device->inScene)
1625 WARN("Rejecting depth / stencil access while in scene.\n");
1626 return WINED3DERR_INVALIDCALL;
1629 if (src_ds_flags != dst_ds_flags)
1631 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1632 return WINED3DERR_INVALIDCALL;
1635 if (src_rect.top || src_rect.left
1636 || src_rect.bottom != src_surface->resource.height
1637 || src_rect.right != src_surface->resource.width)
1639 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1640 wine_dbgstr_rect(&src_rect));
1641 return WINED3DERR_INVALIDCALL;
1644 if (dst_rect.top || dst_rect.left
1645 || dst_rect.bottom != dst_surface->resource.height
1646 || dst_rect.right != dst_surface->resource.width)
1648 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1649 wine_dbgstr_rect(&src_rect));
1650 return WINED3DERR_INVALIDCALL;
1653 if (src_surface->resource.height != dst_surface->resource.height
1654 || src_surface->resource.width != dst_surface->resource.width)
1656 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1657 return WINED3DERR_INVALIDCALL;
1660 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1661 return WINED3D_OK;
1664 else
1666 if (flags & WINEDDBLT_COLORFILL)
1668 WINED3DCOLORVALUE color;
1670 TRACE("Color fill.\n");
1672 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1673 goto fallback;
1675 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1676 return WINED3D_OK;
1678 else
1680 TRACE("Color blit.\n");
1682 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1683 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1684 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1686 TRACE("Using FBO blit.\n");
1688 surface_blt_fbo(device, filter,
1689 src_surface, src_surface->draw_binding, &src_rect,
1690 dst_surface, dst_surface->draw_binding, &dst_rect);
1691 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1692 return WINED3D_OK;
1695 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1696 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1697 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1699 TRACE("Using arbfp blit.\n");
1701 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1702 return WINED3D_OK;
1707 fallback:
1709 /* Special cases for render targets. */
1710 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1711 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1713 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1714 src_surface, &src_rect, flags, fx, filter)))
1715 return WINED3D_OK;
1718 cpu:
1720 /* For the rest call the X11 surface implementation. For render targets
1721 * this should be implemented OpenGL accelerated in BltOverride, other
1722 * blits are rather rare. */
1723 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1726 /* Do not call while under the GL lock. */
1727 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1728 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1730 RECT src_rect, dst_rect;
1731 DWORD flags = 0;
1733 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1734 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1736 surface_get_rect(src_surface, src_rect_in, &src_rect);
1738 dst_rect.left = dst_x;
1739 dst_rect.top = dst_y;
1740 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1741 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1743 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1744 flags |= WINEDDBLT_KEYSRC;
1745 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1746 flags |= WINEDDBLT_KEYDEST;
1747 if (trans & WINEDDBLTFAST_WAIT)
1748 flags |= WINEDDBLT_WAIT;
1749 if (trans & WINEDDBLTFAST_DONOTWAIT)
1750 flags |= WINEDDBLT_DONOTWAIT;
1752 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1755 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1757 TRACE("surface %p, mem %p.\n", surface, mem);
1759 if (mem && mem != surface->resource.allocatedMemory)
1761 void *release = NULL;
1763 /* Do I have to copy the old surface content? */
1764 if (surface->flags & SFLAG_DIBSECTION)
1766 SelectObject(surface->hDC, surface->dib.holdbitmap);
1767 DeleteDC(surface->hDC);
1768 /* Release the DIB section. */
1769 DeleteObject(surface->dib.DIBsection);
1770 surface->dib.bitmap_data = NULL;
1771 surface->resource.allocatedMemory = NULL;
1772 surface->hDC = NULL;
1773 surface->flags &= ~SFLAG_DIBSECTION;
1775 else if (!(surface->flags & SFLAG_USERPTR))
1777 release = surface->resource.heapMemory;
1778 surface->resource.heapMemory = NULL;
1780 surface->resource.allocatedMemory = mem;
1781 surface->flags |= SFLAG_USERPTR;
1783 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1784 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1786 /* For client textures OpenGL has to be notified. */
1787 if (surface->flags & SFLAG_CLIENT)
1788 surface_release_client_storage(surface);
1790 /* Now free the old memory if any. */
1791 HeapFree(GetProcessHeap(), 0, release);
1793 else if (surface->flags & SFLAG_USERPTR)
1795 /* HeapMemory should be NULL already. */
1796 if (surface->resource.heapMemory)
1797 ERR("User pointer surface has heap memory allocated.\n");
1799 if (!mem)
1801 surface->resource.allocatedMemory = NULL;
1802 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1804 if (surface->flags & SFLAG_CLIENT)
1805 surface_release_client_storage(surface);
1807 surface_prepare_system_memory(surface);
1810 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1813 return WINED3D_OK;
1816 /* Context activation is done by the caller. */
1817 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1819 if (!surface->resource.heapMemory)
1821 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1822 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1823 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1826 ENTER_GL();
1827 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1828 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1829 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1830 surface->resource.size, surface->resource.allocatedMemory));
1831 checkGLcall("glGetBufferSubDataARB");
1832 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1833 checkGLcall("glDeleteBuffersARB");
1834 LEAVE_GL();
1836 surface->pbo = 0;
1837 surface->flags &= ~SFLAG_PBO;
1840 /* Do not call while under the GL lock. */
1841 static void surface_unload(struct wined3d_resource *resource)
1843 struct wined3d_surface *surface = surface_from_resource(resource);
1844 struct wined3d_renderbuffer_entry *entry, *entry2;
1845 struct wined3d_device *device = resource->device;
1846 const struct wined3d_gl_info *gl_info;
1847 struct wined3d_context *context;
1849 TRACE("surface %p.\n", surface);
1851 if (resource->pool == WINED3DPOOL_DEFAULT)
1853 /* Default pool resources are supposed to be destroyed before Reset is called.
1854 * Implicit resources stay however. So this means we have an implicit render target
1855 * or depth stencil. The content may be destroyed, but we still have to tear down
1856 * opengl resources, so we cannot leave early.
1858 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1859 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1860 * or the depth stencil into an FBO the texture or render buffer will be removed
1861 * and all flags get lost
1863 surface_init_sysmem(surface);
1865 else
1867 /* Load the surface into system memory */
1868 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1869 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1871 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1872 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1873 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1875 context = context_acquire(device, NULL);
1876 gl_info = context->gl_info;
1878 /* Destroy PBOs, but load them into real sysmem before */
1879 if (surface->flags & SFLAG_PBO)
1880 surface_remove_pbo(surface, gl_info);
1882 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1883 * all application-created targets the application has to release the surface
1884 * before calling _Reset
1886 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1888 ENTER_GL();
1889 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1890 LEAVE_GL();
1891 list_remove(&entry->entry);
1892 HeapFree(GetProcessHeap(), 0, entry);
1894 list_init(&surface->renderbuffers);
1895 surface->current_renderbuffer = NULL;
1897 /* If we're in a texture, the texture name belongs to the texture.
1898 * Otherwise, destroy it. */
1899 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1901 ENTER_GL();
1902 glDeleteTextures(1, &surface->texture_name);
1903 surface->texture_name = 0;
1904 glDeleteTextures(1, &surface->texture_name_srgb);
1905 surface->texture_name_srgb = 0;
1906 LEAVE_GL();
1909 context_release(context);
1911 resource_unload(resource);
1914 static const struct wined3d_resource_ops surface_resource_ops =
1916 surface_unload,
1919 static const struct wined3d_surface_ops surface_ops =
1921 surface_private_setup,
1922 surface_cleanup,
1923 surface_realize_palette,
1924 surface_draw_overlay,
1925 surface_preload,
1926 surface_map,
1927 surface_unmap,
1928 surface_getdc,
1929 surface_flip,
1930 surface_set_mem,
1933 /*****************************************************************************
1934 * Initializes the GDI surface, aka creates the DIB section we render to
1935 * The DIB section creation is done by calling GetDC, which will create the
1936 * section and releasing the dc to allow the app to use it. The dib section
1937 * will stay until the surface is released
1939 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1940 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1941 * avoid confusion in the shared surface code.
1943 * Returns:
1944 * WINED3D_OK on success
1945 * The return values of called methods on failure
1947 *****************************************************************************/
1948 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1950 HRESULT hr;
1952 TRACE("surface %p.\n", surface);
1954 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1956 ERR("Overlays not yet supported by GDI surfaces.\n");
1957 return WINED3DERR_INVALIDCALL;
1960 /* Sysmem textures have memory already allocated - release it,
1961 * this avoids an unnecessary memcpy. */
1962 hr = surface_create_dib_section(surface);
1963 if (SUCCEEDED(hr))
1965 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1966 surface->resource.heapMemory = NULL;
1967 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1970 /* We don't mind the nonpow2 stuff in GDI. */
1971 surface->pow2Width = surface->resource.width;
1972 surface->pow2Height = surface->resource.height;
1974 return WINED3D_OK;
1977 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1979 TRACE("surface %p.\n", surface);
1981 if (surface->flags & SFLAG_DIBSECTION)
1983 /* Release the DC. */
1984 SelectObject(surface->hDC, surface->dib.holdbitmap);
1985 DeleteDC(surface->hDC);
1986 /* Release the DIB section. */
1987 DeleteObject(surface->dib.DIBsection);
1988 surface->dib.bitmap_data = NULL;
1989 surface->resource.allocatedMemory = NULL;
1992 if (surface->flags & SFLAG_USERPTR)
1993 wined3d_surface_set_mem(surface, NULL);
1994 if (surface->overlay_dest)
1995 list_remove(&surface->overlay_entry);
1997 HeapFree(GetProcessHeap(), 0, surface->palette9);
1999 resource_cleanup(&surface->resource);
2002 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
2004 struct wined3d_palette *palette = surface->palette;
2006 TRACE("surface %p.\n", surface);
2008 if (!palette) return;
2010 if (surface->flags & SFLAG_DIBSECTION)
2012 RGBQUAD col[256];
2013 unsigned int i;
2015 TRACE("Updating the DC's palette.\n");
2017 for (i = 0; i < 256; ++i)
2019 col[i].rgbRed = palette->palents[i].peRed;
2020 col[i].rgbGreen = palette->palents[i].peGreen;
2021 col[i].rgbBlue = palette->palents[i].peBlue;
2022 col[i].rgbReserved = 0;
2024 SetDIBColorTable(surface->hDC, 0, 256, col);
2027 /* Update the image because of the palette change. Some games like e.g.
2028 * Red Alert call SetEntries a lot to implement fading. */
2029 /* Tell the swapchain to update the screen. */
2030 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2032 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2033 if (surface == swapchain->front_buffer)
2035 x11_copy_to_screen(swapchain, NULL);
2040 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2042 FIXME("GDI surfaces can't draw overlays yet.\n");
2043 return E_FAIL;
2046 static void gdi_surface_preload(struct wined3d_surface *surface)
2048 TRACE("surface %p.\n", surface);
2050 ERR("Preloading GDI surfaces is not supported.\n");
2053 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2055 TRACE("surface %p, rect %s, flags %#x.\n",
2056 surface, wine_dbgstr_rect(rect), flags);
2058 if (!surface->resource.allocatedMemory)
2060 /* This happens on gdi surfaces if the application set a user pointer
2061 * and resets it. Recreate the DIB section. */
2062 surface_create_dib_section(surface);
2063 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2067 static void gdi_surface_unmap(struct wined3d_surface *surface)
2069 TRACE("surface %p.\n", surface);
2071 /* Tell the swapchain to update the screen. */
2072 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2074 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2075 if (surface == swapchain->front_buffer)
2077 x11_copy_to_screen(swapchain, &surface->lockedRect);
2081 memset(&surface->lockedRect, 0, sizeof(RECT));
2084 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2086 WINED3DLOCKED_RECT lock;
2087 HRESULT hr;
2089 TRACE("surface %p.\n", surface);
2091 /* Should have a DIB section already. */
2092 if (!(surface->flags & SFLAG_DIBSECTION))
2094 WARN("DC not supported on this surface\n");
2095 return WINED3DERR_INVALIDCALL;
2098 /* Map the surface. */
2099 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2100 if (FAILED(hr))
2101 ERR("Map failed, hr %#x.\n", hr);
2103 return hr;
2106 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
2108 TRACE("surface %p, override %p.\n", surface, override);
2110 return WINED3D_OK;
2113 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
2115 TRACE("surface %p, mem %p.\n", surface, mem);
2117 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2118 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2120 ERR("Not supported on render targets.\n");
2121 return WINED3DERR_INVALIDCALL;
2124 if (mem && mem != surface->resource.allocatedMemory)
2126 void *release = NULL;
2128 /* Do I have to copy the old surface content? */
2129 if (surface->flags & SFLAG_DIBSECTION)
2131 SelectObject(surface->hDC, surface->dib.holdbitmap);
2132 DeleteDC(surface->hDC);
2133 /* Release the DIB section. */
2134 DeleteObject(surface->dib.DIBsection);
2135 surface->dib.bitmap_data = NULL;
2136 surface->resource.allocatedMemory = NULL;
2137 surface->hDC = NULL;
2138 surface->flags &= ~SFLAG_DIBSECTION;
2140 else if (!(surface->flags & SFLAG_USERPTR))
2142 release = surface->resource.allocatedMemory;
2144 surface->resource.allocatedMemory = mem;
2145 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2147 /* Now free the old memory, if any. */
2148 HeapFree(GetProcessHeap(), 0, release);
2150 else if (surface->flags & SFLAG_USERPTR)
2152 /* Map() and GetDC() will re-create the dib section and allocated memory. */
2153 surface->resource.allocatedMemory = NULL;
2154 surface->flags &= ~SFLAG_USERPTR;
2157 return WINED3D_OK;
2160 static const struct wined3d_surface_ops gdi_surface_ops =
2162 gdi_surface_private_setup,
2163 surface_gdi_cleanup,
2164 gdi_surface_realize_palette,
2165 gdi_surface_draw_overlay,
2166 gdi_surface_preload,
2167 gdi_surface_map,
2168 gdi_surface_unmap,
2169 gdi_surface_getdc,
2170 gdi_surface_flip,
2171 gdi_surface_set_mem,
2174 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2176 GLuint *name;
2177 DWORD flag;
2179 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2181 if(srgb)
2183 name = &surface->texture_name_srgb;
2184 flag = SFLAG_INSRGBTEX;
2186 else
2188 name = &surface->texture_name;
2189 flag = SFLAG_INTEXTURE;
2192 if (!*name && new_name)
2194 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2195 * surface has no texture name yet. See if we can get rid of this. */
2196 if (surface->flags & flag)
2197 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2198 surface_modify_location(surface, flag, FALSE);
2201 *name = new_name;
2202 surface_force_reload(surface);
2205 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2207 TRACE("surface %p, target %#x.\n", surface, target);
2209 if (surface->texture_target != target)
2211 if (target == GL_TEXTURE_RECTANGLE_ARB)
2213 surface->flags &= ~SFLAG_NORMCOORD;
2215 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2217 surface->flags |= SFLAG_NORMCOORD;
2220 surface->texture_target = target;
2221 surface_force_reload(surface);
2224 /* Context activation is done by the caller. */
2225 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
2227 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
2229 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2231 struct wined3d_texture *texture = surface->container.u.texture;
2233 TRACE("Passing to container (%p).\n", texture);
2234 texture->texture_ops->texture_bind(texture, gl_info, srgb);
2236 else
2238 if (surface->texture_level)
2240 ERR("Standalone surface %p is non-zero texture level %u.\n",
2241 surface, surface->texture_level);
2244 if (srgb)
2245 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2247 ENTER_GL();
2249 if (!surface->texture_name)
2251 glGenTextures(1, &surface->texture_name);
2252 checkGLcall("glGenTextures");
2254 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2256 glBindTexture(surface->texture_target, surface->texture_name);
2257 checkGLcall("glBindTexture");
2258 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2259 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2260 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2261 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2262 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2263 checkGLcall("glTexParameteri");
2265 else
2267 glBindTexture(surface->texture_target, surface->texture_name);
2268 checkGLcall("glBindTexture");
2271 LEAVE_GL();
2275 /* This call just downloads data, the caller is responsible for binding the
2276 * correct texture. */
2277 /* Context activation is done by the caller. */
2278 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2280 const struct wined3d_format *format = surface->resource.format;
2282 /* Only support read back of converted P8 surfaces. */
2283 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2285 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2286 return;
2289 ENTER_GL();
2291 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2293 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2294 surface, surface->texture_level, format->glFormat, format->glType,
2295 surface->resource.allocatedMemory);
2297 if (surface->flags & SFLAG_PBO)
2299 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2300 checkGLcall("glBindBufferARB");
2301 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2302 checkGLcall("glGetCompressedTexImageARB");
2303 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2304 checkGLcall("glBindBufferARB");
2306 else
2308 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2309 surface->texture_level, surface->resource.allocatedMemory));
2310 checkGLcall("glGetCompressedTexImageARB");
2313 LEAVE_GL();
2315 else
2317 void *mem;
2318 GLenum gl_format = format->glFormat;
2319 GLenum gl_type = format->glType;
2320 int src_pitch = 0;
2321 int dst_pitch = 0;
2323 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2324 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2326 gl_format = GL_ALPHA;
2327 gl_type = GL_UNSIGNED_BYTE;
2330 if (surface->flags & SFLAG_NONPOW2)
2332 unsigned char alignment = surface->resource.device->surface_alignment;
2333 src_pitch = format->byte_count * surface->pow2Width;
2334 dst_pitch = wined3d_surface_get_pitch(surface);
2335 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2336 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2338 else
2340 mem = surface->resource.allocatedMemory;
2343 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2344 surface, surface->texture_level, gl_format, gl_type, mem);
2346 if (surface->flags & SFLAG_PBO)
2348 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2349 checkGLcall("glBindBufferARB");
2351 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2352 checkGLcall("glGetTexImage");
2354 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2355 checkGLcall("glBindBufferARB");
2357 else
2359 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2360 checkGLcall("glGetTexImage");
2362 LEAVE_GL();
2364 if (surface->flags & SFLAG_NONPOW2)
2366 const BYTE *src_data;
2367 BYTE *dst_data;
2368 UINT y;
2370 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2371 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2372 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2374 * We're doing this...
2376 * instead of boxing the texture :
2377 * |<-texture width ->| -->pow2width| /\
2378 * |111111111111111111| | |
2379 * |222 Texture 222222| boxed empty | texture height
2380 * |3333 Data 33333333| | |
2381 * |444444444444444444| | \/
2382 * ----------------------------------- |
2383 * | boxed empty | boxed empty | pow2height
2384 * | | | \/
2385 * -----------------------------------
2388 * we're repacking the data to the expected texture width
2390 * |<-texture width ->| -->pow2width| /\
2391 * |111111111111111111222222222222222| |
2392 * |222333333333333333333444444444444| texture height
2393 * |444444 | |
2394 * | | \/
2395 * | | |
2396 * | empty | pow2height
2397 * | | \/
2398 * -----------------------------------
2400 * == is the same as
2402 * |<-texture width ->| /\
2403 * |111111111111111111|
2404 * |222222222222222222|texture height
2405 * |333333333333333333|
2406 * |444444444444444444| \/
2407 * --------------------
2409 * this also means that any references to allocatedMemory should work with the data as if were a
2410 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2412 * internally the texture is still stored in a boxed format so any references to textureName will
2413 * get a boxed texture with width pow2width and not a texture of width resource.width.
2415 * Performance should not be an issue, because applications normally do not lock the surfaces when
2416 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2417 * and doesn't have to be re-read. */
2418 src_data = mem;
2419 dst_data = surface->resource.allocatedMemory;
2420 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2421 for (y = 1; y < surface->resource.height; ++y)
2423 /* skip the first row */
2424 src_data += src_pitch;
2425 dst_data += dst_pitch;
2426 memcpy(dst_data, src_data, dst_pitch);
2429 HeapFree(GetProcessHeap(), 0, mem);
2433 /* Surface has now been downloaded */
2434 surface->flags |= SFLAG_INSYSMEM;
2437 /* This call just uploads data, the caller is responsible for binding the
2438 * correct texture. */
2439 /* Context activation is done by the caller. */
2440 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2441 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2442 BOOL srgb, const struct wined3d_bo_address *data)
2444 UINT update_w = src_rect->right - src_rect->left;
2445 UINT update_h = src_rect->bottom - src_rect->top;
2447 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2448 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2449 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2451 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2452 update_h *= format->heightscale;
2454 ENTER_GL();
2456 if (data->buffer_object)
2458 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2459 checkGLcall("glBindBufferARB");
2462 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2464 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2465 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2466 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2467 const BYTE *addr = data->addr;
2468 GLenum internal;
2470 addr += (src_rect->top / format->block_height) * src_pitch;
2471 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2473 if (srgb)
2474 internal = format->glGammaInternal;
2475 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2476 internal = format->rtInternal;
2477 else
2478 internal = format->glInternal;
2480 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2481 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2482 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2484 if (row_length == src_pitch)
2486 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2487 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2489 else
2491 UINT row, y;
2493 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2494 * can't use the unpack row length like below. */
2495 for (row = 0, y = dst_point->y; row < row_count; ++row)
2497 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2498 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2499 y += format->block_height;
2500 addr += src_pitch;
2503 checkGLcall("glCompressedTexSubImage2DARB");
2505 else
2507 const BYTE *addr = data->addr;
2509 addr += src_rect->top * src_w * format->byte_count;
2510 addr += src_rect->left * format->byte_count;
2512 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2513 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2514 update_w, update_h, format->glFormat, format->glType, addr);
2516 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2517 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2518 update_w, update_h, format->glFormat, format->glType, addr);
2519 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2520 checkGLcall("glTexSubImage2D");
2523 if (data->buffer_object)
2525 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2526 checkGLcall("glBindBufferARB");
2529 LEAVE_GL();
2531 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2533 struct wined3d_device *device = surface->resource.device;
2534 unsigned int i;
2536 for (i = 0; i < device->context_count; ++i)
2538 context_surface_update(device->contexts[i], surface);
2543 /* This call just allocates the texture, the caller is responsible for binding
2544 * the correct texture. */
2545 /* Context activation is done by the caller. */
2546 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2547 const struct wined3d_format *format, BOOL srgb)
2549 BOOL enable_client_storage = FALSE;
2550 GLsizei width = surface->pow2Width;
2551 GLsizei height = surface->pow2Height;
2552 const BYTE *mem = NULL;
2553 GLenum internal;
2555 if (srgb)
2557 internal = format->glGammaInternal;
2559 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2561 internal = format->rtInternal;
2563 else
2565 internal = format->glInternal;
2568 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2570 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n",
2571 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2572 internal, width, height, format->glFormat, format->glType);
2574 ENTER_GL();
2576 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2578 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2579 || !surface->resource.allocatedMemory)
2581 /* In some cases we want to disable client storage.
2582 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2583 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2584 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2585 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2587 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2588 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2589 surface->flags &= ~SFLAG_CLIENT;
2590 enable_client_storage = TRUE;
2592 else
2594 surface->flags |= SFLAG_CLIENT;
2596 /* Point OpenGL to our allocated texture memory. Do not use
2597 * resource.allocatedMemory here because it might point into a
2598 * PBO. Instead use heapMemory, but get the alignment right. */
2599 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2600 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2604 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2606 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2607 internal, width, height, 0, surface->resource.size, mem));
2608 checkGLcall("glCompressedTexImage2DARB");
2610 else
2612 glTexImage2D(surface->texture_target, surface->texture_level,
2613 internal, width, height, 0, format->glFormat, format->glType, mem);
2614 checkGLcall("glTexImage2D");
2617 if(enable_client_storage) {
2618 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2619 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2621 LEAVE_GL();
2624 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2625 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2626 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2627 /* GL locking is done by the caller */
2628 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2630 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2631 struct wined3d_renderbuffer_entry *entry;
2632 GLuint renderbuffer = 0;
2633 unsigned int src_width, src_height;
2634 unsigned int width, height;
2636 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2638 width = rt->pow2Width;
2639 height = rt->pow2Height;
2641 else
2643 width = surface->pow2Width;
2644 height = surface->pow2Height;
2647 src_width = surface->pow2Width;
2648 src_height = surface->pow2Height;
2650 /* A depth stencil smaller than the render target is not valid */
2651 if (width > src_width || height > src_height) return;
2653 /* Remove any renderbuffer set if the sizes match */
2654 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2655 || (width == src_width && height == src_height))
2657 surface->current_renderbuffer = NULL;
2658 return;
2661 /* Look if we've already got a renderbuffer of the correct dimensions */
2662 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2664 if (entry->width == width && entry->height == height)
2666 renderbuffer = entry->id;
2667 surface->current_renderbuffer = entry;
2668 break;
2672 if (!renderbuffer)
2674 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2675 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2676 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2677 surface->resource.format->glInternal, width, height);
2679 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2680 entry->width = width;
2681 entry->height = height;
2682 entry->id = renderbuffer;
2683 list_add_head(&surface->renderbuffers, &entry->entry);
2685 surface->current_renderbuffer = entry;
2688 checkGLcall("set_compatible_renderbuffer");
2691 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2693 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2695 TRACE("surface %p.\n", surface);
2697 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2699 ERR("Surface %p is not on a swapchain.\n", surface);
2700 return GL_NONE;
2703 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2705 if (swapchain->render_to_fbo)
2707 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2708 return GL_COLOR_ATTACHMENT0;
2710 TRACE("Returning GL_BACK\n");
2711 return GL_BACK;
2713 else if (surface == swapchain->front_buffer)
2715 TRACE("Returning GL_FRONT\n");
2716 return GL_FRONT;
2719 FIXME("Higher back buffer, returning GL_BACK\n");
2720 return GL_BACK;
2723 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2724 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2726 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2728 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2729 /* No partial locking for textures yet. */
2730 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2732 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2733 if (dirty_rect)
2735 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2736 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2737 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2738 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2740 else
2742 surface->dirtyRect.left = 0;
2743 surface->dirtyRect.top = 0;
2744 surface->dirtyRect.right = surface->resource.width;
2745 surface->dirtyRect.bottom = surface->resource.height;
2748 /* if the container is a texture then mark it dirty. */
2749 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2751 TRACE("Passing to container.\n");
2752 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2756 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2758 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2759 BOOL ck_changed;
2761 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2763 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2765 ERR("Not supported on scratch surfaces.\n");
2766 return WINED3DERR_INVALIDCALL;
2769 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2771 /* Reload if either the texture and sysmem have different ideas about the
2772 * color key, or the actual key values changed. */
2773 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2774 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2775 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2777 TRACE("Reloading because of color keying\n");
2778 /* To perform the color key conversion we need a sysmem copy of
2779 * the surface. Make sure we have it. */
2781 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2782 /* Make sure the texture is reloaded because of the color key change,
2783 * this kills performance though :( */
2784 /* TODO: This is not necessarily needed with hw palettized texture support. */
2785 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2786 /* Switching color keying on / off may change the internal format. */
2787 if (ck_changed)
2788 surface_force_reload(surface);
2790 else if (!(surface->flags & flag))
2792 TRACE("Reloading because surface is dirty.\n");
2794 else
2796 TRACE("surface is already in texture\n");
2797 return WINED3D_OK;
2800 /* No partial locking for textures yet. */
2801 surface_load_location(surface, flag, NULL);
2802 surface_evict_sysmem(surface);
2804 return WINED3D_OK;
2807 /* See also float_16_to_32() in wined3d_private.h */
2808 static inline unsigned short float_32_to_16(const float *in)
2810 int exp = 0;
2811 float tmp = fabsf(*in);
2812 unsigned int mantissa;
2813 unsigned short ret;
2815 /* Deal with special numbers */
2816 if (*in == 0.0f)
2817 return 0x0000;
2818 if (isnan(*in))
2819 return 0x7c01;
2820 if (isinf(*in))
2821 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2823 if (tmp < powf(2, 10))
2827 tmp = tmp * 2.0f;
2828 exp--;
2829 } while (tmp < powf(2, 10));
2831 else if (tmp >= powf(2, 11))
2835 tmp /= 2.0f;
2836 exp++;
2837 } while (tmp >= powf(2, 11));
2840 mantissa = (unsigned int)tmp;
2841 if (tmp - mantissa >= 0.5f)
2842 ++mantissa; /* Round to nearest, away from zero. */
2844 exp += 10; /* Normalize the mantissa. */
2845 exp += 15; /* Exponent is encoded with excess 15. */
2847 if (exp > 30) /* too big */
2849 ret = 0x7c00; /* INF */
2851 else if (exp <= 0)
2853 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2854 while (exp <= 0)
2856 mantissa = mantissa >> 1;
2857 ++exp;
2859 ret = mantissa & 0x3ff;
2861 else
2863 ret = (exp << 10) | (mantissa & 0x3ff);
2866 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2867 return ret;
2870 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2872 ULONG refcount;
2874 TRACE("Surface %p, container %p of type %#x.\n",
2875 surface, surface->container.u.base, surface->container.type);
2877 switch (surface->container.type)
2879 case WINED3D_CONTAINER_TEXTURE:
2880 return wined3d_texture_incref(surface->container.u.texture);
2882 case WINED3D_CONTAINER_SWAPCHAIN:
2883 return wined3d_swapchain_incref(surface->container.u.swapchain);
2885 default:
2886 ERR("Unhandled container type %#x.\n", surface->container.type);
2887 case WINED3D_CONTAINER_NONE:
2888 break;
2891 refcount = InterlockedIncrement(&surface->resource.ref);
2892 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2894 return refcount;
2897 /* Do not call while under the GL lock. */
2898 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2900 ULONG refcount;
2902 TRACE("Surface %p, container %p of type %#x.\n",
2903 surface, surface->container.u.base, surface->container.type);
2905 switch (surface->container.type)
2907 case WINED3D_CONTAINER_TEXTURE:
2908 return wined3d_texture_decref(surface->container.u.texture);
2910 case WINED3D_CONTAINER_SWAPCHAIN:
2911 return wined3d_swapchain_decref(surface->container.u.swapchain);
2913 default:
2914 ERR("Unhandled container type %#x.\n", surface->container.type);
2915 case WINED3D_CONTAINER_NONE:
2916 break;
2919 refcount = InterlockedDecrement(&surface->resource.ref);
2920 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2922 if (!refcount)
2924 surface->surface_ops->surface_cleanup(surface);
2925 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2927 TRACE("Destroyed surface %p.\n", surface);
2928 HeapFree(GetProcessHeap(), 0, surface);
2931 return refcount;
2934 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2936 return resource_set_priority(&surface->resource, priority);
2939 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2941 return resource_get_priority(&surface->resource);
2944 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2946 TRACE("surface %p.\n", surface);
2948 surface->surface_ops->surface_preload(surface);
2951 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2953 TRACE("surface %p.\n", surface);
2955 return surface->resource.parent;
2958 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2960 TRACE("surface %p.\n", surface);
2962 return &surface->resource;
2965 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2967 TRACE("surface %p, flags %#x.\n", surface, flags);
2969 switch (flags)
2971 case WINEDDGBS_CANBLT:
2972 case WINEDDGBS_ISBLTDONE:
2973 return WINED3D_OK;
2975 default:
2976 return WINED3DERR_INVALIDCALL;
2980 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2982 TRACE("surface %p, flags %#x.\n", surface, flags);
2984 /* XXX: DDERR_INVALIDSURFACETYPE */
2986 switch (flags)
2988 case WINEDDGFS_CANFLIP:
2989 case WINEDDGFS_ISFLIPDONE:
2990 return WINED3D_OK;
2992 default:
2993 return WINED3DERR_INVALIDCALL;
2997 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2999 TRACE("surface %p.\n", surface);
3001 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
3002 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3005 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
3007 TRACE("surface %p.\n", surface);
3009 /* So far we don't lose anything :) */
3010 surface->flags &= ~SFLAG_LOST;
3011 return WINED3D_OK;
3014 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3016 TRACE("surface %p, palette %p.\n", surface, palette);
3018 if (surface->palette == palette)
3020 TRACE("Nop palette change.\n");
3021 return WINED3D_OK;
3024 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3025 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3027 surface->palette = palette;
3029 if (palette)
3031 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3032 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3034 surface->surface_ops->surface_realize_palette(surface);
3037 return WINED3D_OK;
3040 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3041 DWORD flags, const WINEDDCOLORKEY *color_key)
3043 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3045 if (flags & WINEDDCKEY_COLORSPACE)
3047 FIXME(" colorkey value not supported (%08x) !\n", flags);
3048 return WINED3DERR_INVALIDCALL;
3051 /* Dirtify the surface, but only if a key was changed. */
3052 if (color_key)
3054 switch (flags & ~WINEDDCKEY_COLORSPACE)
3056 case WINEDDCKEY_DESTBLT:
3057 surface->DestBltCKey = *color_key;
3058 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3059 break;
3061 case WINEDDCKEY_DESTOVERLAY:
3062 surface->DestOverlayCKey = *color_key;
3063 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3064 break;
3066 case WINEDDCKEY_SRCOVERLAY:
3067 surface->SrcOverlayCKey = *color_key;
3068 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3069 break;
3071 case WINEDDCKEY_SRCBLT:
3072 surface->SrcBltCKey = *color_key;
3073 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3074 break;
3077 else
3079 switch (flags & ~WINEDDCKEY_COLORSPACE)
3081 case WINEDDCKEY_DESTBLT:
3082 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3083 break;
3085 case WINEDDCKEY_DESTOVERLAY:
3086 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3087 break;
3089 case WINEDDCKEY_SRCOVERLAY:
3090 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3091 break;
3093 case WINEDDCKEY_SRCBLT:
3094 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3095 break;
3099 return WINED3D_OK;
3102 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3104 TRACE("surface %p.\n", surface);
3106 return surface->palette;
3109 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3111 const struct wined3d_format *format = surface->resource.format;
3112 DWORD pitch;
3114 TRACE("surface %p.\n", surface);
3116 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3118 /* Since compressed formats are block based, pitch means the amount of
3119 * bytes to the next row of block rather than the next row of pixels. */
3120 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3121 pitch = row_block_count * format->block_byte_count;
3123 else
3125 unsigned char alignment = surface->resource.device->surface_alignment;
3126 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3127 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3130 TRACE("Returning %u.\n", pitch);
3132 return pitch;
3135 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3137 TRACE("surface %p, mem %p.\n", surface, mem);
3139 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3141 WARN("Surface is locked or the DC is in use.\n");
3142 return WINED3DERR_INVALIDCALL;
3145 return surface->surface_ops->surface_set_mem(surface, mem);
3148 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3150 LONG w, h;
3152 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3154 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3156 WARN("Not an overlay surface.\n");
3157 return WINEDDERR_NOTAOVERLAYSURFACE;
3160 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3161 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3162 surface->overlay_destrect.left = x;
3163 surface->overlay_destrect.top = y;
3164 surface->overlay_destrect.right = x + w;
3165 surface->overlay_destrect.bottom = y + h;
3167 surface->surface_ops->surface_draw_overlay(surface);
3169 return WINED3D_OK;
3172 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3174 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3176 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3178 TRACE("Not an overlay surface.\n");
3179 return WINEDDERR_NOTAOVERLAYSURFACE;
3182 if (!surface->overlay_dest)
3184 TRACE("Overlay not visible.\n");
3185 *x = 0;
3186 *y = 0;
3187 return WINEDDERR_OVERLAYNOTVISIBLE;
3190 *x = surface->overlay_destrect.left;
3191 *y = surface->overlay_destrect.top;
3193 TRACE("Returning position %d, %d.\n", *x, *y);
3195 return WINED3D_OK;
3198 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3199 DWORD flags, struct wined3d_surface *ref)
3201 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3203 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3205 TRACE("Not an overlay surface.\n");
3206 return WINEDDERR_NOTAOVERLAYSURFACE;
3209 return WINED3D_OK;
3212 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3213 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3215 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3216 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3218 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3220 WARN("Not an overlay surface.\n");
3221 return WINEDDERR_NOTAOVERLAYSURFACE;
3223 else if (!dst_surface)
3225 WARN("Dest surface is NULL.\n");
3226 return WINED3DERR_INVALIDCALL;
3229 if (src_rect)
3231 surface->overlay_srcrect = *src_rect;
3233 else
3235 surface->overlay_srcrect.left = 0;
3236 surface->overlay_srcrect.top = 0;
3237 surface->overlay_srcrect.right = surface->resource.width;
3238 surface->overlay_srcrect.bottom = surface->resource.height;
3241 if (dst_rect)
3243 surface->overlay_destrect = *dst_rect;
3245 else
3247 surface->overlay_destrect.left = 0;
3248 surface->overlay_destrect.top = 0;
3249 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3250 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3253 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3255 list_remove(&surface->overlay_entry);
3258 if (flags & WINEDDOVER_SHOW)
3260 if (surface->overlay_dest != dst_surface)
3262 surface->overlay_dest = dst_surface;
3263 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3266 else if (flags & WINEDDOVER_HIDE)
3268 /* tests show that the rectangles are erased on hide */
3269 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3270 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3271 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3272 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3273 surface->overlay_dest = NULL;
3276 surface->surface_ops->surface_draw_overlay(surface);
3278 return WINED3D_OK;
3281 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3283 TRACE("surface %p, clipper %p.\n", surface, clipper);
3285 surface->clipper = clipper;
3287 return WINED3D_OK;
3290 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3292 TRACE("surface %p.\n", surface);
3294 return surface->clipper;
3297 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3299 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3301 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3303 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3305 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3306 return WINED3DERR_INVALIDCALL;
3309 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3310 surface->pow2Width, surface->pow2Height);
3311 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3312 surface->resource.format = format;
3314 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3315 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3316 format->glFormat, format->glInternal, format->glType);
3318 return WINED3D_OK;
3321 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3322 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3324 unsigned short *dst_s;
3325 const float *src_f;
3326 unsigned int x, y;
3328 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3330 for (y = 0; y < h; ++y)
3332 src_f = (const float *)(src + y * pitch_in);
3333 dst_s = (unsigned short *) (dst + y * pitch_out);
3334 for (x = 0; x < w; ++x)
3336 dst_s[x] = float_32_to_16(src_f + x);
3341 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3342 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3344 static const unsigned char convert_5to8[] =
3346 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3347 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3348 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3349 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3351 static const unsigned char convert_6to8[] =
3353 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3354 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3355 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3356 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3357 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3358 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3359 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3360 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3362 unsigned int x, y;
3364 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3366 for (y = 0; y < h; ++y)
3368 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3369 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3370 for (x = 0; x < w; ++x)
3372 WORD pixel = src_line[x];
3373 dst_line[x] = 0xff000000
3374 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3375 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3376 | convert_5to8[(pixel & 0x001f)];
3381 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3382 * in both cases we're just setting the X / Alpha channel to 0xff. */
3383 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3384 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3386 unsigned int x, y;
3388 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3390 for (y = 0; y < h; ++y)
3392 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3393 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3395 for (x = 0; x < w; ++x)
3397 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3402 static inline BYTE cliptobyte(int x)
3404 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3407 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3408 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3410 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3411 unsigned int x, y;
3413 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3415 for (y = 0; y < h; ++y)
3417 const BYTE *src_line = src + y * pitch_in;
3418 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3419 for (x = 0; x < w; ++x)
3421 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3422 * C = Y - 16; D = U - 128; E = V - 128;
3423 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3424 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3425 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3426 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3427 * U and V are shared between the pixels. */
3428 if (!(x & 1)) /* For every even pixel, read new U and V. */
3430 d = (int) src_line[1] - 128;
3431 e = (int) src_line[3] - 128;
3432 r2 = 409 * e + 128;
3433 g2 = - 100 * d - 208 * e + 128;
3434 b2 = 516 * d + 128;
3436 c2 = 298 * ((int) src_line[0] - 16);
3437 dst_line[x] = 0xff000000
3438 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3439 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3440 | cliptobyte((c2 + b2) >> 8); /* blue */
3441 /* Scale RGB values to 0..255 range,
3442 * then clip them if still not in range (may be negative),
3443 * then shift them within DWORD if necessary. */
3444 src_line += 2;
3449 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3450 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3452 unsigned int x, y;
3453 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3455 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3457 for (y = 0; y < h; ++y)
3459 const BYTE *src_line = src + y * pitch_in;
3460 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3461 for (x = 0; x < w; ++x)
3463 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3464 * C = Y - 16; D = U - 128; E = V - 128;
3465 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3466 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3467 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3468 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3469 * U and V are shared between the pixels. */
3470 if (!(x & 1)) /* For every even pixel, read new U and V. */
3472 d = (int) src_line[1] - 128;
3473 e = (int) src_line[3] - 128;
3474 r2 = 409 * e + 128;
3475 g2 = - 100 * d - 208 * e + 128;
3476 b2 = 516 * d + 128;
3478 c2 = 298 * ((int) src_line[0] - 16);
3479 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3480 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3481 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3482 /* Scale RGB values to 0..255 range,
3483 * then clip them if still not in range (may be negative),
3484 * then shift them within DWORD if necessary. */
3485 src_line += 2;
3490 struct d3dfmt_convertor_desc
3492 enum wined3d_format_id from, to;
3493 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3496 static const struct d3dfmt_convertor_desc convertors[] =
3498 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3499 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3500 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3501 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3502 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3503 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3506 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3507 enum wined3d_format_id to)
3509 unsigned int i;
3511 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3513 if (convertors[i].from == from && convertors[i].to == to)
3514 return &convertors[i];
3517 return NULL;
3520 /*****************************************************************************
3521 * surface_convert_format
3523 * Creates a duplicate of a surface in a different format. Is used by Blt to
3524 * blit between surfaces with different formats.
3526 * Parameters
3527 * source: Source surface
3528 * fmt: Requested destination format
3530 *****************************************************************************/
3531 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3533 const struct d3dfmt_convertor_desc *conv;
3534 WINED3DLOCKED_RECT lock_src, lock_dst;
3535 struct wined3d_surface *ret = NULL;
3536 HRESULT hr;
3538 conv = find_convertor(source->resource.format->id, to_fmt);
3539 if (!conv)
3541 FIXME("Cannot find a conversion function from format %s to %s.\n",
3542 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3543 return NULL;
3546 wined3d_surface_create(source->resource.device, source->resource.width,
3547 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3548 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3549 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3550 if (!ret)
3552 ERR("Failed to create a destination surface for conversion.\n");
3553 return NULL;
3556 memset(&lock_src, 0, sizeof(lock_src));
3557 memset(&lock_dst, 0, sizeof(lock_dst));
3559 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3560 if (FAILED(hr))
3562 ERR("Failed to lock the source surface.\n");
3563 wined3d_surface_decref(ret);
3564 return NULL;
3566 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3567 if (FAILED(hr))
3569 ERR("Failed to lock the destination surface.\n");
3570 wined3d_surface_unmap(source);
3571 wined3d_surface_decref(ret);
3572 return NULL;
3575 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3576 source->resource.width, source->resource.height);
3578 wined3d_surface_unmap(ret);
3579 wined3d_surface_unmap(source);
3581 return ret;
3584 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3585 unsigned int bpp, UINT pitch, DWORD color)
3587 BYTE *first;
3588 int x, y;
3590 /* Do first row */
3592 #define COLORFILL_ROW(type) \
3593 do { \
3594 type *d = (type *)buf; \
3595 for (x = 0; x < width; ++x) \
3596 d[x] = (type)color; \
3597 } while(0)
3599 switch (bpp)
3601 case 1:
3602 COLORFILL_ROW(BYTE);
3603 break;
3605 case 2:
3606 COLORFILL_ROW(WORD);
3607 break;
3609 case 3:
3611 BYTE *d = buf;
3612 for (x = 0; x < width; ++x, d += 3)
3614 d[0] = (color ) & 0xFF;
3615 d[1] = (color >> 8) & 0xFF;
3616 d[2] = (color >> 16) & 0xFF;
3618 break;
3620 case 4:
3621 COLORFILL_ROW(DWORD);
3622 break;
3624 default:
3625 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3626 return WINED3DERR_NOTAVAILABLE;
3629 #undef COLORFILL_ROW
3631 /* Now copy first row. */
3632 first = buf;
3633 for (y = 1; y < height; ++y)
3635 buf += pitch;
3636 memcpy(buf, first, width * bpp);
3639 return WINED3D_OK;
3642 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3644 TRACE("surface %p.\n", surface);
3646 if (!(surface->flags & SFLAG_LOCKED))
3648 WARN("Trying to unmap unmapped surface.\n");
3649 return WINEDDERR_NOTLOCKED;
3651 surface->flags &= ~SFLAG_LOCKED;
3653 surface->surface_ops->surface_unmap(surface);
3655 return WINED3D_OK;
3658 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3659 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3661 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3662 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3664 if (surface->flags & SFLAG_LOCKED)
3666 WARN("Surface is already mapped.\n");
3667 return WINED3DERR_INVALIDCALL;
3669 surface->flags |= SFLAG_LOCKED;
3671 if (!(surface->flags & SFLAG_LOCKABLE))
3672 WARN("Trying to lock unlockable surface.\n");
3674 surface->surface_ops->surface_map(surface, rect, flags);
3676 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3678 if (!rect)
3680 locked_rect->pBits = surface->resource.allocatedMemory;
3681 surface->lockedRect.left = 0;
3682 surface->lockedRect.top = 0;
3683 surface->lockedRect.right = surface->resource.width;
3684 surface->lockedRect.bottom = surface->resource.height;
3686 else
3688 const struct wined3d_format *format = surface->resource.format;
3690 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3692 /* Compressed textures are block based, so calculate the offset of
3693 * the block that contains the top-left pixel of the locked rectangle. */
3694 locked_rect->pBits = surface->resource.allocatedMemory
3695 + ((rect->top / format->block_height) * locked_rect->Pitch)
3696 + ((rect->left / format->block_width) * format->block_byte_count);
3698 else
3700 locked_rect->pBits = surface->resource.allocatedMemory
3701 + (locked_rect->Pitch * rect->top)
3702 + (rect->left * format->byte_count);
3704 surface->lockedRect.left = rect->left;
3705 surface->lockedRect.top = rect->top;
3706 surface->lockedRect.right = rect->right;
3707 surface->lockedRect.bottom = rect->bottom;
3710 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3711 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3713 return WINED3D_OK;
3716 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3718 HRESULT hr;
3720 TRACE("surface %p, dc %p.\n", surface, dc);
3722 if (surface->flags & SFLAG_USERPTR)
3724 ERR("Not supported on surfaces with application-provided memory.\n");
3725 return WINEDDERR_NODC;
3728 /* Give more detailed info for ddraw. */
3729 if (surface->flags & SFLAG_DCINUSE)
3730 return WINEDDERR_DCALREADYCREATED;
3732 /* Can't GetDC if the surface is locked. */
3733 if (surface->flags & SFLAG_LOCKED)
3734 return WINED3DERR_INVALIDCALL;
3736 hr = surface->surface_ops->surface_getdc(surface);
3737 if (FAILED(hr))
3738 return hr;
3740 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3741 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3743 /* GetDC on palettized formats is unsupported in D3D9, and the method
3744 * is missing in D3D8, so this should only be used for DX <=7
3745 * surfaces (with non-device palettes). */
3746 const PALETTEENTRY *pal = NULL;
3748 if (surface->palette)
3750 pal = surface->palette->palents;
3752 else
3754 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3755 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3757 if (dds_primary && dds_primary->palette)
3758 pal = dds_primary->palette->palents;
3761 if (pal)
3763 RGBQUAD col[256];
3764 unsigned int i;
3766 for (i = 0; i < 256; ++i)
3768 col[i].rgbRed = pal[i].peRed;
3769 col[i].rgbGreen = pal[i].peGreen;
3770 col[i].rgbBlue = pal[i].peBlue;
3771 col[i].rgbReserved = 0;
3773 SetDIBColorTable(surface->hDC, 0, 256, col);
3777 surface->flags |= SFLAG_DCINUSE;
3779 *dc = surface->hDC;
3780 TRACE("Returning dc %p.\n", *dc);
3782 return WINED3D_OK;
3785 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3787 TRACE("surface %p, dc %p.\n", surface, dc);
3789 if (!(surface->flags & SFLAG_DCINUSE))
3790 return WINEDDERR_NODC;
3792 if (surface->hDC != dc)
3794 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3795 dc, surface->hDC);
3796 return WINEDDERR_NODC;
3799 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3801 /* Copy the contents of the DIB over to the PBO. */
3802 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3805 /* We locked first, so unlock now. */
3806 wined3d_surface_unmap(surface);
3808 surface->flags &= ~SFLAG_DCINUSE;
3810 return WINED3D_OK;
3813 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3815 struct wined3d_swapchain *swapchain;
3816 HRESULT hr;
3818 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3820 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3822 ERR("Flipped surface is not on a swapchain.\n");
3823 return WINEDDERR_NOTFLIPPABLE;
3825 swapchain = surface->container.u.swapchain;
3827 hr = surface->surface_ops->surface_flip(surface, override);
3828 if (FAILED(hr))
3829 return hr;
3831 /* Just overwrite the swapchain presentation interval. This is ok because
3832 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3833 * specify the presentation interval. */
3834 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3835 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3836 else if (flags & WINEDDFLIP_NOVSYNC)
3837 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3838 else if (flags & WINEDDFLIP_INTERVAL2)
3839 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3840 else if (flags & WINEDDFLIP_INTERVAL3)
3841 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3842 else
3843 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3845 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3848 /* Do not call while under the GL lock. */
3849 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3851 struct wined3d_device *device = surface->resource.device;
3853 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3855 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3857 struct wined3d_texture *texture = surface->container.u.texture;
3859 TRACE("Passing to container (%p).\n", texture);
3860 texture->texture_ops->texture_preload(texture, srgb);
3862 else
3864 struct wined3d_context *context = NULL;
3866 TRACE("(%p) : About to load surface\n", surface);
3868 if (!device->isInDraw) context = context_acquire(device, NULL);
3870 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3871 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3873 if (palette9_changed(surface))
3875 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3876 /* TODO: This is not necessarily needed with hw palettized texture support */
3877 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3878 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3879 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3883 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3885 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3887 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3888 GLclampf tmp;
3889 tmp = 0.9f;
3890 ENTER_GL();
3891 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3892 LEAVE_GL();
3895 if (context) context_release(context);
3899 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3901 if (!surface->resource.allocatedMemory)
3903 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3904 surface->resource.size + RESOURCE_ALIGNMENT);
3905 if (!surface->resource.heapMemory)
3907 ERR("Out of memory\n");
3908 return FALSE;
3910 surface->resource.allocatedMemory =
3911 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3913 else
3915 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3918 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3920 return TRUE;
3923 /* Read the framebuffer back into the surface */
3924 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3926 struct wined3d_device *device = surface->resource.device;
3927 const struct wined3d_gl_info *gl_info;
3928 struct wined3d_context *context;
3929 BYTE *mem;
3930 GLint fmt;
3931 GLint type;
3932 BYTE *row, *top, *bottom;
3933 int i;
3934 BOOL bpp;
3935 RECT local_rect;
3936 BOOL srcIsUpsideDown;
3937 GLint rowLen = 0;
3938 GLint skipPix = 0;
3939 GLint skipRow = 0;
3941 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3942 static BOOL warned = FALSE;
3943 if(!warned) {
3944 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3945 warned = TRUE;
3947 return;
3950 context = context_acquire(device, surface);
3951 context_apply_blit_state(context, device);
3952 gl_info = context->gl_info;
3954 ENTER_GL();
3956 /* Select the correct read buffer, and give some debug output.
3957 * There is no need to keep track of the current read buffer or reset it, every part of the code
3958 * that reads sets the read buffer as desired.
3960 if (surface_is_offscreen(surface))
3962 /* Mapping the primary render target which is not on a swapchain.
3963 * Read from the back buffer. */
3964 TRACE("Mapping offscreen render target.\n");
3965 glReadBuffer(device->offscreenBuffer);
3966 srcIsUpsideDown = TRUE;
3968 else
3970 /* Onscreen surfaces are always part of a swapchain */
3971 GLenum buffer = surface_get_gl_buffer(surface);
3972 TRACE("Mapping %#x buffer.\n", buffer);
3973 glReadBuffer(buffer);
3974 checkGLcall("glReadBuffer");
3975 srcIsUpsideDown = FALSE;
3978 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3979 if (!rect)
3981 local_rect.left = 0;
3982 local_rect.top = 0;
3983 local_rect.right = surface->resource.width;
3984 local_rect.bottom = surface->resource.height;
3986 else
3988 local_rect = *rect;
3990 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3992 switch (surface->resource.format->id)
3994 case WINED3DFMT_P8_UINT:
3996 if (primary_render_target_is_p8(device))
3998 /* In case of P8 render targets the index is stored in the alpha component */
3999 fmt = GL_ALPHA;
4000 type = GL_UNSIGNED_BYTE;
4001 mem = dest;
4002 bpp = surface->resource.format->byte_count;
4004 else
4006 /* GL can't return palettized data, so read ARGB pixels into a
4007 * separate block of memory and convert them into palettized format
4008 * in software. Slow, but if the app means to use palettized render
4009 * targets and locks it...
4011 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4012 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4013 * for the color channels when palettizing the colors.
4015 fmt = GL_RGB;
4016 type = GL_UNSIGNED_BYTE;
4017 pitch *= 3;
4018 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4019 if (!mem)
4021 ERR("Out of memory\n");
4022 LEAVE_GL();
4023 return;
4025 bpp = surface->resource.format->byte_count * 3;
4028 break;
4030 default:
4031 mem = dest;
4032 fmt = surface->resource.format->glFormat;
4033 type = surface->resource.format->glType;
4034 bpp = surface->resource.format->byte_count;
4037 if (surface->flags & SFLAG_PBO)
4039 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4040 checkGLcall("glBindBufferARB");
4041 if (mem)
4043 ERR("mem not null for pbo -- unexpected\n");
4044 mem = NULL;
4048 /* Save old pixel store pack state */
4049 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4050 checkGLcall("glGetIntegerv");
4051 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4052 checkGLcall("glGetIntegerv");
4053 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4054 checkGLcall("glGetIntegerv");
4056 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4057 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4058 checkGLcall("glPixelStorei");
4059 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4060 checkGLcall("glPixelStorei");
4061 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4062 checkGLcall("glPixelStorei");
4064 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4065 local_rect.right - local_rect.left,
4066 local_rect.bottom - local_rect.top,
4067 fmt, type, mem);
4068 checkGLcall("glReadPixels");
4070 /* Reset previous pixel store pack state */
4071 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4072 checkGLcall("glPixelStorei");
4073 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4074 checkGLcall("glPixelStorei");
4075 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4076 checkGLcall("glPixelStorei");
4078 if (surface->flags & SFLAG_PBO)
4080 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4081 checkGLcall("glBindBufferARB");
4083 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4084 * to get a pointer to it and perform the flipping in software. This is a lot
4085 * faster than calling glReadPixels for each line. In case we want more speed
4086 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4087 if (!srcIsUpsideDown)
4089 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4090 checkGLcall("glBindBufferARB");
4092 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4093 checkGLcall("glMapBufferARB");
4097 /* TODO: Merge this with the palettization loop below for P8 targets */
4098 if(!srcIsUpsideDown) {
4099 UINT len, off;
4100 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4101 Flip the lines in software */
4102 len = (local_rect.right - local_rect.left) * bpp;
4103 off = local_rect.left * bpp;
4105 row = HeapAlloc(GetProcessHeap(), 0, len);
4106 if(!row) {
4107 ERR("Out of memory\n");
4108 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4109 HeapFree(GetProcessHeap(), 0, mem);
4110 LEAVE_GL();
4111 return;
4114 top = mem + pitch * local_rect.top;
4115 bottom = mem + pitch * (local_rect.bottom - 1);
4116 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4117 memcpy(row, top + off, len);
4118 memcpy(top + off, bottom + off, len);
4119 memcpy(bottom + off, row, len);
4120 top += pitch;
4121 bottom -= pitch;
4123 HeapFree(GetProcessHeap(), 0, row);
4125 /* Unmap the temp PBO buffer */
4126 if (surface->flags & SFLAG_PBO)
4128 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4129 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4133 LEAVE_GL();
4134 context_release(context);
4136 /* For P8 textures we need to perform an inverse palette lookup. This is
4137 * done by searching for a palette index which matches the RGB value.
4138 * Note this isn't guaranteed to work when there are multiple entries for
4139 * the same color but we have no choice. In case of P8 render targets,
4140 * the index is stored in the alpha component so no conversion is needed. */
4141 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4143 const PALETTEENTRY *pal = NULL;
4144 DWORD width = pitch / 3;
4145 int x, y, c;
4147 if (surface->palette)
4149 pal = surface->palette->palents;
4151 else
4153 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4154 HeapFree(GetProcessHeap(), 0, mem);
4155 return;
4158 for(y = local_rect.top; y < local_rect.bottom; y++) {
4159 for(x = local_rect.left; x < local_rect.right; x++) {
4160 /* start lines pixels */
4161 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4162 const BYTE *green = blue + 1;
4163 const BYTE *red = green + 1;
4165 for(c = 0; c < 256; c++) {
4166 if(*red == pal[c].peRed &&
4167 *green == pal[c].peGreen &&
4168 *blue == pal[c].peBlue)
4170 *((BYTE *) dest + y * width + x) = c;
4171 break;
4176 HeapFree(GetProcessHeap(), 0, mem);
4180 /* Read the framebuffer contents into a texture */
4181 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4183 struct wined3d_device *device = surface->resource.device;
4184 const struct wined3d_gl_info *gl_info;
4185 struct wined3d_context *context;
4187 if (!surface_is_offscreen(surface))
4189 /* We would need to flip onscreen surfaces, but there's no efficient
4190 * way to do that here. It makes more sense for the caller to
4191 * explicitly go through sysmem. */
4192 ERR("Not supported for onscreen targets.\n");
4193 return;
4196 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4197 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4198 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4200 context = context_acquire(device, surface);
4201 gl_info = context->gl_info;
4202 device_invalidate_state(device, STATE_FRAMEBUFFER);
4204 surface_prepare_texture(surface, gl_info, srgb);
4205 surface_bind_and_dirtify(surface, gl_info, srgb);
4207 TRACE("Reading back offscreen render target %p.\n", surface);
4209 ENTER_GL();
4211 glReadBuffer(device->offscreenBuffer);
4212 checkGLcall("glReadBuffer");
4214 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4215 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4216 checkGLcall("glCopyTexSubImage2D");
4218 LEAVE_GL();
4220 context_release(context);
4223 /* Context activation is done by the caller. */
4224 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4225 const struct wined3d_gl_info *gl_info, BOOL srgb)
4227 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4228 CONVERT_TYPES convert;
4229 struct wined3d_format format;
4231 if (surface->flags & alloc_flag) return;
4233 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4234 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4235 else surface->flags &= ~SFLAG_CONVERTED;
4237 surface_bind_and_dirtify(surface, gl_info, srgb);
4238 surface_allocate_surface(surface, gl_info, &format, srgb);
4239 surface->flags |= alloc_flag;
4242 /* Context activation is done by the caller. */
4243 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4245 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4247 struct wined3d_texture *texture = surface->container.u.texture;
4248 UINT sub_count = texture->level_count * texture->layer_count;
4249 UINT i;
4251 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4253 for (i = 0; i < sub_count; ++i)
4255 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4256 surface_prepare_texture_internal(s, gl_info, srgb);
4259 return;
4262 surface_prepare_texture_internal(surface, gl_info, srgb);
4265 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4266 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4268 struct wined3d_device *device = surface->resource.device;
4269 UINT pitch = wined3d_surface_get_pitch(surface);
4270 const struct wined3d_gl_info *gl_info;
4271 struct wined3d_context *context;
4272 RECT local_rect;
4273 UINT w, h;
4275 surface_get_rect(surface, rect, &local_rect);
4277 mem += local_rect.top * pitch + local_rect.left * bpp;
4278 w = local_rect.right - local_rect.left;
4279 h = local_rect.bottom - local_rect.top;
4281 /* Activate the correct context for the render target */
4282 context = context_acquire(device, surface);
4283 context_apply_blit_state(context, device);
4284 gl_info = context->gl_info;
4286 ENTER_GL();
4288 if (!surface_is_offscreen(surface))
4290 GLenum buffer = surface_get_gl_buffer(surface);
4291 TRACE("Unlocking %#x buffer.\n", buffer);
4292 context_set_draw_buffer(context, buffer);
4294 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4295 glPixelZoom(1.0f, -1.0f);
4297 else
4299 /* Primary offscreen render target */
4300 TRACE("Offscreen render target.\n");
4301 context_set_draw_buffer(context, device->offscreenBuffer);
4303 glPixelZoom(1.0f, 1.0f);
4306 glRasterPos3i(local_rect.left, local_rect.top, 1);
4307 checkGLcall("glRasterPos3i");
4309 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4310 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4312 if (surface->flags & SFLAG_PBO)
4314 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4315 checkGLcall("glBindBufferARB");
4318 glDrawPixels(w, h, fmt, type, mem);
4319 checkGLcall("glDrawPixels");
4321 if (surface->flags & SFLAG_PBO)
4323 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4324 checkGLcall("glBindBufferARB");
4327 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4328 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4330 LEAVE_GL();
4332 if (wined3d_settings.strict_draw_ordering
4333 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4334 && surface->container.u.swapchain->front_buffer == surface))
4335 wglFlush();
4337 context_release(context);
4340 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4341 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4343 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4344 const struct wined3d_device *device = surface->resource.device;
4345 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4346 BOOL blit_supported = FALSE;
4348 /* Copy the default values from the surface. Below we might perform fixups */
4349 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4350 *format = *surface->resource.format;
4351 *convert = NO_CONVERSION;
4353 /* Ok, now look if we have to do any conversion */
4354 switch (surface->resource.format->id)
4356 case WINED3DFMT_P8_UINT:
4357 /* Below the call to blit_supported is disabled for Wine 1.2
4358 * because the function isn't operating correctly yet. At the
4359 * moment 8-bit blits are handled in software and if certain GL
4360 * extensions are around, surface conversion is performed at
4361 * upload time. The blit_supported call recognizes it as a
4362 * destination fixup. This type of upload 'fixup' and 8-bit to
4363 * 8-bit blits need to be handled by the blit_shader.
4364 * TODO: get rid of this #if 0. */
4365 #if 0
4366 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4367 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4368 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4369 #endif
4370 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4372 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4373 * texturing. Further also use conversion in case of color keying.
4374 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4375 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4376 * conflicts with this.
4378 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4379 || colorkey_active || !use_texturing)
4381 format->glFormat = GL_RGBA;
4382 format->glInternal = GL_RGBA;
4383 format->glType = GL_UNSIGNED_BYTE;
4384 format->conv_byte_count = 4;
4385 if (colorkey_active)
4386 *convert = CONVERT_PALETTED_CK;
4387 else
4388 *convert = CONVERT_PALETTED;
4390 break;
4392 case WINED3DFMT_B2G3R3_UNORM:
4393 /* **********************
4394 GL_UNSIGNED_BYTE_3_3_2
4395 ********************** */
4396 if (colorkey_active) {
4397 /* This texture format will never be used.. So do not care about color keying
4398 up until the point in time it will be needed :-) */
4399 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4401 break;
4403 case WINED3DFMT_B5G6R5_UNORM:
4404 if (colorkey_active)
4406 *convert = CONVERT_CK_565;
4407 format->glFormat = GL_RGBA;
4408 format->glInternal = GL_RGB5_A1;
4409 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4410 format->conv_byte_count = 2;
4412 break;
4414 case WINED3DFMT_B5G5R5X1_UNORM:
4415 if (colorkey_active)
4417 *convert = CONVERT_CK_5551;
4418 format->glFormat = GL_BGRA;
4419 format->glInternal = GL_RGB5_A1;
4420 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4421 format->conv_byte_count = 2;
4423 break;
4425 case WINED3DFMT_B8G8R8_UNORM:
4426 if (colorkey_active)
4428 *convert = CONVERT_CK_RGB24;
4429 format->glFormat = GL_RGBA;
4430 format->glInternal = GL_RGBA8;
4431 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4432 format->conv_byte_count = 4;
4434 break;
4436 case WINED3DFMT_B8G8R8X8_UNORM:
4437 if (colorkey_active)
4439 *convert = CONVERT_RGB32_888;
4440 format->glFormat = GL_RGBA;
4441 format->glInternal = GL_RGBA8;
4442 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4443 format->conv_byte_count = 4;
4445 break;
4447 default:
4448 break;
4451 return WINED3D_OK;
4454 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4456 const struct wined3d_device *device = surface->resource.device;
4457 const struct wined3d_palette *pal = surface->palette;
4458 BOOL index_in_alpha = FALSE;
4459 unsigned int i;
4461 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4462 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4463 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4464 * duplicate entries. Store the color key in the unused alpha component to speed the
4465 * download up and to make conversion unneeded. */
4466 index_in_alpha = primary_render_target_is_p8(device);
4468 if (!pal)
4470 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4471 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4473 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4474 if (index_in_alpha)
4476 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4477 * there's no palette at this time. */
4478 for (i = 0; i < 256; i++) table[i][3] = i;
4481 else
4483 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4484 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4485 * capability flag is present (wine does advertise this capability) */
4486 for (i = 0; i < 256; ++i)
4488 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4489 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4490 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4491 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4495 else
4497 TRACE("Using surface palette %p\n", pal);
4498 /* Get the surface's palette */
4499 for (i = 0; i < 256; ++i)
4501 table[i][0] = pal->palents[i].peRed;
4502 table[i][1] = pal->palents[i].peGreen;
4503 table[i][2] = pal->palents[i].peBlue;
4505 /* When index_in_alpha is set the palette index is stored in the
4506 * alpha component. In case of a readback we can then read
4507 * GL_ALPHA. Color keying is handled in BltOverride using a
4508 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4509 * color key itself is passed to glAlphaFunc in other cases the
4510 * alpha component of pixels that should be masked away is set to 0. */
4511 if (index_in_alpha)
4513 table[i][3] = i;
4515 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4516 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4518 table[i][3] = 0x00;
4520 else if (pal->flags & WINEDDPCAPS_ALPHA)
4522 table[i][3] = pal->palents[i].peFlags;
4524 else
4526 table[i][3] = 0xFF;
4532 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4533 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4535 const BYTE *source;
4536 BYTE *dest;
4537 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4539 switch (convert) {
4540 case NO_CONVERSION:
4542 memcpy(dst, src, pitch * height);
4543 break;
4545 case CONVERT_PALETTED:
4546 case CONVERT_PALETTED_CK:
4548 BYTE table[256][4];
4549 unsigned int x, y;
4551 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4553 for (y = 0; y < height; y++)
4555 source = src + pitch * y;
4556 dest = dst + outpitch * y;
4557 /* This is an 1 bpp format, using the width here is fine */
4558 for (x = 0; x < width; x++) {
4559 BYTE color = *source++;
4560 *dest++ = table[color][0];
4561 *dest++ = table[color][1];
4562 *dest++ = table[color][2];
4563 *dest++ = table[color][3];
4567 break;
4569 case CONVERT_CK_565:
4571 /* Converting the 565 format in 5551 packed to emulate color-keying.
4573 Note : in all these conversion, it would be best to average the averaging
4574 pixels to get the color of the pixel that will be color-keyed to
4575 prevent 'color bleeding'. This will be done later on if ever it is
4576 too visible.
4578 Note2: Nvidia documents say that their driver does not support alpha + color keying
4579 on the same surface and disables color keying in such a case
4581 unsigned int x, y;
4582 const WORD *Source;
4583 WORD *Dest;
4585 TRACE("Color keyed 565\n");
4587 for (y = 0; y < height; y++) {
4588 Source = (const WORD *)(src + y * pitch);
4589 Dest = (WORD *) (dst + y * outpitch);
4590 for (x = 0; x < width; x++ ) {
4591 WORD color = *Source++;
4592 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4593 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4594 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4595 *Dest |= 0x0001;
4596 Dest++;
4600 break;
4602 case CONVERT_CK_5551:
4604 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4605 unsigned int x, y;
4606 const WORD *Source;
4607 WORD *Dest;
4608 TRACE("Color keyed 5551\n");
4609 for (y = 0; y < height; y++) {
4610 Source = (const WORD *)(src + y * pitch);
4611 Dest = (WORD *) (dst + y * outpitch);
4612 for (x = 0; x < width; x++ ) {
4613 WORD color = *Source++;
4614 *Dest = color;
4615 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4616 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4617 *Dest |= (1 << 15);
4618 else
4619 *Dest &= ~(1 << 15);
4620 Dest++;
4624 break;
4626 case CONVERT_CK_RGB24:
4628 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4629 unsigned int x, y;
4630 for (y = 0; y < height; y++)
4632 source = src + pitch * y;
4633 dest = dst + outpitch * y;
4634 for (x = 0; x < width; x++) {
4635 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4636 DWORD dstcolor = color << 8;
4637 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4638 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4639 dstcolor |= 0xff;
4640 *(DWORD*)dest = dstcolor;
4641 source += 3;
4642 dest += 4;
4646 break;
4648 case CONVERT_RGB32_888:
4650 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4651 unsigned int x, y;
4652 for (y = 0; y < height; y++)
4654 source = src + pitch * y;
4655 dest = dst + outpitch * y;
4656 for (x = 0; x < width; x++) {
4657 DWORD color = 0xffffff & *(const DWORD*)source;
4658 DWORD dstcolor = color << 8;
4659 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4660 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4661 dstcolor |= 0xff;
4662 *(DWORD*)dest = dstcolor;
4663 source += 4;
4664 dest += 4;
4668 break;
4670 default:
4671 ERR("Unsupported conversion type %#x.\n", convert);
4673 return WINED3D_OK;
4676 BOOL palette9_changed(struct wined3d_surface *surface)
4678 struct wined3d_device *device = surface->resource.device;
4680 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4681 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4683 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4684 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4686 return FALSE;
4689 if (surface->palette9)
4691 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4693 return FALSE;
4696 else
4698 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4700 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4702 return TRUE;
4705 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4707 /* Flip the surface contents */
4708 /* Flip the DC */
4710 HDC tmp;
4711 tmp = front->hDC;
4712 front->hDC = back->hDC;
4713 back->hDC = tmp;
4716 /* Flip the DIBsection */
4718 HBITMAP tmp;
4719 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4720 tmp = front->dib.DIBsection;
4721 front->dib.DIBsection = back->dib.DIBsection;
4722 back->dib.DIBsection = tmp;
4724 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4725 else front->flags &= ~SFLAG_DIBSECTION;
4726 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4727 else back->flags &= ~SFLAG_DIBSECTION;
4730 /* Flip the surface data */
4732 void* tmp;
4734 tmp = front->dib.bitmap_data;
4735 front->dib.bitmap_data = back->dib.bitmap_data;
4736 back->dib.bitmap_data = tmp;
4738 tmp = front->resource.allocatedMemory;
4739 front->resource.allocatedMemory = back->resource.allocatedMemory;
4740 back->resource.allocatedMemory = tmp;
4742 tmp = front->resource.heapMemory;
4743 front->resource.heapMemory = back->resource.heapMemory;
4744 back->resource.heapMemory = tmp;
4747 /* Flip the PBO */
4749 GLuint tmp_pbo = front->pbo;
4750 front->pbo = back->pbo;
4751 back->pbo = tmp_pbo;
4754 /* client_memory should not be different, but just in case */
4756 BOOL tmp;
4757 tmp = front->dib.client_memory;
4758 front->dib.client_memory = back->dib.client_memory;
4759 back->dib.client_memory = tmp;
4762 /* Flip the opengl texture */
4764 GLuint tmp;
4766 tmp = back->texture_name;
4767 back->texture_name = front->texture_name;
4768 front->texture_name = tmp;
4770 tmp = back->texture_name_srgb;
4771 back->texture_name_srgb = front->texture_name_srgb;
4772 front->texture_name_srgb = tmp;
4774 resource_unload(&back->resource);
4775 resource_unload(&front->resource);
4779 DWORD tmp_flags = back->flags;
4780 back->flags = front->flags;
4781 front->flags = tmp_flags;
4785 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4786 * pixel copy calls. */
4787 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4788 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4790 struct wined3d_device *device = dst_surface->resource.device;
4791 float xrel, yrel;
4792 UINT row;
4793 struct wined3d_context *context;
4794 BOOL upsidedown = FALSE;
4795 RECT dst_rect = *dst_rect_in;
4797 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4798 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4800 if(dst_rect.top > dst_rect.bottom) {
4801 UINT tmp = dst_rect.bottom;
4802 dst_rect.bottom = dst_rect.top;
4803 dst_rect.top = tmp;
4804 upsidedown = TRUE;
4807 context = context_acquire(device, src_surface);
4808 context_apply_blit_state(context, device);
4809 surface_internal_preload(dst_surface, SRGB_RGB);
4810 ENTER_GL();
4812 /* Bind the target texture */
4813 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4814 checkGLcall("glBindTexture");
4815 if (surface_is_offscreen(src_surface))
4817 TRACE("Reading from an offscreen target\n");
4818 upsidedown = !upsidedown;
4819 glReadBuffer(device->offscreenBuffer);
4821 else
4823 glReadBuffer(surface_get_gl_buffer(src_surface));
4825 checkGLcall("glReadBuffer");
4827 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4828 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4830 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4832 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4834 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4835 ERR("Texture filtering not supported in direct blit\n");
4838 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4839 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4841 ERR("Texture filtering not supported in direct blit\n");
4844 if (upsidedown
4845 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4846 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4848 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4850 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4851 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4852 src_rect->left, src_surface->resource.height - src_rect->bottom,
4853 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4855 else
4857 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4858 /* I have to process this row by row to swap the image,
4859 * otherwise it would be upside down, so stretching in y direction
4860 * doesn't cost extra time
4862 * However, stretching in x direction can be avoided if not necessary
4864 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4865 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4867 /* Well, that stuff works, but it's very slow.
4868 * find a better way instead
4870 UINT col;
4872 for (col = dst_rect.left; col < dst_rect.right; ++col)
4874 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4875 dst_rect.left + col /* x offset */, row /* y offset */,
4876 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4879 else
4881 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4882 dst_rect.left /* x offset */, row /* y offset */,
4883 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4887 checkGLcall("glCopyTexSubImage2D");
4889 LEAVE_GL();
4890 context_release(context);
4892 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4893 * path is never entered
4895 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4898 /* Uses the hardware to stretch and flip the image */
4899 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4900 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4902 struct wined3d_device *device = dst_surface->resource.device;
4903 struct wined3d_swapchain *src_swapchain = NULL;
4904 GLuint src, backup = 0;
4905 float left, right, top, bottom; /* Texture coordinates */
4906 UINT fbwidth = src_surface->resource.width;
4907 UINT fbheight = src_surface->resource.height;
4908 struct wined3d_context *context;
4909 GLenum drawBuffer = GL_BACK;
4910 GLenum texture_target;
4911 BOOL noBackBufferBackup;
4912 BOOL src_offscreen;
4913 BOOL upsidedown = FALSE;
4914 RECT dst_rect = *dst_rect_in;
4916 TRACE("Using hwstretch blit\n");
4917 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4918 context = context_acquire(device, src_surface);
4919 context_apply_blit_state(context, device);
4920 surface_internal_preload(dst_surface, SRGB_RGB);
4922 src_offscreen = surface_is_offscreen(src_surface);
4923 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4924 if (!noBackBufferBackup && !src_surface->texture_name)
4926 /* Get it a description */
4927 surface_internal_preload(src_surface, SRGB_RGB);
4929 ENTER_GL();
4931 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4932 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4934 if (context->aux_buffers >= 2)
4936 /* Got more than one aux buffer? Use the 2nd aux buffer */
4937 drawBuffer = GL_AUX1;
4939 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4941 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4942 drawBuffer = GL_AUX0;
4945 if(noBackBufferBackup) {
4946 glGenTextures(1, &backup);
4947 checkGLcall("glGenTextures");
4948 glBindTexture(GL_TEXTURE_2D, backup);
4949 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4950 texture_target = GL_TEXTURE_2D;
4951 } else {
4952 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4953 * we are reading from the back buffer, the backup can be used as source texture
4955 texture_target = src_surface->texture_target;
4956 glBindTexture(texture_target, src_surface->texture_name);
4957 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4958 glEnable(texture_target);
4959 checkGLcall("glEnable(texture_target)");
4961 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4962 src_surface->flags &= ~SFLAG_INTEXTURE;
4965 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4966 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4968 if(dst_rect.top > dst_rect.bottom) {
4969 UINT tmp = dst_rect.bottom;
4970 dst_rect.bottom = dst_rect.top;
4971 dst_rect.top = tmp;
4972 upsidedown = TRUE;
4975 if (src_offscreen)
4977 TRACE("Reading from an offscreen target\n");
4978 upsidedown = !upsidedown;
4979 glReadBuffer(device->offscreenBuffer);
4981 else
4983 glReadBuffer(surface_get_gl_buffer(src_surface));
4986 /* TODO: Only back up the part that will be overwritten */
4987 glCopyTexSubImage2D(texture_target, 0,
4988 0, 0 /* read offsets */,
4989 0, 0,
4990 fbwidth,
4991 fbheight);
4993 checkGLcall("glCopyTexSubImage2D");
4995 /* No issue with overriding these - the sampler is dirty due to blit usage */
4996 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4997 wined3d_gl_mag_filter(magLookup, Filter));
4998 checkGLcall("glTexParameteri");
4999 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5000 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5001 checkGLcall("glTexParameteri");
5003 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5004 src_swapchain = src_surface->container.u.swapchain;
5005 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5007 src = backup ? backup : src_surface->texture_name;
5009 else
5011 glReadBuffer(GL_FRONT);
5012 checkGLcall("glReadBuffer(GL_FRONT)");
5014 glGenTextures(1, &src);
5015 checkGLcall("glGenTextures(1, &src)");
5016 glBindTexture(GL_TEXTURE_2D, src);
5017 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
5019 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5020 * out for power of 2 sizes
5022 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5023 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5024 checkGLcall("glTexImage2D");
5025 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5026 0, 0 /* read offsets */,
5027 0, 0,
5028 fbwidth,
5029 fbheight);
5031 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5032 checkGLcall("glTexParameteri");
5033 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5034 checkGLcall("glTexParameteri");
5036 glReadBuffer(GL_BACK);
5037 checkGLcall("glReadBuffer(GL_BACK)");
5039 if(texture_target != GL_TEXTURE_2D) {
5040 glDisable(texture_target);
5041 glEnable(GL_TEXTURE_2D);
5042 texture_target = GL_TEXTURE_2D;
5045 checkGLcall("glEnd and previous");
5047 left = src_rect->left;
5048 right = src_rect->right;
5050 if (!upsidedown)
5052 top = src_surface->resource.height - src_rect->top;
5053 bottom = src_surface->resource.height - src_rect->bottom;
5055 else
5057 top = src_surface->resource.height - src_rect->bottom;
5058 bottom = src_surface->resource.height - src_rect->top;
5061 if (src_surface->flags & SFLAG_NORMCOORD)
5063 left /= src_surface->pow2Width;
5064 right /= src_surface->pow2Width;
5065 top /= src_surface->pow2Height;
5066 bottom /= src_surface->pow2Height;
5069 /* draw the source texture stretched and upside down. The correct surface is bound already */
5070 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5071 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5073 context_set_draw_buffer(context, drawBuffer);
5074 glReadBuffer(drawBuffer);
5076 glBegin(GL_QUADS);
5077 /* bottom left */
5078 glTexCoord2f(left, bottom);
5079 glVertex2i(0, 0);
5081 /* top left */
5082 glTexCoord2f(left, top);
5083 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5085 /* top right */
5086 glTexCoord2f(right, top);
5087 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5089 /* bottom right */
5090 glTexCoord2f(right, bottom);
5091 glVertex2i(dst_rect.right - dst_rect.left, 0);
5092 glEnd();
5093 checkGLcall("glEnd and previous");
5095 if (texture_target != dst_surface->texture_target)
5097 glDisable(texture_target);
5098 glEnable(dst_surface->texture_target);
5099 texture_target = dst_surface->texture_target;
5102 /* Now read the stretched and upside down image into the destination texture */
5103 glBindTexture(texture_target, dst_surface->texture_name);
5104 checkGLcall("glBindTexture");
5105 glCopyTexSubImage2D(texture_target,
5107 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5108 0, 0, /* We blitted the image to the origin */
5109 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5110 checkGLcall("glCopyTexSubImage2D");
5112 if(drawBuffer == GL_BACK) {
5113 /* Write the back buffer backup back */
5114 if(backup) {
5115 if(texture_target != GL_TEXTURE_2D) {
5116 glDisable(texture_target);
5117 glEnable(GL_TEXTURE_2D);
5118 texture_target = GL_TEXTURE_2D;
5120 glBindTexture(GL_TEXTURE_2D, backup);
5121 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5123 else
5125 if (texture_target != src_surface->texture_target)
5127 glDisable(texture_target);
5128 glEnable(src_surface->texture_target);
5129 texture_target = src_surface->texture_target;
5131 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5132 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5135 glBegin(GL_QUADS);
5136 /* top left */
5137 glTexCoord2f(0.0f, 0.0f);
5138 glVertex2i(0, fbheight);
5140 /* bottom left */
5141 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5142 glVertex2i(0, 0);
5144 /* bottom right */
5145 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5146 (float)fbheight / (float)src_surface->pow2Height);
5147 glVertex2i(fbwidth, 0);
5149 /* top right */
5150 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5151 glVertex2i(fbwidth, fbheight);
5152 glEnd();
5154 glDisable(texture_target);
5155 checkGLcall("glDisable(texture_target)");
5157 /* Cleanup */
5158 if (src != src_surface->texture_name && src != backup)
5160 glDeleteTextures(1, &src);
5161 checkGLcall("glDeleteTextures(1, &src)");
5163 if(backup) {
5164 glDeleteTextures(1, &backup);
5165 checkGLcall("glDeleteTextures(1, &backup)");
5168 LEAVE_GL();
5170 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5172 context_release(context);
5174 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5175 * path is never entered
5177 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5180 /* Front buffer coordinates are always full screen coordinates, but our GL
5181 * drawable is limited to the window's client area. The sysmem and texture
5182 * copies do have the full screen size. Note that GL has a bottom-left
5183 * origin, while D3D has a top-left origin. */
5184 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5186 UINT drawable_height;
5188 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5189 && surface == surface->container.u.swapchain->front_buffer)
5191 POINT offset = {0, 0};
5192 RECT windowsize;
5194 ScreenToClient(window, &offset);
5195 OffsetRect(rect, offset.x, offset.y);
5197 GetClientRect(window, &windowsize);
5198 drawable_height = windowsize.bottom - windowsize.top;
5200 else
5202 drawable_height = surface->resource.height;
5205 rect->top = drawable_height - rect->top;
5206 rect->bottom = drawable_height - rect->bottom;
5209 static void surface_blt_to_drawable(struct wined3d_device *device,
5210 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5211 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5212 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5214 struct wined3d_context *context;
5215 RECT src_rect, dst_rect;
5217 src_rect = *src_rect_in;
5218 dst_rect = *dst_rect_in;
5220 /* Make sure the surface is up-to-date. This should probably use
5221 * surface_load_location() and worry about the destination surface too,
5222 * unless we're overwriting it completely. */
5223 surface_internal_preload(src_surface, SRGB_RGB);
5225 /* Activate the destination context, set it up for blitting */
5226 context = context_acquire(device, dst_surface);
5227 context_apply_blit_state(context, device);
5229 if (!surface_is_offscreen(dst_surface))
5230 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5232 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5234 ENTER_GL();
5236 if (color_key)
5238 glEnable(GL_ALPHA_TEST);
5239 checkGLcall("glEnable(GL_ALPHA_TEST)");
5241 /* When the primary render target uses P8, the alpha component
5242 * contains the palette index. Which means that the colorkey is one of
5243 * the palette entries. In other cases pixels that should be masked
5244 * away have alpha set to 0. */
5245 if (primary_render_target_is_p8(device))
5246 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5247 else
5248 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5249 checkGLcall("glAlphaFunc");
5251 else
5253 glDisable(GL_ALPHA_TEST);
5254 checkGLcall("glDisable(GL_ALPHA_TEST)");
5257 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5259 if (color_key)
5261 glDisable(GL_ALPHA_TEST);
5262 checkGLcall("glDisable(GL_ALPHA_TEST)");
5265 LEAVE_GL();
5267 /* Leave the opengl state valid for blitting */
5268 device->blitter->unset_shader(context->gl_info);
5270 if (wined3d_settings.strict_draw_ordering
5271 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5272 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5273 wglFlush(); /* Flush to ensure ordering across contexts. */
5275 context_release(context);
5278 /* Do not call while under the GL lock. */
5279 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5281 struct wined3d_device *device = s->resource.device;
5282 const struct blit_shader *blitter;
5284 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5285 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5286 if (!blitter)
5288 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5289 return WINED3DERR_INVALIDCALL;
5292 return blitter->color_fill(device, s, rect, color);
5295 /* Do not call while under the GL lock. */
5296 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5297 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5298 WINED3DTEXTUREFILTERTYPE Filter)
5300 struct wined3d_device *device = dst_surface->resource.device;
5301 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5302 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5304 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5305 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5306 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5308 /* Get the swapchain. One of the surfaces has to be a primary surface */
5309 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5311 WARN("Destination is in sysmem, rejecting gl blt\n");
5312 return WINED3DERR_INVALIDCALL;
5315 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5316 dstSwapchain = dst_surface->container.u.swapchain;
5318 if (src_surface)
5320 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5322 WARN("Src is in sysmem, rejecting gl blt\n");
5323 return WINED3DERR_INVALIDCALL;
5326 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5327 srcSwapchain = src_surface->container.u.swapchain;
5330 /* Early sort out of cases where no render target is used */
5331 if (!dstSwapchain && !srcSwapchain
5332 && src_surface != device->fb.render_targets[0]
5333 && dst_surface != device->fb.render_targets[0])
5335 TRACE("No surface is render target, not using hardware blit.\n");
5336 return WINED3DERR_INVALIDCALL;
5339 /* No destination color keying supported */
5340 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5342 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5343 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5344 return WINED3DERR_INVALIDCALL;
5347 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5348 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5349 && dst_surface == dstSwapchain->front_buffer
5350 && src_surface == dstSwapchain->back_buffers[0])
5352 /* Half-Life does a Blt from the back buffer to the front buffer,
5353 * Full surface size, no flags... Use present instead
5355 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5358 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5359 for (;;)
5361 TRACE("Looking if a Present can be done...\n");
5362 /* Source Rectangle must be full surface */
5363 if (src_rect->left || src_rect->top
5364 || src_rect->right != src_surface->resource.width
5365 || src_rect->bottom != src_surface->resource.height)
5367 TRACE("No, Source rectangle doesn't match\n");
5368 break;
5371 /* No stretching may occur */
5372 if (src_rect->right != dst_rect->right - dst_rect->left
5373 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5375 TRACE("No, stretching is done\n");
5376 break;
5379 /* Destination must be full surface or match the clipping rectangle */
5380 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5382 RECT cliprect;
5383 POINT pos[2];
5384 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5385 pos[0].x = dst_rect->left;
5386 pos[0].y = dst_rect->top;
5387 pos[1].x = dst_rect->right;
5388 pos[1].y = dst_rect->bottom;
5389 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5391 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5392 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5394 TRACE("No, dest rectangle doesn't match(clipper)\n");
5395 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5396 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5397 break;
5400 else if (dst_rect->left || dst_rect->top
5401 || dst_rect->right != dst_surface->resource.width
5402 || dst_rect->bottom != dst_surface->resource.height)
5404 TRACE("No, dest rectangle doesn't match(surface size)\n");
5405 break;
5408 TRACE("Yes\n");
5410 /* These flags are unimportant for the flag check, remove them */
5411 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5413 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5415 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5416 * take very long, while a flip is fast.
5417 * This applies to Half-Life, which does such Blts every time it finished
5418 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5419 * menu. This is also used by all apps when they do windowed rendering
5421 * The problem is that flipping is not really the same as copying. After a
5422 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5423 * untouched. Therefore it's necessary to override the swap effect
5424 * and to set it back after the flip.
5426 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5427 * testcases.
5430 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5431 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5433 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5434 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5436 dstSwapchain->presentParms.SwapEffect = orig_swap;
5438 return WINED3D_OK;
5440 break;
5443 TRACE("Unsupported blit between buffers on the same swapchain\n");
5444 return WINED3DERR_INVALIDCALL;
5445 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5446 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5447 return WINED3DERR_INVALIDCALL;
5448 } else if(dstSwapchain && srcSwapchain) {
5449 FIXME("Implement hardware blit between two different swapchains\n");
5450 return WINED3DERR_INVALIDCALL;
5452 else 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);
5504 } else {
5505 TRACE("Using hardware stretching to flip / stretch the texture\n");
5506 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5509 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5511 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5512 dst_surface->resource.allocatedMemory = NULL;
5513 dst_surface->resource.heapMemory = NULL;
5515 else
5517 dst_surface->flags &= ~SFLAG_INSYSMEM;
5520 return WINED3D_OK;
5522 else if (src_surface)
5524 /* Blit from offscreen surface to render target */
5525 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5526 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5528 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5530 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5531 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5532 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5534 FIXME("Unsupported blit operation falling back to software\n");
5535 return WINED3DERR_INVALIDCALL;
5538 /* Color keying: Check if we have to do a color keyed blt,
5539 * and if not check if a color key is activated.
5541 * Just modify the color keying parameters in the surface and restore them afterwards
5542 * The surface keeps track of the color key last used to load the opengl surface.
5543 * PreLoad will catch the change to the flags and color key and reload if necessary.
5545 if (flags & WINEDDBLT_KEYSRC)
5547 /* Use color key from surface */
5549 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5551 /* Use color key from DDBltFx */
5552 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5553 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5555 else
5557 /* Do not use color key */
5558 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5561 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5562 src_surface, src_rect, dst_surface, dst_rect);
5564 /* Restore the color key parameters */
5565 src_surface->CKeyFlags = oldCKeyFlags;
5566 src_surface->SrcBltCKey = oldBltCKey;
5568 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5570 return WINED3D_OK;
5573 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5574 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5575 return WINED3DERR_INVALIDCALL;
5578 /* GL locking is done by the caller */
5579 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5580 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5582 struct wined3d_device *device = surface->resource.device;
5583 GLint compare_mode = GL_NONE;
5584 struct blt_info info;
5585 GLint old_binding = 0;
5586 RECT rect;
5588 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5590 glDisable(GL_CULL_FACE);
5591 glDisable(GL_BLEND);
5592 glDisable(GL_ALPHA_TEST);
5593 glDisable(GL_SCISSOR_TEST);
5594 glDisable(GL_STENCIL_TEST);
5595 glEnable(GL_DEPTH_TEST);
5596 glDepthFunc(GL_ALWAYS);
5597 glDepthMask(GL_TRUE);
5598 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5599 glViewport(x, y, w, h);
5601 SetRect(&rect, 0, h, w, 0);
5602 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5603 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5604 glGetIntegerv(info.binding, &old_binding);
5605 glBindTexture(info.bind_target, texture);
5606 if (gl_info->supported[ARB_SHADOW])
5608 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5609 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5612 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5613 gl_info, info.tex_type, &surface->ds_current_size);
5615 glBegin(GL_TRIANGLE_STRIP);
5616 glTexCoord3fv(info.coords[0]);
5617 glVertex2f(-1.0f, -1.0f);
5618 glTexCoord3fv(info.coords[1]);
5619 glVertex2f(1.0f, -1.0f);
5620 glTexCoord3fv(info.coords[2]);
5621 glVertex2f(-1.0f, 1.0f);
5622 glTexCoord3fv(info.coords[3]);
5623 glVertex2f(1.0f, 1.0f);
5624 glEnd();
5626 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5627 glBindTexture(info.bind_target, old_binding);
5629 glPopAttrib();
5631 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5634 void surface_modify_ds_location(struct wined3d_surface *surface,
5635 DWORD location, UINT w, UINT h)
5637 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5639 if (location & ~SFLAG_DS_LOCATIONS)
5640 FIXME("Invalid location (%#x) specified.\n", location);
5642 surface->ds_current_size.cx = w;
5643 surface->ds_current_size.cy = h;
5644 surface->flags &= ~SFLAG_DS_LOCATIONS;
5645 surface->flags |= location;
5648 /* Context activation is done by the caller. */
5649 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5651 struct wined3d_device *device = surface->resource.device;
5652 const struct wined3d_gl_info *gl_info = context->gl_info;
5653 GLsizei w, h;
5655 TRACE("surface %p, new location %#x.\n", surface, location);
5657 /* TODO: Make this work for modes other than FBO */
5658 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5660 if (!(surface->flags & location))
5662 w = surface->ds_current_size.cx;
5663 h = surface->ds_current_size.cy;
5664 surface->ds_current_size.cx = 0;
5665 surface->ds_current_size.cy = 0;
5667 else
5669 w = surface->resource.width;
5670 h = surface->resource.height;
5673 if (surface->ds_current_size.cx == surface->resource.width
5674 && surface->ds_current_size.cy == surface->resource.height)
5676 TRACE("Location (%#x) is already up to date.\n", location);
5677 return;
5680 if (surface->current_renderbuffer)
5682 FIXME("Not supported with fixed up depth stencil.\n");
5683 return;
5686 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5688 /* This mostly happens when a depth / stencil is used without being
5689 * cleared first. In principle we could upload from sysmem, or
5690 * explicitly clear before first usage. For the moment there don't
5691 * appear to be a lot of applications depending on this, so a FIXME
5692 * should do. */
5693 FIXME("No up to date depth stencil location.\n");
5694 surface->flags |= location;
5695 surface->ds_current_size.cx = surface->resource.width;
5696 surface->ds_current_size.cy = surface->resource.height;
5697 return;
5700 if (location == SFLAG_DS_OFFSCREEN)
5702 GLint old_binding = 0;
5703 GLenum bind_target;
5705 /* The render target is allowed to be smaller than the depth/stencil
5706 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5707 * than the offscreen surface. Don't overwrite the offscreen surface
5708 * with undefined data. */
5709 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5710 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5712 TRACE("Copying onscreen depth buffer to depth texture.\n");
5714 ENTER_GL();
5716 if (!device->depth_blt_texture)
5718 glGenTextures(1, &device->depth_blt_texture);
5721 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5722 * directly on the FBO texture. That's because we need to flip. */
5723 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5724 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5725 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5727 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5728 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5730 else
5732 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5733 bind_target = GL_TEXTURE_2D;
5735 glBindTexture(bind_target, device->depth_blt_texture);
5736 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5737 * internal format, because the internal format might include stencil
5738 * data. In principle we should copy stencil data as well, but unless
5739 * the driver supports stencil export it's hard to do, and doesn't
5740 * seem to be needed in practice. If the hardware doesn't support
5741 * writing stencil data, the glCopyTexImage2D() call might trigger
5742 * software fallbacks. */
5743 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5744 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5745 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5746 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5747 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5748 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5749 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5750 glBindTexture(bind_target, old_binding);
5752 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5753 NULL, surface, SFLAG_INTEXTURE);
5754 context_set_draw_buffer(context, GL_NONE);
5755 glReadBuffer(GL_NONE);
5757 /* Do the actual blit */
5758 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5759 checkGLcall("depth_blt");
5761 context_invalidate_state(context, STATE_FRAMEBUFFER);
5763 LEAVE_GL();
5765 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5767 else if (location == SFLAG_DS_ONSCREEN)
5769 TRACE("Copying depth texture to onscreen depth buffer.\n");
5771 ENTER_GL();
5773 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5774 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5775 surface_depth_blt(surface, gl_info, surface->texture_name,
5776 0, surface->pow2Height - h, w, h, surface->texture_target);
5777 checkGLcall("depth_blt");
5779 context_invalidate_state(context, STATE_FRAMEBUFFER);
5781 LEAVE_GL();
5783 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5785 else
5787 ERR("Invalid location (%#x) specified.\n", location);
5790 surface->flags |= location;
5791 surface->ds_current_size.cx = surface->resource.width;
5792 surface->ds_current_size.cy = surface->resource.height;
5795 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5797 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5798 struct wined3d_surface *overlay;
5800 TRACE("surface %p, location %s, persistent %#x.\n",
5801 surface, debug_surflocation(location), persistent);
5803 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5805 if (surface_is_offscreen(surface))
5807 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5808 * for offscreen targets. */
5809 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5810 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5812 else
5814 TRACE("Surface %p is an onscreen surface.\n", surface);
5818 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5819 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5820 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5822 if (persistent)
5824 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5825 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5827 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5829 TRACE("Passing to container.\n");
5830 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5833 surface->flags &= ~SFLAG_LOCATIONS;
5834 surface->flags |= location;
5836 /* Redraw emulated overlays, if any */
5837 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5839 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5841 overlay->surface_ops->surface_draw_overlay(overlay);
5845 else
5847 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5849 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5851 TRACE("Passing to container\n");
5852 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5855 surface->flags &= ~location;
5858 if (!(surface->flags & SFLAG_LOCATIONS))
5860 ERR("Surface %p does not have any up to date location.\n", surface);
5864 static DWORD resource_access_from_location(DWORD location)
5866 switch (location)
5868 case SFLAG_INSYSMEM:
5869 return WINED3D_RESOURCE_ACCESS_CPU;
5871 case SFLAG_INDRAWABLE:
5872 case SFLAG_INSRGBTEX:
5873 case SFLAG_INTEXTURE:
5874 return WINED3D_RESOURCE_ACCESS_GPU;
5876 default:
5877 FIXME("Unhandled location %#x.\n", location);
5878 return 0;
5882 static void surface_load_sysmem(struct wined3d_surface *surface,
5883 const struct wined3d_gl_info *gl_info, const RECT *rect)
5885 surface_prepare_system_memory(surface);
5887 /* Download the surface to system memory. */
5888 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5890 struct wined3d_device *device = surface->resource.device;
5891 struct wined3d_context *context = NULL;
5893 if (!device->isInDraw)
5894 context = context_acquire(device, NULL);
5896 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5897 surface_download_data(surface, gl_info);
5899 if (context)
5900 context_release(context);
5902 return;
5905 /* Note: It might be faster to download into a texture first. */
5906 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5907 wined3d_surface_get_pitch(surface));
5910 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5911 const struct wined3d_gl_info *gl_info, const RECT *rect)
5913 struct wined3d_device *device = surface->resource.device;
5914 struct wined3d_format format;
5915 CONVERT_TYPES convert;
5916 UINT byte_count;
5917 BYTE *mem;
5919 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5920 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5922 if (surface->flags & SFLAG_INTEXTURE)
5924 RECT r;
5926 surface_get_rect(surface, rect, &r);
5927 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5929 return WINED3D_OK;
5932 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5934 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5935 * path through sysmem. */
5936 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5939 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5941 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5942 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5943 * called. */
5944 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5946 struct wined3d_context *context = NULL;
5948 TRACE("Removing the pbo attached to surface %p.\n", surface);
5950 if (!device->isInDraw)
5951 context = context_acquire(device, NULL);
5953 surface_remove_pbo(surface, gl_info);
5955 if (context)
5956 context_release(context);
5959 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5961 UINT height = surface->resource.height;
5962 UINT width = surface->resource.width;
5963 UINT src_pitch, dst_pitch;
5965 byte_count = format.conv_byte_count;
5966 src_pitch = wined3d_surface_get_pitch(surface);
5968 /* Stick to the alignment for the converted surface too, makes it
5969 * easier to load the surface. */
5970 dst_pitch = width * byte_count;
5971 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5973 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5975 ERR("Out of memory (%u).\n", dst_pitch * height);
5976 return E_OUTOFMEMORY;
5979 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5980 src_pitch, width, height, dst_pitch, convert, surface);
5982 surface->flags |= SFLAG_CONVERTED;
5984 else
5986 surface->flags &= ~SFLAG_CONVERTED;
5987 mem = surface->resource.allocatedMemory;
5988 byte_count = format.byte_count;
5991 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5993 /* Don't delete PBO memory. */
5994 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5995 HeapFree(GetProcessHeap(), 0, mem);
5997 return WINED3D_OK;
6000 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6001 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6003 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
6004 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6005 struct wined3d_device *device = surface->resource.device;
6006 struct wined3d_context *context = NULL;
6007 UINT width, src_pitch, dst_pitch;
6008 struct wined3d_bo_address data;
6009 struct wined3d_format format;
6010 POINT dst_point = {0, 0};
6011 CONVERT_TYPES convert;
6012 BYTE *mem;
6014 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6015 && surface_is_offscreen(surface)
6016 && (surface->flags & SFLAG_INDRAWABLE))
6018 read_from_framebuffer_texture(surface, srgb);
6020 return WINED3D_OK;
6023 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6024 && (surface->resource.format->flags & attach_flags) == attach_flags
6025 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6026 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6027 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6029 if (srgb)
6030 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6031 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6032 else
6033 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6034 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6036 return WINED3D_OK;
6039 /* Upload from system memory */
6041 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6042 TRUE /* We will use textures */, &format, &convert);
6044 if (srgb)
6046 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6048 /* Performance warning... */
6049 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6050 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6053 else
6055 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6057 /* Performance warning... */
6058 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6059 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6063 if (!(surface->flags & SFLAG_INSYSMEM))
6065 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6066 /* Lets hope we get it from somewhere... */
6067 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6070 if (!device->isInDraw)
6071 context = context_acquire(device, NULL);
6073 surface_prepare_texture(surface, gl_info, srgb);
6074 surface_bind_and_dirtify(surface, gl_info, srgb);
6076 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6078 surface->flags |= SFLAG_GLCKEY;
6079 surface->glCKey = surface->SrcBltCKey;
6081 else surface->flags &= ~SFLAG_GLCKEY;
6083 width = surface->resource.width;
6084 src_pitch = wined3d_surface_get_pitch(surface);
6086 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6087 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6088 * called. */
6089 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6091 TRACE("Removing the pbo attached to surface %p.\n", surface);
6092 surface_remove_pbo(surface, gl_info);
6095 if (format.convert)
6097 /* This code is entered for texture formats which need a fixup. */
6098 UINT height = surface->resource.height;
6100 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6101 dst_pitch = width * format.conv_byte_count;
6102 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6104 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6106 ERR("Out of memory (%u).\n", dst_pitch * height);
6107 if (context)
6108 context_release(context);
6109 return E_OUTOFMEMORY;
6111 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6113 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6115 /* This code is only entered for color keying fixups */
6116 UINT height = surface->resource.height;
6118 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6119 dst_pitch = width * format.conv_byte_count;
6120 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6122 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6124 ERR("Out of memory (%u).\n", dst_pitch * height);
6125 if (context)
6126 context_release(context);
6127 return E_OUTOFMEMORY;
6129 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6130 width, height, dst_pitch, convert, surface);
6132 else
6134 mem = surface->resource.allocatedMemory;
6137 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6138 data.addr = mem;
6139 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6141 if (context)
6142 context_release(context);
6144 /* Don't delete PBO memory. */
6145 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6146 HeapFree(GetProcessHeap(), 0, mem);
6148 return WINED3D_OK;
6151 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6153 struct wined3d_device *device = surface->resource.device;
6154 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6155 BOOL in_fbo = FALSE;
6156 HRESULT hr;
6158 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6160 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6162 if (location == SFLAG_INTEXTURE)
6164 struct wined3d_context *context = context_acquire(device, NULL);
6165 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6166 context_release(context);
6167 return WINED3D_OK;
6169 else
6171 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6172 return WINED3DERR_INVALIDCALL;
6176 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6178 if (surface_is_offscreen(surface))
6180 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6181 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6182 if (location == SFLAG_INDRAWABLE)
6183 location = SFLAG_INTEXTURE;
6184 in_fbo = TRUE;
6186 else
6188 TRACE("Surface %p is an onscreen surface.\n", surface);
6192 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6193 location = SFLAG_INTEXTURE;
6195 if (surface->flags & location)
6197 TRACE("Location already up to date.\n");
6198 return WINED3D_OK;
6201 if (WARN_ON(d3d_surface))
6203 DWORD required_access = resource_access_from_location(location);
6204 if ((surface->resource.access_flags & required_access) != required_access)
6205 WARN("Operation requires %#x access, but surface only has %#x.\n",
6206 required_access, surface->resource.access_flags);
6209 if (!(surface->flags & SFLAG_LOCATIONS))
6211 ERR("Surface %p does not have any up to date location.\n", surface);
6212 surface->flags |= SFLAG_LOST;
6213 return WINED3DERR_DEVICELOST;
6216 switch (location)
6218 case SFLAG_INSYSMEM:
6219 surface_load_sysmem(surface, gl_info, rect);
6220 break;
6222 case SFLAG_INDRAWABLE:
6223 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6224 return hr;
6225 break;
6227 case SFLAG_INTEXTURE:
6228 case SFLAG_INSRGBTEX:
6229 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6230 return hr;
6231 break;
6233 default:
6234 ERR("Don't know how to handle location %#x.\n", location);
6235 break;
6238 if (!rect)
6240 surface->flags |= location;
6242 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6243 surface_evict_sysmem(surface);
6246 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6248 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6249 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6252 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6253 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6255 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6258 return WINED3D_OK;
6261 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6263 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6265 /* Not on a swapchain - must be offscreen */
6266 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6268 /* The front buffer is always onscreen */
6269 if (surface == swapchain->front_buffer) return FALSE;
6271 /* If the swapchain is rendered to an FBO, the backbuffer is
6272 * offscreen, otherwise onscreen */
6273 return swapchain->render_to_fbo;
6276 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6277 /* Context activation is done by the caller. */
6278 static void ffp_blit_free(struct wined3d_device *device) { }
6280 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6281 /* Context activation is done by the caller. */
6282 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6284 BYTE table[256][4];
6285 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6287 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6289 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6290 ENTER_GL();
6291 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6292 LEAVE_GL();
6295 /* Context activation is done by the caller. */
6296 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6298 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6300 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6301 * else the surface is converted in software at upload time in LoadLocation.
6303 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6304 ffp_blit_p8_upload_palette(surface, gl_info);
6306 ENTER_GL();
6307 glEnable(surface->texture_target);
6308 checkGLcall("glEnable(surface->texture_target)");
6309 LEAVE_GL();
6310 return WINED3D_OK;
6313 /* Context activation is done by the caller. */
6314 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6316 ENTER_GL();
6317 glDisable(GL_TEXTURE_2D);
6318 checkGLcall("glDisable(GL_TEXTURE_2D)");
6319 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6321 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6322 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6324 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6326 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6327 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6329 LEAVE_GL();
6332 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6333 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6334 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6336 enum complex_fixup src_fixup;
6338 switch (blit_op)
6340 case WINED3D_BLIT_OP_COLOR_BLIT:
6341 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6342 return FALSE;
6344 src_fixup = get_complex_fixup(src_format->color_fixup);
6345 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6347 TRACE("Checking support for fixup:\n");
6348 dump_color_fixup_desc(src_format->color_fixup);
6351 if (!is_identity_fixup(dst_format->color_fixup))
6353 TRACE("Destination fixups are not supported\n");
6354 return FALSE;
6357 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6359 TRACE("P8 fixup supported\n");
6360 return TRUE;
6363 /* We only support identity conversions. */
6364 if (is_identity_fixup(src_format->color_fixup))
6366 TRACE("[OK]\n");
6367 return TRUE;
6370 TRACE("[FAILED]\n");
6371 return FALSE;
6373 case WINED3D_BLIT_OP_COLOR_FILL:
6374 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6375 return FALSE;
6377 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6379 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6380 return FALSE;
6382 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6384 TRACE("Color fill not supported\n");
6385 return FALSE;
6388 /* FIXME: We should reject color fills on formats with fixups,
6389 * but this would break P8 color fills for example. */
6391 return TRUE;
6393 case WINED3D_BLIT_OP_DEPTH_FILL:
6394 return TRUE;
6396 default:
6397 TRACE("Unsupported blit_op=%d\n", blit_op);
6398 return FALSE;
6402 /* Do not call while under the GL lock. */
6403 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6404 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6406 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6407 struct wined3d_fb_state fb = {&dst_surface, NULL};
6409 return device_clear_render_targets(device, 1, &fb,
6410 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6413 /* Do not call while under the GL lock. */
6414 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6415 struct wined3d_surface *surface, const RECT *rect, float depth)
6417 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6418 struct wined3d_fb_state fb = {NULL, surface};
6420 return device_clear_render_targets(device, 0, &fb,
6421 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6424 const struct blit_shader ffp_blit = {
6425 ffp_blit_alloc,
6426 ffp_blit_free,
6427 ffp_blit_set,
6428 ffp_blit_unset,
6429 ffp_blit_supported,
6430 ffp_blit_color_fill,
6431 ffp_blit_depth_fill,
6434 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6436 return WINED3D_OK;
6439 /* Context activation is done by the caller. */
6440 static void cpu_blit_free(struct wined3d_device *device)
6444 /* Context activation is done by the caller. */
6445 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6447 return WINED3D_OK;
6450 /* Context activation is done by the caller. */
6451 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6455 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6456 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6457 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6459 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6461 return TRUE;
6464 return FALSE;
6467 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6468 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6469 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6471 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6472 const struct wined3d_format *src_format, *dst_format;
6473 struct wined3d_surface *orig_src = src_surface;
6474 WINED3DLOCKED_RECT dlock, slock;
6475 HRESULT hr = WINED3D_OK;
6476 const BYTE *sbuf;
6477 RECT xdst,xsrc;
6478 BYTE *dbuf;
6479 int x, y;
6481 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6482 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6483 flags, fx, debug_d3dtexturefiltertype(filter));
6485 xsrc = *src_rect;
6487 if (!src_surface)
6489 RECT full_rect;
6491 full_rect.left = 0;
6492 full_rect.top = 0;
6493 full_rect.right = dst_surface->resource.width;
6494 full_rect.bottom = dst_surface->resource.height;
6495 IntersectRect(&xdst, &full_rect, dst_rect);
6497 else
6499 BOOL clip_horiz, clip_vert;
6501 xdst = *dst_rect;
6502 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6503 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6505 if (clip_vert || clip_horiz)
6507 /* Now check if this is a special case or not... */
6508 if ((flags & WINEDDBLT_DDFX)
6509 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6510 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6512 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6513 return WINED3D_OK;
6516 if (clip_horiz)
6518 if (xdst.left < 0)
6520 xsrc.left -= xdst.left;
6521 xdst.left = 0;
6523 if (xdst.right > dst_surface->resource.width)
6525 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6526 xdst.right = (int)dst_surface->resource.width;
6530 if (clip_vert)
6532 if (xdst.top < 0)
6534 xsrc.top -= xdst.top;
6535 xdst.top = 0;
6537 if (xdst.bottom > dst_surface->resource.height)
6539 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6540 xdst.bottom = (int)dst_surface->resource.height;
6544 /* And check if after clipping something is still to be done... */
6545 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6546 || (xdst.left >= (int)dst_surface->resource.width)
6547 || (xdst.top >= (int)dst_surface->resource.height)
6548 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6549 || (xsrc.left >= (int)src_surface->resource.width)
6550 || (xsrc.top >= (int)src_surface->resource.height))
6552 TRACE("Nothing to be done after clipping.\n");
6553 return WINED3D_OK;
6558 if (src_surface == dst_surface)
6560 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6561 slock = dlock;
6562 src_format = dst_surface->resource.format;
6563 dst_format = src_format;
6565 else
6567 dst_format = dst_surface->resource.format;
6568 if (src_surface)
6570 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6572 src_surface = surface_convert_format(src_surface, dst_format->id);
6573 if (!src_surface)
6575 /* The conv function writes a FIXME */
6576 WARN("Cannot convert source surface format to dest format.\n");
6577 goto release;
6580 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6581 src_format = src_surface->resource.format;
6583 else
6585 src_format = dst_format;
6587 if (dst_rect)
6588 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6589 else
6590 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6593 bpp = dst_surface->resource.format->byte_count;
6594 srcheight = xsrc.bottom - xsrc.top;
6595 srcwidth = xsrc.right - xsrc.left;
6596 dstheight = xdst.bottom - xdst.top;
6597 dstwidth = xdst.right - xdst.left;
6598 width = (xdst.right - xdst.left) * bpp;
6600 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6602 UINT row_block_count;
6604 if (flags || src_surface == dst_surface)
6606 FIXME("Only plain blits supported on compressed surfaces.\n");
6607 hr = E_NOTIMPL;
6608 goto release;
6611 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6613 if (srcheight != dstheight || srcwidth != dstwidth)
6615 WARN("Stretching not supported on compressed surfaces.\n");
6616 hr = WINED3DERR_INVALIDCALL;
6617 goto release;
6620 dbuf = dlock.pBits;
6621 sbuf = slock.pBits;
6623 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6624 for (y = 0; y < dstheight; y += dst_format->block_height)
6626 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6627 dbuf += dlock.Pitch;
6628 sbuf += slock.Pitch;
6631 goto release;
6634 if (dst_rect && src_surface != dst_surface)
6635 dbuf = dlock.pBits;
6636 else
6637 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6639 /* First, all the 'source-less' blits */
6640 if (flags & WINEDDBLT_COLORFILL)
6642 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6643 flags &= ~WINEDDBLT_COLORFILL;
6646 if (flags & WINEDDBLT_DEPTHFILL)
6648 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6650 if (flags & WINEDDBLT_ROP)
6652 /* Catch some degenerate cases here. */
6653 switch (fx->dwROP)
6655 case BLACKNESS:
6656 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6657 break;
6658 case 0xAA0029: /* No-op */
6659 break;
6660 case WHITENESS:
6661 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6662 break;
6663 case SRCCOPY: /* Well, we do that below? */
6664 break;
6665 default:
6666 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6667 goto error;
6669 flags &= ~WINEDDBLT_ROP;
6671 if (flags & WINEDDBLT_DDROPS)
6673 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6675 /* Now the 'with source' blits. */
6676 if (src_surface)
6678 const BYTE *sbase;
6679 int sx, xinc, sy, yinc;
6681 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6682 goto release;
6684 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6685 && (srcwidth != dstwidth || srcheight != dstheight))
6687 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6688 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6691 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6692 xinc = (srcwidth << 16) / dstwidth;
6693 yinc = (srcheight << 16) / dstheight;
6695 if (!flags)
6697 /* No effects, we can cheat here. */
6698 if (dstwidth == srcwidth)
6700 if (dstheight == srcheight)
6702 /* No stretching in either direction. This needs to be as
6703 * fast as possible. */
6704 sbuf = sbase;
6706 /* Check for overlapping surfaces. */
6707 if (src_surface != dst_surface || xdst.top < xsrc.top
6708 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6710 /* No overlap, or dst above src, so copy from top downwards. */
6711 for (y = 0; y < dstheight; ++y)
6713 memcpy(dbuf, sbuf, width);
6714 sbuf += slock.Pitch;
6715 dbuf += dlock.Pitch;
6718 else if (xdst.top > xsrc.top)
6720 /* Copy from bottom upwards. */
6721 sbuf += (slock.Pitch*dstheight);
6722 dbuf += (dlock.Pitch*dstheight);
6723 for (y = 0; y < dstheight; ++y)
6725 sbuf -= slock.Pitch;
6726 dbuf -= dlock.Pitch;
6727 memcpy(dbuf, sbuf, width);
6730 else
6732 /* Src and dst overlapping on the same line, use memmove. */
6733 for (y = 0; y < dstheight; ++y)
6735 memmove(dbuf, sbuf, width);
6736 sbuf += slock.Pitch;
6737 dbuf += dlock.Pitch;
6741 else
6743 /* Stretching in y direction only. */
6744 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6746 sbuf = sbase + (sy >> 16) * slock.Pitch;
6747 memcpy(dbuf, sbuf, width);
6748 dbuf += dlock.Pitch;
6752 else
6754 /* Stretching in X direction. */
6755 int last_sy = -1;
6756 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6758 sbuf = sbase + (sy >> 16) * slock.Pitch;
6760 if ((sy >> 16) == (last_sy >> 16))
6762 /* This source row is the same as last source row -
6763 * Copy the already stretched row. */
6764 memcpy(dbuf, dbuf - dlock.Pitch, width);
6766 else
6768 #define STRETCH_ROW(type) \
6769 do { \
6770 const type *s = (const type *)sbuf; \
6771 type *d = (type *)dbuf; \
6772 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6773 d[x] = s[sx >> 16]; \
6774 } while(0)
6776 switch(bpp)
6778 case 1:
6779 STRETCH_ROW(BYTE);
6780 break;
6781 case 2:
6782 STRETCH_ROW(WORD);
6783 break;
6784 case 4:
6785 STRETCH_ROW(DWORD);
6786 break;
6787 case 3:
6789 const BYTE *s;
6790 BYTE *d = dbuf;
6791 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6793 DWORD pixel;
6795 s = sbuf + 3 * (sx >> 16);
6796 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6797 d[0] = (pixel ) & 0xff;
6798 d[1] = (pixel >> 8) & 0xff;
6799 d[2] = (pixel >> 16) & 0xff;
6800 d += 3;
6802 break;
6804 default:
6805 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6806 hr = WINED3DERR_NOTAVAILABLE;
6807 goto error;
6809 #undef STRETCH_ROW
6811 dbuf += dlock.Pitch;
6812 last_sy = sy;
6816 else
6818 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6819 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6820 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6821 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6823 /* The color keying flags are checked for correctness in ddraw */
6824 if (flags & WINEDDBLT_KEYSRC)
6826 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6827 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6829 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6831 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6832 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6835 if (flags & WINEDDBLT_KEYDEST)
6837 /* Destination color keys are taken from the source surface! */
6838 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6839 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6841 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6843 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6844 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6847 if (bpp == 1)
6849 keymask = 0xff;
6851 else
6853 keymask = src_format->red_mask
6854 | src_format->green_mask
6855 | src_format->blue_mask;
6857 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6860 if (flags & WINEDDBLT_DDFX)
6862 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6863 LONG tmpxy;
6864 dTopLeft = dbuf;
6865 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6866 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6867 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6869 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6871 /* I don't think we need to do anything about this flag */
6872 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6874 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6876 tmp = dTopRight;
6877 dTopRight = dTopLeft;
6878 dTopLeft = tmp;
6879 tmp = dBottomRight;
6880 dBottomRight = dBottomLeft;
6881 dBottomLeft = tmp;
6882 dstxinc = dstxinc * -1;
6884 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6886 tmp = dTopLeft;
6887 dTopLeft = dBottomLeft;
6888 dBottomLeft = tmp;
6889 tmp = dTopRight;
6890 dTopRight = dBottomRight;
6891 dBottomRight = tmp;
6892 dstyinc = dstyinc * -1;
6894 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6896 /* I don't think we need to do anything about this flag */
6897 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6899 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6901 tmp = dBottomRight;
6902 dBottomRight = dTopLeft;
6903 dTopLeft = tmp;
6904 tmp = dBottomLeft;
6905 dBottomLeft = dTopRight;
6906 dTopRight = tmp;
6907 dstxinc = dstxinc * -1;
6908 dstyinc = dstyinc * -1;
6910 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6912 tmp = dTopLeft;
6913 dTopLeft = dBottomLeft;
6914 dBottomLeft = dBottomRight;
6915 dBottomRight = dTopRight;
6916 dTopRight = tmp;
6917 tmpxy = dstxinc;
6918 dstxinc = dstyinc;
6919 dstyinc = tmpxy;
6920 dstxinc = dstxinc * -1;
6922 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6924 tmp = dTopLeft;
6925 dTopLeft = dTopRight;
6926 dTopRight = dBottomRight;
6927 dBottomRight = dBottomLeft;
6928 dBottomLeft = tmp;
6929 tmpxy = dstxinc;
6930 dstxinc = dstyinc;
6931 dstyinc = tmpxy;
6932 dstyinc = dstyinc * -1;
6934 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6936 /* I don't think we need to do anything about this flag */
6937 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6939 dbuf = dTopLeft;
6940 flags &= ~(WINEDDBLT_DDFX);
6943 #define COPY_COLORKEY_FX(type) \
6944 do { \
6945 const type *s; \
6946 type *d = (type *)dbuf, *dx, tmp; \
6947 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6949 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6950 dx = d; \
6951 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6953 tmp = s[sx >> 16]; \
6954 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6955 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6957 dx[0] = tmp; \
6959 dx = (type *)(((BYTE *)dx) + dstxinc); \
6961 d = (type *)(((BYTE *)d) + dstyinc); \
6963 } while(0)
6965 switch (bpp)
6967 case 1:
6968 COPY_COLORKEY_FX(BYTE);
6969 break;
6970 case 2:
6971 COPY_COLORKEY_FX(WORD);
6972 break;
6973 case 4:
6974 COPY_COLORKEY_FX(DWORD);
6975 break;
6976 case 3:
6978 const BYTE *s;
6979 BYTE *d = dbuf, *dx;
6980 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6982 sbuf = sbase + (sy >> 16) * slock.Pitch;
6983 dx = d;
6984 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6986 DWORD pixel, dpixel = 0;
6987 s = sbuf + 3 * (sx>>16);
6988 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6989 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6990 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6991 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6993 dx[0] = (pixel ) & 0xff;
6994 dx[1] = (pixel >> 8) & 0xff;
6995 dx[2] = (pixel >> 16) & 0xff;
6997 dx += dstxinc;
6999 d += dstyinc;
7001 break;
7003 default:
7004 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7005 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7006 hr = WINED3DERR_NOTAVAILABLE;
7007 goto error;
7008 #undef COPY_COLORKEY_FX
7013 error:
7014 if (flags && FIXME_ON(d3d_surface))
7016 FIXME("\tUnsupported flags: %#x.\n", flags);
7019 release:
7020 wined3d_surface_unmap(dst_surface);
7021 if (src_surface && src_surface != dst_surface)
7022 wined3d_surface_unmap(src_surface);
7023 /* Release the converted surface, if any. */
7024 if (src_surface && src_surface != orig_src)
7025 wined3d_surface_decref(src_surface);
7027 return hr;
7030 /* Do not call while under the GL lock. */
7031 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7032 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7034 static const RECT src_rect;
7035 WINEDDBLTFX BltFx;
7037 memset(&BltFx, 0, sizeof(BltFx));
7038 BltFx.dwSize = sizeof(BltFx);
7039 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7040 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7041 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7044 /* Do not call while under the GL lock. */
7045 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7046 struct wined3d_surface *surface, const RECT *rect, float depth)
7048 FIXME("Depth filling not implemented by cpu_blit.\n");
7049 return WINED3DERR_INVALIDCALL;
7052 const struct blit_shader cpu_blit = {
7053 cpu_blit_alloc,
7054 cpu_blit_free,
7055 cpu_blit_set,
7056 cpu_blit_unset,
7057 cpu_blit_supported,
7058 cpu_blit_color_fill,
7059 cpu_blit_depth_fill,
7062 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7063 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7064 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7065 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7067 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7068 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7069 unsigned int resource_size;
7070 HRESULT hr;
7072 if (multisample_quality > 0)
7074 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7075 multisample_quality = 0;
7078 /* Quick lockable sanity check.
7079 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7080 * this function is too deep to need to care about things like this.
7081 * Levels need to be checked too, since they all affect what can be done. */
7082 switch (pool)
7084 case WINED3DPOOL_SCRATCH:
7085 if (!lockable)
7087 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7088 "which are mutually exclusive, setting lockable to TRUE.\n");
7089 lockable = TRUE;
7091 break;
7093 case WINED3DPOOL_SYSTEMMEM:
7094 if (!lockable)
7095 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7096 break;
7098 case WINED3DPOOL_MANAGED:
7099 if (usage & WINED3DUSAGE_DYNAMIC)
7100 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7101 break;
7103 case WINED3DPOOL_DEFAULT:
7104 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7105 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7106 break;
7108 default:
7109 FIXME("Unknown pool %#x.\n", pool);
7110 break;
7113 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7114 FIXME("Trying to create a render target that isn't in the default pool.\n");
7116 /* FIXME: Check that the format is supported by the device. */
7118 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7119 if (!resource_size)
7120 return WINED3DERR_INVALIDCALL;
7122 surface->surface_type = surface_type;
7124 switch (surface_type)
7126 case SURFACE_OPENGL:
7127 surface->surface_ops = &surface_ops;
7128 break;
7130 case SURFACE_GDI:
7131 surface->surface_ops = &gdi_surface_ops;
7132 break;
7134 default:
7135 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7136 return WINED3DERR_INVALIDCALL;
7139 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7140 multisample_type, multisample_quality, usage, pool, width, height, 1,
7141 resource_size, parent, parent_ops, &surface_resource_ops);
7142 if (FAILED(hr))
7144 WARN("Failed to initialize resource, returning %#x.\n", hr);
7145 return hr;
7148 /* "Standalone" surface. */
7149 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7151 surface->texture_level = level;
7152 list_init(&surface->overlays);
7154 /* Flags */
7155 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7156 if (discard)
7157 surface->flags |= SFLAG_DISCARD;
7158 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7159 surface->flags |= SFLAG_LOCKABLE;
7160 /* I'm not sure if this qualifies as a hack or as an optimization. It
7161 * seems reasonable to assume that lockable render targets will get
7162 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7163 * creation. However, the other reason we want to do this is that several
7164 * ddraw applications access surface memory while the surface isn't
7165 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7166 * future locks prevents these from crashing. */
7167 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7168 surface->flags |= SFLAG_DYNLOCK;
7170 /* Mark the texture as dirty so that it gets loaded first time around. */
7171 surface_add_dirty_rect(surface, NULL);
7172 list_init(&surface->renderbuffers);
7174 TRACE("surface %p, memory %p, size %u\n",
7175 surface, surface->resource.allocatedMemory, surface->resource.size);
7177 /* Call the private setup routine */
7178 hr = surface->surface_ops->surface_private_setup(surface);
7179 if (FAILED(hr))
7181 ERR("Private setup failed, returning %#x\n", hr);
7182 surface->surface_ops->surface_cleanup(surface);
7183 return hr;
7186 return hr;
7189 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7190 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7191 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7192 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7194 struct wined3d_surface *object;
7195 HRESULT hr;
7197 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7198 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7199 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7200 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7201 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7203 if (surface_type == SURFACE_OPENGL && !device->adapter)
7205 ERR("OpenGL surfaces are not available without OpenGL.\n");
7206 return WINED3DERR_NOTAVAILABLE;
7209 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7210 if (!object)
7212 ERR("Failed to allocate surface memory.\n");
7213 return WINED3DERR_OUTOFVIDEOMEMORY;
7216 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7217 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7218 if (FAILED(hr))
7220 WARN("Failed to initialize surface, returning %#x.\n", hr);
7221 HeapFree(GetProcessHeap(), 0, object);
7222 return hr;
7225 TRACE("Created surface %p.\n", object);
7226 *surface = object;
7228 return hr;