wined3d: Replace surface_update_draw_binding() with wined3d_resource_update_draw_bind...
[wine.git] / dlls / wined3d / surface.c
blob6603d4647b244613ab95566ea13068c66411696a
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 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_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
147 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
149 surface->swapchain = swapchain;
150 wined3d_resource_update_draw_binding(&surface->resource);
153 struct blt_info
155 GLenum binding;
156 GLenum bind_target;
157 enum tex_types tex_type;
158 GLfloat coords[4][3];
161 struct float_rect
163 float l;
164 float t;
165 float r;
166 float b;
169 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
171 f->l = ((r->left * 2.0f) / w) - 1.0f;
172 f->t = ((r->top * 2.0f) / h) - 1.0f;
173 f->r = ((r->right * 2.0f) / w) - 1.0f;
174 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
177 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
179 GLfloat (*coords)[3] = info->coords;
180 struct float_rect f;
182 switch (target)
184 default:
185 FIXME("Unsupported texture target %#x\n", target);
186 /* Fall back to GL_TEXTURE_2D */
187 case GL_TEXTURE_2D:
188 info->binding = GL_TEXTURE_BINDING_2D;
189 info->bind_target = GL_TEXTURE_2D;
190 info->tex_type = tex_2d;
191 coords[0][0] = (float)rect->left / w;
192 coords[0][1] = (float)rect->top / h;
193 coords[0][2] = 0.0f;
195 coords[1][0] = (float)rect->right / w;
196 coords[1][1] = (float)rect->top / h;
197 coords[1][2] = 0.0f;
199 coords[2][0] = (float)rect->left / w;
200 coords[2][1] = (float)rect->bottom / h;
201 coords[2][2] = 0.0f;
203 coords[3][0] = (float)rect->right / w;
204 coords[3][1] = (float)rect->bottom / h;
205 coords[3][2] = 0.0f;
206 break;
208 case GL_TEXTURE_RECTANGLE_ARB:
209 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
210 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
211 info->tex_type = tex_rect;
212 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
213 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
214 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
215 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
216 break;
218 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
219 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
220 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
221 info->tex_type = tex_cube;
222 cube_coords_float(rect, w, h, &f);
224 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
225 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
226 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
227 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
228 break;
230 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
231 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
232 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
233 info->tex_type = tex_cube;
234 cube_coords_float(rect, w, h, &f);
236 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
237 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
238 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
239 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
240 break;
242 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
243 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
244 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
245 info->tex_type = tex_cube;
246 cube_coords_float(rect, w, h, &f);
248 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
249 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
250 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
251 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
252 break;
254 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
255 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
256 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
257 info->tex_type = tex_cube;
258 cube_coords_float(rect, w, h, &f);
260 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
261 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
262 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
263 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
264 break;
266 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
267 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
268 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
269 info->tex_type = tex_cube;
270 cube_coords_float(rect, w, h, &f);
272 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
273 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
274 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
275 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
276 break;
278 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
279 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
280 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
281 info->tex_type = tex_cube;
282 cube_coords_float(rect, w, h, &f);
284 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
285 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
286 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
287 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
288 break;
292 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
294 if (rect_in)
295 *rect_out = *rect_in;
296 else
298 rect_out->left = 0;
299 rect_out->top = 0;
300 rect_out->right = surface->resource.width;
301 rect_out->bottom = surface->resource.height;
305 /* Context activation is done by the caller. */
306 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
307 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
309 const struct wined3d_gl_info *gl_info = context->gl_info;
310 struct wined3d_texture *texture = src_surface->container;
311 struct blt_info info;
313 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
315 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
316 checkGLcall("glEnable(bind_target)");
318 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
320 /* Filtering for StretchRect */
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
322 wined3d_gl_mag_filter(magLookup, filter));
323 checkGLcall("glTexParameteri");
324 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
325 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
326 checkGLcall("glTexParameteri");
327 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
328 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
329 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
330 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
331 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
332 checkGLcall("glTexEnvi");
334 /* Draw a quad */
335 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
339 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
340 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
342 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
343 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
345 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
346 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
347 gl_info->gl_ops.gl.p_glEnd();
349 /* Unbind the texture */
350 context_bind_texture(context, info.bind_target, 0);
352 /* We changed the filtering settings on the texture. Inform the
353 * container about this to get the filters reset properly next draw. */
354 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
355 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
356 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
357 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
360 /* Works correctly only for <= 4 bpp formats. */
361 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
363 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
364 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
365 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
368 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
370 const struct wined3d_format *format = surface->resource.format;
371 SYSTEM_INFO sysInfo;
372 BITMAPINFO *b_info;
373 int extraline = 0;
374 DWORD *masks;
376 TRACE("surface %p.\n", surface);
378 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
380 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
381 return WINED3DERR_INVALIDCALL;
384 switch (format->byte_count)
386 case 2:
387 case 4:
388 /* Allocate extra space to store the RGB bit masks. */
389 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
390 break;
392 case 3:
393 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
394 break;
396 default:
397 /* Allocate extra space for a palette. */
398 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
399 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
400 break;
403 if (!b_info)
404 return E_OUTOFMEMORY;
406 /* Some applications access the surface in via DWORDs, and do not take
407 * the necessary care at the end of the surface. So we need at least
408 * 4 extra bytes at the end of the surface. Check against the page size,
409 * if the last page used for the surface has at least 4 spare bytes we're
410 * safe, otherwise add an extra line to the DIB section. */
411 GetSystemInfo(&sysInfo);
412 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
414 extraline = 1;
415 TRACE("Adding an extra line to the DIB section.\n");
418 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
419 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
420 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
421 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
422 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
423 * wined3d_surface_get_pitch(surface);
424 b_info->bmiHeader.biPlanes = 1;
425 b_info->bmiHeader.biBitCount = format->byte_count * 8;
427 b_info->bmiHeader.biXPelsPerMeter = 0;
428 b_info->bmiHeader.biYPelsPerMeter = 0;
429 b_info->bmiHeader.biClrUsed = 0;
430 b_info->bmiHeader.biClrImportant = 0;
432 /* Get the bit masks */
433 masks = (DWORD *)b_info->bmiColors;
434 switch (surface->resource.format->id)
436 case WINED3DFMT_B8G8R8_UNORM:
437 b_info->bmiHeader.biCompression = BI_RGB;
438 break;
440 case WINED3DFMT_B5G5R5X1_UNORM:
441 case WINED3DFMT_B5G5R5A1_UNORM:
442 case WINED3DFMT_B4G4R4A4_UNORM:
443 case WINED3DFMT_B4G4R4X4_UNORM:
444 case WINED3DFMT_B2G3R3_UNORM:
445 case WINED3DFMT_B2G3R3A8_UNORM:
446 case WINED3DFMT_R10G10B10A2_UNORM:
447 case WINED3DFMT_R8G8B8A8_UNORM:
448 case WINED3DFMT_R8G8B8X8_UNORM:
449 case WINED3DFMT_B10G10R10A2_UNORM:
450 case WINED3DFMT_B5G6R5_UNORM:
451 case WINED3DFMT_R16G16B16A16_UNORM:
452 b_info->bmiHeader.biCompression = BI_BITFIELDS;
453 get_color_masks(format, masks);
454 break;
456 default:
457 /* Don't know palette */
458 b_info->bmiHeader.biCompression = BI_RGB;
459 break;
462 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
463 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
464 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
465 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
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 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
477 HeapFree(GetProcessHeap(), 0, b_info);
479 /* Now allocate a DC. */
480 surface->hDC = CreateCompatibleDC(0);
481 SelectObject(surface->hDC, surface->dib.DIBsection);
483 surface->flags |= SFLAG_DIBSECTION;
485 return WINED3D_OK;
488 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
489 DWORD location)
491 if (location & WINED3D_LOCATION_BUFFER)
493 data->addr = NULL;
494 data->buffer_object = surface->pbo;
495 return;
497 if (location & WINED3D_LOCATION_USER_MEMORY)
499 data->addr = surface->user_memory;
500 data->buffer_object = 0;
501 return;
503 if (location & WINED3D_LOCATION_DIB)
505 data->addr = surface->dib.bitmap_data;
506 data->buffer_object = 0;
507 return;
509 if (location & WINED3D_LOCATION_SYSMEM)
511 data->addr = surface->resource.heap_memory;
512 data->buffer_object = 0;
513 return;
516 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
517 data->addr = NULL;
518 data->buffer_object = 0;
521 static void surface_prepare_buffer(struct wined3d_surface *surface)
523 struct wined3d_context *context;
524 GLenum error;
525 const struct wined3d_gl_info *gl_info;
527 if (surface->pbo)
528 return;
530 context = context_acquire(surface->resource.device, NULL);
531 gl_info = context->gl_info;
533 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
534 error = gl_info->gl_ops.gl.p_glGetError();
535 if (!surface->pbo || error != GL_NO_ERROR)
536 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
538 TRACE("Binding PBO %u.\n", surface->pbo);
540 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
541 checkGLcall("glBindBufferARB");
543 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
544 NULL, GL_STREAM_DRAW_ARB));
545 checkGLcall("glBufferDataARB");
547 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
548 checkGLcall("glBindBufferARB");
550 context_release(context);
553 static void surface_prepare_system_memory(struct wined3d_surface *surface)
555 TRACE("surface %p.\n", surface);
557 if (surface->resource.heap_memory)
558 return;
560 /* Whatever surface we have, make sure that there is memory allocated
561 * for the downloaded copy, or a PBO to map. */
562 if (!wined3d_resource_allocate_sysmem(&surface->resource))
563 ERR("Failed to allocate system memory.\n");
565 if (surface->locations & WINED3D_LOCATION_SYSMEM)
566 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
569 void surface_prepare_map_memory(struct wined3d_surface *surface)
571 switch (surface->resource.map_binding)
573 case WINED3D_LOCATION_SYSMEM:
574 surface_prepare_system_memory(surface);
575 break;
577 case WINED3D_LOCATION_USER_MEMORY:
578 if (!surface->user_memory)
579 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
580 break;
582 case WINED3D_LOCATION_DIB:
583 if (!surface->dib.bitmap_data)
584 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
585 break;
587 case WINED3D_LOCATION_BUFFER:
588 surface_prepare_buffer(surface);
589 break;
591 default:
592 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
596 static void surface_evict_sysmem(struct wined3d_surface *surface)
598 if (surface->resource.map_count || surface->flags & SFLAG_DONOTFREE)
599 return;
601 wined3d_resource_free_sysmem(&surface->resource);
602 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
605 static void surface_force_reload(struct wined3d_surface *surface)
607 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
610 static void surface_release_client_storage(struct wined3d_surface *surface)
612 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
613 const struct wined3d_gl_info *gl_info = context->gl_info;
615 if (surface->container->texture_rgb.name)
617 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
618 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
619 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
621 if (surface->container->texture_srgb.name)
623 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
624 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
625 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
628 context_release(context);
630 surface_invalidate_location(surface, WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
631 surface_force_reload(surface);
634 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
636 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
638 return surface->resource.pool == WINED3D_POOL_DEFAULT
639 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
640 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
641 && !surface->resource.format->convert
642 && !(surface->flags & (SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM));
645 static HRESULT surface_private_setup(struct wined3d_surface *surface)
647 /* TODO: Check against the maximum texture sizes supported by the video card. */
648 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
649 unsigned int pow2Width, pow2Height;
651 TRACE("surface %p.\n", surface);
653 /* Non-power2 support */
654 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
655 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
657 pow2Width = surface->resource.width;
658 pow2Height = surface->resource.height;
660 else
662 /* Find the nearest pow2 match */
663 pow2Width = pow2Height = 1;
664 while (pow2Width < surface->resource.width)
665 pow2Width <<= 1;
666 while (pow2Height < surface->resource.height)
667 pow2Height <<= 1;
669 surface->pow2Width = pow2Width;
670 surface->pow2Height = pow2Height;
672 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
674 /* TODO: Add support for non power two compressed textures. */
675 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
677 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
678 surface, surface->resource.width, surface->resource.height);
679 return WINED3DERR_NOTAVAILABLE;
683 if (pow2Width != surface->resource.width
684 || pow2Height != surface->resource.height)
686 surface->flags |= SFLAG_NONPOW2;
689 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
690 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
692 /* One of three options:
693 * 1: Do the same as we do with NPOT and scale the texture, (any
694 * texture ops would require the texture to be scaled which is
695 * potentially slow)
696 * 2: Set the texture to the maximum size (bad idea).
697 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
698 * 4: Create the surface, but allow it to be used only for DirectDraw
699 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
700 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
701 * the render target. */
702 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
704 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
705 return WINED3DERR_NOTAVAILABLE;
708 /* We should never use this surface in combination with OpenGL! */
709 TRACE("Creating an oversized surface: %ux%u.\n",
710 surface->pow2Width, surface->pow2Height);
713 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
714 surface->locations = WINED3D_LOCATION_DISCARDED;
716 if (surface_use_pbo(surface))
717 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
719 return WINED3D_OK;
722 static void surface_unmap(struct wined3d_surface *surface)
724 struct wined3d_device *device = surface->resource.device;
725 const struct wined3d_gl_info *gl_info;
726 struct wined3d_context *context;
728 TRACE("surface %p.\n", surface);
730 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
732 switch (surface->resource.map_binding)
734 case WINED3D_LOCATION_SYSMEM:
735 case WINED3D_LOCATION_USER_MEMORY:
736 case WINED3D_LOCATION_DIB:
737 break;
739 case WINED3D_LOCATION_BUFFER:
740 context = context_acquire(device, NULL);
741 gl_info = context->gl_info;
743 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
744 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
745 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
746 checkGLcall("glUnmapBufferARB");
747 context_release(context);
748 break;
750 default:
751 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
754 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
756 TRACE("Not dirtified, nothing to do.\n");
757 return;
760 if (surface->swapchain && surface->swapchain->front_buffer == surface)
761 surface_load_location(surface, surface->resource.draw_binding);
762 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
763 FIXME("Depth / stencil buffer locking is not implemented.\n");
766 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
768 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
769 return FALSE;
770 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
771 return FALSE;
772 return TRUE;
775 static void surface_depth_blt_fbo(const struct wined3d_device *device,
776 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
777 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
779 const struct wined3d_gl_info *gl_info;
780 struct wined3d_context *context;
781 DWORD src_mask, dst_mask;
782 GLbitfield gl_mask;
784 TRACE("device %p\n", device);
785 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
786 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
787 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
788 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
790 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
791 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
793 if (src_mask != dst_mask)
795 ERR("Incompatible formats %s and %s.\n",
796 debug_d3dformat(src_surface->resource.format->id),
797 debug_d3dformat(dst_surface->resource.format->id));
798 return;
801 if (!src_mask)
803 ERR("Not a depth / stencil format: %s.\n",
804 debug_d3dformat(src_surface->resource.format->id));
805 return;
808 gl_mask = 0;
809 if (src_mask & WINED3DFMT_FLAG_DEPTH)
810 gl_mask |= GL_DEPTH_BUFFER_BIT;
811 if (src_mask & WINED3DFMT_FLAG_STENCIL)
812 gl_mask |= GL_STENCIL_BUFFER_BIT;
814 /* Make sure the locations are up-to-date. Loading the destination
815 * surface isn't required if the entire surface is overwritten. */
816 surface_load_location(src_surface, src_location);
817 if (!surface_is_full_rect(dst_surface, dst_rect))
818 surface_load_location(dst_surface, dst_location);
820 context = context_acquire(device, NULL);
821 if (!context->valid)
823 context_release(context);
824 WARN("Invalid context, skipping blit.\n");
825 return;
828 gl_info = context->gl_info;
830 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
831 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
833 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
834 context_set_draw_buffer(context, GL_NONE);
835 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
836 context_invalidate_state(context, STATE_FRAMEBUFFER);
838 if (gl_mask & GL_DEPTH_BUFFER_BIT)
840 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
841 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
843 if (gl_mask & GL_STENCIL_BUFFER_BIT)
845 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
847 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
848 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
850 gl_info->gl_ops.gl.p_glStencilMask(~0U);
851 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
854 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
855 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
857 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
858 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
859 checkGLcall("glBlitFramebuffer()");
861 if (wined3d_settings.strict_draw_ordering)
862 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
864 context_release(context);
867 /* Blit between surface locations. Onscreen on different swapchains is not supported.
868 * Depth / stencil is not supported. */
869 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
870 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
871 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
873 const struct wined3d_gl_info *gl_info;
874 struct wined3d_context *context;
875 RECT src_rect, dst_rect;
876 GLenum gl_filter;
877 GLenum buffer;
879 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
880 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
881 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
882 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
883 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
885 src_rect = *src_rect_in;
886 dst_rect = *dst_rect_in;
888 switch (filter)
890 case WINED3D_TEXF_LINEAR:
891 gl_filter = GL_LINEAR;
892 break;
894 default:
895 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
896 case WINED3D_TEXF_NONE:
897 case WINED3D_TEXF_POINT:
898 gl_filter = GL_NEAREST;
899 break;
902 /* Resolve the source surface first if needed. */
903 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
904 && (src_surface->resource.format->id != dst_surface->resource.format->id
905 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
906 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
907 src_location = WINED3D_LOCATION_RB_RESOLVED;
909 /* Make sure the locations are up-to-date. Loading the destination
910 * surface isn't required if the entire surface is overwritten. (And is
911 * in fact harmful if we're being called by surface_load_location() with
912 * the purpose of loading the destination surface.) */
913 surface_load_location(src_surface, src_location);
914 if (!surface_is_full_rect(dst_surface, &dst_rect))
915 surface_load_location(dst_surface, dst_location);
917 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
918 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
919 else context = context_acquire(device, NULL);
921 if (!context->valid)
923 context_release(context);
924 WARN("Invalid context, skipping blit.\n");
925 return;
928 gl_info = context->gl_info;
930 if (src_location == WINED3D_LOCATION_DRAWABLE)
932 TRACE("Source surface %p is onscreen.\n", src_surface);
933 buffer = surface_get_gl_buffer(src_surface);
934 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
936 else
938 TRACE("Source surface %p is offscreen.\n", src_surface);
939 buffer = GL_COLOR_ATTACHMENT0;
942 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
943 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
944 checkGLcall("glReadBuffer()");
945 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
947 if (dst_location == WINED3D_LOCATION_DRAWABLE)
949 TRACE("Destination surface %p is onscreen.\n", dst_surface);
950 buffer = surface_get_gl_buffer(dst_surface);
951 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
953 else
955 TRACE("Destination surface %p is offscreen.\n", dst_surface);
956 buffer = GL_COLOR_ATTACHMENT0;
959 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
960 context_set_draw_buffer(context, buffer);
961 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
962 context_invalidate_state(context, STATE_FRAMEBUFFER);
964 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
965 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
966 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
967 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
968 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
970 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
971 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
973 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
974 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
975 checkGLcall("glBlitFramebuffer()");
977 if (wined3d_settings.strict_draw_ordering
978 || (dst_location == WINED3D_LOCATION_DRAWABLE
979 && dst_surface->swapchain->front_buffer == dst_surface))
980 gl_info->gl_ops.gl.p_glFlush();
982 context_release(context);
985 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
986 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
987 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
989 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
990 return FALSE;
992 /* Source and/or destination need to be on the GL side */
993 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
994 return FALSE;
996 switch (blit_op)
998 case WINED3D_BLIT_OP_COLOR_BLIT:
999 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1000 return FALSE;
1001 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1002 return FALSE;
1003 break;
1005 case WINED3D_BLIT_OP_DEPTH_BLIT:
1006 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1007 return FALSE;
1008 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1009 return FALSE;
1010 break;
1012 default:
1013 return FALSE;
1016 if (!(src_format->id == dst_format->id
1017 || (is_identity_fixup(src_format->color_fixup)
1018 && is_identity_fixup(dst_format->color_fixup))))
1019 return FALSE;
1021 return TRUE;
1024 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1025 DWORD color, struct wined3d_color *float_color)
1027 const struct wined3d_format *format = surface->resource.format;
1028 const struct wined3d_palette *palette;
1030 switch (format->id)
1032 case WINED3DFMT_P8_UINT:
1033 palette = surface->swapchain ? surface->swapchain->palette : NULL;
1035 if (palette)
1037 float_color->r = palette->colors[color].rgbRed / 255.0f;
1038 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1039 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1041 else
1043 float_color->r = 0.0f;
1044 float_color->g = 0.0f;
1045 float_color->b = 0.0f;
1047 float_color->a = color / 255.0f;
1048 break;
1050 case WINED3DFMT_B5G6R5_UNORM:
1051 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1052 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1053 float_color->b = (color & 0x1f) / 31.0f;
1054 float_color->a = 1.0f;
1055 break;
1057 case WINED3DFMT_B8G8R8_UNORM:
1058 case WINED3DFMT_B8G8R8X8_UNORM:
1059 float_color->r = D3DCOLOR_R(color);
1060 float_color->g = D3DCOLOR_G(color);
1061 float_color->b = D3DCOLOR_B(color);
1062 float_color->a = 1.0f;
1063 break;
1065 case WINED3DFMT_B8G8R8A8_UNORM:
1066 float_color->r = D3DCOLOR_R(color);
1067 float_color->g = D3DCOLOR_G(color);
1068 float_color->b = D3DCOLOR_B(color);
1069 float_color->a = D3DCOLOR_A(color);
1070 break;
1072 default:
1073 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1074 return FALSE;
1077 return TRUE;
1080 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1082 const struct wined3d_format *format = surface->resource.format;
1084 switch (format->id)
1086 case WINED3DFMT_S1_UINT_D15_UNORM:
1087 *float_depth = depth / (float)0x00007fff;
1088 break;
1090 case WINED3DFMT_D16_UNORM:
1091 *float_depth = depth / (float)0x0000ffff;
1092 break;
1094 case WINED3DFMT_D24_UNORM_S8_UINT:
1095 case WINED3DFMT_X8D24_UNORM:
1096 *float_depth = depth / (float)0x00ffffff;
1097 break;
1099 case WINED3DFMT_D32_UNORM:
1100 *float_depth = depth / (float)0xffffffff;
1101 break;
1103 default:
1104 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1105 return FALSE;
1108 return TRUE;
1111 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1113 const struct wined3d_resource *resource = &surface->resource;
1114 struct wined3d_device *device = resource->device;
1115 const struct blit_shader *blitter;
1117 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1118 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1119 if (!blitter)
1121 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1122 return WINED3DERR_INVALIDCALL;
1125 return blitter->depth_fill(device, surface, rect, depth);
1128 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1129 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1131 struct wined3d_device *device = src_surface->resource.device;
1133 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1134 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1135 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1136 return WINED3DERR_INVALIDCALL;
1138 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1140 surface_modify_ds_location(dst_surface, dst_location,
1141 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1143 return WINED3D_OK;
1146 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1147 struct wined3d_surface *render_target)
1149 TRACE("surface %p, render_target %p.\n", surface, render_target);
1151 /* TODO: Check surface sizes, pools, etc. */
1153 if (render_target->resource.multisample_type)
1154 return WINED3DERR_INVALIDCALL;
1156 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1159 /* Context activation is done by the caller. */
1160 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1162 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1163 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1165 surface->pbo = 0;
1166 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1169 static void surface_unload(struct wined3d_resource *resource)
1171 struct wined3d_surface *surface = surface_from_resource(resource);
1172 struct wined3d_renderbuffer_entry *entry, *entry2;
1173 struct wined3d_device *device = resource->device;
1174 const struct wined3d_gl_info *gl_info;
1175 struct wined3d_context *context;
1177 TRACE("surface %p.\n", surface);
1179 if (resource->pool == WINED3D_POOL_DEFAULT)
1181 /* Default pool resources are supposed to be destroyed before Reset is called.
1182 * Implicit resources stay however. So this means we have an implicit render target
1183 * or depth stencil. The content may be destroyed, but we still have to tear down
1184 * opengl resources, so we cannot leave early.
1186 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1187 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1188 * or the depth stencil into an FBO the texture or render buffer will be removed
1189 * and all flags get lost */
1190 surface_prepare_system_memory(surface);
1191 memset(surface->resource.heap_memory, 0, surface->resource.size);
1192 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1193 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1195 /* We also get here when the ddraw swapchain is destroyed, for example
1196 * for a mode switch. In this case this surface won't necessarily be
1197 * an implicit surface. We have to mark it lost so that the
1198 * application can restore it after the mode switch. */
1199 surface->flags |= SFLAG_LOST;
1201 else
1203 surface_prepare_map_memory(surface);
1204 surface_load_location(surface, surface->resource.map_binding);
1205 surface_invalidate_location(surface, ~surface->resource.map_binding);
1207 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1209 context = context_acquire(device, NULL);
1210 gl_info = context->gl_info;
1212 /* Destroy PBOs, but load them into real sysmem before */
1213 if (surface->pbo)
1214 surface_remove_pbo(surface, gl_info);
1216 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1217 * all application-created targets the application has to release the surface
1218 * before calling _Reset
1220 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1222 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1223 list_remove(&entry->entry);
1224 HeapFree(GetProcessHeap(), 0, entry);
1226 list_init(&surface->renderbuffers);
1227 surface->current_renderbuffer = NULL;
1229 if (surface->rb_multisample)
1231 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1232 surface->rb_multisample = 0;
1234 if (surface->rb_resolved)
1236 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1237 surface->rb_resolved = 0;
1240 context_release(context);
1242 resource_unload(resource);
1245 static const struct wined3d_resource_ops surface_resource_ops =
1247 surface_unload,
1250 static const struct wined3d_surface_ops surface_ops =
1252 surface_private_setup,
1253 surface_unmap,
1256 /*****************************************************************************
1257 * Initializes the GDI surface, aka creates the DIB section we render to
1258 * The DIB section creation is done by calling GetDC, which will create the
1259 * section and releasing the dc to allow the app to use it. The dib section
1260 * will stay until the surface is released
1262 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1263 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1264 * avoid confusion in the shared surface code.
1266 * Returns:
1267 * WINED3D_OK on success
1268 * The return values of called methods on failure
1270 *****************************************************************************/
1271 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1273 HRESULT hr;
1275 TRACE("surface %p.\n", surface);
1277 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1279 ERR("Overlays not yet supported by GDI surfaces.\n");
1280 return WINED3DERR_INVALIDCALL;
1283 /* Sysmem textures have memory already allocated - release it,
1284 * this avoids an unnecessary memcpy. */
1285 hr = surface_create_dib_section(surface);
1286 if (FAILED(hr))
1287 return hr;
1288 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1290 /* We don't mind the nonpow2 stuff in GDI. */
1291 surface->pow2Width = surface->resource.width;
1292 surface->pow2Height = surface->resource.height;
1294 return WINED3D_OK;
1297 static void gdi_surface_unmap(struct wined3d_surface *surface)
1299 TRACE("surface %p.\n", surface);
1301 /* Tell the swapchain to update the screen. */
1302 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1303 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1305 memset(&surface->lockedRect, 0, sizeof(RECT));
1308 static const struct wined3d_surface_ops gdi_surface_ops =
1310 gdi_surface_private_setup,
1311 gdi_surface_unmap,
1314 /* This call just downloads data, the caller is responsible for binding the
1315 * correct texture. */
1316 /* Context activation is done by the caller. */
1317 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1318 DWORD dst_location)
1320 const struct wined3d_format *format = surface->resource.format;
1321 struct wined3d_bo_address data;
1323 /* Only support read back of converted P8 surfaces. */
1324 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1326 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1327 return;
1330 surface_get_memory(surface, &data, dst_location);
1332 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1334 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1335 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1337 if (data.buffer_object)
1339 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1340 checkGLcall("glBindBufferARB");
1341 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1342 checkGLcall("glGetCompressedTexImageARB");
1343 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1344 checkGLcall("glBindBufferARB");
1346 else
1348 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1349 surface->texture_level, data.addr));
1350 checkGLcall("glGetCompressedTexImageARB");
1353 else
1355 void *mem;
1356 GLenum gl_format = format->glFormat;
1357 GLenum gl_type = format->glType;
1358 int src_pitch = 0;
1359 int dst_pitch = 0;
1361 if (surface->flags & SFLAG_NONPOW2)
1363 unsigned char alignment = surface->resource.device->surface_alignment;
1364 src_pitch = format->byte_count * surface->pow2Width;
1365 dst_pitch = wined3d_surface_get_pitch(surface);
1366 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1367 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1369 else
1371 mem = data.addr;
1374 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1375 surface, surface->texture_level, gl_format, gl_type, mem);
1377 if (data.buffer_object)
1379 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1380 checkGLcall("glBindBufferARB");
1382 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1383 gl_format, gl_type, NULL);
1384 checkGLcall("glGetTexImage");
1386 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1387 checkGLcall("glBindBufferARB");
1389 else
1391 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1392 gl_format, gl_type, mem);
1393 checkGLcall("glGetTexImage");
1396 if (surface->flags & SFLAG_NONPOW2)
1398 const BYTE *src_data;
1399 BYTE *dst_data;
1400 UINT y;
1402 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1403 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1404 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1406 * We're doing this...
1408 * instead of boxing the texture :
1409 * |<-texture width ->| -->pow2width| /\
1410 * |111111111111111111| | |
1411 * |222 Texture 222222| boxed empty | texture height
1412 * |3333 Data 33333333| | |
1413 * |444444444444444444| | \/
1414 * ----------------------------------- |
1415 * | boxed empty | boxed empty | pow2height
1416 * | | | \/
1417 * -----------------------------------
1420 * we're repacking the data to the expected texture width
1422 * |<-texture width ->| -->pow2width| /\
1423 * |111111111111111111222222222222222| |
1424 * |222333333333333333333444444444444| texture height
1425 * |444444 | |
1426 * | | \/
1427 * | | |
1428 * | empty | pow2height
1429 * | | \/
1430 * -----------------------------------
1432 * == is the same as
1434 * |<-texture width ->| /\
1435 * |111111111111111111|
1436 * |222222222222222222|texture height
1437 * |333333333333333333|
1438 * |444444444444444444| \/
1439 * --------------------
1441 * This also means that any references to surface memory should work with the data as if it were a
1442 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1444 * internally the texture is still stored in a boxed format so any references to textureName will
1445 * get a boxed texture with width pow2width and not a texture of width resource.width.
1447 * Performance should not be an issue, because applications normally do not lock the surfaces when
1448 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1449 * and doesn't have to be re-read. */
1450 src_data = mem;
1451 dst_data = data.addr;
1452 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1453 for (y = 0; y < surface->resource.height; ++y)
1455 memcpy(dst_data, src_data, dst_pitch);
1456 src_data += src_pitch;
1457 dst_data += dst_pitch;
1460 HeapFree(GetProcessHeap(), 0, mem);
1465 /* This call just uploads data, the caller is responsible for binding the
1466 * correct texture. */
1467 /* Context activation is done by the caller. */
1468 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1469 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1470 BOOL srgb, const struct wined3d_bo_address *data)
1472 UINT update_w = src_rect->right - src_rect->left;
1473 UINT update_h = src_rect->bottom - src_rect->top;
1475 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1476 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1477 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1479 if (surface->resource.map_count)
1481 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1482 surface->flags |= SFLAG_PIN_SYSMEM;
1485 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1487 update_h *= format->height_scale.numerator;
1488 update_h /= format->height_scale.denominator;
1491 if (data->buffer_object)
1493 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1494 checkGLcall("glBindBufferARB");
1497 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1499 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1500 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1501 const BYTE *addr = data->addr;
1502 GLenum internal;
1504 addr += (src_rect->top / format->block_height) * src_pitch;
1505 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1507 if (srgb)
1508 internal = format->glGammaInternal;
1509 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1510 && wined3d_resource_is_offscreen(&surface->resource))
1511 internal = format->rtInternal;
1512 else
1513 internal = format->glInternal;
1515 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1516 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1517 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1519 if (row_length == src_pitch)
1521 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1522 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1524 else
1526 UINT row, y;
1528 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1529 * can't use the unpack row length like below. */
1530 for (row = 0, y = dst_point->y; row < row_count; ++row)
1532 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1533 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1534 y += format->block_height;
1535 addr += src_pitch;
1538 checkGLcall("glCompressedTexSubImage2DARB");
1540 else
1542 const BYTE *addr = data->addr;
1544 addr += src_rect->top * src_pitch;
1545 addr += src_rect->left * format->byte_count;
1547 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1548 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1549 update_w, update_h, format->glFormat, format->glType, addr);
1551 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1552 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1553 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1554 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1555 checkGLcall("glTexSubImage2D");
1558 if (data->buffer_object)
1560 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1561 checkGLcall("glBindBufferARB");
1564 if (wined3d_settings.strict_draw_ordering)
1565 gl_info->gl_ops.gl.p_glFlush();
1567 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1569 struct wined3d_device *device = surface->resource.device;
1570 unsigned int i;
1572 for (i = 0; i < device->context_count; ++i)
1574 context_surface_update(device->contexts[i], surface);
1579 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1580 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1582 BOOL colorkey_active = need_alpha_ck && (surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1583 const struct wined3d_device *device = surface->resource.device;
1584 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1585 BOOL blit_supported = FALSE;
1587 /* Copy the default values from the surface. Below we might perform fixups */
1588 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1589 *format = *surface->resource.format;
1590 *conversion_type = WINED3D_CT_NONE;
1592 /* Ok, now look if we have to do any conversion */
1593 switch (surface->resource.format->id)
1595 case WINED3DFMT_P8_UINT:
1596 /* Below the call to blit_supported is disabled for Wine 1.2
1597 * because the function isn't operating correctly yet. At the
1598 * moment 8-bit blits are handled in software and if certain GL
1599 * extensions are around, surface conversion is performed at
1600 * upload time. The blit_supported call recognizes it as a
1601 * destination fixup. This type of upload 'fixup' and 8-bit to
1602 * 8-bit blits need to be handled by the blit_shader.
1603 * TODO: get rid of this #if 0. */
1604 #if 0
1605 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1606 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1607 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1608 #endif
1609 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1611 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1612 * texturing. Further also use conversion in case of color keying.
1613 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1614 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1615 * conflicts with this.
1617 if (!((blit_supported && surface->swapchain && surface == surface->swapchain->front_buffer))
1618 || colorkey_active || !use_texturing)
1620 format->glFormat = GL_RGBA;
1621 format->glInternal = GL_RGBA;
1622 format->glType = GL_UNSIGNED_BYTE;
1623 format->conv_byte_count = 4;
1624 *conversion_type = WINED3D_CT_PALETTED;
1626 break;
1628 case WINED3DFMT_B2G3R3_UNORM:
1629 /* **********************
1630 GL_UNSIGNED_BYTE_3_3_2
1631 ********************** */
1632 if (colorkey_active) {
1633 /* This texture format will never be used.. So do not care about color keying
1634 up until the point in time it will be needed :-) */
1635 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1637 break;
1639 case WINED3DFMT_B5G6R5_UNORM:
1640 if (colorkey_active)
1642 *conversion_type = WINED3D_CT_CK_565;
1643 format->glFormat = GL_RGBA;
1644 format->glInternal = GL_RGB5_A1;
1645 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1646 format->conv_byte_count = 2;
1648 break;
1650 case WINED3DFMT_B5G5R5X1_UNORM:
1651 if (colorkey_active)
1653 *conversion_type = WINED3D_CT_CK_5551;
1654 format->glFormat = GL_BGRA;
1655 format->glInternal = GL_RGB5_A1;
1656 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1657 format->conv_byte_count = 2;
1659 break;
1661 case WINED3DFMT_B8G8R8_UNORM:
1662 if (colorkey_active)
1664 *conversion_type = WINED3D_CT_CK_RGB24;
1665 format->glFormat = GL_RGBA;
1666 format->glInternal = GL_RGBA8;
1667 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1668 format->conv_byte_count = 4;
1670 break;
1672 case WINED3DFMT_B8G8R8X8_UNORM:
1673 if (colorkey_active)
1675 *conversion_type = WINED3D_CT_RGB32_888;
1676 format->glFormat = GL_RGBA;
1677 format->glInternal = GL_RGBA8;
1678 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1679 format->conv_byte_count = 4;
1681 break;
1683 case WINED3DFMT_B8G8R8A8_UNORM:
1684 if (colorkey_active)
1686 *conversion_type = WINED3D_CT_CK_ARGB32;
1687 format->conv_byte_count = 4;
1689 break;
1691 default:
1692 break;
1695 if (*conversion_type != WINED3D_CT_NONE)
1697 format->rtInternal = format->glInternal;
1698 format->glGammaInternal = format->glInternal;
1701 return WINED3D_OK;
1704 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1706 UINT width_mask, height_mask;
1708 if (!rect->left && !rect->top
1709 && rect->right == surface->resource.width
1710 && rect->bottom == surface->resource.height)
1711 return TRUE;
1713 /* This assumes power of two block sizes, but NPOT block sizes would be
1714 * silly anyway. */
1715 width_mask = surface->resource.format->block_width - 1;
1716 height_mask = surface->resource.format->block_height - 1;
1718 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1719 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1720 return TRUE;
1722 return FALSE;
1725 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1726 struct wined3d_surface *src_surface, const RECT *src_rect)
1728 const struct wined3d_format *src_format;
1729 const struct wined3d_format *dst_format;
1730 const struct wined3d_gl_info *gl_info;
1731 enum wined3d_conversion_type convert;
1732 struct wined3d_context *context;
1733 struct wined3d_bo_address data;
1734 struct wined3d_format format;
1735 UINT update_w, update_h;
1736 UINT dst_w, dst_h;
1737 RECT r, dst_rect;
1738 UINT src_pitch;
1739 POINT p;
1741 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1742 dst_surface, wine_dbgstr_point(dst_point),
1743 src_surface, wine_dbgstr_rect(src_rect));
1745 src_format = src_surface->resource.format;
1746 dst_format = dst_surface->resource.format;
1748 if (src_format->id != dst_format->id)
1750 WARN("Source and destination surfaces should have the same format.\n");
1751 return WINED3DERR_INVALIDCALL;
1754 if (!dst_point)
1756 p.x = 0;
1757 p.y = 0;
1758 dst_point = &p;
1760 else if (dst_point->x < 0 || dst_point->y < 0)
1762 WARN("Invalid destination point.\n");
1763 return WINED3DERR_INVALIDCALL;
1766 if (!src_rect)
1768 r.left = 0;
1769 r.top = 0;
1770 r.right = src_surface->resource.width;
1771 r.bottom = src_surface->resource.height;
1772 src_rect = &r;
1774 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1775 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1777 WARN("Invalid source rectangle.\n");
1778 return WINED3DERR_INVALIDCALL;
1781 dst_w = dst_surface->resource.width;
1782 dst_h = dst_surface->resource.height;
1784 update_w = src_rect->right - src_rect->left;
1785 update_h = src_rect->bottom - src_rect->top;
1787 if (update_w > dst_w || dst_point->x > dst_w - update_w
1788 || update_h > dst_h || dst_point->y > dst_h - update_h)
1790 WARN("Destination out of bounds.\n");
1791 return WINED3DERR_INVALIDCALL;
1794 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1796 WARN("Source rectangle not block-aligned.\n");
1797 return WINED3DERR_INVALIDCALL;
1800 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1801 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1803 WARN("Destination rectangle not block-aligned.\n");
1804 return WINED3DERR_INVALIDCALL;
1807 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1808 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1809 if (convert != WINED3D_CT_NONE || format.convert)
1810 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1812 context = context_acquire(dst_surface->resource.device, NULL);
1813 gl_info = context->gl_info;
1815 /* Only load the surface for partial updates. For newly allocated texture
1816 * the texture wouldn't be the current location, and we'd upload zeroes
1817 * just to overwrite them again. */
1818 if (update_w == dst_w && update_h == dst_h)
1819 surface_prepare_texture(dst_surface, context, FALSE);
1820 else
1821 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1822 wined3d_texture_bind(dst_surface->container, context, FALSE);
1824 surface_get_memory(src_surface, &data, src_surface->locations);
1825 src_pitch = wined3d_surface_get_pitch(src_surface);
1827 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1829 context_invalidate_active_texture(context);
1831 context_release(context);
1833 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1834 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1836 return WINED3D_OK;
1839 /* This call just allocates the texture, the caller is responsible for binding
1840 * the correct texture. */
1841 /* Context activation is done by the caller. */
1842 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1843 const struct wined3d_format *format, BOOL srgb)
1845 BOOL disable_client_storage = FALSE;
1846 GLsizei width = surface->pow2Width;
1847 GLsizei height = surface->pow2Height;
1848 const BYTE *mem = NULL;
1849 GLenum internal;
1851 if (srgb)
1852 internal = format->glGammaInternal;
1853 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1854 && wined3d_resource_is_offscreen(&surface->resource))
1855 internal = format->rtInternal;
1856 else
1857 internal = format->glInternal;
1859 if (!internal)
1860 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1862 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1864 height *= format->height_scale.numerator;
1865 height /= format->height_scale.denominator;
1868 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",
1869 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1870 internal, width, height, format->glFormat, format->glType);
1872 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1874 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1875 || !surface->resource.heap_memory)
1877 /* In some cases we want to disable client storage.
1878 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1879 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1880 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1881 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1883 surface->flags &= ~SFLAG_CLIENT;
1885 else
1887 surface->flags |= SFLAG_CLIENT;
1888 mem = surface->resource.heap_memory;
1890 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1891 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1892 disable_client_storage = TRUE;
1896 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1898 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
1899 internal, width, height, 0, surface->resource.size, mem));
1900 checkGLcall("glCompressedTexImage2DARB");
1902 else
1904 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
1905 internal, width, height, 0, format->glFormat, format->glType, mem);
1906 checkGLcall("glTexImage2D");
1909 if (disable_client_storage)
1911 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1912 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1916 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1917 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1918 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1919 /* Context activation is done by the caller. */
1920 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1922 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1923 struct wined3d_renderbuffer_entry *entry;
1924 GLuint renderbuffer = 0;
1925 unsigned int src_width, src_height;
1926 unsigned int width, height;
1928 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1930 width = rt->pow2Width;
1931 height = rt->pow2Height;
1933 else
1935 width = surface->pow2Width;
1936 height = surface->pow2Height;
1939 src_width = surface->pow2Width;
1940 src_height = surface->pow2Height;
1942 /* A depth stencil smaller than the render target is not valid */
1943 if (width > src_width || height > src_height) return;
1945 /* Remove any renderbuffer set if the sizes match */
1946 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1947 || (width == src_width && height == src_height))
1949 surface->current_renderbuffer = NULL;
1950 return;
1953 /* Look if we've already got a renderbuffer of the correct dimensions */
1954 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1956 if (entry->width == width && entry->height == height)
1958 renderbuffer = entry->id;
1959 surface->current_renderbuffer = entry;
1960 break;
1964 if (!renderbuffer)
1966 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1967 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1968 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1969 surface->resource.format->glInternal, width, height);
1971 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1972 entry->width = width;
1973 entry->height = height;
1974 entry->id = renderbuffer;
1975 list_add_head(&surface->renderbuffers, &entry->entry);
1977 surface->current_renderbuffer = entry;
1980 checkGLcall("set_compatible_renderbuffer");
1983 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1985 const struct wined3d_swapchain *swapchain = surface->swapchain;
1987 TRACE("surface %p.\n", surface);
1989 if (!swapchain)
1991 ERR("Surface %p is not on a swapchain.\n", surface);
1992 return GL_NONE;
1995 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
1997 if (swapchain->render_to_fbo)
1999 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2000 return GL_COLOR_ATTACHMENT0;
2002 TRACE("Returning GL_BACK\n");
2003 return GL_BACK;
2005 else if (surface == swapchain->front_buffer)
2007 TRACE("Returning GL_FRONT\n");
2008 return GL_FRONT;
2011 FIXME("Higher back buffer, returning GL_BACK\n");
2012 return GL_BACK;
2015 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2017 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2018 BOOL ck_changed;
2020 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2022 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2023 ERR("Not supported on scratch surfaces.\n");
2025 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2027 /* Reload if either the texture and sysmem have different ideas about the
2028 * color key, or the actual key values changed. */
2029 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2030 && (surface->gl_color_key.color_space_low_value
2031 != surface->container->src_blt_color_key.color_space_low_value
2032 || surface->gl_color_key.color_space_high_value
2033 != surface->container->src_blt_color_key.color_space_high_value)))
2035 TRACE("Reloading because of color keying\n");
2036 /* To perform the color key conversion we need a sysmem copy of
2037 * the surface. Make sure we have it. */
2039 surface_prepare_map_memory(surface);
2040 surface_load_location(surface, surface->resource.map_binding);
2041 surface_invalidate_location(surface, ~surface->resource.map_binding);
2042 /* Switching color keying on / off may change the internal format. */
2043 if (ck_changed)
2044 surface_force_reload(surface);
2046 else if (!(surface->locations & location))
2048 TRACE("Reloading because surface is dirty.\n");
2050 else
2052 TRACE("surface is already in texture\n");
2053 return;
2056 surface_load_location(surface, location);
2057 surface_evict_sysmem(surface);
2060 /* See also float_16_to_32() in wined3d_private.h */
2061 static inline unsigned short float_32_to_16(const float *in)
2063 int exp = 0;
2064 float tmp = fabsf(*in);
2065 unsigned int mantissa;
2066 unsigned short ret;
2068 /* Deal with special numbers */
2069 if (*in == 0.0f)
2070 return 0x0000;
2071 if (isnan(*in))
2072 return 0x7c01;
2073 if (isinf(*in))
2074 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2076 if (tmp < powf(2, 10))
2080 tmp = tmp * 2.0f;
2081 exp--;
2082 } while (tmp < powf(2, 10));
2084 else if (tmp >= powf(2, 11))
2088 tmp /= 2.0f;
2089 exp++;
2090 } while (tmp >= powf(2, 11));
2093 mantissa = (unsigned int)tmp;
2094 if (tmp - mantissa >= 0.5f)
2095 ++mantissa; /* Round to nearest, away from zero. */
2097 exp += 10; /* Normalize the mantissa. */
2098 exp += 15; /* Exponent is encoded with excess 15. */
2100 if (exp > 30) /* too big */
2102 ret = 0x7c00; /* INF */
2104 else if (exp <= 0)
2106 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2107 while (exp <= 0)
2109 mantissa = mantissa >> 1;
2110 ++exp;
2112 ret = mantissa & 0x3ff;
2114 else
2116 ret = (exp << 10) | (mantissa & 0x3ff);
2119 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2120 return ret;
2123 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2125 TRACE("surface %p, swapchain %p, container %p.\n",
2126 surface, surface->swapchain, surface->container);
2128 if (surface->swapchain)
2129 return wined3d_swapchain_incref(surface->swapchain);
2131 return wined3d_texture_incref(surface->container);
2134 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2136 TRACE("surface %p, swapchain %p, container %p.\n",
2137 surface, surface->swapchain, surface->container);
2139 if (surface->swapchain)
2140 return wined3d_swapchain_decref(surface->swapchain);
2142 return wined3d_texture_decref(surface->container);
2145 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2147 TRACE("surface %p.\n", surface);
2149 if (!surface->resource.device->d3d_initialized)
2151 ERR("D3D not initialized.\n");
2152 return;
2155 wined3d_texture_preload(surface->container);
2158 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2160 TRACE("surface %p.\n", surface);
2162 return surface->resource.parent;
2165 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2167 TRACE("surface %p.\n", surface);
2169 return &surface->resource;
2172 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2174 TRACE("surface %p, flags %#x.\n", surface, flags);
2176 switch (flags)
2178 case WINEDDGBS_CANBLT:
2179 case WINEDDGBS_ISBLTDONE:
2180 return WINED3D_OK;
2182 default:
2183 return WINED3DERR_INVALIDCALL;
2187 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2189 TRACE("surface %p, flags %#x.\n", surface, flags);
2191 /* XXX: DDERR_INVALIDSURFACETYPE */
2193 switch (flags)
2195 case WINEDDGFS_CANFLIP:
2196 case WINEDDGFS_ISFLIPDONE:
2197 return WINED3D_OK;
2199 default:
2200 return WINED3DERR_INVALIDCALL;
2204 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2206 TRACE("surface %p.\n", surface);
2208 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2209 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2212 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2214 TRACE("surface %p.\n", surface);
2216 surface->flags &= ~SFLAG_LOST;
2217 return WINED3D_OK;
2220 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2222 unsigned int alignment;
2223 DWORD pitch;
2225 TRACE("surface %p.\n", surface);
2227 if (surface->pitch)
2228 return surface->pitch;
2230 alignment = surface->resource.device->surface_alignment;
2231 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2232 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2234 TRACE("Returning %u.\n", pitch);
2236 return pitch;
2239 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2241 LONG w, h;
2243 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2245 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2247 WARN("Not an overlay surface.\n");
2248 return WINEDDERR_NOTAOVERLAYSURFACE;
2251 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2252 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2253 surface->overlay_destrect.left = x;
2254 surface->overlay_destrect.top = y;
2255 surface->overlay_destrect.right = x + w;
2256 surface->overlay_destrect.bottom = y + h;
2258 return WINED3D_OK;
2261 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2263 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2265 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2267 TRACE("Not an overlay surface.\n");
2268 return WINEDDERR_NOTAOVERLAYSURFACE;
2271 if (!surface->overlay_dest)
2273 TRACE("Overlay not visible.\n");
2274 *x = 0;
2275 *y = 0;
2276 return WINEDDERR_OVERLAYNOTVISIBLE;
2279 *x = surface->overlay_destrect.left;
2280 *y = surface->overlay_destrect.top;
2282 TRACE("Returning position %d, %d.\n", *x, *y);
2284 return WINED3D_OK;
2287 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2288 DWORD flags, struct wined3d_surface *ref)
2290 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2292 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2294 TRACE("Not an overlay surface.\n");
2295 return WINEDDERR_NOTAOVERLAYSURFACE;
2298 return WINED3D_OK;
2301 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2302 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2304 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2305 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2307 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2309 WARN("Not an overlay surface.\n");
2310 return WINEDDERR_NOTAOVERLAYSURFACE;
2312 else if (!dst_surface)
2314 WARN("Dest surface is NULL.\n");
2315 return WINED3DERR_INVALIDCALL;
2318 if (src_rect)
2320 surface->overlay_srcrect = *src_rect;
2322 else
2324 surface->overlay_srcrect.left = 0;
2325 surface->overlay_srcrect.top = 0;
2326 surface->overlay_srcrect.right = surface->resource.width;
2327 surface->overlay_srcrect.bottom = surface->resource.height;
2330 if (dst_rect)
2332 surface->overlay_destrect = *dst_rect;
2334 else
2336 surface->overlay_destrect.left = 0;
2337 surface->overlay_destrect.top = 0;
2338 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2339 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2342 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2344 surface->overlay_dest = NULL;
2345 list_remove(&surface->overlay_entry);
2348 if (flags & WINEDDOVER_SHOW)
2350 if (surface->overlay_dest != dst_surface)
2352 surface->overlay_dest = dst_surface;
2353 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2356 else if (flags & WINEDDOVER_HIDE)
2358 /* tests show that the rectangles are erased on hide */
2359 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2360 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2361 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2362 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2363 surface->overlay_dest = NULL;
2366 return WINED3D_OK;
2369 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2370 UINT width, UINT height, enum wined3d_format_id format_id,
2371 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2372 void *mem, UINT pitch)
2374 struct wined3d_device *device = surface->resource.device;
2375 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2376 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2377 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2378 BOOL create_dib = FALSE;
2379 HRESULT hr;
2380 DWORD valid_location = 0;
2382 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, "
2383 "mem %p, pitch %u.\n",
2384 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type, mem, pitch);
2386 if (!resource_size)
2387 return WINED3DERR_INVALIDCALL;
2389 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2391 WARN("Surface is mapped or the DC is in use.\n");
2392 return WINED3DERR_INVALIDCALL;
2395 if (device->d3d_initialized)
2396 surface->resource.resource_ops->resource_unload(&surface->resource);
2398 if (surface->flags & SFLAG_DIBSECTION)
2400 DeleteDC(surface->hDC);
2401 DeleteObject(surface->dib.DIBsection);
2402 surface->dib.bitmap_data = NULL;
2403 surface->flags &= ~SFLAG_DIBSECTION;
2404 create_dib = TRUE;
2407 surface->locations = 0;
2408 wined3d_resource_free_sysmem(&surface->resource);
2410 surface->resource.width = width;
2411 surface->resource.height = height;
2412 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2413 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2415 surface->pow2Width = width;
2416 surface->pow2Height = height;
2418 else
2420 surface->pow2Width = surface->pow2Height = 1;
2421 while (surface->pow2Width < width)
2422 surface->pow2Width <<= 1;
2423 while (surface->pow2Height < height)
2424 surface->pow2Height <<= 1;
2427 if (surface->pow2Width != width || surface->pow2Height != height)
2428 surface->flags |= SFLAG_NONPOW2;
2429 else
2430 surface->flags &= ~SFLAG_NONPOW2;
2432 surface->user_memory = mem;
2433 if (surface->user_memory)
2435 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2436 valid_location = WINED3D_LOCATION_USER_MEMORY;
2438 surface->pitch = pitch;
2439 surface->resource.format = format;
2440 surface->resource.multisample_type = multisample_type;
2441 surface->resource.multisample_quality = multisample_quality;
2442 if (surface->pitch)
2443 surface->resource.size = height * surface->pitch;
2444 else
2445 surface->resource.size = resource_size;
2447 /* The format might be changed to a format that needs conversion.
2448 * If the surface didn't use PBOs previously but could now, don't
2449 * change it - whatever made us not use PBOs might come back, e.g.
2450 * color keys. */
2451 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2452 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2454 if (create_dib)
2456 if (FAILED(hr = surface_create_dib_section(surface)))
2458 ERR("Failed to create dib section, hr %#x.\n", hr);
2459 return hr;
2461 if (!valid_location)
2462 valid_location = WINED3D_LOCATION_DIB;
2465 if (!valid_location)
2467 surface_prepare_system_memory(surface);
2468 valid_location = WINED3D_LOCATION_SYSMEM;
2471 surface_validate_location(surface, valid_location);
2473 return WINED3D_OK;
2476 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2477 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2479 unsigned short *dst_s;
2480 const float *src_f;
2481 unsigned int x, y;
2483 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2485 for (y = 0; y < h; ++y)
2487 src_f = (const float *)(src + y * pitch_in);
2488 dst_s = (unsigned short *) (dst + y * pitch_out);
2489 for (x = 0; x < w; ++x)
2491 dst_s[x] = float_32_to_16(src_f + x);
2496 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2497 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2499 static const unsigned char convert_5to8[] =
2501 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2502 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2503 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2504 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2506 static const unsigned char convert_6to8[] =
2508 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2509 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2510 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2511 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2512 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2513 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2514 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2515 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2517 unsigned int x, y;
2519 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2521 for (y = 0; y < h; ++y)
2523 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2524 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2525 for (x = 0; x < w; ++x)
2527 WORD pixel = src_line[x];
2528 dst_line[x] = 0xff000000
2529 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2530 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2531 | convert_5to8[(pixel & 0x001f)];
2536 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2537 * in both cases we're just setting the X / Alpha channel to 0xff. */
2538 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2539 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2541 unsigned int x, y;
2543 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2545 for (y = 0; y < h; ++y)
2547 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2548 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2550 for (x = 0; x < w; ++x)
2552 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2557 static inline BYTE cliptobyte(int x)
2559 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2562 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2563 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2565 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2566 unsigned int x, y;
2568 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2570 for (y = 0; y < h; ++y)
2572 const BYTE *src_line = src + y * pitch_in;
2573 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2574 for (x = 0; x < w; ++x)
2576 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2577 * C = Y - 16; D = U - 128; E = V - 128;
2578 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2579 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2580 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2581 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2582 * U and V are shared between the pixels. */
2583 if (!(x & 1)) /* For every even pixel, read new U and V. */
2585 d = (int) src_line[1] - 128;
2586 e = (int) src_line[3] - 128;
2587 r2 = 409 * e + 128;
2588 g2 = - 100 * d - 208 * e + 128;
2589 b2 = 516 * d + 128;
2591 c2 = 298 * ((int) src_line[0] - 16);
2592 dst_line[x] = 0xff000000
2593 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2594 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2595 | cliptobyte((c2 + b2) >> 8); /* blue */
2596 /* Scale RGB values to 0..255 range,
2597 * then clip them if still not in range (may be negative),
2598 * then shift them within DWORD if necessary. */
2599 src_line += 2;
2604 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2605 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2607 unsigned int x, y;
2608 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2610 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2612 for (y = 0; y < h; ++y)
2614 const BYTE *src_line = src + y * pitch_in;
2615 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2616 for (x = 0; x < w; ++x)
2618 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2619 * C = Y - 16; D = U - 128; E = V - 128;
2620 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2621 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2622 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2623 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2624 * U and V are shared between the pixels. */
2625 if (!(x & 1)) /* For every even pixel, read new U and V. */
2627 d = (int) src_line[1] - 128;
2628 e = (int) src_line[3] - 128;
2629 r2 = 409 * e + 128;
2630 g2 = - 100 * d - 208 * e + 128;
2631 b2 = 516 * d + 128;
2633 c2 = 298 * ((int) src_line[0] - 16);
2634 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2635 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2636 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2637 /* Scale RGB values to 0..255 range,
2638 * then clip them if still not in range (may be negative),
2639 * then shift them within DWORD if necessary. */
2640 src_line += 2;
2645 struct d3dfmt_converter_desc
2647 enum wined3d_format_id from, to;
2648 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2651 static const struct d3dfmt_converter_desc converters[] =
2653 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2654 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2655 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2656 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2657 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2658 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2661 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2662 enum wined3d_format_id to)
2664 unsigned int i;
2666 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2668 if (converters[i].from == from && converters[i].to == to)
2669 return &converters[i];
2672 return NULL;
2675 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2677 struct wined3d_map_desc src_map, dst_map;
2678 const struct d3dfmt_converter_desc *conv;
2679 struct wined3d_texture *ret = NULL;
2680 struct wined3d_resource_desc desc;
2681 struct wined3d_surface *dst;
2683 conv = find_converter(source->resource.format->id, to_fmt);
2684 if (!conv)
2686 FIXME("Cannot find a conversion function from format %s to %s.\n",
2687 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2688 return NULL;
2691 /* FIXME: Multisampled conversion? */
2692 wined3d_resource_get_desc(&source->resource, &desc);
2693 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2694 desc.format = to_fmt;
2695 desc.usage = 0;
2696 desc.pool = WINED3D_POOL_SCRATCH;
2697 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2698 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2700 ERR("Failed to create a destination surface for conversion.\n");
2701 return NULL;
2703 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2705 memset(&src_map, 0, sizeof(src_map));
2706 memset(&dst_map, 0, sizeof(dst_map));
2708 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2710 ERR("Failed to lock the source surface.\n");
2711 wined3d_texture_decref(ret);
2712 return NULL;
2714 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2716 ERR("Failed to lock the destination surface.\n");
2717 wined3d_surface_unmap(source);
2718 wined3d_texture_decref(ret);
2719 return NULL;
2722 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2723 source->resource.width, source->resource.height);
2725 wined3d_surface_unmap(dst);
2726 wined3d_surface_unmap(source);
2728 return ret;
2731 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2732 unsigned int bpp, UINT pitch, DWORD color)
2734 BYTE *first;
2735 unsigned int x, y;
2737 /* Do first row */
2739 #define COLORFILL_ROW(type) \
2740 do { \
2741 type *d = (type *)buf; \
2742 for (x = 0; x < width; ++x) \
2743 d[x] = (type)color; \
2744 } while(0)
2746 switch (bpp)
2748 case 1:
2749 COLORFILL_ROW(BYTE);
2750 break;
2752 case 2:
2753 COLORFILL_ROW(WORD);
2754 break;
2756 case 3:
2758 BYTE *d = buf;
2759 for (x = 0; x < width; ++x, d += 3)
2761 d[0] = (color ) & 0xff;
2762 d[1] = (color >> 8) & 0xff;
2763 d[2] = (color >> 16) & 0xff;
2765 break;
2767 case 4:
2768 COLORFILL_ROW(DWORD);
2769 break;
2771 default:
2772 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2773 return WINED3DERR_NOTAVAILABLE;
2776 #undef COLORFILL_ROW
2778 /* Now copy first row. */
2779 first = buf;
2780 for (y = 1; y < height; ++y)
2782 buf += pitch;
2783 memcpy(buf, first, width * bpp);
2786 return WINED3D_OK;
2789 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2791 return surface_from_resource(resource);
2794 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2796 TRACE("surface %p.\n", surface);
2798 if (!surface->resource.map_count)
2800 WARN("Trying to unmap unmapped surface.\n");
2801 return WINEDDERR_NOTLOCKED;
2803 --surface->resource.map_count;
2805 surface->surface_ops->surface_unmap(surface);
2807 return WINED3D_OK;
2810 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2811 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2813 const struct wined3d_format *format = surface->resource.format;
2814 struct wined3d_device *device = surface->resource.device;
2815 struct wined3d_context *context;
2816 const struct wined3d_gl_info *gl_info;
2817 BYTE *base_memory;
2819 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2820 surface, map_desc, wine_dbgstr_rect(rect), flags);
2822 if (surface->resource.map_count)
2824 WARN("Surface is already mapped.\n");
2825 return WINED3DERR_INVALIDCALL;
2828 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2829 && !surface_check_block_align(surface, rect))
2831 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2832 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2834 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2835 return WINED3DERR_INVALIDCALL;
2838 ++surface->resource.map_count;
2840 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2841 WARN("Trying to lock unlockable surface.\n");
2843 /* Performance optimization: Count how often a surface is mapped, if it is
2844 * mapped regularly do not throw away the system memory copy. This avoids
2845 * the need to download the surface from OpenGL all the time. The surface
2846 * is still downloaded if the OpenGL texture is changed. */
2847 if (!(surface->flags & SFLAG_DYNLOCK) && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2849 if (++surface->lockCount > MAXLOCKCOUNT)
2851 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2852 surface->flags |= SFLAG_DYNLOCK;
2856 surface_prepare_map_memory(surface);
2857 if (flags & WINED3D_MAP_DISCARD)
2859 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2860 wined3d_debug_location(surface->resource.map_binding));
2861 surface_validate_location(surface, surface->resource.map_binding);
2863 else
2865 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2866 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2868 surface_load_location(surface, surface->resource.map_binding);
2871 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2872 surface_invalidate_location(surface, ~surface->resource.map_binding);
2874 switch (surface->resource.map_binding)
2876 case WINED3D_LOCATION_SYSMEM:
2877 base_memory = surface->resource.heap_memory;
2878 break;
2880 case WINED3D_LOCATION_USER_MEMORY:
2881 base_memory = surface->user_memory;
2882 break;
2884 case WINED3D_LOCATION_DIB:
2885 base_memory = surface->dib.bitmap_data;
2886 break;
2888 case WINED3D_LOCATION_BUFFER:
2889 context = context_acquire(device, NULL);
2890 gl_info = context->gl_info;
2892 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2893 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2895 checkGLcall("map PBO");
2897 context_release(context);
2898 break;
2900 default:
2901 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2902 base_memory = NULL;
2905 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2906 map_desc->row_pitch = surface->resource.width * format->byte_count;
2907 else
2908 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2909 map_desc->slice_pitch = 0;
2911 if (!rect)
2913 map_desc->data = base_memory;
2914 surface->lockedRect.left = 0;
2915 surface->lockedRect.top = 0;
2916 surface->lockedRect.right = surface->resource.width;
2917 surface->lockedRect.bottom = surface->resource.height;
2919 else
2921 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2923 /* Compressed textures are block based, so calculate the offset of
2924 * the block that contains the top-left pixel of the locked rectangle. */
2925 map_desc->data = base_memory
2926 + ((rect->top / format->block_height) * map_desc->row_pitch)
2927 + ((rect->left / format->block_width) * format->block_byte_count);
2929 else
2931 map_desc->data = base_memory
2932 + (map_desc->row_pitch * rect->top)
2933 + (rect->left * format->byte_count);
2935 surface->lockedRect.left = rect->left;
2936 surface->lockedRect.top = rect->top;
2937 surface->lockedRect.right = rect->right;
2938 surface->lockedRect.bottom = rect->bottom;
2941 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2942 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2944 return WINED3D_OK;
2947 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2949 HRESULT hr;
2951 TRACE("surface %p, dc %p.\n", surface, dc);
2953 /* Give more detailed info for ddraw. */
2954 if (surface->flags & SFLAG_DCINUSE)
2955 return WINEDDERR_DCALREADYCREATED;
2957 /* Can't GetDC if the surface is locked. */
2958 if (surface->resource.map_count)
2959 return WINED3DERR_INVALIDCALL;
2961 /* Create a DIB section if there isn't a dc yet. */
2962 if (!surface->hDC)
2964 if (surface->flags & SFLAG_CLIENT)
2966 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2967 surface_release_client_storage(surface);
2969 hr = surface_create_dib_section(surface);
2970 if (FAILED(hr))
2971 return WINED3DERR_INVALIDCALL;
2972 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2973 || surface->flags & SFLAG_PIN_SYSMEM
2974 || surface->pbo))
2975 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2978 surface_load_location(surface, WINED3D_LOCATION_DIB);
2979 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2981 surface->flags |= SFLAG_DCINUSE;
2982 surface->resource.map_count++;
2984 *dc = surface->hDC;
2985 TRACE("Returning dc %p.\n", *dc);
2987 return WINED3D_OK;
2990 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2992 TRACE("surface %p, dc %p.\n", surface, dc);
2994 if (!(surface->flags & SFLAG_DCINUSE))
2995 return WINEDDERR_NODC;
2997 if (surface->hDC != dc)
2999 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3000 dc, surface->hDC);
3001 return WINEDDERR_NODC;
3004 surface->resource.map_count--;
3005 surface->flags &= ~SFLAG_DCINUSE;
3007 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
3008 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
3010 /* The game Salammbo modifies the surface contents without mapping the surface between
3011 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
3012 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
3013 * Do not only copy the DIB to the map location, but also make sure the map location is
3014 * copied back to the DIB in the next getdc call.
3016 * The same consideration applies to user memory surfaces. */
3017 surface_load_location(surface, surface->resource.map_binding);
3018 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
3021 return WINED3D_OK;
3024 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3026 struct wined3d_device *device = surface->resource.device;
3027 const struct wined3d_gl_info *gl_info;
3028 struct wined3d_context *context;
3029 BYTE *mem;
3030 BYTE *row, *top, *bottom;
3031 int i;
3032 BOOL srcIsUpsideDown;
3033 struct wined3d_bo_address data;
3035 surface_get_memory(surface, &data, dst_location);
3037 context = context_acquire(device, surface);
3038 context_apply_blit_state(context, device);
3039 gl_info = context->gl_info;
3041 /* Select the correct read buffer, and give some debug output.
3042 * There is no need to keep track of the current read buffer or reset it, every part of the code
3043 * that reads sets the read buffer as desired.
3045 if (wined3d_resource_is_offscreen(&surface->resource))
3047 /* Mapping the primary render target which is not on a swapchain.
3048 * Read from the back buffer. */
3049 TRACE("Mapping offscreen render target.\n");
3050 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3051 srcIsUpsideDown = TRUE;
3053 else
3055 /* Onscreen surfaces are always part of a swapchain */
3056 GLenum buffer = surface_get_gl_buffer(surface);
3057 TRACE("Mapping %#x buffer.\n", buffer);
3058 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3059 checkGLcall("glReadBuffer");
3060 srcIsUpsideDown = FALSE;
3063 if (data.buffer_object)
3065 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3066 checkGLcall("glBindBufferARB");
3069 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3070 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3071 checkGLcall("glPixelStorei");
3073 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3074 surface->resource.width, surface->resource.height,
3075 surface->resource.format->glFormat,
3076 surface->resource.format->glType, data.addr);
3077 checkGLcall("glReadPixels");
3079 /* Reset previous pixel store pack state */
3080 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3081 checkGLcall("glPixelStorei");
3083 if (!srcIsUpsideDown)
3085 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3086 * Flip the lines in software. */
3087 UINT pitch = wined3d_surface_get_pitch(surface);
3089 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3090 goto error;
3092 if (data.buffer_object)
3094 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3095 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3097 else
3098 mem = data.addr;
3100 top = mem;
3101 bottom = mem + pitch * (surface->resource.height - 1);
3102 for (i = 0; i < surface->resource.height / 2; i++)
3104 memcpy(row, top, pitch);
3105 memcpy(top, bottom, pitch);
3106 memcpy(bottom, row, pitch);
3107 top += pitch;
3108 bottom -= pitch;
3110 HeapFree(GetProcessHeap(), 0, row);
3112 if (data.buffer_object)
3113 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3116 error:
3117 if (data.buffer_object)
3119 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3120 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3123 context_release(context);
3126 /* Read the framebuffer contents into a texture. Note that this function
3127 * doesn't do any kind of flipping. Using this on an onscreen surface will
3128 * result in a flipped D3D texture. */
3129 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3131 struct wined3d_device *device = surface->resource.device;
3132 const struct wined3d_gl_info *gl_info;
3133 struct wined3d_context *context;
3135 context = context_acquire(device, surface);
3136 gl_info = context->gl_info;
3137 device_invalidate_state(device, STATE_FRAMEBUFFER);
3139 surface_prepare_texture(surface, context, srgb);
3140 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3142 TRACE("Reading back offscreen render target %p.\n", surface);
3144 if (wined3d_resource_is_offscreen(&surface->resource))
3145 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3146 else
3147 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3148 checkGLcall("glReadBuffer");
3150 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3151 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3152 checkGLcall("glCopyTexSubImage2D");
3154 context_release(context);
3157 /* Context activation is done by the caller. */
3158 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3159 struct wined3d_context *context, BOOL srgb)
3161 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3162 enum wined3d_conversion_type convert;
3163 struct wined3d_format format;
3165 if (surface->flags & alloc_flag) return;
3167 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3168 if (convert != WINED3D_CT_NONE || format.convert)
3169 surface->flags |= SFLAG_CONVERTED;
3170 else surface->flags &= ~SFLAG_CONVERTED;
3172 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3173 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3174 surface->flags |= alloc_flag;
3177 /* Context activation is done by the caller. */
3178 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3180 struct wined3d_texture *texture = surface->container;
3181 UINT sub_count = texture->level_count * texture->layer_count;
3182 UINT i;
3184 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3186 for (i = 0; i < sub_count; ++i)
3188 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3189 surface_prepare_texture_internal(s, context, srgb);
3192 return;
3195 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3197 if (multisample)
3199 if (surface->rb_multisample)
3200 return;
3202 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3203 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3204 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3205 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3206 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3208 else
3210 if (surface->rb_resolved)
3211 return;
3213 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3214 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3215 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3216 surface->pow2Width, surface->pow2Height);
3217 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3221 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3223 /* FIXME: Is this really how color keys are supposed to work? I think it
3224 * makes more sense to compare the individual channels. */
3225 return color >= color_key->color_space_low_value
3226 && color <= color_key->color_space_high_value;
3229 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3230 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3232 const BYTE *source;
3233 BYTE *dest;
3235 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3236 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3238 switch (conversion_type)
3240 case WINED3D_CT_NONE:
3242 memcpy(dst, src, pitch * height);
3243 break;
3246 case WINED3D_CT_PALETTED:
3247 if (surface->swapchain && surface->swapchain->palette)
3249 unsigned int x, y;
3250 const struct wined3d_palette *palette = surface->swapchain->palette;
3251 for (y = 0; y < height; y++)
3253 source = src + pitch * y;
3254 dest = dst + outpitch * y;
3255 for (x = 0; x < width; x++)
3257 BYTE color = *source++;
3258 *dest++ = palette->colors[color].rgbRed;
3259 *dest++ = palette->colors[color].rgbGreen;
3260 *dest++ = palette->colors[color].rgbBlue;
3261 *dest++ = 0;
3265 else
3267 /* This should probably use the system palette, but unless
3268 * the X server is running in P8 mode there is no such thing.
3269 * The probably best solution is to set the fixed 20 colors
3270 * from the default windows palette and set the rest to black,
3271 * white, or some ugly pink. For now use black for the entire
3272 * palette. Don't use pink everywhere. Age of Empires 2 draws
3273 * a front buffer filled with zeroes without a palette when
3274 * starting and we don't want the screen to flash in an ugly
3275 * color. */
3276 FIXME("P8 surface loaded without a palette.\n");
3277 memset(dst, 0, height * outpitch);
3280 break;
3282 case WINED3D_CT_CK_565:
3284 /* Converting the 565 format in 5551 packed to emulate color-keying.
3286 Note : in all these conversion, it would be best to average the averaging
3287 pixels to get the color of the pixel that will be color-keyed to
3288 prevent 'color bleeding'. This will be done later on if ever it is
3289 too visible.
3291 Note2: Nvidia documents say that their driver does not support alpha + color keying
3292 on the same surface and disables color keying in such a case
3294 unsigned int x, y;
3295 const WORD *Source;
3296 WORD *Dest;
3298 TRACE("Color keyed 565\n");
3300 for (y = 0; y < height; y++) {
3301 Source = (const WORD *)(src + y * pitch);
3302 Dest = (WORD *) (dst + y * outpitch);
3303 for (x = 0; x < width; x++ ) {
3304 WORD color = *Source++;
3305 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3306 if (!color_in_range(&surface->container->src_blt_color_key, color))
3307 *Dest |= 0x0001;
3308 Dest++;
3312 break;
3314 case WINED3D_CT_CK_5551:
3316 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3317 unsigned int x, y;
3318 const WORD *Source;
3319 WORD *Dest;
3320 TRACE("Color keyed 5551\n");
3321 for (y = 0; y < height; y++) {
3322 Source = (const WORD *)(src + y * pitch);
3323 Dest = (WORD *) (dst + y * outpitch);
3324 for (x = 0; x < width; x++ ) {
3325 WORD color = *Source++;
3326 *Dest = color;
3327 if (!color_in_range(&surface->container->src_blt_color_key, color))
3328 *Dest |= (1 << 15);
3329 else
3330 *Dest &= ~(1 << 15);
3331 Dest++;
3335 break;
3337 case WINED3D_CT_CK_RGB24:
3339 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3340 unsigned int x, y;
3341 for (y = 0; y < height; y++)
3343 source = src + pitch * y;
3344 dest = dst + outpitch * y;
3345 for (x = 0; x < width; x++) {
3346 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3347 DWORD dstcolor = color << 8;
3348 if (!color_in_range(&surface->container->src_blt_color_key, color))
3349 dstcolor |= 0xff;
3350 *(DWORD*)dest = dstcolor;
3351 source += 3;
3352 dest += 4;
3356 break;
3358 case WINED3D_CT_RGB32_888:
3360 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3361 unsigned int x, y;
3362 for (y = 0; y < height; y++)
3364 source = src + pitch * y;
3365 dest = dst + outpitch * y;
3366 for (x = 0; x < width; x++) {
3367 DWORD color = 0xffffff & *(const DWORD*)source;
3368 DWORD dstcolor = color << 8;
3369 if (!color_in_range(&surface->container->src_blt_color_key, color))
3370 dstcolor |= 0xff;
3371 *(DWORD*)dest = dstcolor;
3372 source += 4;
3373 dest += 4;
3377 break;
3379 case WINED3D_CT_CK_ARGB32:
3381 unsigned int x, y;
3382 for (y = 0; y < height; ++y)
3384 source = src + pitch * y;
3385 dest = dst + outpitch * y;
3386 for (x = 0; x < width; ++x)
3388 DWORD color = *(const DWORD *)source;
3389 if (color_in_range(&surface->container->src_blt_color_key, color))
3390 color &= ~0xff000000;
3391 *(DWORD*)dest = color;
3392 source += 4;
3393 dest += 4;
3397 break;
3399 default:
3400 ERR("Unsupported conversion type %#x.\n", conversion_type);
3402 return WINED3D_OK;
3405 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3407 if (front->container->level_count != 1 || front->container->layer_count != 1
3408 || back->container->level_count != 1 || back->container->layer_count != 1)
3409 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3411 /* Flip the surface contents */
3412 /* Flip the DC */
3414 HDC tmp;
3415 tmp = front->hDC;
3416 front->hDC = back->hDC;
3417 back->hDC = tmp;
3420 /* Flip the DIBsection */
3422 HBITMAP tmp = front->dib.DIBsection;
3423 front->dib.DIBsection = back->dib.DIBsection;
3424 back->dib.DIBsection = tmp;
3427 /* Flip the surface data */
3429 void* tmp;
3431 tmp = front->dib.bitmap_data;
3432 front->dib.bitmap_data = back->dib.bitmap_data;
3433 back->dib.bitmap_data = tmp;
3435 tmp = front->resource.heap_memory;
3436 front->resource.heap_memory = back->resource.heap_memory;
3437 back->resource.heap_memory = tmp;
3440 /* Flip the PBO */
3442 GLuint tmp_pbo = front->pbo;
3443 front->pbo = back->pbo;
3444 back->pbo = tmp_pbo;
3447 /* Flip the opengl texture */
3449 GLuint tmp;
3451 tmp = back->container->texture_rgb.name;
3452 back->container->texture_rgb.name = front->container->texture_rgb.name;
3453 front->container->texture_rgb.name = tmp;
3455 tmp = back->container->texture_srgb.name;
3456 back->container->texture_srgb.name = front->container->texture_srgb.name;
3457 front->container->texture_srgb.name = tmp;
3459 tmp = back->rb_multisample;
3460 back->rb_multisample = front->rb_multisample;
3461 front->rb_multisample = tmp;
3463 tmp = back->rb_resolved;
3464 back->rb_resolved = front->rb_resolved;
3465 front->rb_resolved = tmp;
3467 resource_unload(&back->resource);
3468 resource_unload(&front->resource);
3472 DWORD tmp_flags = back->flags;
3473 back->flags = front->flags;
3474 front->flags = tmp_flags;
3476 tmp_flags = back->locations;
3477 back->locations = front->locations;
3478 front->locations = tmp_flags;
3482 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3483 * pixel copy calls. */
3484 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3485 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3487 struct wined3d_device *device = dst_surface->resource.device;
3488 const struct wined3d_gl_info *gl_info;
3489 float xrel, yrel;
3490 struct wined3d_context *context;
3491 BOOL upsidedown = FALSE;
3492 RECT dst_rect = *dst_rect_in;
3494 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3495 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3497 if(dst_rect.top > dst_rect.bottom) {
3498 UINT tmp = dst_rect.bottom;
3499 dst_rect.bottom = dst_rect.top;
3500 dst_rect.top = tmp;
3501 upsidedown = TRUE;
3504 context = context_acquire(device, src_surface);
3505 gl_info = context->gl_info;
3506 context_apply_blit_state(context, device);
3507 wined3d_texture_load(dst_surface->container, context, FALSE);
3509 /* Bind the target texture */
3510 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3511 if (wined3d_resource_is_offscreen(&src_surface->resource))
3513 TRACE("Reading from an offscreen target\n");
3514 upsidedown = !upsidedown;
3515 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3517 else
3519 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3521 checkGLcall("glReadBuffer");
3523 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3524 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3526 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3528 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3530 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3531 ERR("Texture filtering not supported in direct blit.\n");
3533 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3534 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3536 ERR("Texture filtering not supported in direct blit\n");
3539 if (upsidedown
3540 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3541 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3543 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3544 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3545 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3546 src_rect->left, src_surface->resource.height - src_rect->bottom,
3547 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3549 else
3551 LONG row;
3552 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3553 /* I have to process this row by row to swap the image,
3554 * otherwise it would be upside down, so stretching in y direction
3555 * doesn't cost extra time
3557 * However, stretching in x direction can be avoided if not necessary
3559 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3560 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3562 /* Well, that stuff works, but it's very slow.
3563 * find a better way instead
3565 LONG col;
3567 for (col = dst_rect.left; col < dst_rect.right; ++col)
3569 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3570 dst_rect.left + col /* x offset */, row /* y offset */,
3571 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3574 else
3576 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3577 dst_rect.left /* x offset */, row /* y offset */,
3578 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3582 checkGLcall("glCopyTexSubImage2D");
3584 context_release(context);
3586 /* The texture is now most up to date - If the surface is a render target
3587 * and has a drawable, this path is never entered. */
3588 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3589 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3592 /* Uses the hardware to stretch and flip the image */
3593 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3594 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3596 struct wined3d_device *device = dst_surface->resource.device;
3597 GLuint src, backup = 0;
3598 float left, right, top, bottom; /* Texture coordinates */
3599 UINT fbwidth = src_surface->resource.width;
3600 UINT fbheight = src_surface->resource.height;
3601 const struct wined3d_gl_info *gl_info;
3602 struct wined3d_context *context;
3603 GLenum drawBuffer = GL_BACK;
3604 GLenum texture_target;
3605 BOOL noBackBufferBackup;
3606 BOOL src_offscreen;
3607 BOOL upsidedown = FALSE;
3608 RECT dst_rect = *dst_rect_in;
3610 TRACE("Using hwstretch blit\n");
3611 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3612 context = context_acquire(device, src_surface);
3613 gl_info = context->gl_info;
3614 context_apply_blit_state(context, device);
3615 wined3d_texture_load(dst_surface->container, context, FALSE);
3617 src_offscreen = wined3d_resource_is_offscreen(&src_surface->resource);
3618 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3619 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3621 /* Get it a description */
3622 wined3d_texture_load(src_surface->container, context, FALSE);
3625 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3626 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3628 if (context->aux_buffers >= 2)
3630 /* Got more than one aux buffer? Use the 2nd aux buffer */
3631 drawBuffer = GL_AUX1;
3633 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3635 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3636 drawBuffer = GL_AUX0;
3639 if (noBackBufferBackup)
3641 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3642 checkGLcall("glGenTextures");
3643 context_bind_texture(context, GL_TEXTURE_2D, backup);
3644 texture_target = GL_TEXTURE_2D;
3646 else
3648 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3649 * we are reading from the back buffer, the backup can be used as source texture
3651 texture_target = src_surface->texture_target;
3652 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3653 gl_info->gl_ops.gl.p_glEnable(texture_target);
3654 checkGLcall("glEnable(texture_target)");
3656 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3657 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3660 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3661 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3663 if(dst_rect.top > dst_rect.bottom) {
3664 UINT tmp = dst_rect.bottom;
3665 dst_rect.bottom = dst_rect.top;
3666 dst_rect.top = tmp;
3667 upsidedown = TRUE;
3670 if (src_offscreen)
3672 TRACE("Reading from an offscreen target\n");
3673 upsidedown = !upsidedown;
3674 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3676 else
3678 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3681 /* TODO: Only back up the part that will be overwritten */
3682 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3684 checkGLcall("glCopyTexSubImage2D");
3686 /* No issue with overriding these - the sampler is dirty due to blit usage */
3687 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3688 wined3d_gl_mag_filter(magLookup, filter));
3689 checkGLcall("glTexParameteri");
3690 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3691 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3692 checkGLcall("glTexParameteri");
3694 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
3696 src = backup ? backup : src_surface->container->texture_rgb.name;
3698 else
3700 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3701 checkGLcall("glReadBuffer(GL_FRONT)");
3703 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3704 checkGLcall("glGenTextures(1, &src)");
3705 context_bind_texture(context, GL_TEXTURE_2D, src);
3707 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3708 * out for power of 2 sizes
3710 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3711 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3712 checkGLcall("glTexImage2D");
3713 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3715 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3716 checkGLcall("glTexParameteri");
3717 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3718 checkGLcall("glTexParameteri");
3720 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3721 checkGLcall("glReadBuffer(GL_BACK)");
3723 if (texture_target != GL_TEXTURE_2D)
3725 gl_info->gl_ops.gl.p_glDisable(texture_target);
3726 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3727 texture_target = GL_TEXTURE_2D;
3730 checkGLcall("glEnd and previous");
3732 left = src_rect->left;
3733 right = src_rect->right;
3735 if (!upsidedown)
3737 top = src_surface->resource.height - src_rect->top;
3738 bottom = src_surface->resource.height - src_rect->bottom;
3740 else
3742 top = src_surface->resource.height - src_rect->bottom;
3743 bottom = src_surface->resource.height - src_rect->top;
3746 if (src_surface->flags & SFLAG_NORMCOORD)
3748 left /= src_surface->pow2Width;
3749 right /= src_surface->pow2Width;
3750 top /= src_surface->pow2Height;
3751 bottom /= src_surface->pow2Height;
3754 /* draw the source texture stretched and upside down. The correct surface is bound already */
3755 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3756 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3758 context_set_draw_buffer(context, drawBuffer);
3759 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3761 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3762 /* bottom left */
3763 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3764 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3766 /* top left */
3767 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3768 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3770 /* top right */
3771 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3772 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3774 /* bottom right */
3775 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3776 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3777 gl_info->gl_ops.gl.p_glEnd();
3778 checkGLcall("glEnd and previous");
3780 if (texture_target != dst_surface->texture_target)
3782 gl_info->gl_ops.gl.p_glDisable(texture_target);
3783 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3784 texture_target = dst_surface->texture_target;
3787 /* Now read the stretched and upside down image into the destination texture */
3788 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3789 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3791 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3792 0, 0, /* We blitted the image to the origin */
3793 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3794 checkGLcall("glCopyTexSubImage2D");
3796 if (drawBuffer == GL_BACK)
3798 /* Write the back buffer backup back. */
3799 if (backup)
3801 if (texture_target != GL_TEXTURE_2D)
3803 gl_info->gl_ops.gl.p_glDisable(texture_target);
3804 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3805 texture_target = GL_TEXTURE_2D;
3807 context_bind_texture(context, GL_TEXTURE_2D, backup);
3809 else
3811 if (texture_target != src_surface->texture_target)
3813 gl_info->gl_ops.gl.p_glDisable(texture_target);
3814 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3815 texture_target = src_surface->texture_target;
3817 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3820 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3821 /* top left */
3822 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3823 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3825 /* bottom left */
3826 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3827 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3829 /* bottom right */
3830 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3831 (float)fbheight / (float)src_surface->pow2Height);
3832 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3834 /* top right */
3835 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3836 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3837 gl_info->gl_ops.gl.p_glEnd();
3839 gl_info->gl_ops.gl.p_glDisable(texture_target);
3840 checkGLcall("glDisable(texture_target)");
3842 /* Cleanup */
3843 if (src != src_surface->container->texture_rgb.name && src != backup)
3845 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3846 checkGLcall("glDeleteTextures(1, &src)");
3848 if (backup)
3850 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3851 checkGLcall("glDeleteTextures(1, &backup)");
3854 if (wined3d_settings.strict_draw_ordering)
3855 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3857 context_release(context);
3859 /* The texture is now most up to date - If the surface is a render target
3860 * and has a drawable, this path is never entered. */
3861 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3862 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3865 /* Front buffer coordinates are always full screen coordinates, but our GL
3866 * drawable is limited to the window's client area. The sysmem and texture
3867 * copies do have the full screen size. Note that GL has a bottom-left
3868 * origin, while D3D has a top-left origin. */
3869 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3871 UINT drawable_height;
3873 if (surface->swapchain && surface == surface->swapchain->front_buffer)
3875 POINT offset = {0, 0};
3876 RECT windowsize;
3878 ScreenToClient(window, &offset);
3879 OffsetRect(rect, offset.x, offset.y);
3881 GetClientRect(window, &windowsize);
3882 drawable_height = windowsize.bottom - windowsize.top;
3884 else
3886 drawable_height = surface->resource.height;
3889 rect->top = drawable_height - rect->top;
3890 rect->bottom = drawable_height - rect->bottom;
3893 static void surface_blt_to_drawable(const struct wined3d_device *device,
3894 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3895 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3896 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3898 const struct wined3d_gl_info *gl_info;
3899 struct wined3d_context *context;
3900 RECT src_rect, dst_rect;
3902 src_rect = *src_rect_in;
3903 dst_rect = *dst_rect_in;
3905 context = context_acquire(device, dst_surface);
3906 gl_info = context->gl_info;
3908 /* Make sure the surface is up-to-date. This should probably use
3909 * surface_load_location() and worry about the destination surface too,
3910 * unless we're overwriting it completely. */
3911 wined3d_texture_load(src_surface->container, context, FALSE);
3913 /* Activate the destination context, set it up for blitting */
3914 context_apply_blit_state(context, device);
3916 if (!wined3d_resource_is_offscreen(&dst_surface->resource))
3917 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3919 device->blitter->set_shader(device->blit_priv, context, src_surface);
3921 if (alpha_test)
3923 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3924 checkGLcall("glEnable(GL_ALPHA_TEST)");
3926 /* For P8 surfaces, the alpha component contains the palette index.
3927 * Which means that the colorkey is one of the palette entries. In
3928 * other cases pixels that should be masked away have alpha set to 0. */
3929 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3930 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3931 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3932 else
3933 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3934 checkGLcall("glAlphaFunc");
3936 else
3938 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3939 checkGLcall("glDisable(GL_ALPHA_TEST)");
3942 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3944 if (alpha_test)
3946 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3947 checkGLcall("glDisable(GL_ALPHA_TEST)");
3950 /* Leave the opengl state valid for blitting */
3951 device->blitter->unset_shader(context->gl_info);
3953 if (wined3d_settings.strict_draw_ordering
3954 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
3955 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3957 context_release(context);
3960 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3962 struct wined3d_device *device = s->resource.device;
3963 const struct blit_shader *blitter;
3965 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3966 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3967 if (!blitter)
3969 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3970 return WINED3DERR_INVALIDCALL;
3973 return blitter->color_fill(device, s, rect, color);
3976 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3977 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3978 enum wined3d_texture_filter_type filter)
3980 struct wined3d_device *device = dst_surface->resource.device;
3981 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3982 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3984 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3985 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3986 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3988 /* Get the swapchain. One of the surfaces has to be a primary surface */
3989 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3991 WARN("Destination is in sysmem, rejecting gl blt\n");
3992 return WINED3DERR_INVALIDCALL;
3995 dst_swapchain = dst_surface->swapchain;
3997 if (src_surface)
3999 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4001 WARN("Src is in sysmem, rejecting gl blt\n");
4002 return WINED3DERR_INVALIDCALL;
4005 src_swapchain = src_surface->swapchain;
4007 else
4009 src_swapchain = NULL;
4012 /* Early sort out of cases where no render target is used */
4013 if (!dst_swapchain && !src_swapchain
4014 && src_surface != device->fb.render_targets[0]
4015 && dst_surface != device->fb.render_targets[0])
4017 TRACE("No surface is render target, not using hardware blit.\n");
4018 return WINED3DERR_INVALIDCALL;
4021 /* No destination color keying supported */
4022 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4024 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4025 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4026 return WINED3DERR_INVALIDCALL;
4029 if (dst_swapchain && dst_swapchain == src_swapchain)
4031 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4032 return WINED3DERR_INVALIDCALL;
4035 if (dst_swapchain && src_swapchain)
4037 FIXME("Implement hardware blit between two different swapchains\n");
4038 return WINED3DERR_INVALIDCALL;
4041 if (dst_swapchain)
4043 /* Handled with regular texture -> swapchain blit */
4044 if (src_surface == device->fb.render_targets[0])
4045 TRACE("Blit from active render target to a swapchain\n");
4047 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4049 FIXME("Implement blit from a swapchain to the active render target\n");
4050 return WINED3DERR_INVALIDCALL;
4053 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4055 /* Blit from render target to texture */
4056 BOOL stretchx;
4058 /* P8 read back is not implemented */
4059 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4060 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4062 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4063 return WINED3DERR_INVALIDCALL;
4066 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4068 TRACE("Color keying not supported by frame buffer to texture blit\n");
4069 return WINED3DERR_INVALIDCALL;
4070 /* Destination color key is checked above */
4073 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4074 stretchx = TRUE;
4075 else
4076 stretchx = FALSE;
4078 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4079 * flip the image nor scale it.
4081 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4082 * -> If the app wants an image width an unscaled width, copy it line per line
4083 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4084 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4085 * back buffer. This is slower than reading line per line, thus not used for flipping
4086 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4087 * pixel by pixel. */
4088 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4089 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4091 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4092 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4094 else
4096 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4097 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4100 surface_evict_sysmem(dst_surface);
4102 return WINED3D_OK;
4104 else if (src_surface)
4106 /* Blit from offscreen surface to render target */
4107 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4108 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4110 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4112 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4113 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4114 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4116 FIXME("Unsupported blit operation falling back to software\n");
4117 return WINED3DERR_INVALIDCALL;
4120 /* Color keying: Check if we have to do a color keyed blt,
4121 * and if not check if a color key is activated.
4123 * Just modify the color keying parameters in the surface and restore them afterwards
4124 * The surface keeps track of the color key last used to load the opengl surface.
4125 * PreLoad will catch the change to the flags and color key and reload if necessary.
4127 if (flags & WINEDDBLT_KEYSRC)
4129 /* Use color key from surface */
4131 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4133 /* Use color key from DDBltFx */
4134 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4135 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4137 else
4139 /* Do not use color key */
4140 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4143 surface_blt_to_drawable(device, filter,
4144 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4145 src_surface, src_rect, dst_surface, dst_rect);
4147 /* Restore the color key parameters */
4148 src_surface->container->color_key_flags = old_color_key_flags;
4149 src_surface->container->src_blt_color_key = old_blt_key;
4151 surface_validate_location(dst_surface, dst_surface->resource.draw_binding);
4152 surface_invalidate_location(dst_surface, ~dst_surface->resource.draw_binding);
4154 return WINED3D_OK;
4157 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4158 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4159 return WINED3DERR_INVALIDCALL;
4162 /* Context activation is done by the caller. */
4163 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4164 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4166 struct wined3d_device *device = surface->resource.device;
4167 const struct wined3d_gl_info *gl_info = context->gl_info;
4168 GLint compare_mode = GL_NONE;
4169 struct blt_info info;
4170 GLint old_binding = 0;
4171 RECT rect;
4173 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4175 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4176 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4177 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4178 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4179 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4180 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4181 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4182 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4183 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4184 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4185 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4187 SetRect(&rect, 0, h, w, 0);
4188 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4189 context_active_texture(context, context->gl_info, 0);
4190 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4191 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4192 if (gl_info->supported[ARB_SHADOW])
4194 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4195 if (compare_mode != GL_NONE)
4196 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4199 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4200 gl_info, info.tex_type, &surface->ds_current_size);
4202 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4203 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4204 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4205 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4206 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4207 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4208 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4209 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4210 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4211 gl_info->gl_ops.gl.p_glEnd();
4213 if (compare_mode != GL_NONE)
4214 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4215 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4217 gl_info->gl_ops.gl.p_glPopAttrib();
4219 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4222 void surface_modify_ds_location(struct wined3d_surface *surface,
4223 DWORD location, UINT w, UINT h)
4225 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4227 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4228 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4229 wined3d_texture_set_dirty(surface->container);
4231 surface->ds_current_size.cx = w;
4232 surface->ds_current_size.cy = h;
4233 surface->locations = location;
4236 /* Context activation is done by the caller. */
4237 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4239 const struct wined3d_gl_info *gl_info = context->gl_info;
4240 struct wined3d_device *device = surface->resource.device;
4241 GLsizei w, h;
4243 TRACE("surface %p, new location %#x.\n", surface, location);
4245 /* TODO: Make this work for modes other than FBO */
4246 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4248 if (!(surface->locations & location))
4250 w = surface->ds_current_size.cx;
4251 h = surface->ds_current_size.cy;
4252 surface->ds_current_size.cx = 0;
4253 surface->ds_current_size.cy = 0;
4255 else
4257 w = surface->resource.width;
4258 h = surface->resource.height;
4261 if (surface->ds_current_size.cx == surface->resource.width
4262 && surface->ds_current_size.cy == surface->resource.height)
4264 TRACE("Location (%#x) is already up to date.\n", location);
4265 return;
4268 if (surface->current_renderbuffer)
4270 FIXME("Not supported with fixed up depth stencil.\n");
4271 return;
4274 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4276 TRACE("Surface was discarded, no need copy data.\n");
4277 switch (location)
4279 case WINED3D_LOCATION_TEXTURE_RGB:
4280 surface_prepare_texture(surface, context, FALSE);
4281 break;
4282 case WINED3D_LOCATION_RB_MULTISAMPLE:
4283 surface_prepare_rb(surface, gl_info, TRUE);
4284 break;
4285 case WINED3D_LOCATION_DRAWABLE:
4286 /* Nothing to do */
4287 break;
4288 default:
4289 FIXME("Unhandled location %#x\n", location);
4291 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4292 surface->locations |= location;
4293 surface->ds_current_size.cx = surface->resource.width;
4294 surface->ds_current_size.cy = surface->resource.height;
4295 return;
4298 if (!surface->locations)
4300 FIXME("No up to date depth stencil location.\n");
4301 surface->locations |= location;
4302 surface->ds_current_size.cx = surface->resource.width;
4303 surface->ds_current_size.cy = surface->resource.height;
4304 return;
4307 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4309 GLint old_binding = 0;
4310 GLenum bind_target;
4312 /* The render target is allowed to be smaller than the depth/stencil
4313 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4314 * than the offscreen surface. Don't overwrite the offscreen surface
4315 * with undefined data. */
4316 w = min(w, context->swapchain->desc.backbuffer_width);
4317 h = min(h, context->swapchain->desc.backbuffer_height);
4319 TRACE("Copying onscreen depth buffer to depth texture.\n");
4321 if (!device->depth_blt_texture)
4322 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4324 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4325 * directly on the FBO texture. That's because we need to flip. */
4326 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4327 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4328 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4330 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4331 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4333 else
4335 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4336 bind_target = GL_TEXTURE_2D;
4338 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4339 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4340 * internal format, because the internal format might include stencil
4341 * data. In principle we should copy stencil data as well, but unless
4342 * the driver supports stencil export it's hard to do, and doesn't
4343 * seem to be needed in practice. If the hardware doesn't support
4344 * writing stencil data, the glCopyTexImage2D() call might trigger
4345 * software fallbacks. */
4346 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4347 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4348 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4349 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4350 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4351 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4352 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4353 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4355 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4356 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4357 context_set_draw_buffer(context, GL_NONE);
4359 /* Do the actual blit */
4360 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4361 checkGLcall("depth_blt");
4363 context_invalidate_state(context, STATE_FRAMEBUFFER);
4365 if (wined3d_settings.strict_draw_ordering)
4366 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4368 else if (location == WINED3D_LOCATION_DRAWABLE)
4370 TRACE("Copying depth texture to onscreen depth buffer.\n");
4372 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4373 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4374 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4375 0, surface->pow2Height - h, w, h, surface->texture_target);
4376 checkGLcall("depth_blt");
4378 context_invalidate_state(context, STATE_FRAMEBUFFER);
4380 if (wined3d_settings.strict_draw_ordering)
4381 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4383 else
4385 ERR("Invalid location (%#x) specified.\n", location);
4388 surface->locations |= location;
4389 surface->ds_current_size.cx = surface->resource.width;
4390 surface->ds_current_size.cy = surface->resource.height;
4393 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4395 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4397 surface->locations |= location;
4400 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4402 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4404 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4405 wined3d_texture_set_dirty(surface->container);
4406 surface->locations &= ~location;
4408 if (!surface->locations)
4409 ERR("Surface %p does not have any up to date location.\n", surface);
4412 static DWORD resource_access_from_location(DWORD location)
4414 switch (location)
4416 case WINED3D_LOCATION_SYSMEM:
4417 case WINED3D_LOCATION_USER_MEMORY:
4418 case WINED3D_LOCATION_DIB:
4419 case WINED3D_LOCATION_BUFFER:
4420 return WINED3D_RESOURCE_ACCESS_CPU;
4422 case WINED3D_LOCATION_DRAWABLE:
4423 case WINED3D_LOCATION_TEXTURE_SRGB:
4424 case WINED3D_LOCATION_TEXTURE_RGB:
4425 case WINED3D_LOCATION_RB_MULTISAMPLE:
4426 case WINED3D_LOCATION_RB_RESOLVED:
4427 return WINED3D_RESOURCE_ACCESS_GPU;
4429 default:
4430 FIXME("Unhandled location %#x.\n", location);
4431 return 0;
4435 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4437 struct wined3d_device *device = surface->resource.device;
4438 struct wined3d_context *context;
4439 const struct wined3d_gl_info *gl_info;
4440 struct wined3d_bo_address dst, src;
4441 UINT size = surface->resource.size;
4443 surface_get_memory(surface, &dst, location);
4444 surface_get_memory(surface, &src, surface->locations);
4446 if (dst.buffer_object)
4448 context = context_acquire(device, NULL);
4449 gl_info = context->gl_info;
4450 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4451 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4452 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4453 checkGLcall("Upload PBO");
4454 context_release(context);
4455 return;
4457 if (src.buffer_object)
4459 context = context_acquire(device, NULL);
4460 gl_info = context->gl_info;
4461 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4462 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4463 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4464 checkGLcall("Download PBO");
4465 context_release(context);
4466 return;
4468 memcpy(dst.addr, src.addr, size);
4471 static void surface_load_sysmem(struct wined3d_surface *surface,
4472 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4474 if (surface->locations & surface_simple_locations)
4476 surface_copy_simple_location(surface, dst_location);
4477 return;
4480 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4481 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4483 /* Download the surface to system memory. */
4484 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4486 struct wined3d_device *device = surface->resource.device;
4487 struct wined3d_context *context;
4489 /* TODO: Use already acquired context when possible. */
4490 context = context_acquire(device, NULL);
4492 wined3d_texture_bind_and_dirtify(surface->container, context,
4493 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4494 surface_download_data(surface, gl_info, dst_location);
4496 context_release(context);
4498 return;
4501 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4503 read_from_framebuffer(surface, dst_location);
4504 return;
4507 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4508 surface, wined3d_debug_location(surface->locations));
4511 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4512 const struct wined3d_gl_info *gl_info)
4514 RECT r;
4516 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4517 && wined3d_resource_is_offscreen(&surface->resource))
4519 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4520 return WINED3DERR_INVALIDCALL;
4523 surface_get_rect(surface, NULL, &r);
4524 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4525 surface_blt_to_drawable(surface->resource.device,
4526 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4528 return WINED3D_OK;
4531 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4532 const struct wined3d_gl_info *gl_info, BOOL srgb)
4534 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4535 struct wined3d_device *device = surface->resource.device;
4536 enum wined3d_conversion_type convert;
4537 struct wined3d_context *context;
4538 UINT width, src_pitch, dst_pitch;
4539 struct wined3d_bo_address data;
4540 struct wined3d_format format;
4541 POINT dst_point = {0, 0};
4542 BYTE *mem = NULL;
4544 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4545 && wined3d_resource_is_offscreen(&surface->resource)
4546 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4548 surface_load_fb_texture(surface, srgb);
4550 return WINED3D_OK;
4553 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4554 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4555 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4556 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4557 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4559 if (srgb)
4560 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4561 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4562 else
4563 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4564 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4566 return WINED3D_OK;
4569 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4570 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4571 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4572 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4573 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4575 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4576 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4577 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4578 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4580 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4581 &rect, surface, dst_location, &rect);
4583 return WINED3D_OK;
4586 /* Upload from system memory */
4588 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4589 TRUE /* We will use textures */, &format, &convert);
4591 if (srgb)
4593 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4594 == WINED3D_LOCATION_TEXTURE_RGB)
4596 /* Performance warning... */
4597 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4598 surface_prepare_map_memory(surface);
4599 surface_load_location(surface, surface->resource.map_binding);
4602 else
4604 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4605 == WINED3D_LOCATION_TEXTURE_SRGB)
4607 /* Performance warning... */
4608 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4609 surface_prepare_map_memory(surface);
4610 surface_load_location(surface, surface->resource.map_binding);
4614 if (!(surface->locations & surface_simple_locations))
4616 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4617 /* Lets hope we get it from somewhere... */
4618 surface_prepare_system_memory(surface);
4619 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4622 /* TODO: Use already acquired context when possible. */
4623 context = context_acquire(device, NULL);
4625 surface_prepare_texture(surface, context, srgb);
4626 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4628 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4630 surface->flags |= SFLAG_GLCKEY;
4631 surface->gl_color_key = surface->container->src_blt_color_key;
4633 else surface->flags &= ~SFLAG_GLCKEY;
4635 width = surface->resource.width;
4636 src_pitch = wined3d_surface_get_pitch(surface);
4638 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4639 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4640 * called. */
4641 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4643 TRACE("Removing the pbo attached to surface %p.\n", surface);
4645 if (surface->flags & SFLAG_DIBSECTION)
4646 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4647 else
4648 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4650 surface_prepare_map_memory(surface);
4651 surface_load_location(surface, surface->resource.map_binding);
4652 surface_remove_pbo(surface, gl_info);
4655 surface_get_memory(surface, &data, surface->locations);
4656 if (format.convert)
4658 /* This code is entered for texture formats which need a fixup. */
4659 UINT height = surface->resource.height;
4661 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4662 dst_pitch = width * format.conv_byte_count;
4663 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4665 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4667 ERR("Out of memory (%u).\n", dst_pitch * height);
4668 context_release(context);
4669 return E_OUTOFMEMORY;
4671 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4672 dst_pitch, dst_pitch * height, width, height, 1);
4673 format.byte_count = format.conv_byte_count;
4674 src_pitch = dst_pitch;
4675 data.addr = mem;
4677 else if (convert != WINED3D_CT_NONE)
4679 /* This code is only entered for color keying fixups */
4680 UINT height = surface->resource.height;
4682 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4683 dst_pitch = width * format.conv_byte_count;
4684 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4686 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4688 ERR("Out of memory (%u).\n", dst_pitch * height);
4689 context_release(context);
4690 return E_OUTOFMEMORY;
4692 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4693 width, height, dst_pitch, convert, surface);
4694 format.byte_count = format.conv_byte_count;
4695 src_pitch = dst_pitch;
4696 data.addr = mem;
4699 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4701 context_release(context);
4703 HeapFree(GetProcessHeap(), 0, mem);
4705 return WINED3D_OK;
4708 static void surface_multisample_resolve(struct wined3d_surface *surface)
4710 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4712 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4713 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4714 surface);
4716 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4717 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4720 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4722 struct wined3d_device *device = surface->resource.device;
4723 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4724 HRESULT hr;
4726 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4728 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4730 if (location == WINED3D_LOCATION_TEXTURE_RGB
4731 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4733 struct wined3d_context *context = context_acquire(device, NULL);
4734 surface_load_ds_location(surface, context, location);
4735 context_release(context);
4736 return WINED3D_OK;
4738 else if (location & surface->locations && surface->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4740 /* Already up to date, nothing to do. */
4741 return WINED3D_OK;
4743 else
4745 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4746 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4747 return WINED3DERR_INVALIDCALL;
4751 if (surface->locations & location)
4753 TRACE("Location already up to date.\n");
4754 return WINED3D_OK;
4757 if (WARN_ON(d3d_surface))
4759 DWORD required_access = resource_access_from_location(location);
4760 if ((surface->resource.access_flags & required_access) != required_access)
4761 WARN("Operation requires %#x access, but surface only has %#x.\n",
4762 required_access, surface->resource.access_flags);
4765 if (!surface->locations)
4767 ERR("Surface %p does not have any up to date location.\n", surface);
4768 surface->flags |= SFLAG_LOST;
4769 return WINED3DERR_DEVICELOST;
4772 switch (location)
4774 case WINED3D_LOCATION_DIB:
4775 case WINED3D_LOCATION_USER_MEMORY:
4776 case WINED3D_LOCATION_SYSMEM:
4777 case WINED3D_LOCATION_BUFFER:
4778 surface_load_sysmem(surface, gl_info, location);
4779 break;
4781 case WINED3D_LOCATION_DRAWABLE:
4782 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4783 return hr;
4784 break;
4786 case WINED3D_LOCATION_RB_RESOLVED:
4787 surface_multisample_resolve(surface);
4788 break;
4790 case WINED3D_LOCATION_TEXTURE_RGB:
4791 case WINED3D_LOCATION_TEXTURE_SRGB:
4792 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4793 return hr;
4794 break;
4796 default:
4797 ERR("Don't know how to handle location %#x.\n", location);
4798 break;
4801 surface_validate_location(surface, location);
4803 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4804 surface_evict_sysmem(surface);
4806 return WINED3D_OK;
4809 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4810 /* Context activation is done by the caller. */
4811 static void ffp_blit_free(struct wined3d_device *device) { }
4813 /* Context activation is done by the caller. */
4814 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4816 const struct wined3d_gl_info *gl_info = context->gl_info;
4818 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4819 checkGLcall("glEnable(target)");
4821 return WINED3D_OK;
4824 /* Context activation is done by the caller. */
4825 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4827 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4828 checkGLcall("glDisable(GL_TEXTURE_2D)");
4829 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4831 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4832 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4834 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4836 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4837 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4841 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4842 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4843 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4845 switch (blit_op)
4847 case WINED3D_BLIT_OP_COLOR_BLIT:
4848 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4849 return FALSE;
4851 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4853 TRACE("Checking support for fixup:\n");
4854 dump_color_fixup_desc(src_format->color_fixup);
4857 /* We only support identity conversions. */
4858 if (!is_identity_fixup(src_format->color_fixup)
4859 || !is_identity_fixup(dst_format->color_fixup))
4861 TRACE("Fixups are not supported.\n");
4862 return FALSE;
4865 return TRUE;
4867 case WINED3D_BLIT_OP_COLOR_FILL:
4868 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4869 return FALSE;
4871 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4873 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4874 return FALSE;
4876 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4878 TRACE("Color fill not supported\n");
4879 return FALSE;
4882 /* FIXME: We should reject color fills on formats with fixups,
4883 * but this would break P8 color fills for example. */
4885 return TRUE;
4887 case WINED3D_BLIT_OP_DEPTH_FILL:
4888 return TRUE;
4890 default:
4891 TRACE("Unsupported blit_op=%d\n", blit_op);
4892 return FALSE;
4896 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4897 const RECT *dst_rect, const struct wined3d_color *color)
4899 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4900 struct wined3d_fb_state fb = {&dst_surface, NULL};
4902 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4904 return WINED3D_OK;
4907 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
4908 struct wined3d_surface *surface, const RECT *rect, float depth)
4910 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
4911 struct wined3d_fb_state fb = {NULL, surface};
4913 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4915 return WINED3D_OK;
4918 const struct blit_shader ffp_blit = {
4919 ffp_blit_alloc,
4920 ffp_blit_free,
4921 ffp_blit_set,
4922 ffp_blit_unset,
4923 ffp_blit_supported,
4924 ffp_blit_color_fill,
4925 ffp_blit_depth_fill,
4928 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4930 return WINED3D_OK;
4933 /* Context activation is done by the caller. */
4934 static void cpu_blit_free(struct wined3d_device *device)
4938 /* Context activation is done by the caller. */
4939 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4941 return WINED3D_OK;
4944 /* Context activation is done by the caller. */
4945 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4949 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4950 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4951 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4953 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4955 return TRUE;
4958 return FALSE;
4961 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4962 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4963 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4965 UINT row_block_count;
4966 const BYTE *src_row;
4967 BYTE *dst_row;
4968 UINT x, y;
4970 src_row = src_data;
4971 dst_row = dst_data;
4973 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4975 if (!flags)
4977 for (y = 0; y < update_h; y += format->block_height)
4979 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4980 src_row += src_pitch;
4981 dst_row += dst_pitch;
4984 return WINED3D_OK;
4987 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4989 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4991 switch (format->id)
4993 case WINED3DFMT_DXT1:
4994 for (y = 0; y < update_h; y += format->block_height)
4996 struct block
4998 WORD color[2];
4999 BYTE control_row[4];
5002 const struct block *s = (const struct block *)src_row;
5003 struct block *d = (struct block *)dst_row;
5005 for (x = 0; x < row_block_count; ++x)
5007 d[x].color[0] = s[x].color[0];
5008 d[x].color[1] = s[x].color[1];
5009 d[x].control_row[0] = s[x].control_row[3];
5010 d[x].control_row[1] = s[x].control_row[2];
5011 d[x].control_row[2] = s[x].control_row[1];
5012 d[x].control_row[3] = s[x].control_row[0];
5014 src_row -= src_pitch;
5015 dst_row += dst_pitch;
5017 return WINED3D_OK;
5019 case WINED3DFMT_DXT2:
5020 case WINED3DFMT_DXT3:
5021 for (y = 0; y < update_h; y += format->block_height)
5023 struct block
5025 WORD alpha_row[4];
5026 WORD color[2];
5027 BYTE control_row[4];
5030 const struct block *s = (const struct block *)src_row;
5031 struct block *d = (struct block *)dst_row;
5033 for (x = 0; x < row_block_count; ++x)
5035 d[x].alpha_row[0] = s[x].alpha_row[3];
5036 d[x].alpha_row[1] = s[x].alpha_row[2];
5037 d[x].alpha_row[2] = s[x].alpha_row[1];
5038 d[x].alpha_row[3] = s[x].alpha_row[0];
5039 d[x].color[0] = s[x].color[0];
5040 d[x].color[1] = s[x].color[1];
5041 d[x].control_row[0] = s[x].control_row[3];
5042 d[x].control_row[1] = s[x].control_row[2];
5043 d[x].control_row[2] = s[x].control_row[1];
5044 d[x].control_row[3] = s[x].control_row[0];
5046 src_row -= src_pitch;
5047 dst_row += dst_pitch;
5049 return WINED3D_OK;
5051 default:
5052 FIXME("Compressed flip not implemented for format %s.\n",
5053 debug_d3dformat(format->id));
5054 return E_NOTIMPL;
5058 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5059 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5061 return E_NOTIMPL;
5064 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5065 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5066 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5068 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5069 const struct wined3d_format *src_format, *dst_format;
5070 struct wined3d_texture *src_texture = NULL;
5071 struct wined3d_map_desc dst_map, src_map;
5072 const BYTE *sbase = NULL;
5073 HRESULT hr = WINED3D_OK;
5074 const BYTE *sbuf;
5075 BYTE *dbuf;
5076 int x, y;
5078 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5079 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5080 flags, fx, debug_d3dtexturefiltertype(filter));
5082 if (src_surface == dst_surface)
5084 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5085 src_map = dst_map;
5086 src_format = dst_surface->resource.format;
5087 dst_format = src_format;
5089 else
5091 dst_format = dst_surface->resource.format;
5092 if (src_surface)
5094 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5096 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5098 /* The conv function writes a FIXME */
5099 WARN("Cannot convert source surface format to dest format.\n");
5100 goto release;
5102 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5104 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5105 src_format = src_surface->resource.format;
5107 else
5109 src_format = dst_format;
5112 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5115 bpp = dst_surface->resource.format->byte_count;
5116 srcheight = src_rect->bottom - src_rect->top;
5117 srcwidth = src_rect->right - src_rect->left;
5118 dstheight = dst_rect->bottom - dst_rect->top;
5119 dstwidth = dst_rect->right - dst_rect->left;
5120 width = (dst_rect->right - dst_rect->left) * bpp;
5122 if (src_surface)
5123 sbase = (BYTE *)src_map.data
5124 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5125 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5126 if (src_surface != dst_surface)
5127 dbuf = dst_map.data;
5128 else
5129 dbuf = (BYTE *)dst_map.data
5130 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5131 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5133 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5135 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5137 if (src_surface == dst_surface)
5139 FIXME("Only plain blits supported on compressed surfaces.\n");
5140 hr = E_NOTIMPL;
5141 goto release;
5144 if (srcheight != dstheight || srcwidth != dstwidth)
5146 WARN("Stretching not supported on compressed surfaces.\n");
5147 hr = WINED3DERR_INVALIDCALL;
5148 goto release;
5151 if (!surface_check_block_align(src_surface, src_rect))
5153 WARN("Source rectangle not block-aligned.\n");
5154 hr = WINED3DERR_INVALIDCALL;
5155 goto release;
5158 if (!surface_check_block_align(dst_surface, dst_rect))
5160 WARN("Destination rectangle not block-aligned.\n");
5161 hr = WINED3DERR_INVALIDCALL;
5162 goto release;
5165 hr = surface_cpu_blt_compressed(sbase, dbuf,
5166 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5167 src_format, flags, fx);
5168 goto release;
5171 /* First, all the 'source-less' blits */
5172 if (flags & WINEDDBLT_COLORFILL)
5174 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5175 flags &= ~WINEDDBLT_COLORFILL;
5178 if (flags & WINEDDBLT_DEPTHFILL)
5180 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5182 if (flags & WINEDDBLT_ROP)
5184 /* Catch some degenerate cases here. */
5185 switch (fx->dwROP)
5187 case BLACKNESS:
5188 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5189 break;
5190 case 0xaa0029: /* No-op */
5191 break;
5192 case WHITENESS:
5193 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5194 break;
5195 case SRCCOPY: /* Well, we do that below? */
5196 break;
5197 default:
5198 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5199 goto error;
5201 flags &= ~WINEDDBLT_ROP;
5203 if (flags & WINEDDBLT_DDROPS)
5205 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5207 /* Now the 'with source' blits. */
5208 if (src_surface)
5210 int sx, xinc, sy, yinc;
5212 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5213 goto release;
5215 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5216 && (srcwidth != dstwidth || srcheight != dstheight))
5218 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5219 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5222 xinc = (srcwidth << 16) / dstwidth;
5223 yinc = (srcheight << 16) / dstheight;
5225 if (!flags)
5227 /* No effects, we can cheat here. */
5228 if (dstwidth == srcwidth)
5230 if (dstheight == srcheight)
5232 /* No stretching in either direction. This needs to be as
5233 * fast as possible. */
5234 sbuf = sbase;
5236 /* Check for overlapping surfaces. */
5237 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5238 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5240 /* No overlap, or dst above src, so copy from top downwards. */
5241 for (y = 0; y < dstheight; ++y)
5243 memcpy(dbuf, sbuf, width);
5244 sbuf += src_map.row_pitch;
5245 dbuf += dst_map.row_pitch;
5248 else if (dst_rect->top > src_rect->top)
5250 /* Copy from bottom upwards. */
5251 sbuf += src_map.row_pitch * dstheight;
5252 dbuf += dst_map.row_pitch * dstheight;
5253 for (y = 0; y < dstheight; ++y)
5255 sbuf -= src_map.row_pitch;
5256 dbuf -= dst_map.row_pitch;
5257 memcpy(dbuf, sbuf, width);
5260 else
5262 /* Src and dst overlapping on the same line, use memmove. */
5263 for (y = 0; y < dstheight; ++y)
5265 memmove(dbuf, sbuf, width);
5266 sbuf += src_map.row_pitch;
5267 dbuf += dst_map.row_pitch;
5271 else
5273 /* Stretching in y direction only. */
5274 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5276 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5277 memcpy(dbuf, sbuf, width);
5278 dbuf += dst_map.row_pitch;
5282 else
5284 /* Stretching in X direction. */
5285 int last_sy = -1;
5286 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5288 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5290 if ((sy >> 16) == (last_sy >> 16))
5292 /* This source row is the same as last source row -
5293 * Copy the already stretched row. */
5294 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5296 else
5298 #define STRETCH_ROW(type) \
5299 do { \
5300 const type *s = (const type *)sbuf; \
5301 type *d = (type *)dbuf; \
5302 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5303 d[x] = s[sx >> 16]; \
5304 } while(0)
5306 switch(bpp)
5308 case 1:
5309 STRETCH_ROW(BYTE);
5310 break;
5311 case 2:
5312 STRETCH_ROW(WORD);
5313 break;
5314 case 4:
5315 STRETCH_ROW(DWORD);
5316 break;
5317 case 3:
5319 const BYTE *s;
5320 BYTE *d = dbuf;
5321 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5323 DWORD pixel;
5325 s = sbuf + 3 * (sx >> 16);
5326 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5327 d[0] = (pixel ) & 0xff;
5328 d[1] = (pixel >> 8) & 0xff;
5329 d[2] = (pixel >> 16) & 0xff;
5330 d += 3;
5332 break;
5334 default:
5335 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5336 hr = WINED3DERR_NOTAVAILABLE;
5337 goto error;
5339 #undef STRETCH_ROW
5341 dbuf += dst_map.row_pitch;
5342 last_sy = sy;
5346 else
5348 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5349 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5350 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5351 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5353 /* The color keying flags are checked for correctness in ddraw */
5354 if (flags & WINEDDBLT_KEYSRC)
5356 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5357 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5359 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5361 keylow = fx->ddckSrcColorkey.color_space_low_value;
5362 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5365 if (flags & WINEDDBLT_KEYDEST)
5367 /* Destination color keys are taken from the source surface! */
5368 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5369 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5371 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5373 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5374 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5377 if (bpp == 1)
5379 keymask = 0xff;
5381 else
5383 DWORD masks[3];
5384 get_color_masks(src_format, masks);
5385 keymask = masks[0]
5386 | masks[1]
5387 | masks[2];
5389 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5392 if (flags & WINEDDBLT_DDFX)
5394 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5395 LONG tmpxy;
5396 dTopLeft = dbuf;
5397 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5398 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5399 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5401 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5403 /* I don't think we need to do anything about this flag */
5404 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5406 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5408 tmp = dTopRight;
5409 dTopRight = dTopLeft;
5410 dTopLeft = tmp;
5411 tmp = dBottomRight;
5412 dBottomRight = dBottomLeft;
5413 dBottomLeft = tmp;
5414 dstxinc = dstxinc * -1;
5416 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5418 tmp = dTopLeft;
5419 dTopLeft = dBottomLeft;
5420 dBottomLeft = tmp;
5421 tmp = dTopRight;
5422 dTopRight = dBottomRight;
5423 dBottomRight = tmp;
5424 dstyinc = dstyinc * -1;
5426 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5428 /* I don't think we need to do anything about this flag */
5429 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5431 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5433 tmp = dBottomRight;
5434 dBottomRight = dTopLeft;
5435 dTopLeft = tmp;
5436 tmp = dBottomLeft;
5437 dBottomLeft = dTopRight;
5438 dTopRight = tmp;
5439 dstxinc = dstxinc * -1;
5440 dstyinc = dstyinc * -1;
5442 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5444 tmp = dTopLeft;
5445 dTopLeft = dBottomLeft;
5446 dBottomLeft = dBottomRight;
5447 dBottomRight = dTopRight;
5448 dTopRight = tmp;
5449 tmpxy = dstxinc;
5450 dstxinc = dstyinc;
5451 dstyinc = tmpxy;
5452 dstxinc = dstxinc * -1;
5454 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5456 tmp = dTopLeft;
5457 dTopLeft = dTopRight;
5458 dTopRight = dBottomRight;
5459 dBottomRight = dBottomLeft;
5460 dBottomLeft = tmp;
5461 tmpxy = dstxinc;
5462 dstxinc = dstyinc;
5463 dstyinc = tmpxy;
5464 dstyinc = dstyinc * -1;
5466 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5468 /* I don't think we need to do anything about this flag */
5469 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5471 dbuf = dTopLeft;
5472 flags &= ~(WINEDDBLT_DDFX);
5475 #define COPY_COLORKEY_FX(type) \
5476 do { \
5477 const type *s; \
5478 type *d = (type *)dbuf, *dx, tmp; \
5479 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5481 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5482 dx = d; \
5483 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5485 tmp = s[sx >> 16]; \
5486 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5487 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5489 dx[0] = tmp; \
5491 dx = (type *)(((BYTE *)dx) + dstxinc); \
5493 d = (type *)(((BYTE *)d) + dstyinc); \
5495 } while(0)
5497 switch (bpp)
5499 case 1:
5500 COPY_COLORKEY_FX(BYTE);
5501 break;
5502 case 2:
5503 COPY_COLORKEY_FX(WORD);
5504 break;
5505 case 4:
5506 COPY_COLORKEY_FX(DWORD);
5507 break;
5508 case 3:
5510 const BYTE *s;
5511 BYTE *d = dbuf, *dx;
5512 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5514 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5515 dx = d;
5516 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5518 DWORD pixel, dpixel = 0;
5519 s = sbuf + 3 * (sx>>16);
5520 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5521 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5522 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5523 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5525 dx[0] = (pixel ) & 0xff;
5526 dx[1] = (pixel >> 8) & 0xff;
5527 dx[2] = (pixel >> 16) & 0xff;
5529 dx += dstxinc;
5531 d += dstyinc;
5533 break;
5535 default:
5536 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5537 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5538 hr = WINED3DERR_NOTAVAILABLE;
5539 goto error;
5540 #undef COPY_COLORKEY_FX
5545 error:
5546 if (flags && FIXME_ON(d3d_surface))
5548 FIXME("\tUnsupported flags: %#x.\n", flags);
5551 release:
5552 wined3d_surface_unmap(dst_surface);
5553 if (src_surface && src_surface != dst_surface)
5554 wined3d_surface_unmap(src_surface);
5555 /* Release the converted surface, if any. */
5556 if (src_texture)
5557 wined3d_texture_decref(src_texture);
5559 return hr;
5562 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5563 const RECT *dst_rect, const struct wined3d_color *color)
5565 static const RECT src_rect;
5566 WINEDDBLTFX BltFx;
5568 memset(&BltFx, 0, sizeof(BltFx));
5569 BltFx.dwSize = sizeof(BltFx);
5570 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5571 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5572 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5575 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5576 struct wined3d_surface *surface, const RECT *rect, float depth)
5578 FIXME("Depth filling not implemented by cpu_blit.\n");
5579 return WINED3DERR_INVALIDCALL;
5582 const struct blit_shader cpu_blit = {
5583 cpu_blit_alloc,
5584 cpu_blit_free,
5585 cpu_blit_set,
5586 cpu_blit_unset,
5587 cpu_blit_supported,
5588 cpu_blit_color_fill,
5589 cpu_blit_depth_fill,
5592 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5593 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5594 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5596 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5597 struct wined3d_device *device = dst_surface->resource.device;
5598 DWORD src_ds_flags, dst_ds_flags;
5599 RECT src_rect, dst_rect;
5600 BOOL scale, convert;
5601 enum wined3d_conversion_type dst_convert_type;
5602 struct wined3d_format dst_conv_fmt;
5604 static const DWORD simple_blit = WINEDDBLT_ASYNC
5605 | WINEDDBLT_COLORFILL
5606 | WINEDDBLT_WAIT
5607 | WINEDDBLT_DEPTHFILL
5608 | WINEDDBLT_DONOTWAIT;
5610 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5611 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5612 flags, fx, debug_d3dtexturefiltertype(filter));
5613 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5615 if (fx)
5617 TRACE("dwSize %#x.\n", fx->dwSize);
5618 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5619 TRACE("dwROP %#x.\n", fx->dwROP);
5620 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5621 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5622 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5623 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5624 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5625 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5626 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5627 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5628 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5629 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5630 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5631 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5632 TRACE("dwReserved %#x.\n", fx->dwReserved);
5633 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5634 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5635 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5636 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5637 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5638 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5639 fx->ddckDestColorkey.color_space_low_value,
5640 fx->ddckDestColorkey.color_space_high_value);
5641 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5642 fx->ddckSrcColorkey.color_space_low_value,
5643 fx->ddckSrcColorkey.color_space_high_value);
5646 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5648 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5649 return WINEDDERR_SURFACEBUSY;
5652 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5654 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5655 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5656 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5657 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5658 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5660 WARN("The application gave us a bad destination rectangle.\n");
5661 return WINEDDERR_INVALIDRECT;
5664 if (src_surface)
5666 surface_get_rect(src_surface, src_rect_in, &src_rect);
5668 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5669 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5670 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5671 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5672 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5674 WARN("Application gave us bad source rectangle for Blt.\n");
5675 return WINEDDERR_INVALIDRECT;
5678 else
5680 memset(&src_rect, 0, sizeof(src_rect));
5683 if (!fx || !(fx->dwDDFX))
5684 flags &= ~WINEDDBLT_DDFX;
5686 if (flags & WINEDDBLT_WAIT)
5687 flags &= ~WINEDDBLT_WAIT;
5689 if (flags & WINEDDBLT_ASYNC)
5691 static unsigned int once;
5693 if (!once++)
5694 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5695 flags &= ~WINEDDBLT_ASYNC;
5698 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5699 if (flags & WINEDDBLT_DONOTWAIT)
5701 static unsigned int once;
5703 if (!once++)
5704 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5705 flags &= ~WINEDDBLT_DONOTWAIT;
5708 if (!device->d3d_initialized)
5710 WARN("D3D not initialized, using fallback.\n");
5711 goto cpu;
5714 /* We want to avoid invalidating the sysmem location for converted
5715 * surfaces, since otherwise we'd have to convert the data back when
5716 * locking them. */
5717 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
5718 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5720 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5721 goto cpu;
5724 if (flags & ~simple_blit)
5726 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5727 goto fallback;
5730 if (src_surface)
5731 src_swapchain = src_surface->swapchain;
5732 else
5733 src_swapchain = NULL;
5735 dst_swapchain = dst_surface->swapchain;
5737 /* This isn't strictly needed. FBO blits for example could deal with
5738 * cross-swapchain blits by first downloading the source to a texture
5739 * before switching to the destination context. We just have this here to
5740 * not have to deal with the issue, since cross-swapchain blits should be
5741 * rare. */
5742 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5744 FIXME("Using fallback for cross-swapchain blit.\n");
5745 goto fallback;
5748 scale = src_surface
5749 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5750 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5751 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5753 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5754 if (src_surface)
5755 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5756 else
5757 src_ds_flags = 0;
5759 if (src_ds_flags || dst_ds_flags)
5761 if (flags & WINEDDBLT_DEPTHFILL)
5763 float depth;
5765 TRACE("Depth fill.\n");
5767 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5768 return WINED3DERR_INVALIDCALL;
5770 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5771 return WINED3D_OK;
5773 else
5775 if (src_ds_flags != dst_ds_flags)
5777 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5778 return WINED3DERR_INVALIDCALL;
5781 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->resource.draw_binding, &src_rect,
5782 dst_surface, dst_surface->resource.draw_binding, &dst_rect)))
5783 return WINED3D_OK;
5786 else
5788 /* In principle this would apply to depth blits as well, but we don't
5789 * implement those in the CPU blitter at the moment. */
5790 if ((dst_surface->locations & dst_surface->resource.map_binding)
5791 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5793 if (scale)
5794 TRACE("Not doing sysmem blit because of scaling.\n");
5795 else if (convert)
5796 TRACE("Not doing sysmem blit because of format conversion.\n");
5797 else
5798 goto cpu;
5801 if (flags & WINEDDBLT_COLORFILL)
5803 struct wined3d_color color;
5805 TRACE("Color fill.\n");
5807 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5808 goto fallback;
5810 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5811 return WINED3D_OK;
5813 else
5815 TRACE("Color blit.\n");
5817 /* Upload */
5818 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5819 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5821 if (scale)
5822 TRACE("Not doing upload because of scaling.\n");
5823 else if (convert)
5824 TRACE("Not doing upload because of format conversion.\n");
5825 else
5827 POINT dst_point = {dst_rect.left, dst_rect.top};
5829 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5831 if (!wined3d_resource_is_offscreen(&dst_surface->resource))
5832 surface_load_location(dst_surface, dst_surface->resource.draw_binding);
5833 return WINED3D_OK;
5838 /* Use present for back -> front blits. The idea behind this is
5839 * that present is potentially faster than a blit, in particular
5840 * when FBO blits aren't available. Some ddraw applications like
5841 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5842 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5843 * applications can't blit directly to the frontbuffer. */
5844 if (dst_swapchain && dst_swapchain->back_buffers
5845 && dst_surface == dst_swapchain->front_buffer
5846 && src_surface == dst_swapchain->back_buffers[0])
5848 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5850 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5852 /* Set the swap effect to COPY, we don't want the backbuffer
5853 * to become undefined. */
5854 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5855 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5856 dst_swapchain->desc.swap_effect = swap_effect;
5858 return WINED3D_OK;
5861 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5862 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5863 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5865 TRACE("Using FBO blit.\n");
5867 surface_blt_fbo(device, filter,
5868 src_surface, src_surface->resource.draw_binding, &src_rect,
5869 dst_surface, dst_surface->resource.draw_binding, &dst_rect);
5870 surface_validate_location(dst_surface, dst_surface->resource.draw_binding);
5871 surface_invalidate_location(dst_surface, ~dst_surface->resource.draw_binding);
5873 return WINED3D_OK;
5876 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5877 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5878 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5880 TRACE("Using arbfp blit.\n");
5882 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5883 return WINED3D_OK;
5888 fallback:
5889 /* Special cases for render targets. */
5890 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5891 return WINED3D_OK;
5893 cpu:
5895 /* For the rest call the X11 surface implementation. For render targets
5896 * this should be implemented OpenGL accelerated in surface_blt_special(),
5897 * other blits are rather rare. */
5898 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5901 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5902 const struct wined3d_resource_desc *desc, GLenum target, GLint level, DWORD flags)
5904 struct wined3d_device *device = container->resource.device;
5905 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5906 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5907 UINT multisample_quality = desc->multisample_quality;
5908 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5909 unsigned int resource_size;
5910 HRESULT hr;
5912 if (multisample_quality > 0)
5914 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5915 multisample_quality = 0;
5918 /* Quick lockable sanity check.
5919 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5920 * this function is too deep to need to care about things like this.
5921 * Levels need to be checked too, since they all affect what can be done. */
5922 switch (desc->pool)
5924 case WINED3D_POOL_MANAGED:
5925 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5926 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5927 break;
5929 case WINED3D_POOL_DEFAULT:
5930 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5931 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5932 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5933 break;
5935 case WINED3D_POOL_SCRATCH:
5936 case WINED3D_POOL_SYSTEM_MEM:
5937 break;
5939 default:
5940 FIXME("Unknown pool %#x.\n", desc->pool);
5941 break;
5944 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5945 FIXME("Trying to create a render target that isn't in the default pool.\n");
5947 /* FIXME: Check that the format is supported by the device. */
5949 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5950 if (!resource_size)
5951 return WINED3DERR_INVALIDCALL;
5953 if (device->wined3d->flags & WINED3D_NO3D)
5954 surface->surface_ops = &gdi_surface_ops;
5955 else
5956 surface->surface_ops = &surface_ops;
5958 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5959 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5960 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5962 WARN("Failed to initialize resource, returning %#x.\n", hr);
5963 return hr;
5966 surface->container = container;
5967 wined3d_resource_update_draw_binding(&surface->resource);
5968 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5969 list_init(&surface->renderbuffers);
5970 list_init(&surface->overlays);
5972 /* Flags */
5973 if (target != GL_TEXTURE_RECTANGLE_ARB)
5974 surface->flags |= SFLAG_NORMCOORD;
5975 if (flags & WINED3D_SURFACE_DISCARD)
5976 surface->flags |= SFLAG_DISCARD;
5977 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
5978 surface->flags |= SFLAG_PIN_SYSMEM;
5979 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5980 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5982 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
5983 surface->texture_target = target;
5984 surface->texture_level = level;
5986 /* Call the private setup routine */
5987 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5989 ERR("Private setup failed, hr %#x.\n", hr);
5990 surface_cleanup(surface);
5991 return hr;
5994 /* Similar to lockable rendertargets above, creating the DIB section
5995 * during surface initialization prevents the sysmem pointer from changing
5996 * after a wined3d_surface_getdc() call. */
5997 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5998 && SUCCEEDED(surface_create_dib_section(surface)))
5999 surface->resource.map_binding = WINED3D_LOCATION_DIB;
6001 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
6003 wined3d_resource_free_sysmem(&surface->resource);
6004 surface_validate_location(surface, WINED3D_LOCATION_DIB);
6005 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
6008 return hr;
6011 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
6012 GLenum target, GLint level, DWORD flags, struct wined3d_surface **surface)
6014 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6015 const struct wined3d_parent_ops *parent_ops;
6016 struct wined3d_surface *object;
6017 void *parent;
6018 HRESULT hr;
6020 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
6021 "multisample_type %#x, multisample_quality %u, target %#x, level %d, flags %#x, surface %p.\n",
6022 container, desc->width, desc->height, debug_d3dformat(desc->format),
6023 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6024 desc->multisample_type, desc->multisample_quality, target, level, flags, surface);
6026 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6027 return E_OUTOFMEMORY;
6029 if (FAILED(hr = surface_init(object, container, desc, target, level, flags)))
6031 WARN("Failed to initialize surface, returning %#x.\n", hr);
6032 HeapFree(GetProcessHeap(), 0, object);
6033 return hr;
6036 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6037 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6039 WARN("Failed to create surface parent, hr %#x.\n", hr);
6040 wined3d_surface_destroy(object);
6041 return hr;
6044 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6046 object->resource.parent = parent;
6047 object->resource.parent_ops = parent_ops;
6048 *surface = object;
6050 return hr;