VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine/wined3d/surface.c@ 30916

Last change on this file since 30916 was 30916, checked in by vboxsync, 15 years ago

wddm/3d: Aero: fix gdi-backend textures update, avoid extra memcpy for sysmem textures Lock/Unlock

  • Property svn:eol-style set to native
File size: 189.3 KB
Line 
1/*
2 * IWineD3DSurface Implementation
3 *
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 */
29
30/*
31 * Sun LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
32 * other than GPL or LGPL is available it will apply instead, Sun elects to use only
33 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
34 * a choice of LGPL license versions is made available with the language indicating
35 * that LGPLv2 or any later version may be used, or where a choice of which version
36 * of the LGPL is applied is otherwise unspecified.
37 */
38
39#include "config.h"
40#include "wine/port.h"
41#include "wined3d_private.h"
42
43WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
44WINE_DECLARE_DEBUG_CHANNEL(d3d);
45
46#define GLINFO_LOCATION (*gl_info)
47
48static void surface_cleanup(IWineD3DSurfaceImpl *This)
49{
50 IWineD3DDeviceImpl *device = This->resource.device;
51 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
52 struct wined3d_context *context = NULL;
53 renderbuffer_entry_t *entry, *entry2;
54
55 TRACE("(%p) : Cleaning up.\n", This);
56
57 /* Need a context to destroy the texture. Use the currently active render
58 * target, but only if the primary render target exists. Otherwise
59 * lastActiveRenderTarget is garbage. When destroying the primary render
60 * target, Uninit3D() will activate a context before doing anything. */
61 if (device->render_targets && device->render_targets[0])
62 {
63 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
64 }
65
66 ENTER_GL();
67
68 if (This->texture_name)
69 {
70 /* Release the OpenGL texture. */
71 TRACE("Deleting texture %u.\n", This->texture_name);
72 glDeleteTextures(1, &This->texture_name);
73 }
74
75 if (This->Flags & SFLAG_PBO)
76 {
77 /* Delete the PBO. */
78 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
79 }
80
81 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
82 {
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
84 HeapFree(GetProcessHeap(), 0, entry);
85 }
86
87 LEAVE_GL();
88
89 if (This->Flags & SFLAG_DIBSECTION)
90 {
91 /* Release the DC. */
92 SelectObject(This->hDC, This->dib.holdbitmap);
93 DeleteDC(This->hDC);
94 /* Release the DIB section. */
95 DeleteObject(This->dib.DIBsection);
96 This->dib.bitmap_data = NULL;
97 This->resource.allocatedMemory = NULL;
98 }
99
100 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
101 if (This->overlay_dest) list_remove(&This->overlay_entry);
102
103 HeapFree(GetProcessHeap(), 0, This->palette9);
104
105 resource_cleanup((IWineD3DResource *)This);
106
107 if (context) context_release(context);
108}
109
110UINT surface_calculate_size(const struct wined3d_format_desc *format_desc, UINT alignment, UINT width, UINT height)
111{
112 UINT size;
113
114 if (format_desc->format == WINED3DFMT_UNKNOWN)
115 {
116 size = 0;
117 }
118 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
119 {
120 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
121 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
122 size = row_count * row_block_count * format_desc->block_byte_count;
123 }
124 else
125 {
126 /* The pitch is a multiple of 4 bytes. */
127 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
128 }
129
130 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
131
132 return size;
133}
134
135struct blt_info
136{
137 GLenum binding;
138 GLenum bind_target;
139 enum tex_types tex_type;
140 GLfloat coords[4][3];
141};
142
143struct float_rect
144{
145 float l;
146 float t;
147 float r;
148 float b;
149};
150
151static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
152{
153 f->l = ((r->left * 2.0f) / w) - 1.0f;
154 f->t = ((r->top * 2.0f) / h) - 1.0f;
155 f->r = ((r->right * 2.0f) / w) - 1.0f;
156 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
157}
158
159static void surface_get_blt_info(GLenum target, const RECT *rect_in, GLsizei w, GLsizei h, struct blt_info *info)
160{
161 GLfloat (*coords)[3] = info->coords;
162 RECT rect;
163 struct float_rect f;
164
165 if (rect_in)
166 rect = *rect_in;
167 else
168 {
169 rect.left = 0;
170 rect.top = h;
171 rect.right = w;
172 rect.bottom = 0;
173 }
174
175 switch (target)
176 {
177 default:
178 FIXME("Unsupported texture target %#x\n", target);
179 /* Fall back to GL_TEXTURE_2D */
180 case GL_TEXTURE_2D:
181 info->binding = GL_TEXTURE_BINDING_2D;
182 info->bind_target = GL_TEXTURE_2D;
183 info->tex_type = tex_2d;
184 coords[0][0] = (float)rect.left / w;
185 coords[0][1] = (float)rect.top / h;
186 coords[0][2] = 0.0f;
187
188 coords[1][0] = (float)rect.right / w;
189 coords[1][1] = (float)rect.top / h;
190 coords[1][2] = 0.0f;
191
192 coords[2][0] = (float)rect.left / w;
193 coords[2][1] = (float)rect.bottom / h;
194 coords[2][2] = 0.0f;
195
196 coords[3][0] = (float)rect.right / w;
197 coords[3][1] = (float)rect.bottom / h;
198 coords[3][2] = 0.0f;
199 break;
200
201 case GL_TEXTURE_RECTANGLE_ARB:
202 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
203 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
204 info->tex_type = tex_rect;
205 coords[0][0] = rect.left; coords[0][1] = rect.top; coords[0][2] = 0.0f;
206 coords[1][0] = rect.right; coords[1][1] = rect.top; coords[1][2] = 0.0f;
207 coords[2][0] = rect.left; coords[2][1] = rect.bottom; coords[2][2] = 0.0f;
208 coords[3][0] = rect.right; coords[3][1] = rect.bottom; coords[3][2] = 0.0f;
209 break;
210
211 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
212 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
213 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
214 info->tex_type = tex_cube;
215 cube_coords_float(&rect, w, h, &f);
216
217 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
218 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
219 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
220 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
221 break;
222
223 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
224 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
225 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
226 info->tex_type = tex_cube;
227 cube_coords_float(&rect, w, h, &f);
228
229 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
230 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
231 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
232 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
233 break;
234
235 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
236 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
237 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
238 info->tex_type = tex_cube;
239 cube_coords_float(&rect, w, h, &f);
240
241 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
242 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
243 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
244 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
245 break;
246
247 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
248 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
249 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
250 info->tex_type = tex_cube;
251 cube_coords_float(&rect, w, h, &f);
252
253 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
254 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
255 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
256 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
257 break;
258
259 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
260 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
261 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
262 info->tex_type = tex_cube;
263 cube_coords_float(&rect, w, h, &f);
264
265 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
266 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
267 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
268 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
269 break;
270
271 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
272 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
273 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
274 info->tex_type = tex_cube;
275 cube_coords_float(&rect, w, h, &f);
276
277 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
278 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
279 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
280 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
281 break;
282 }
283}
284
285static inline void surface_get_rect(IWineD3DSurfaceImpl *This, const RECT *rect_in, RECT *rect_out)
286{
287 if (rect_in)
288 *rect_out = *rect_in;
289 else
290 {
291 rect_out->left = 0;
292 rect_out->top = 0;
293 rect_out->right = This->currentDesc.Width;
294 rect_out->bottom = This->currentDesc.Height;
295 }
296}
297
298/* GL locking and context activation is done by the caller */
299void draw_textured_quad(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
300{
301 IWineD3DBaseTextureImpl *texture;
302 struct blt_info info;
303
304 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
305
306 glEnable(info.bind_target);
307 checkGLcall("glEnable(bind_target)");
308
309 /* Bind the texture */
310 glBindTexture(info.bind_target, src_surface->texture_name);
311 checkGLcall("glBindTexture");
312
313 /* Filtering for StretchRect */
314 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
315 wined3d_gl_mag_filter(magLookup, Filter));
316 checkGLcall("glTexParameteri");
317 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
318 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
319 checkGLcall("glTexParameteri");
320 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
321 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
322 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
324
325 /* Draw a quad */
326 glBegin(GL_TRIANGLE_STRIP);
327 glTexCoord3fv(info.coords[0]);
328 glVertex2i(dst_rect->left, dst_rect->top);
329
330 glTexCoord3fv(info.coords[1]);
331 glVertex2i(dst_rect->right, dst_rect->top);
332
333 glTexCoord3fv(info.coords[2]);
334 glVertex2i(dst_rect->left, dst_rect->bottom);
335
336 glTexCoord3fv(info.coords[3]);
337 glVertex2i(dst_rect->right, dst_rect->bottom);
338 glEnd();
339
340 /* Unbind the texture */
341 glBindTexture(info.bind_target, 0);
342 checkGLcall("glBindTexture(info->bind_target, 0)");
343
344 /* We changed the filtering settings on the texture. Inform the
345 * container about this to get the filters reset properly next draw. */
346 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)src_surface, &IID_IWineD3DBaseTexture, (void **)&texture)))
347 {
348 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
349 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
350 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
351 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture);
352 }
353}
354
355HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
356 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
357 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
358 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops
359#ifdef VBOXWDDM
360 , HANDLE *shared_handle
361 , void *pvClientMem
362#endif
363 )
364{
365 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
366 const struct wined3d_format_desc *format_desc = getFormatDescEntry(format, gl_info);
367 void (*cleanup)(IWineD3DSurfaceImpl *This);
368 unsigned int resource_size;
369 HRESULT hr;
370
371 if (multisample_quality > 0)
372 {
373 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
374 multisample_quality = 0;
375 }
376
377 /* FIXME: Check that the format is supported by the device. */
378
379 resource_size = surface_calculate_size(format_desc, alignment, width, height);
380
381 /* Look at the implementation and set the correct Vtable. */
382 switch (surface_type)
383 {
384 case SURFACE_OPENGL:
385 surface->lpVtbl = &IWineD3DSurface_Vtbl;
386 cleanup = surface_cleanup;
387 break;
388
389 case SURFACE_GDI:
390 surface->lpVtbl = &IWineGDISurface_Vtbl;
391 cleanup = surface_gdi_cleanup;
392 break;
393
394 default:
395 ERR("Requested unknown surface implementation %#x.\n", surface_type);
396 return WINED3DERR_INVALIDCALL;
397 }
398
399 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
400 device, resource_size, usage, format_desc, pool, parent, parent_ops
401#ifdef VBOXWDDM
402 , shared_handle
403 , pvClientMem
404#endif
405 );
406 if (FAILED(hr))
407 {
408 WARN("Failed to initialize resource, returning %#x.\n", hr);
409 return hr;
410 }
411
412 /* "Standalone" surface. */
413 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
414
415 surface->currentDesc.Width = width;
416 surface->currentDesc.Height = height;
417 surface->currentDesc.MultiSampleType = multisample_type;
418 surface->currentDesc.MultiSampleQuality = multisample_quality;
419 surface->texture_level = level;
420 list_init(&surface->overlays);
421
422 /* Flags */
423 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
424#ifdef VBOXWDDM
425 if (pool == WINED3DPOOL_SYSTEMMEM && pvClientMem) surface->Flags |= SFLAG_CLIENTMEM;
426#endif
427 if (discard) surface->Flags |= SFLAG_DISCARD;
428 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
429
430 /* Quick lockable sanity check.
431 * TODO: remove this after surfaces, usage and lockability have been debugged properly
432 * this function is too deep to need to care about things like this.
433 * Levels need to be checked too, since they all affect what can be done. */
434 switch (pool)
435 {
436 case WINED3DPOOL_SCRATCH:
437 if(!lockable)
438 {
439 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
440 "which are mutually exclusive, setting lockable to TRUE.\n");
441 lockable = TRUE;
442 }
443 break;
444
445 case WINED3DPOOL_SYSTEMMEM:
446 if (!lockable)
447 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
448 break;
449
450 case WINED3DPOOL_MANAGED:
451 if (usage & WINED3DUSAGE_DYNAMIC)
452 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
453 break;
454
455 case WINED3DPOOL_DEFAULT:
456 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
457 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
458 break;
459
460 default:
461 FIXME("Unknown pool %#x.\n", pool);
462 break;
463 };
464
465 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
466 {
467 FIXME("Trying to create a render target that isn't in the default pool.\n");
468 }
469
470 /* Mark the texture as dirty so that it gets loaded first time around. */
471 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
472 list_init(&surface->renderbuffers);
473
474 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
475
476 /* Call the private setup routine */
477 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
478 if (FAILED(hr))
479 {
480 ERR("Private setup failed, returning %#x\n", hr);
481 cleanup(surface);
482 return hr;
483 }
484
485 return hr;
486}
487
488static void surface_force_reload(IWineD3DSurface *iface)
489{
490 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
491
492 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
493}
494
495void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
496{
497 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
498 GLuint *name;
499 DWORD flag;
500
501 if(srgb)
502 {
503 name = &This->texture_name_srgb;
504 flag = SFLAG_INSRGBTEX;
505 }
506 else
507 {
508 name = &This->texture_name;
509 flag = SFLAG_INTEXTURE;
510 }
511
512 TRACE("(%p) : setting texture name %u\n", This, new_name);
513
514 if (!*name && new_name)
515 {
516 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
517 * surface has no texture name yet. See if we can get rid of this. */
518 if (This->Flags & flag)
519 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
520 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
521 }
522
523 *name = new_name;
524 surface_force_reload(iface);
525}
526
527void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
528{
529 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
530
531 TRACE("(%p) : setting target %#x\n", This, target);
532
533 if (This->texture_target != target)
534 {
535 if (target == GL_TEXTURE_RECTANGLE_ARB)
536 {
537 This->Flags &= ~SFLAG_NORMCOORD;
538 }
539 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
540 {
541 This->Flags |= SFLAG_NORMCOORD;
542 }
543 }
544 This->texture_target = target;
545 surface_force_reload(iface);
546}
547
548/* Context activation is done by the caller. */
549static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
550 DWORD active_sampler;
551
552 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
553 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
554 * gl states. The current texture unit should always be a valid one.
555 *
556 * To be more specific, this is tricky because we can implicitly be called
557 * from sampler() in state.c. This means we can't touch anything other than
558 * whatever happens to be the currently active texture, or we would risk
559 * marking already applied sampler states dirty again.
560 *
561 * TODO: Track the current active texture per GL context instead of using glGet
562 */
563 GLint active_texture=GL_TEXTURE0_ARB;
564 ENTER_GL();
565 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
566 LEAVE_GL();
567 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
568
569 if (active_sampler != WINED3D_UNMAPPED_STAGE)
570 {
571 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
572 }
573 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
574}
575
576/* This function checks if the primary render target uses the 8bit paletted format. */
577static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
578{
579 if (device->render_targets && device->render_targets[0]) {
580 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
581 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
582 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
583 return TRUE;
584 }
585 return FALSE;
586}
587
588/* This call just downloads data, the caller is responsible for binding the
589 * correct texture. */
590/* Context activation is done by the caller. */
591static void surface_download_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
592{
593 const struct wined3d_format_desc *format_desc = This->resource.format_desc;
594
595 /* Only support read back of converted P8 surfaces */
596 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
597 {
598 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
599 return;
600 }
601
602 ENTER_GL();
603
604 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
605 {
606 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
607 This, This->texture_level, format_desc->glFormat, format_desc->glType,
608 This->resource.allocatedMemory);
609
610 if (This->Flags & SFLAG_PBO)
611 {
612 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
613 checkGLcall("glBindBufferARB");
614 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
615 checkGLcall("glGetCompressedTexImageARB");
616 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
617 checkGLcall("glBindBufferARB");
618 }
619 else
620 {
621 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
622 This->texture_level, This->resource.allocatedMemory));
623 checkGLcall("glGetCompressedTexImageARB");
624 }
625
626 LEAVE_GL();
627 } else {
628 void *mem;
629 GLenum format = format_desc->glFormat;
630 GLenum type = format_desc->glType;
631 int src_pitch = 0;
632 int dst_pitch = 0;
633
634 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
635 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
636 {
637 format = GL_ALPHA;
638 type = GL_UNSIGNED_BYTE;
639 }
640
641 if (This->Flags & SFLAG_NONPOW2) {
642 unsigned char alignment = This->resource.device->surface_alignment;
643 src_pitch = format_desc->byte_count * This->pow2Width;
644 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
645 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
646 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
647 } else {
648 mem = This->resource.allocatedMemory;
649 }
650
651 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
652 This, This->texture_level, format, type, mem);
653
654 if(This->Flags & SFLAG_PBO) {
655 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
656 checkGLcall("glBindBufferARB");
657
658 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
659 checkGLcall("glGetTexImage");
660
661 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
662 checkGLcall("glBindBufferARB");
663 } else {
664 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
665 checkGLcall("glGetTexImage");
666 }
667 LEAVE_GL();
668
669 if (This->Flags & SFLAG_NONPOW2) {
670 const BYTE *src_data;
671 BYTE *dst_data;
672 UINT y;
673 /*
674 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
675 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
676 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
677 *
678 * We're doing this...
679 *
680 * instead of boxing the texture :
681 * |<-texture width ->| -->pow2width| /\
682 * |111111111111111111| | |
683 * |222 Texture 222222| boxed empty | texture height
684 * |3333 Data 33333333| | |
685 * |444444444444444444| | \/
686 * ----------------------------------- |
687 * | boxed empty | boxed empty | pow2height
688 * | | | \/
689 * -----------------------------------
690 *
691 *
692 * we're repacking the data to the expected texture width
693 *
694 * |<-texture width ->| -->pow2width| /\
695 * |111111111111111111222222222222222| |
696 * |222333333333333333333444444444444| texture height
697 * |444444 | |
698 * | | \/
699 * | | |
700 * | empty | pow2height
701 * | | \/
702 * -----------------------------------
703 *
704 * == is the same as
705 *
706 * |<-texture width ->| /\
707 * |111111111111111111|
708 * |222222222222222222|texture height
709 * |333333333333333333|
710 * |444444444444444444| \/
711 * --------------------
712 *
713 * this also means that any references to allocatedMemory should work with the data as if were a
714 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
715 *
716 * internally the texture is still stored in a boxed format so any references to textureName will
717 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
718 *
719 * Performance should not be an issue, because applications normally do not lock the surfaces when
720 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
721 * and doesn't have to be re-read.
722 */
723 src_data = mem;
724 dst_data = This->resource.allocatedMemory;
725 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
726 for (y = 1 ; y < This->currentDesc.Height; y++) {
727 /* skip the first row */
728 src_data += src_pitch;
729 dst_data += dst_pitch;
730 memcpy(dst_data, src_data, dst_pitch);
731 }
732
733 HeapFree(GetProcessHeap(), 0, mem);
734 }
735 }
736
737 /* Surface has now been downloaded */
738 This->Flags |= SFLAG_INSYSMEM;
739}
740
741/* This call just uploads data, the caller is responsible for binding the
742 * correct texture. */
743/* Context activation is done by the caller. */
744static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
745 const struct wined3d_format_desc *format_desc, BOOL srgb, const GLvoid *data)
746{
747 GLsizei width = This->currentDesc.Width;
748 GLsizei height = This->currentDesc.Height;
749 GLenum internal;
750
751 if (srgb)
752 {
753 internal = format_desc->glGammaInternal;
754 }
755 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
756 && surface_is_offscreen((IWineD3DSurface *)This))
757 {
758 internal = format_desc->rtInternal;
759 }
760 else
761 {
762 internal = format_desc->glInternal;
763 }
764
765 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
766 This, internal, width, height, format_desc->glFormat, format_desc->glType, data);
767 TRACE("target %#x, level %u, resource size %u.\n",
768 This->texture_target, This->texture_level, This->resource.size);
769
770 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
771
772 ENTER_GL();
773
774 if (This->Flags & SFLAG_PBO)
775 {
776 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
777 checkGLcall("glBindBufferARB");
778
779 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
780 data = NULL;
781 }
782
783 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
784 {
785 TRACE("Calling glCompressedTexSubImage2DARB.\n");
786
787 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
788 0, 0, width, height, internal, This->resource.size, data));
789 checkGLcall("glCompressedTexSubImage2DARB");
790 }
791 else
792 {
793 TRACE("Calling glTexSubImage2D.\n");
794
795 glTexSubImage2D(This->texture_target, This->texture_level,
796 0, 0, width, height, format_desc->glFormat, format_desc->glType, data);
797 checkGLcall("glTexSubImage2D");
798 }
799
800 if (This->Flags & SFLAG_PBO)
801 {
802 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
803 checkGLcall("glBindBufferARB");
804 }
805
806 LEAVE_GL();
807
808 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
809 {
810 IWineD3DDeviceImpl *device = This->resource.device;
811 unsigned int i;
812
813 for (i = 0; i < device->numContexts; ++i)
814 {
815 context_surface_update(device->contexts[i], This);
816 }
817 }
818}
819
820/* This call just allocates the texture, the caller is responsible for binding
821 * the correct texture. */
822/* Context activation is done by the caller. */
823static void surface_allocate_surface(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
824 const struct wined3d_format_desc *format_desc, BOOL srgb)
825{
826 BOOL enable_client_storage = FALSE;
827 GLsizei width = This->pow2Width;
828 GLsizei height = This->pow2Height;
829 const BYTE *mem = NULL;
830 GLenum internal;
831
832 if (srgb)
833 {
834 internal = format_desc->glGammaInternal;
835 }
836 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
837 && surface_is_offscreen((IWineD3DSurface *)This))
838 {
839 internal = format_desc->rtInternal;
840 }
841 else
842 {
843 internal = format_desc->glInternal;
844 }
845
846 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
847
848 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",
849 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
850 internal, width, height, format_desc->glFormat, format_desc->glType);
851
852 ENTER_GL();
853
854 if (gl_info->supported[APPLE_CLIENT_STORAGE])
855 {
856 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
857 /* In some cases we want to disable client storage.
858 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
859 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
860 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
861 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
862 */
863 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
864 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
865 This->Flags &= ~SFLAG_CLIENT;
866 enable_client_storage = TRUE;
867 } else {
868 This->Flags |= SFLAG_CLIENT;
869
870 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
871 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
872 */
873 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
874 }
875 }
876
877 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
878 {
879 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
880 internal, width, height, 0, This->resource.size, mem));
881 }
882 else
883 {
884 glTexImage2D(This->texture_target, This->texture_level,
885 internal, width, height, 0, format_desc->glFormat, format_desc->glType, mem);
886 checkGLcall("glTexImage2D");
887 }
888
889 if(enable_client_storage) {
890 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
891 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
892 }
893 LEAVE_GL();
894}
895
896/* In D3D the depth stencil dimensions have to be greater than or equal to the
897 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
898/* TODO: We should synchronize the renderbuffer's content with the texture's content. */
899/* GL locking is done by the caller */
900void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
901 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
902 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
903 renderbuffer_entry_t *entry;
904 GLuint renderbuffer = 0;
905 unsigned int src_width, src_height;
906
907 src_width = This->pow2Width;
908 src_height = This->pow2Height;
909
910 /* A depth stencil smaller than the render target is not valid */
911 if (width > src_width || height > src_height) return;
912
913 /* Remove any renderbuffer set if the sizes match */
914 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
915 || (width == src_width && height == src_height))
916 {
917 This->current_renderbuffer = NULL;
918 return;
919 }
920
921 /* Look if we've already got a renderbuffer of the correct dimensions */
922 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
923 if (entry->width == width && entry->height == height) {
924 renderbuffer = entry->id;
925 This->current_renderbuffer = entry;
926 break;
927 }
928 }
929
930 if (!renderbuffer) {
931 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
932 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
933 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
934 This->resource.format_desc->glInternal, width, height);
935
936 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
937 entry->width = width;
938 entry->height = height;
939 entry->id = renderbuffer;
940 list_add_head(&This->renderbuffers, &entry->entry);
941
942 This->current_renderbuffer = entry;
943 }
944
945 checkGLcall("set_compatible_renderbuffer");
946}
947
948GLenum surface_get_gl_buffer(IWineD3DSurface *iface)
949{
950 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
951 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->container;
952
953 TRACE("iface %p.\n", iface);
954
955 if (!(This->Flags & SFLAG_SWAPCHAIN))
956 {
957 ERR("Surface %p is not on a swapchain.\n", iface);
958 return GL_NONE;
959 }
960
961 if (swapchain->backBuffer && swapchain->backBuffer[0] == iface)
962 {
963 if (swapchain->render_to_fbo)
964 {
965 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
966 return GL_COLOR_ATTACHMENT0;
967 }
968 TRACE("Returning GL_BACK\n");
969 return GL_BACK;
970 }
971 else if (swapchain->frontBuffer == iface)
972 {
973 TRACE("Returning GL_FRONT\n");
974 return GL_FRONT;
975 }
976
977 FIXME("Higher back buffer, returning GL_BACK\n");
978 return GL_BACK;
979}
980
981/* Slightly inefficient way to handle multiple dirty rects but it works :) */
982void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
983{
984 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
985 IWineD3DBaseTexture *baseTexture = NULL;
986
987 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
988 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
989
990 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
991 if (dirty_rect)
992 {
993 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
994 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
995 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
996 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
997 }
998 else
999 {
1000 This->dirtyRect.left = 0;
1001 This->dirtyRect.top = 0;
1002 This->dirtyRect.right = This->currentDesc.Width;
1003 This->dirtyRect.bottom = This->currentDesc.Height;
1004 }
1005
1006 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
1007 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
1008
1009 /* if the container is a basetexture then mark it dirty. */
1010 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
1011 {
1012 TRACE("Passing to container\n");
1013 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
1014 IWineD3DBaseTexture_Release(baseTexture);
1015 }
1016}
1017
1018static BOOL surface_convert_color_to_argb(IWineD3DSurfaceImpl *This, DWORD color, DWORD *argb_color)
1019{
1020 IWineD3DDeviceImpl *device = This->resource.device;
1021
1022 switch(This->resource.format_desc->format)
1023 {
1024 case WINED3DFMT_P8_UINT:
1025 {
1026 DWORD alpha;
1027
1028 if (primary_render_target_is_p8(device))
1029 alpha = color << 24;
1030 else
1031 alpha = 0xFF000000;
1032
1033 if (This->palette) {
1034 *argb_color = (alpha |
1035 (This->palette->palents[color].peRed << 16) |
1036 (This->palette->palents[color].peGreen << 8) |
1037 (This->palette->palents[color].peBlue));
1038 } else {
1039 *argb_color = alpha;
1040 }
1041 }
1042 break;
1043
1044 case WINED3DFMT_B5G6R5_UNORM:
1045 {
1046 if (color == 0xFFFF) {
1047 *argb_color = 0xFFFFFFFF;
1048 } else {
1049 *argb_color = ((0xFF000000) |
1050 ((color & 0xF800) << 8) |
1051 ((color & 0x07E0) << 5) |
1052 ((color & 0x001F) << 3));
1053 }
1054 }
1055 break;
1056
1057 case WINED3DFMT_B8G8R8_UNORM:
1058 case WINED3DFMT_B8G8R8X8_UNORM:
1059 *argb_color = 0xFF000000 | color;
1060 break;
1061
1062 case WINED3DFMT_B8G8R8A8_UNORM:
1063 *argb_color = color;
1064 break;
1065
1066 default:
1067 ERR("Unhandled conversion from %s to ARGB!\n", debug_d3dformat(This->resource.format_desc->format));
1068 return FALSE;
1069 }
1070 return TRUE;
1071}
1072
1073static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
1074{
1075 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1076 ULONG ref = InterlockedDecrement(&This->resource.ref);
1077 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
1078
1079 if (!ref)
1080 {
1081 surface_cleanup(This);
1082 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
1083
1084 TRACE("(%p) Released.\n", This);
1085 HeapFree(GetProcessHeap(), 0, This);
1086 }
1087
1088 return ref;
1089}
1090
1091/* ****************************************************
1092 IWineD3DSurface IWineD3DResource parts follow
1093 **************************************************** */
1094
1095void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
1096{
1097 /* TODO: check for locks */
1098 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1099 IWineD3DDeviceImpl *device = This->resource.device;
1100 IWineD3DBaseTexture *baseTexture = NULL;
1101
1102 TRACE("(%p)Checking to see if the container is a base texture\n", This);
1103 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
1104 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
1105 TRACE("Passing to container\n");
1106 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
1107 IWineD3DBaseTexture_Release(baseTexture);
1108 } else {
1109 struct wined3d_context *context = NULL;
1110
1111 TRACE("(%p) : About to load surface\n", This);
1112
1113 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1114
1115 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1116 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1117 {
1118 if(palette9_changed(This)) {
1119 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1120 /* TODO: This is not necessarily needed with hw palettized texture support */
1121 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1122 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
1123 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1124 }
1125 }
1126
1127 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
1128
1129 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
1130 /* Tell opengl to try and keep this texture in video ram (well mostly) */
1131 GLclampf tmp;
1132 tmp = 0.9f;
1133 ENTER_GL();
1134 glPrioritizeTextures(1, &This->texture_name, &tmp);
1135 LEAVE_GL();
1136 }
1137
1138 if (context) context_release(context);
1139 }
1140}
1141
1142static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
1143 surface_internal_preload(iface, SRGB_ANY);
1144}
1145
1146/* Context activation is done by the caller. */
1147static void surface_remove_pbo(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
1148{
1149 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1150 This->resource.allocatedMemory =
1151 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1152
1153 ENTER_GL();
1154 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1155 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
1156 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
1157 checkGLcall("glGetBufferSubDataARB");
1158 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
1159 checkGLcall("glDeleteBuffersARB");
1160 LEAVE_GL();
1161
1162 This->pbo = 0;
1163 This->Flags &= ~SFLAG_PBO;
1164}
1165
1166BOOL surface_init_sysmem(IWineD3DSurface *iface)
1167{
1168 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1169
1170 if(!This->resource.allocatedMemory)
1171 {
1172 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
1173 if(!This->resource.heapMemory)
1174 {
1175 ERR("Out of memory\n");
1176 return FALSE;
1177 }
1178 This->resource.allocatedMemory =
1179 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1180 }
1181 else
1182 {
1183 memset(This->resource.allocatedMemory, 0, This->resource.size);
1184 }
1185
1186 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
1187 return TRUE;
1188}
1189
1190static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
1191 IWineD3DBaseTexture *texture = NULL;
1192 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1193 IWineD3DDeviceImpl *device = This->resource.device;
1194 const struct wined3d_gl_info *gl_info;
1195 renderbuffer_entry_t *entry, *entry2;
1196 struct wined3d_context *context;
1197
1198 TRACE("(%p)\n", iface);
1199
1200 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
1201 /* Default pool resources are supposed to be destroyed before Reset is called.
1202 * Implicit resources stay however. So this means we have an implicit render target
1203 * or depth stencil. The content may be destroyed, but we still have to tear down
1204 * opengl resources, so we cannot leave early.
1205 *
1206 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1207 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1208 * or the depth stencil into an FBO the texture or render buffer will be removed
1209 * and all flags get lost
1210 */
1211 surface_init_sysmem(iface);
1212 } else {
1213 /* Load the surface into system memory */
1214 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1215 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
1216 }
1217 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1218 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1219 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1220
1221 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1222 gl_info = context->gl_info;
1223
1224 /* Destroy PBOs, but load them into real sysmem before */
1225 if (This->Flags & SFLAG_PBO)
1226 surface_remove_pbo(This, gl_info);
1227
1228 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1229 * all application-created targets the application has to release the surface
1230 * before calling _Reset
1231 */
1232 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
1233 ENTER_GL();
1234 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1235 LEAVE_GL();
1236 list_remove(&entry->entry);
1237 HeapFree(GetProcessHeap(), 0, entry);
1238 }
1239 list_init(&This->renderbuffers);
1240 This->current_renderbuffer = NULL;
1241
1242 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1243 * destroy it
1244 */
1245 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
1246 if(!texture) {
1247 ENTER_GL();
1248 glDeleteTextures(1, &This->texture_name);
1249 This->texture_name = 0;
1250 glDeleteTextures(1, &This->texture_name_srgb);
1251 This->texture_name_srgb = 0;
1252 LEAVE_GL();
1253 } else {
1254 IWineD3DBaseTexture_Release(texture);
1255 }
1256
1257 context_release(context);
1258}
1259
1260/* ******************************************************
1261 IWineD3DSurface IWineD3DSurface parts follow
1262 ****************************************************** */
1263
1264/* Read the framebuffer back into the surface */
1265static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
1266{
1267 IWineD3DDeviceImpl *myDevice = This->resource.device;
1268 const struct wined3d_gl_info *gl_info;
1269 struct wined3d_context *context;
1270 BYTE *mem;
1271 GLint fmt;
1272 GLint type;
1273 BYTE *row, *top, *bottom;
1274 int i;
1275 BOOL bpp;
1276 RECT local_rect;
1277 BOOL srcIsUpsideDown;
1278 GLint rowLen = 0;
1279 GLint skipPix = 0;
1280 GLint skipRow = 0;
1281
1282 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1283 static BOOL warned = FALSE;
1284 if(!warned) {
1285 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1286 warned = TRUE;
1287 }
1288 return;
1289 }
1290
1291 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1292 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1293 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1294 * context->last_was_blit set on the unlock.
1295 */
1296 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1297 gl_info = context->gl_info;
1298
1299 ENTER_GL();
1300
1301 /* Select the correct read buffer, and give some debug output.
1302 * There is no need to keep track of the current read buffer or reset it, every part of the code
1303 * that reads sets the read buffer as desired.
1304 */
1305 if (surface_is_offscreen((IWineD3DSurface *) This))
1306 {
1307 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1308 * Read from the back buffer
1309 */
1310 TRACE("Locking offscreen render target\n");
1311 glReadBuffer(myDevice->offscreenBuffer);
1312 srcIsUpsideDown = TRUE;
1313 }
1314 else
1315 {
1316 /* Onscreen surfaces are always part of a swapchain */
1317 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1318 TRACE("Locking %#x buffer\n", buffer);
1319 glReadBuffer(buffer);
1320 checkGLcall("glReadBuffer");
1321 srcIsUpsideDown = FALSE;
1322 }
1323
1324 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1325 if(!rect) {
1326 local_rect.left = 0;
1327 local_rect.top = 0;
1328 local_rect.right = This->currentDesc.Width;
1329 local_rect.bottom = This->currentDesc.Height;
1330 } else {
1331 local_rect = *rect;
1332 }
1333 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1334
1335 switch(This->resource.format_desc->format)
1336 {
1337 case WINED3DFMT_P8_UINT:
1338 {
1339 if(primary_render_target_is_p8(myDevice)) {
1340 /* In case of P8 render targets the index is stored in the alpha component */
1341 fmt = GL_ALPHA;
1342 type = GL_UNSIGNED_BYTE;
1343 mem = dest;
1344 bpp = This->resource.format_desc->byte_count;
1345 } else {
1346 /* GL can't return palettized data, so read ARGB pixels into a
1347 * separate block of memory and convert them into palettized format
1348 * in software. Slow, but if the app means to use palettized render
1349 * targets and locks it...
1350 *
1351 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1352 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1353 * for the color channels when palettizing the colors.
1354 */
1355 fmt = GL_RGB;
1356 type = GL_UNSIGNED_BYTE;
1357 pitch *= 3;
1358 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1359 if(!mem) {
1360 ERR("Out of memory\n");
1361 LEAVE_GL();
1362 return;
1363 }
1364 bpp = This->resource.format_desc->byte_count * 3;
1365 }
1366 }
1367 break;
1368
1369 default:
1370 mem = dest;
1371 fmt = This->resource.format_desc->glFormat;
1372 type = This->resource.format_desc->glType;
1373 bpp = This->resource.format_desc->byte_count;
1374 }
1375
1376 if(This->Flags & SFLAG_PBO) {
1377 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1378 checkGLcall("glBindBufferARB");
1379 if(mem != NULL) {
1380 ERR("mem not null for pbo -- unexpected\n");
1381 mem = NULL;
1382 }
1383 }
1384
1385 /* Save old pixel store pack state */
1386 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1387 checkGLcall("glGetIntegerv");
1388 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1389 checkGLcall("glGetIntegerv");
1390 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1391 checkGLcall("glGetIntegerv");
1392
1393 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1394 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1395 checkGLcall("glPixelStorei");
1396 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1397 checkGLcall("glPixelStorei");
1398 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1399 checkGLcall("glPixelStorei");
1400
1401 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1402 local_rect.right - local_rect.left,
1403 local_rect.bottom - local_rect.top,
1404 fmt, type, mem);
1405 checkGLcall("glReadPixels");
1406
1407 /* Reset previous pixel store pack state */
1408 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1409 checkGLcall("glPixelStorei");
1410 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1411 checkGLcall("glPixelStorei");
1412 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1413 checkGLcall("glPixelStorei");
1414
1415 if(This->Flags & SFLAG_PBO) {
1416 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1417 checkGLcall("glBindBufferARB");
1418
1419 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1420 * to get a pointer to it and perform the flipping in software. This is a lot
1421 * faster than calling glReadPixels for each line. In case we want more speed
1422 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1423 if(!srcIsUpsideDown) {
1424 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1425 checkGLcall("glBindBufferARB");
1426
1427 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1428 checkGLcall("glMapBufferARB");
1429 }
1430 }
1431
1432 /* TODO: Merge this with the palettization loop below for P8 targets */
1433 if(!srcIsUpsideDown) {
1434 UINT len, off;
1435 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1436 Flip the lines in software */
1437 len = (local_rect.right - local_rect.left) * bpp;
1438 off = local_rect.left * bpp;
1439
1440 row = HeapAlloc(GetProcessHeap(), 0, len);
1441 if(!row) {
1442 ERR("Out of memory\n");
1443 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1444 LEAVE_GL();
1445 return;
1446 }
1447
1448 top = mem + pitch * local_rect.top;
1449 bottom = mem + pitch * (local_rect.bottom - 1);
1450 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1451 memcpy(row, top + off, len);
1452 memcpy(top + off, bottom + off, len);
1453 memcpy(bottom + off, row, len);
1454 top += pitch;
1455 bottom -= pitch;
1456 }
1457 HeapFree(GetProcessHeap(), 0, row);
1458
1459 /* Unmap the temp PBO buffer */
1460 if(This->Flags & SFLAG_PBO) {
1461 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1462 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1463 }
1464 }
1465
1466 LEAVE_GL();
1467 context_release(context);
1468
1469 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1470 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1471 * the same color but we have no choice.
1472 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1473 */
1474 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1475 {
1476 const PALETTEENTRY *pal = NULL;
1477 DWORD width = pitch / 3;
1478 int x, y, c;
1479
1480 if(This->palette) {
1481 pal = This->palette->palents;
1482 } else {
1483 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1484 HeapFree(GetProcessHeap(), 0, mem);
1485 return ;
1486 }
1487
1488 for(y = local_rect.top; y < local_rect.bottom; y++) {
1489 for(x = local_rect.left; x < local_rect.right; x++) {
1490 /* start lines pixels */
1491 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1492 const BYTE *green = blue + 1;
1493 const BYTE *red = green + 1;
1494
1495 for(c = 0; c < 256; c++) {
1496 if(*red == pal[c].peRed &&
1497 *green == pal[c].peGreen &&
1498 *blue == pal[c].peBlue)
1499 {
1500 *((BYTE *) dest + y * width + x) = c;
1501 break;
1502 }
1503 }
1504 }
1505 }
1506 HeapFree(GetProcessHeap(), 0, mem);
1507 }
1508}
1509
1510/* Read the framebuffer contents into a texture */
1511static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1512{
1513 IWineD3DDeviceImpl *device = This->resource.device;
1514 const struct wined3d_gl_info *gl_info;
1515 struct wined3d_context *context;
1516 GLint prevRead;
1517 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1518
1519 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1520 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1521 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1522 */
1523 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1524 gl_info = context->gl_info;
1525
1526 surface_bind_and_dirtify(This, srgb);
1527
1528 ENTER_GL();
1529 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1530 LEAVE_GL();
1531
1532 /* Select the correct read buffer, and give some debug output.
1533 * There is no need to keep track of the current read buffer or reset it, every part of the code
1534 * that reads sets the read buffer as desired.
1535 */
1536 if (!surface_is_offscreen((IWineD3DSurface *)This))
1537 {
1538 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1539 TRACE("Locking %#x buffer\n", buffer);
1540
1541 ENTER_GL();
1542 glReadBuffer(buffer);
1543 checkGLcall("glReadBuffer");
1544 LEAVE_GL();
1545 }
1546 else
1547 {
1548 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1549 * Read from the back buffer
1550 */
1551 TRACE("Locking offscreen render target\n");
1552 ENTER_GL();
1553 glReadBuffer(device->offscreenBuffer);
1554 checkGLcall("glReadBuffer");
1555 LEAVE_GL();
1556 }
1557
1558 if (!(This->Flags & alloc_flag))
1559 {
1560 surface_allocate_surface(This, gl_info, This->resource.format_desc, srgb);
1561 This->Flags |= alloc_flag;
1562 }
1563
1564 ENTER_GL();
1565 /* If !SrcIsUpsideDown we should flip the surface.
1566 * This can be done using glCopyTexSubImage2D but this
1567 * is VERY slow, so don't do that. We should prevent
1568 * this code from getting called in such cases or perhaps
1569 * we can use FBOs */
1570
1571 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1572 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1573 checkGLcall("glCopyTexSubImage2D");
1574
1575 glReadBuffer(prevRead);
1576 checkGLcall("glReadBuffer");
1577
1578 LEAVE_GL();
1579
1580 context_release(context);
1581
1582 TRACE("Updated target %d\n", This->texture_target);
1583}
1584
1585/* Context activation is done by the caller. */
1586void surface_prepare_texture(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1587{
1588 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1589 CONVERT_TYPES convert;
1590 struct wined3d_format_desc desc;
1591
1592 if (surface->Flags & alloc_flag) return;
1593
1594 d3dfmt_get_conv(surface, TRUE, TRUE, &desc, &convert);
1595 if(convert != NO_CONVERSION) surface->Flags |= SFLAG_CONVERTED;
1596 else surface->Flags &= ~SFLAG_CONVERTED;
1597
1598 surface_bind_and_dirtify(surface, srgb);
1599 surface_allocate_surface(surface, gl_info, &desc, srgb);
1600 surface->Flags |= alloc_flag;
1601}
1602
1603static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1604{
1605 IWineD3DDeviceImpl *device = This->resource.device;
1606 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1607
1608 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1609 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1610 * changed
1611 */
1612 if(!(This->Flags & SFLAG_DYNLOCK)) {
1613 This->lockCount++;
1614 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1615 if(This->lockCount > MAXLOCKCOUNT) {
1616 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1617 This->Flags |= SFLAG_DYNLOCK;
1618 }
1619 }
1620
1621 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1622 * Also don't create a PBO for systemmem surfaces.
1623 */
1624 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1625 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1626 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1627 {
1628 GLenum error;
1629 struct wined3d_context *context;
1630
1631 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1632 ENTER_GL();
1633
1634 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1635 error = glGetError();
1636 if(This->pbo == 0 || error != GL_NO_ERROR) {
1637 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1638 }
1639
1640 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1641
1642 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1643 checkGLcall("glBindBufferARB");
1644
1645 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1646 checkGLcall("glBufferDataARB");
1647
1648 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1649 checkGLcall("glBindBufferARB");
1650
1651 /* We don't need the system memory anymore and we can't even use it for PBOs */
1652 if(!(This->Flags & SFLAG_CLIENT)) {
1653 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1654 This->resource.heapMemory = NULL;
1655 }
1656 This->resource.allocatedMemory = NULL;
1657 This->Flags |= SFLAG_PBO;
1658 LEAVE_GL();
1659 context_release(context);
1660 }
1661 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1662 {
1663 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1664 * or a pbo to map
1665 */
1666 if(!This->resource.heapMemory) {
1667 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1668 }
1669 This->resource.allocatedMemory =
1670 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1671 if(This->Flags & SFLAG_INSYSMEM) {
1672 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1673 }
1674 }
1675}
1676
1677static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1678 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1679 IWineD3DDeviceImpl *myDevice = This->resource.device;
1680 const RECT *pass_rect = pRect;
1681
1682 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1683
1684 /* This is also done in the base class, but we have to verify this before loading any data from
1685 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1686 * may interfere, and all other bad things may happen
1687 */
1688 if (This->Flags & SFLAG_LOCKED) {
1689 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1690 return WINED3DERR_INVALIDCALL;
1691 }
1692 This->Flags |= SFLAG_LOCKED;
1693
1694 if (!(This->Flags & SFLAG_LOCKABLE))
1695 {
1696 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1697 }
1698
1699 if (Flags & WINED3DLOCK_DISCARD) {
1700 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1701 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1702 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1703 This->Flags |= SFLAG_INSYSMEM;
1704 goto lock_end;
1705 }
1706
1707 if (This->Flags & SFLAG_INSYSMEM) {
1708 TRACE("Local copy is up to date, not downloading data\n");
1709 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1710 goto lock_end;
1711 }
1712
1713 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1714 * the full surface. Most callers don't need that, so do it here. */
1715 if (pRect && pRect->top == 0 && pRect->left == 0
1716 && pRect->right == This->currentDesc.Width
1717 && pRect->bottom == This->currentDesc.Height)
1718 {
1719 pass_rect = NULL;
1720 }
1721
1722 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1723 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1724 {
1725 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1726 }
1727
1728lock_end:
1729 if (This->Flags & SFLAG_PBO)
1730 {
1731 const struct wined3d_gl_info *gl_info;
1732 struct wined3d_context *context;
1733
1734 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1735 gl_info = context->gl_info;
1736
1737 ENTER_GL();
1738 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1739 checkGLcall("glBindBufferARB");
1740
1741 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1742 if(This->resource.allocatedMemory) {
1743 ERR("The surface already has PBO memory allocated!\n");
1744 }
1745
1746 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1747 checkGLcall("glMapBufferARB");
1748
1749 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1751 checkGLcall("glBindBufferARB");
1752
1753 LEAVE_GL();
1754 context_release(context);
1755 }
1756
1757 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1758 /* Don't dirtify */
1759 } else {
1760 IWineD3DBaseTexture *pBaseTexture;
1761 /**
1762 * Dirtify on lock
1763 * as seen in msdn docs
1764 */
1765 surface_add_dirty_rect(iface, pRect);
1766
1767 /** Dirtify Container if needed */
1768 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1769 TRACE("Making container dirty\n");
1770 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1771 IWineD3DBaseTexture_Release(pBaseTexture);
1772 } else {
1773 TRACE("Surface is standalone, no need to dirty the container\n");
1774 }
1775 }
1776
1777 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1778}
1779
1780static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1781 GLint prev_store;
1782 GLint prev_rasterpos[4];
1783 GLint skipBytes = 0;
1784 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1785 IWineD3DDeviceImpl *myDevice = This->resource.device;
1786 const struct wined3d_gl_info *gl_info;
1787 struct wined3d_context *context;
1788
1789 /* Activate the correct context for the render target */
1790 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1791 gl_info = context->gl_info;
1792
1793 ENTER_GL();
1794
1795 if (!surface_is_offscreen((IWineD3DSurface *)This))
1796 {
1797 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1798 TRACE("Unlocking %#x buffer.\n", buffer);
1799 context_set_draw_buffer(context, buffer);
1800 }
1801 else
1802 {
1803 /* Primary offscreen render target */
1804 TRACE("Offscreen render target.\n");
1805 context_set_draw_buffer(context, myDevice->offscreenBuffer);
1806 }
1807
1808 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1809 checkGLcall("glGetIntegerv");
1810 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1811 checkGLcall("glGetIntegerv");
1812 glPixelZoom(1.0f, -1.0f);
1813 checkGLcall("glPixelZoom");
1814
1815 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1816 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1817 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1818
1819 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1820 checkGLcall("glRasterPos3i");
1821
1822 /* Some drivers(radeon dri, others?) don't like exceptions during
1823 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1824 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1825 * catch to put the dib section in InSync mode, which leads to a crash
1826 * and a blocked x server on my radeon card.
1827 *
1828 * The following lines read the dib section so it is put in InSync mode
1829 * before glDrawPixels is called and the crash is prevented. There won't
1830 * be any interfering gdi accesses, because UnlockRect is called from
1831 * ReleaseDC, and the app won't use the dc any more afterwards.
1832 */
1833 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1834 volatile BYTE read;
1835 read = This->resource.allocatedMemory[0];
1836 }
1837
1838 if(This->Flags & SFLAG_PBO) {
1839 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1840 checkGLcall("glBindBufferARB");
1841 }
1842
1843 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1844 if(This->Flags & SFLAG_LOCKED) {
1845 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1846 (This->lockedRect.bottom - This->lockedRect.top)-1,
1847 fmt, type,
1848 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1849 checkGLcall("glDrawPixels");
1850 } else {
1851 glDrawPixels(This->currentDesc.Width,
1852 This->currentDesc.Height,
1853 fmt, type, mem);
1854 checkGLcall("glDrawPixels");
1855 }
1856
1857 if(This->Flags & SFLAG_PBO) {
1858 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1859 checkGLcall("glBindBufferARB");
1860 }
1861
1862 glPixelZoom(1.0f, 1.0f);
1863 checkGLcall("glPixelZoom");
1864
1865 glRasterPos3iv(&prev_rasterpos[0]);
1866 checkGLcall("glRasterPos3iv");
1867
1868 /* Reset to previous pack row length */
1869 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1870 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1871
1872 LEAVE_GL();
1873 context_release(context);
1874}
1875
1876static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1877 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1878 IWineD3DDeviceImpl *myDevice = This->resource.device;
1879 BOOL fullsurface;
1880
1881 if (!(This->Flags & SFLAG_LOCKED)) {
1882 WARN("trying to Unlock an unlocked surf@%p\n", This);
1883 return WINEDDERR_NOTLOCKED;
1884 }
1885
1886 if (This->Flags & SFLAG_PBO)
1887 {
1888 const struct wined3d_gl_info *gl_info;
1889 struct wined3d_context *context;
1890
1891 TRACE("Freeing PBO memory\n");
1892
1893 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1894 gl_info = context->gl_info;
1895
1896 ENTER_GL();
1897 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1898 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1899 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1900 checkGLcall("glUnmapBufferARB");
1901 LEAVE_GL();
1902 context_release(context);
1903
1904 This->resource.allocatedMemory = NULL;
1905 }
1906
1907 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1908
1909 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1910 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1911 goto unlock_end;
1912 }
1913
1914 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1915 {
1916 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1917 static BOOL warned = FALSE;
1918 if(!warned) {
1919 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1920 warned = TRUE;
1921 }
1922 goto unlock_end;
1923 }
1924
1925 if(This->dirtyRect.left == 0 &&
1926 This->dirtyRect.top == 0 &&
1927 This->dirtyRect.right == This->currentDesc.Width &&
1928 This->dirtyRect.bottom == This->currentDesc.Height) {
1929 fullsurface = TRUE;
1930 } else {
1931 /* TODO: Proper partial rectangle tracking */
1932 fullsurface = FALSE;
1933 This->Flags |= SFLAG_INSYSMEM;
1934 }
1935
1936 switch(wined3d_settings.rendertargetlock_mode) {
1937 case RTL_READTEX:
1938 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1939 /* drop through */
1940
1941 case RTL_READDRAW:
1942 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1943 break;
1944 }
1945
1946 if(!fullsurface) {
1947 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1948 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1949 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1950 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1951 * not fully up to date because only a subrectangle was read in LockRect.
1952 */
1953 This->Flags &= ~SFLAG_INSYSMEM;
1954 This->Flags |= SFLAG_INDRAWABLE;
1955 }
1956
1957 This->dirtyRect.left = This->currentDesc.Width;
1958 This->dirtyRect.top = This->currentDesc.Height;
1959 This->dirtyRect.right = 0;
1960 This->dirtyRect.bottom = 0;
1961 } else if(iface == myDevice->stencilBufferTarget) {
1962 FIXME("Depth Stencil buffer locking is not implemented\n");
1963 } else {
1964 /* The rest should be a normal texture */
1965 IWineD3DBaseTextureImpl *impl;
1966 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1967 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1968 * states need resetting
1969 */
1970 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1971 if(impl->baseTexture.bindCount) {
1972 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1973 }
1974 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1975 }
1976 }
1977
1978 unlock_end:
1979 This->Flags &= ~SFLAG_LOCKED;
1980 memset(&This->lockedRect, 0, sizeof(RECT));
1981
1982 /* Overlays have to be redrawn manually after changes with the GL implementation */
1983 if(This->overlay_dest) {
1984 IWineD3DSurface_DrawOverlay(iface);
1985 }
1986 return WINED3D_OK;
1987}
1988
1989static void surface_release_client_storage(IWineD3DSurface *iface)
1990{
1991 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1992 struct wined3d_context *context;
1993
1994 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
1995
1996 ENTER_GL();
1997 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1998 if(This->texture_name)
1999 {
2000 surface_bind_and_dirtify(This, FALSE);
2001 glTexImage2D(This->texture_target, This->texture_level,
2002 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
2003 }
2004 if(This->texture_name_srgb)
2005 {
2006 surface_bind_and_dirtify(This, TRUE);
2007 glTexImage2D(This->texture_target, This->texture_level,
2008 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
2009 }
2010 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2011
2012 LEAVE_GL();
2013 context_release(context);
2014
2015 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
2016 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
2017 surface_force_reload(iface);
2018}
2019
2020static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
2021{
2022 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2023 WINED3DLOCKED_RECT lock;
2024 HRESULT hr;
2025 RGBQUAD col[256];
2026
2027 TRACE("(%p)->(%p)\n",This,pHDC);
2028
2029 if(This->Flags & SFLAG_USERPTR) {
2030 ERR("Not supported on surfaces with an application-provided surfaces\n");
2031 return WINEDDERR_NODC;
2032 }
2033
2034 /* Give more detailed info for ddraw */
2035 if (This->Flags & SFLAG_DCINUSE)
2036 return WINEDDERR_DCALREADYCREATED;
2037
2038 /* Can't GetDC if the surface is locked */
2039 if (This->Flags & SFLAG_LOCKED)
2040 return WINED3DERR_INVALIDCALL;
2041
2042 memset(&lock, 0, sizeof(lock)); /* To be sure */
2043
2044 /* Create a DIB section if there isn't a hdc yet */
2045 if(!This->hDC) {
2046 if(This->Flags & SFLAG_CLIENT) {
2047 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2048 surface_release_client_storage(iface);
2049 }
2050 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
2051 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
2052
2053 /* Use the dib section from now on if we are not using a PBO */
2054 if(!(This->Flags & SFLAG_PBO))
2055 This->resource.allocatedMemory = This->dib.bitmap_data;
2056 }
2057
2058 /* Lock the surface */
2059 hr = IWineD3DSurface_LockRect(iface,
2060 &lock,
2061 NULL,
2062 0);
2063
2064 if(This->Flags & SFLAG_PBO) {
2065 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
2066 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
2067 }
2068
2069 if(FAILED(hr)) {
2070 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
2071 /* keep the dib section */
2072 return hr;
2073 }
2074
2075 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
2076 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
2077 {
2078 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
2079 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
2080 unsigned int n;
2081 const PALETTEENTRY *pal = NULL;
2082
2083 if(This->palette) {
2084 pal = This->palette->palents;
2085 } else {
2086 IWineD3DSurfaceImpl *dds_primary;
2087 IWineD3DSwapChainImpl *swapchain;
2088 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
2089 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
2090 if (dds_primary && dds_primary->palette)
2091 pal = dds_primary->palette->palents;
2092 }
2093
2094 if (pal) {
2095 for (n=0; n<256; n++) {
2096 col[n].rgbRed = pal[n].peRed;
2097 col[n].rgbGreen = pal[n].peGreen;
2098 col[n].rgbBlue = pal[n].peBlue;
2099 col[n].rgbReserved = 0;
2100 }
2101 SetDIBColorTable(This->hDC, 0, 256, col);
2102 }
2103 }
2104
2105 *pHDC = This->hDC;
2106 TRACE("returning %p\n",*pHDC);
2107 This->Flags |= SFLAG_DCINUSE;
2108
2109 return WINED3D_OK;
2110}
2111
2112static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
2113{
2114 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2115
2116 TRACE("(%p)->(%p)\n",This,hDC);
2117
2118 if (!(This->Flags & SFLAG_DCINUSE))
2119 return WINEDDERR_NODC;
2120
2121 if (This->hDC !=hDC) {
2122 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
2123 return WINEDDERR_NODC;
2124 }
2125
2126 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
2127 /* Copy the contents of the DIB over to the PBO */
2128 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
2129 }
2130
2131 /* we locked first, so unlock now */
2132 IWineD3DSurface_UnlockRect(iface);
2133
2134 This->Flags &= ~SFLAG_DCINUSE;
2135
2136 return WINED3D_OK;
2137}
2138
2139/* ******************************************************
2140 IWineD3DSurface Internal (No mapping to directx api) parts follow
2141 ****************************************************** */
2142
2143HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, struct wined3d_format_desc *desc, CONVERT_TYPES *convert)
2144{
2145 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
2146 IWineD3DDeviceImpl *device = This->resource.device;
2147 BOOL blit_supported = FALSE;
2148 RECT rect = {0, 0, This->pow2Width, This->pow2Height};
2149
2150 /* Copy the default values from the surface. Below we might perform fixups */
2151 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2152 *desc = *This->resource.format_desc;
2153 *convert = NO_CONVERSION;
2154
2155 /* Ok, now look if we have to do any conversion */
2156 switch(This->resource.format_desc->format)
2157 {
2158 case WINED3DFMT_P8_UINT:
2159 /* ****************
2160 Paletted Texture
2161 **************** */
2162
2163 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
2164 &rect, This->resource.usage, This->resource.pool,
2165 This->resource.format_desc, &rect, This->resource.usage,
2166 This->resource.pool, This->resource.format_desc);
2167
2168 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2169 * texturing. Further also use conversion in case of color keying.
2170 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2171 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2172 * conflicts with this.
2173 */
2174 if (!((blit_supported && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
2175 || colorkey_active || !use_texturing)
2176 {
2177 desc->glFormat = GL_RGBA;
2178 desc->glInternal = GL_RGBA;
2179 desc->glType = GL_UNSIGNED_BYTE;
2180 desc->conv_byte_count = 4;
2181 if(colorkey_active) {
2182 *convert = CONVERT_PALETTED_CK;
2183 } else {
2184 *convert = CONVERT_PALETTED;
2185 }
2186 }
2187 break;
2188
2189 case WINED3DFMT_B2G3R3_UNORM:
2190 /* **********************
2191 GL_UNSIGNED_BYTE_3_3_2
2192 ********************** */
2193 if (colorkey_active) {
2194 /* This texture format will never be used.. So do not care about color keying
2195 up until the point in time it will be needed :-) */
2196 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2197 }
2198 break;
2199
2200 case WINED3DFMT_B5G6R5_UNORM:
2201 if (colorkey_active) {
2202 *convert = CONVERT_CK_565;
2203 desc->glFormat = GL_RGBA;
2204 desc->glInternal = GL_RGB5_A1;
2205 desc->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2206 desc->conv_byte_count = 2;
2207 }
2208 break;
2209
2210 case WINED3DFMT_B5G5R5X1_UNORM:
2211 if (colorkey_active) {
2212 *convert = CONVERT_CK_5551;
2213 desc->glFormat = GL_BGRA;
2214 desc->glInternal = GL_RGB5_A1;
2215 desc->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2216 desc->conv_byte_count = 2;
2217 }
2218 break;
2219
2220 case WINED3DFMT_B8G8R8_UNORM:
2221 if (colorkey_active) {
2222 *convert = CONVERT_CK_RGB24;
2223 desc->glFormat = GL_RGBA;
2224 desc->glInternal = GL_RGBA8;
2225 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2226 desc->conv_byte_count = 4;
2227 }
2228 break;
2229
2230 case WINED3DFMT_B8G8R8X8_UNORM:
2231 if (colorkey_active) {
2232 *convert = CONVERT_RGB32_888;
2233 desc->glFormat = GL_RGBA;
2234 desc->glInternal = GL_RGBA8;
2235 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2236 desc->conv_byte_count = 4;
2237 }
2238 break;
2239
2240 default:
2241 break;
2242 }
2243
2244 return WINED3D_OK;
2245}
2246
2247void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2248{
2249 IWineD3DDeviceImpl *device = This->resource.device;
2250 IWineD3DPaletteImpl *pal = This->palette;
2251 BOOL index_in_alpha = FALSE;
2252 unsigned int i;
2253
2254 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2255 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2256 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2257 * duplicate entries. Store the color key in the unused alpha component to speed the
2258 * download up and to make conversion unneeded. */
2259 index_in_alpha = primary_render_target_is_p8(device);
2260
2261 if (!pal)
2262 {
2263 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2264
2265 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2266 if (dxVersion <= 7)
2267 {
2268 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2269 if (index_in_alpha)
2270 {
2271 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2272 * there's no palette at this time. */
2273 for (i = 0; i < 256; i++) table[i][3] = i;
2274 }
2275 }
2276 else
2277 {
2278 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2279 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2280 * capability flag is present (wine does advertise this capability) */
2281 for (i = 0; i < 256; ++i)
2282 {
2283 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2284 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2285 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2286 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2287 }
2288 }
2289 }
2290 else
2291 {
2292 TRACE("Using surface palette %p\n", pal);
2293 /* Get the surface's palette */
2294 for (i = 0; i < 256; ++i)
2295 {
2296 table[i][0] = pal->palents[i].peRed;
2297 table[i][1] = pal->palents[i].peGreen;
2298 table[i][2] = pal->palents[i].peBlue;
2299
2300 /* When index_in_alpha is set the palette index is stored in the
2301 * alpha component. In case of a readback we can then read
2302 * GL_ALPHA. Color keying is handled in BltOverride using a
2303 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2304 * color key itself is passed to glAlphaFunc in other cases the
2305 * alpha component of pixels that should be masked away is set to 0. */
2306 if (index_in_alpha)
2307 {
2308 table[i][3] = i;
2309 }
2310 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2311 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2312 {
2313 table[i][3] = 0x00;
2314 }
2315 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2316 {
2317 table[i][3] = pal->palents[i].peFlags;
2318 }
2319 else
2320 {
2321 table[i][3] = 0xFF;
2322 }
2323 }
2324 }
2325}
2326
2327static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2328 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2329{
2330 const BYTE *source;
2331 BYTE *dest;
2332 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2333
2334 switch (convert) {
2335 case NO_CONVERSION:
2336 {
2337 memcpy(dst, src, pitch * height);
2338 break;
2339 }
2340 case CONVERT_PALETTED:
2341 case CONVERT_PALETTED_CK:
2342 {
2343 IWineD3DPaletteImpl* pal = This->palette;
2344 BYTE table[256][4];
2345 unsigned int x, y;
2346
2347 if( pal == NULL) {
2348 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2349 }
2350
2351 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2352
2353 for (y = 0; y < height; y++)
2354 {
2355 source = src + pitch * y;
2356 dest = dst + outpitch * y;
2357 /* This is an 1 bpp format, using the width here is fine */
2358 for (x = 0; x < width; x++) {
2359 BYTE color = *source++;
2360 *dest++ = table[color][0];
2361 *dest++ = table[color][1];
2362 *dest++ = table[color][2];
2363 *dest++ = table[color][3];
2364 }
2365 }
2366 }
2367 break;
2368
2369 case CONVERT_CK_565:
2370 {
2371 /* Converting the 565 format in 5551 packed to emulate color-keying.
2372
2373 Note : in all these conversion, it would be best to average the averaging
2374 pixels to get the color of the pixel that will be color-keyed to
2375 prevent 'color bleeding'. This will be done later on if ever it is
2376 too visible.
2377
2378 Note2: Nvidia documents say that their driver does not support alpha + color keying
2379 on the same surface and disables color keying in such a case
2380 */
2381 unsigned int x, y;
2382 const WORD *Source;
2383 WORD *Dest;
2384
2385 TRACE("Color keyed 565\n");
2386
2387 for (y = 0; y < height; y++) {
2388 Source = (const WORD *)(src + y * pitch);
2389 Dest = (WORD *) (dst + y * outpitch);
2390 for (x = 0; x < width; x++ ) {
2391 WORD color = *Source++;
2392 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2393 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2394 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2395 *Dest |= 0x0001;
2396 }
2397 Dest++;
2398 }
2399 }
2400 }
2401 break;
2402
2403 case CONVERT_CK_5551:
2404 {
2405 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2406 unsigned int x, y;
2407 const WORD *Source;
2408 WORD *Dest;
2409 TRACE("Color keyed 5551\n");
2410 for (y = 0; y < height; y++) {
2411 Source = (const WORD *)(src + y * pitch);
2412 Dest = (WORD *) (dst + y * outpitch);
2413 for (x = 0; x < width; x++ ) {
2414 WORD color = *Source++;
2415 *Dest = color;
2416 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2417 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2418 *Dest |= (1 << 15);
2419 }
2420 else {
2421 *Dest &= ~(1 << 15);
2422 }
2423 Dest++;
2424 }
2425 }
2426 }
2427 break;
2428
2429 case CONVERT_CK_RGB24:
2430 {
2431 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2432 unsigned int x, y;
2433 for (y = 0; y < height; y++)
2434 {
2435 source = src + pitch * y;
2436 dest = dst + outpitch * y;
2437 for (x = 0; x < width; x++) {
2438 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2439 DWORD dstcolor = color << 8;
2440 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2441 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2442 dstcolor |= 0xff;
2443 }
2444 *(DWORD*)dest = dstcolor;
2445 source += 3;
2446 dest += 4;
2447 }
2448 }
2449 }
2450 break;
2451
2452 case CONVERT_RGB32_888:
2453 {
2454 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2455 unsigned int x, y;
2456 for (y = 0; y < height; y++)
2457 {
2458 source = src + pitch * y;
2459 dest = dst + outpitch * y;
2460 for (x = 0; x < width; x++) {
2461 DWORD color = 0xffffff & *(const DWORD*)source;
2462 DWORD dstcolor = color << 8;
2463 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2464 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2465 dstcolor |= 0xff;
2466 }
2467 *(DWORD*)dest = dstcolor;
2468 source += 4;
2469 dest += 4;
2470 }
2471 }
2472 }
2473 break;
2474
2475 default:
2476 ERR("Unsupported conversion type %#x.\n", convert);
2477 }
2478 return WINED3D_OK;
2479}
2480
2481BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2482{
2483 IWineD3DDeviceImpl *device = This->resource.device;
2484
2485 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2486 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2487 {
2488 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2489 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2490 */
2491 return FALSE;
2492 }
2493
2494 if (This->palette9)
2495 {
2496 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2497 {
2498 return FALSE;
2499 }
2500 } else {
2501 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2502 }
2503 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2504 return TRUE;
2505}
2506
2507static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2508 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2509 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2510
2511 if (!(This->Flags & flag)) {
2512 TRACE("Reloading because surface is dirty\n");
2513 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2514 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2515 /* Reload: vice versa OR */
2516 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2517 /* Also reload: Color key is active AND the color key has changed */
2518 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2519 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2520 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2521 TRACE("Reloading because of color keying\n");
2522 /* To perform the color key conversion we need a sysmem copy of
2523 * the surface. Make sure we have it
2524 */
2525
2526 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2527 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2528 /* TODO: This is not necessarily needed with hw palettized texture support */
2529 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2530 } else {
2531 TRACE("surface is already in texture\n");
2532 return WINED3D_OK;
2533 }
2534
2535 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2536 * These resources are not bound by device size or format restrictions. Because of this,
2537 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2538 * However, these resources can always be created, locked, and copied.
2539 */
2540 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2541 {
2542 FIXME("(%p) Operation not supported for scratch textures\n",This);
2543 return WINED3DERR_INVALIDCALL;
2544 }
2545
2546 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2547
2548#if 0
2549 {
2550 static unsigned int gen = 0;
2551 char buffer[4096];
2552 ++gen;
2553 if ((gen % 10) == 0) {
2554 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2555 This, This->texture_target, This->texture_level, gen);
2556 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2557 }
2558 /*
2559 * debugging crash code
2560 if (gen == 250) {
2561 void** test = NULL;
2562 *test = 0;
2563 }
2564 */
2565 }
2566#endif
2567
2568 if (!(This->Flags & SFLAG_DONOTFREE)) {
2569 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2570 This->resource.allocatedMemory = NULL;
2571 This->resource.heapMemory = NULL;
2572 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2573 }
2574
2575 return WINED3D_OK;
2576}
2577
2578/* Context activation is done by the caller. */
2579static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2580 /* TODO: check for locks */
2581 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2582 IWineD3DBaseTexture *baseTexture = NULL;
2583
2584 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2585 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2586 TRACE("Passing to container\n");
2587 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2588 IWineD3DBaseTexture_Release(baseTexture);
2589 }
2590 else
2591 {
2592 GLuint *name;
2593
2594 TRACE("(%p) : Binding surface\n", This);
2595
2596 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2597
2598 ENTER_GL();
2599
2600 if (!This->texture_level)
2601 {
2602 if (!*name) {
2603 glGenTextures(1, name);
2604 checkGLcall("glGenTextures");
2605 TRACE("Surface %p given name %d\n", This, *name);
2606
2607 glBindTexture(This->texture_target, *name);
2608 checkGLcall("glBindTexture");
2609 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2610 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2611 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2612 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2613 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2614 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2615 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2616 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2617 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2618 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2619 }
2620 /* This is where we should be reducing the amount of GLMemoryUsed */
2621 } else if (*name) {
2622 /* Mipmap surfaces should have a base texture container */
2623 ERR("Mipmap surface has a glTexture bound to it!\n");
2624 }
2625
2626 glBindTexture(This->texture_target, *name);
2627 checkGLcall("glBindTexture");
2628
2629 LEAVE_GL();
2630 }
2631}
2632
2633#include <errno.h>
2634#include <stdio.h>
2635static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2636{
2637 FILE* f = NULL;
2638 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2639 char *allocatedMemory;
2640 const char *textureRow;
2641 IWineD3DSwapChain *swapChain = NULL;
2642 int width, height, i, y;
2643 GLuint tmpTexture = 0;
2644 DWORD color;
2645 /*FIXME:
2646 Textures may not be stored in ->allocatedgMemory and a GlTexture
2647 so we should lock the surface before saving a snapshot, or at least check that
2648 */
2649 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2650 by calling GetTexImage and in compressed form by calling
2651 GetCompressedTexImageARB. Queried compressed images can be saved and
2652 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2653 texture images do not need to be processed by the GL and should
2654 significantly improve texture loading performance relative to uncompressed
2655 images. */
2656
2657/* Setup the width and height to be the internal texture width and height. */
2658 width = This->pow2Width;
2659 height = This->pow2Height;
2660/* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2661 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2662
2663 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2664 /* if were not a real texture then read the back buffer into a real texture */
2665 /* we don't want to interfere with the back buffer so read the data into a temporary
2666 * texture and then save the data out of the temporary texture
2667 */
2668 GLint prevRead;
2669 ENTER_GL();
2670 TRACE("(%p) Reading render target into texture\n", This);
2671
2672 glGenTextures(1, &tmpTexture);
2673 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2674
2675 glTexImage2D(GL_TEXTURE_2D,
2676 0,
2677 GL_RGBA,
2678 width,
2679 height,
2680 0/*border*/,
2681 GL_RGBA,
2682 GL_UNSIGNED_INT_8_8_8_8_REV,
2683 NULL);
2684
2685 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2686 checkGLcall("glGetIntegerv");
2687 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2688 checkGLcall("glReadBuffer");
2689 glCopyTexImage2D(GL_TEXTURE_2D,
2690 0,
2691 GL_RGBA,
2692 0,
2693 0,
2694 width,
2695 height,
2696 0);
2697
2698 checkGLcall("glCopyTexImage2D");
2699 glReadBuffer(prevRead);
2700 LEAVE_GL();
2701
2702 } else { /* bind the real texture, and make sure it up to date */
2703 surface_internal_preload(iface, SRGB_RGB);
2704 surface_bind_and_dirtify(This, FALSE);
2705 }
2706 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2707 ENTER_GL();
2708 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2709 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2710 checkGLcall("glGetTexImage");
2711 if (tmpTexture) {
2712 glBindTexture(GL_TEXTURE_2D, 0);
2713 glDeleteTextures(1, &tmpTexture);
2714 }
2715 LEAVE_GL();
2716
2717 f = fopen(filename, "w+");
2718 if (NULL == f) {
2719 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2720 return WINED3DERR_INVALIDCALL;
2721 }
2722/* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2723 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2724/* TGA header */
2725 fputc(0,f);
2726 fputc(0,f);
2727 fputc(2,f);
2728 fputc(0,f);
2729 fputc(0,f);
2730 fputc(0,f);
2731 fputc(0,f);
2732 fputc(0,f);
2733 fputc(0,f);
2734 fputc(0,f);
2735 fputc(0,f);
2736 fputc(0,f);
2737/* short width*/
2738 fwrite(&width,2,1,f);
2739/* short height */
2740 fwrite(&height,2,1,f);
2741/* format rgba */
2742 fputc(0x20,f);
2743 fputc(0x28,f);
2744/* raw data */
2745 /* if the data is upside down if we've fetched it from a back buffer, so it needs flipping again to make it the correct way up */
2746 if(swapChain)
2747 textureRow = allocatedMemory + (width * (height - 1) *4);
2748 else
2749 textureRow = allocatedMemory;
2750 for (y = 0 ; y < height; y++) {
2751 for (i = 0; i < width; i++) {
2752 color = *((const DWORD*)textureRow);
2753 fputc((color >> 16) & 0xFF, f); /* B */
2754 fputc((color >> 8) & 0xFF, f); /* G */
2755 fputc((color >> 0) & 0xFF, f); /* R */
2756 fputc((color >> 24) & 0xFF, f); /* A */
2757 textureRow += 4;
2758 }
2759 /* take two rows of the pointer to the texture memory */
2760 if(swapChain)
2761 (textureRow-= width << 3);
2762
2763 }
2764 TRACE("Closing file\n");
2765 fclose(f);
2766
2767 if(swapChain) {
2768 IWineD3DSwapChain_Release(swapChain);
2769 }
2770 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2771 return WINED3D_OK;
2772}
2773
2774static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2775 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2776 HRESULT hr;
2777
2778 TRACE("(%p) : Calling base function first\n", This);
2779 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2780 if(SUCCEEDED(hr)) {
2781 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2782 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2783 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2784 }
2785 return hr;
2786}
2787
2788static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2789 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2790
2791 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2792 WARN("Surface is locked or the HDC is in use\n");
2793 return WINED3DERR_INVALIDCALL;
2794 }
2795
2796 if(Mem && Mem != This->resource.allocatedMemory) {
2797 void *release = NULL;
2798
2799 /* Do I have to copy the old surface content? */
2800 if(This->Flags & SFLAG_DIBSECTION) {
2801 /* Release the DC. No need to hold the critical section for the update
2802 * Thread because this thread runs only on front buffers, but this method
2803 * fails for render targets in the check above.
2804 */
2805 SelectObject(This->hDC, This->dib.holdbitmap);
2806 DeleteDC(This->hDC);
2807 /* Release the DIB section */
2808 DeleteObject(This->dib.DIBsection);
2809 This->dib.bitmap_data = NULL;
2810 This->resource.allocatedMemory = NULL;
2811 This->hDC = NULL;
2812 This->Flags &= ~SFLAG_DIBSECTION;
2813 } else if(!(This->Flags & SFLAG_USERPTR)) {
2814 release = This->resource.heapMemory;
2815 This->resource.heapMemory = NULL;
2816 }
2817 This->resource.allocatedMemory = Mem;
2818 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2819
2820 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2821 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2822
2823 /* For client textures opengl has to be notified */
2824 if(This->Flags & SFLAG_CLIENT) {
2825 surface_release_client_storage(iface);
2826 }
2827
2828 /* Now free the old memory if any */
2829 HeapFree(GetProcessHeap(), 0, release);
2830 } else if(This->Flags & SFLAG_USERPTR) {
2831 /* LockRect and GetDC will re-create the dib section and allocated memory */
2832 This->resource.allocatedMemory = NULL;
2833 /* HeapMemory should be NULL already */
2834 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2835 This->Flags &= ~SFLAG_USERPTR;
2836
2837 if(This->Flags & SFLAG_CLIENT) {
2838 surface_release_client_storage(iface);
2839 }
2840 }
2841 return WINED3D_OK;
2842}
2843
2844void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2845
2846 /* Flip the surface contents */
2847 /* Flip the DC */
2848 {
2849 HDC tmp;
2850 tmp = front->hDC;
2851 front->hDC = back->hDC;
2852 back->hDC = tmp;
2853 }
2854
2855 /* Flip the DIBsection */
2856 {
2857 HBITMAP tmp;
2858 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2859 tmp = front->dib.DIBsection;
2860 front->dib.DIBsection = back->dib.DIBsection;
2861 back->dib.DIBsection = tmp;
2862
2863 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2864 else front->Flags &= ~SFLAG_DIBSECTION;
2865 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2866 else back->Flags &= ~SFLAG_DIBSECTION;
2867 }
2868
2869 /* Flip the surface data */
2870 {
2871 void* tmp;
2872
2873 tmp = front->dib.bitmap_data;
2874 front->dib.bitmap_data = back->dib.bitmap_data;
2875 back->dib.bitmap_data = tmp;
2876
2877 tmp = front->resource.allocatedMemory;
2878 front->resource.allocatedMemory = back->resource.allocatedMemory;
2879 back->resource.allocatedMemory = tmp;
2880
2881 tmp = front->resource.heapMemory;
2882 front->resource.heapMemory = back->resource.heapMemory;
2883 back->resource.heapMemory = tmp;
2884 }
2885
2886 /* Flip the PBO */
2887 {
2888 GLuint tmp_pbo = front->pbo;
2889 front->pbo = back->pbo;
2890 back->pbo = tmp_pbo;
2891 }
2892
2893 /* client_memory should not be different, but just in case */
2894 {
2895 BOOL tmp;
2896 tmp = front->dib.client_memory;
2897 front->dib.client_memory = back->dib.client_memory;
2898 back->dib.client_memory = tmp;
2899 }
2900
2901 /* Flip the opengl texture */
2902 {
2903 GLuint tmp;
2904
2905 tmp = back->texture_name;
2906 back->texture_name = front->texture_name;
2907 front->texture_name = tmp;
2908
2909 tmp = back->texture_name_srgb;
2910 back->texture_name_srgb = front->texture_name_srgb;
2911 front->texture_name_srgb = tmp;
2912 }
2913
2914 {
2915 DWORD tmp_flags = back->Flags;
2916 back->Flags = front->Flags;
2917 front->Flags = tmp_flags;
2918 }
2919}
2920
2921static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2922 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2923 IWineD3DSwapChainImpl *swapchain = NULL;
2924 HRESULT hr;
2925 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2926
2927 /* Flipping is only supported on RenderTargets and overlays*/
2928 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2929 WARN("Tried to flip a non-render target, non-overlay surface\n");
2930 return WINEDDERR_NOTFLIPPABLE;
2931 }
2932
2933 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2934 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2935
2936 /* Update the overlay if it is visible */
2937 if(This->overlay_dest) {
2938 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2939 } else {
2940 return WINED3D_OK;
2941 }
2942 }
2943
2944 if(override) {
2945 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2946 * FIXME("(%p) Target override is not supported by now\n", This);
2947 * Additionally, it isn't really possible to support triple-buffering
2948 * properly on opengl at all
2949 */
2950 }
2951
2952 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2953 if(!swapchain) {
2954 ERR("Flipped surface is not on a swapchain\n");
2955 return WINEDDERR_NOTFLIPPABLE;
2956 }
2957
2958 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2959 * and only d3d8 and d3d9 apps specify the presentation interval
2960 */
2961 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2962 /* Most common case first to avoid wasting time on all the other cases */
2963 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2964 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2965 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2966 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2967 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2968 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2969 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2970 } else {
2971 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2972 }
2973
2974 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2975 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
2976 NULL, NULL, swapchain->win_handle, NULL, 0);
2977 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2978 return hr;
2979}
2980
2981/* Does a direct frame buffer -> texture copy. Stretching is done
2982 * with single pixel copy calls
2983 */
2984static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2985 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
2986{
2987 IWineD3DDeviceImpl *myDevice = This->resource.device;
2988 float xrel, yrel;
2989 UINT row;
2990 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2991 struct wined3d_context *context;
2992 BOOL upsidedown = FALSE;
2993 RECT dst_rect = *dst_rect_in;
2994
2995 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2996 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2997 */
2998 if(dst_rect.top > dst_rect.bottom) {
2999 UINT tmp = dst_rect.bottom;
3000 dst_rect.bottom = dst_rect.top;
3001 dst_rect.top = tmp;
3002 upsidedown = TRUE;
3003 }
3004
3005 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3006 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3007 ENTER_GL();
3008
3009 /* Bind the target texture */
3010 glBindTexture(This->texture_target, This->texture_name);
3011 checkGLcall("glBindTexture");
3012 if(surface_is_offscreen(SrcSurface)) {
3013 TRACE("Reading from an offscreen target\n");
3014 upsidedown = !upsidedown;
3015 glReadBuffer(myDevice->offscreenBuffer);
3016 }
3017 else
3018 {
3019 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3020 }
3021 checkGLcall("glReadBuffer");
3022
3023 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3024 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3025
3026 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3027 {
3028 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3029
3030 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3031 ERR("Texture filtering not supported in direct blit\n");
3032 }
3033 }
3034 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3035 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3036 {
3037 ERR("Texture filtering not supported in direct blit\n");
3038 }
3039
3040 if (upsidedown
3041 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3042 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3043 {
3044 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3045
3046 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3047 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3048 src_rect->left, Src->currentDesc.Height - src_rect->bottom,
3049 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3050 } else {
3051 UINT yoffset = Src->currentDesc.Height - src_rect->top + dst_rect.top - 1;
3052 /* I have to process this row by row to swap the image,
3053 * otherwise it would be upside down, so stretching in y direction
3054 * doesn't cost extra time
3055 *
3056 * However, stretching in x direction can be avoided if not necessary
3057 */
3058 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3059 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3060 {
3061 /* Well, that stuff works, but it's very slow.
3062 * find a better way instead
3063 */
3064 UINT col;
3065
3066 for(col = dst_rect.left; col < dst_rect.right; col++) {
3067 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3068 dst_rect.left + col /* x offset */, row /* y offset */,
3069 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3070 }
3071 } else {
3072 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3073 dst_rect.left /* x offset */, row /* y offset */,
3074 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3075 }
3076 }
3077 }
3078 checkGLcall("glCopyTexSubImage2D");
3079
3080 LEAVE_GL();
3081 context_release(context);
3082
3083 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3084 * path is never entered
3085 */
3086 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3087}
3088
3089/* Uses the hardware to stretch and flip the image */
3090static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3091 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
3092{
3093 IWineD3DDeviceImpl *myDevice = This->resource.device;
3094 GLuint src, backup = 0;
3095 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3096 IWineD3DSwapChainImpl *src_swapchain = NULL;
3097 float left, right, top, bottom; /* Texture coordinates */
3098 UINT fbwidth = Src->currentDesc.Width;
3099 UINT fbheight = Src->currentDesc.Height;
3100 struct wined3d_context *context;
3101 GLenum drawBuffer = GL_BACK;
3102 GLenum texture_target;
3103 BOOL noBackBufferBackup;
3104 BOOL src_offscreen;
3105 BOOL upsidedown = FALSE;
3106 RECT dst_rect = *dst_rect_in;
3107
3108 TRACE("Using hwstretch blit\n");
3109 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3110 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3111 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3112
3113 src_offscreen = surface_is_offscreen(SrcSurface);
3114 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3115 if (!noBackBufferBackup && !Src->texture_name)
3116 {
3117 /* Get it a description */
3118 surface_internal_preload(SrcSurface, SRGB_RGB);
3119 }
3120 ENTER_GL();
3121
3122 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3123 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3124 */
3125 if (context->aux_buffers >= 2)
3126 {
3127 /* Got more than one aux buffer? Use the 2nd aux buffer */
3128 drawBuffer = GL_AUX1;
3129 }
3130 else if ((!src_offscreen || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3131 {
3132 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3133 drawBuffer = GL_AUX0;
3134 }
3135
3136 if(noBackBufferBackup) {
3137 glGenTextures(1, &backup);
3138 checkGLcall("glGenTextures");
3139 glBindTexture(GL_TEXTURE_2D, backup);
3140 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3141 texture_target = GL_TEXTURE_2D;
3142 } else {
3143 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3144 * we are reading from the back buffer, the backup can be used as source texture
3145 */
3146 texture_target = Src->texture_target;
3147 glBindTexture(texture_target, Src->texture_name);
3148 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3149 glEnable(texture_target);
3150 checkGLcall("glEnable(texture_target)");
3151
3152 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3153 Src->Flags &= ~SFLAG_INTEXTURE;
3154 }
3155
3156 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3157 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3158 */
3159 if(dst_rect.top > dst_rect.bottom) {
3160 UINT tmp = dst_rect.bottom;
3161 dst_rect.bottom = dst_rect.top;
3162 dst_rect.top = tmp;
3163 upsidedown = TRUE;
3164 }
3165
3166 if (src_offscreen)
3167 {
3168 TRACE("Reading from an offscreen target\n");
3169 upsidedown = !upsidedown;
3170 glReadBuffer(myDevice->offscreenBuffer);
3171 }
3172 else
3173 {
3174 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3175 }
3176
3177 /* TODO: Only back up the part that will be overwritten */
3178 glCopyTexSubImage2D(texture_target, 0,
3179 0, 0 /* read offsets */,
3180 0, 0,
3181 fbwidth,
3182 fbheight);
3183
3184 checkGLcall("glCopyTexSubImage2D");
3185
3186 /* No issue with overriding these - the sampler is dirty due to blit usage */
3187 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3188 wined3d_gl_mag_filter(magLookup, Filter));
3189 checkGLcall("glTexParameteri");
3190 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3191 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3192 checkGLcall("glTexParameteri");
3193
3194 IWineD3DSurface_GetContainer((IWineD3DSurface *)SrcSurface, &IID_IWineD3DSwapChain, (void **)&src_swapchain);
3195 if (src_swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)src_swapchain);
3196 if (!src_swapchain || (IWineD3DSurface *) Src == src_swapchain->backBuffer[0]) {
3197 src = backup ? backup : Src->texture_name;
3198 } else {
3199 glReadBuffer(GL_FRONT);
3200 checkGLcall("glReadBuffer(GL_FRONT)");
3201
3202 glGenTextures(1, &src);
3203 checkGLcall("glGenTextures(1, &src)");
3204 glBindTexture(GL_TEXTURE_2D, src);
3205 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3206
3207 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3208 * out for power of 2 sizes
3209 */
3210 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3211 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3212 checkGLcall("glTexImage2D");
3213 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3214 0, 0 /* read offsets */,
3215 0, 0,
3216 fbwidth,
3217 fbheight);
3218
3219 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3220 checkGLcall("glTexParameteri");
3221 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3222 checkGLcall("glTexParameteri");
3223
3224 glReadBuffer(GL_BACK);
3225 checkGLcall("glReadBuffer(GL_BACK)");
3226
3227 if(texture_target != GL_TEXTURE_2D) {
3228 glDisable(texture_target);
3229 glEnable(GL_TEXTURE_2D);
3230 texture_target = GL_TEXTURE_2D;
3231 }
3232 }
3233 checkGLcall("glEnd and previous");
3234
3235 left = src_rect->left;
3236 right = src_rect->right;
3237
3238 if(upsidedown) {
3239 top = Src->currentDesc.Height - src_rect->top;
3240 bottom = Src->currentDesc.Height - src_rect->bottom;
3241 } else {
3242 top = Src->currentDesc.Height - src_rect->bottom;
3243 bottom = Src->currentDesc.Height - src_rect->top;
3244 }
3245
3246 if(Src->Flags & SFLAG_NORMCOORD) {
3247 left /= Src->pow2Width;
3248 right /= Src->pow2Width;
3249 top /= Src->pow2Height;
3250 bottom /= Src->pow2Height;
3251 }
3252
3253 /* draw the source texture stretched and upside down. The correct surface is bound already */
3254 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3255 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3256
3257 context_set_draw_buffer(context, drawBuffer);
3258 glReadBuffer(drawBuffer);
3259
3260 glBegin(GL_QUADS);
3261 /* bottom left */
3262 glTexCoord2f(left, bottom);
3263 glVertex2i(0, fbheight);
3264
3265 /* top left */
3266 glTexCoord2f(left, top);
3267 glVertex2i(0, fbheight - dst_rect.bottom - dst_rect.top);
3268
3269 /* top right */
3270 glTexCoord2f(right, top);
3271 glVertex2i(dst_rect.right - dst_rect.left, fbheight - dst_rect.bottom - dst_rect.top);
3272
3273 /* bottom right */
3274 glTexCoord2f(right, bottom);
3275 glVertex2i(dst_rect.right - dst_rect.left, fbheight);
3276 glEnd();
3277 checkGLcall("glEnd and previous");
3278
3279 if (texture_target != This->texture_target)
3280 {
3281 glDisable(texture_target);
3282 glEnable(This->texture_target);
3283 texture_target = This->texture_target;
3284 }
3285
3286 /* Now read the stretched and upside down image into the destination texture */
3287 glBindTexture(texture_target, This->texture_name);
3288 checkGLcall("glBindTexture");
3289 glCopyTexSubImage2D(texture_target,
3290 0,
3291 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3292 0, 0, /* We blitted the image to the origin */
3293 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3294 checkGLcall("glCopyTexSubImage2D");
3295
3296 if(drawBuffer == GL_BACK) {
3297 /* Write the back buffer backup back */
3298 if(backup) {
3299 if(texture_target != GL_TEXTURE_2D) {
3300 glDisable(texture_target);
3301 glEnable(GL_TEXTURE_2D);
3302 texture_target = GL_TEXTURE_2D;
3303 }
3304 glBindTexture(GL_TEXTURE_2D, backup);
3305 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3306 } else {
3307 if (texture_target != Src->texture_target)
3308 {
3309 glDisable(texture_target);
3310 glEnable(Src->texture_target);
3311 texture_target = Src->texture_target;
3312 }
3313 glBindTexture(Src->texture_target, Src->texture_name);
3314 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3315 }
3316
3317 glBegin(GL_QUADS);
3318 /* top left */
3319 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3320 glVertex2i(0, 0);
3321
3322 /* bottom left */
3323 glTexCoord2f(0.0f, 0.0f);
3324 glVertex2i(0, fbheight);
3325
3326 /* bottom right */
3327 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3328 glVertex2i(fbwidth, Src->currentDesc.Height);
3329
3330 /* top right */
3331 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3332 glVertex2i(fbwidth, 0);
3333 glEnd();
3334 }
3335 glDisable(texture_target);
3336 checkGLcall("glDisable(texture_target)");
3337
3338 /* Cleanup */
3339 if (src != Src->texture_name && src != backup)
3340 {
3341 glDeleteTextures(1, &src);
3342 checkGLcall("glDeleteTextures(1, &src)");
3343 }
3344 if(backup) {
3345 glDeleteTextures(1, &backup);
3346 checkGLcall("glDeleteTextures(1, &backup)");
3347 }
3348
3349 LEAVE_GL();
3350
3351 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
3352
3353 context_release(context);
3354
3355 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3356 * path is never entered
3357 */
3358 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3359}
3360
3361/* Until the blit_shader is ready, define some prototypes here. */
3362static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
3363 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
3364 const struct wined3d_format_desc *src_format_desc,
3365 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
3366 const struct wined3d_format_desc *dst_format_desc);
3367
3368/* Not called from the VTable */
3369static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3370 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3371 WINED3DTEXTUREFILTERTYPE Filter)
3372{
3373 IWineD3DDeviceImpl *myDevice = This->resource.device;
3374 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3375 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3376 RECT dst_rect, src_rect;
3377
3378 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3379
3380 /* Get the swapchain. One of the surfaces has to be a primary surface */
3381 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3382 WARN("Destination is in sysmem, rejecting gl blt\n");
3383 return WINED3DERR_INVALIDCALL;
3384 }
3385 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3386 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3387 if(Src) {
3388 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3389 WARN("Src is in sysmem, rejecting gl blt\n");
3390 return WINED3DERR_INVALIDCALL;
3391 }
3392 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3393 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3394 }
3395
3396 /* Early sort out of cases where no render target is used */
3397 if(!dstSwapchain && !srcSwapchain &&
3398 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3399 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3400 return WINED3DERR_INVALIDCALL;
3401 }
3402
3403 /* No destination color keying supported */
3404 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3405 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3406 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3407 return WINED3DERR_INVALIDCALL;
3408 }
3409
3410 surface_get_rect(This, DestRect, &dst_rect);
3411 if(Src) surface_get_rect(Src, SrcRect, &src_rect);
3412
3413 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3414 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3415 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3416 /* Half-life does a Blt from the back buffer to the front buffer,
3417 * Full surface size, no flags... Use present instead
3418 *
3419 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3420 */
3421
3422 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3423 while(1)
3424 {
3425 TRACE("Looking if a Present can be done...\n");
3426 /* Source Rectangle must be full surface */
3427 if(src_rect.left != 0 || src_rect.top != 0 ||
3428 src_rect.right != Src->currentDesc.Width || src_rect.bottom != Src->currentDesc.Height) {
3429 TRACE("No, Source rectangle doesn't match\n");
3430 break;
3431 }
3432
3433 /* No stretching may occur */
3434 if(src_rect.right != dst_rect.right - dst_rect.left ||
3435 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
3436 TRACE("No, stretching is done\n");
3437 break;
3438 }
3439
3440 /* Destination must be full surface or match the clipping rectangle */
3441 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3442 {
3443 RECT cliprect;
3444 POINT pos[2];
3445 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3446 pos[0].x = dst_rect.left;
3447 pos[0].y = dst_rect.top;
3448 pos[1].x = dst_rect.right;
3449 pos[1].y = dst_rect.bottom;
3450 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3451 pos, 2);
3452
3453 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3454 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3455 {
3456 TRACE("No, dest rectangle doesn't match(clipper)\n");
3457 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
3458 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
3459 break;
3460 }
3461 }
3462 else
3463 {
3464 if(dst_rect.left != 0 || dst_rect.top != 0 ||
3465 dst_rect.right != This->currentDesc.Width || dst_rect.bottom != This->currentDesc.Height) {
3466 TRACE("No, dest rectangle doesn't match(surface size)\n");
3467 break;
3468 }
3469 }
3470
3471 TRACE("Yes\n");
3472
3473 /* These flags are unimportant for the flag check, remove them */
3474 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3475 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3476
3477 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3478 * take very long, while a flip is fast.
3479 * This applies to Half-Life, which does such Blts every time it finished
3480 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3481 * menu. This is also used by all apps when they do windowed rendering
3482 *
3483 * The problem is that flipping is not really the same as copying. After a
3484 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3485 * untouched. Therefore it's necessary to override the swap effect
3486 * and to set it back after the flip.
3487 *
3488 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3489 * testcases.
3490 */
3491
3492 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3493 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3494
3495 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3496 IWineD3DSwapChain_Present((IWineD3DSwapChain *)dstSwapchain,
3497 NULL, NULL, dstSwapchain->win_handle, NULL, 0);
3498
3499 dstSwapchain->presentParms.SwapEffect = orig_swap;
3500
3501 return WINED3D_OK;
3502 }
3503 break;
3504 }
3505
3506 TRACE("Unsupported blit between buffers on the same swapchain\n");
3507 return WINED3DERR_INVALIDCALL;
3508 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3509 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3510 return WINED3DERR_INVALIDCALL;
3511 } else if(dstSwapchain && srcSwapchain) {
3512 FIXME("Implement hardware blit between two different swapchains\n");
3513 return WINED3DERR_INVALIDCALL;
3514 } else if(dstSwapchain) {
3515 if(SrcSurface == myDevice->render_targets[0]) {
3516 TRACE("Blit from active render target to a swapchain\n");
3517 /* Handled with regular texture -> swapchain blit */
3518 }
3519 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3520 FIXME("Implement blit from a swapchain to the active render target\n");
3521 return WINED3DERR_INVALIDCALL;
3522 }
3523
3524 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3525 /* Blit from render target to texture */
3526 BOOL stretchx;
3527
3528 /* P8 read back is not implemented */
3529 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT ||
3530 This->resource.format_desc->format == WINED3DFMT_P8_UINT)
3531 {
3532 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3533 return WINED3DERR_INVALIDCALL;
3534 }
3535
3536 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3537 TRACE("Color keying not supported by frame buffer to texture blit\n");
3538 return WINED3DERR_INVALIDCALL;
3539 /* Destination color key is checked above */
3540 }
3541
3542 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
3543 stretchx = TRUE;
3544 } else {
3545 stretchx = FALSE;
3546 }
3547
3548 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3549 * flip the image nor scale it.
3550 *
3551 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3552 * -> If the app wants a image width an unscaled width, copy it line per line
3553 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3554 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3555 * back buffer. This is slower than reading line per line, thus not used for flipping
3556 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3557 * pixel by pixel
3558 *
3559 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3560 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3561 * backends.
3562 */
3563 if (fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3564 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3565 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3566 {
3567 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3568 (IWineD3DSurface *)This, &dst_rect, Filter);
3569 } else if((!stretchx) || dst_rect.right - dst_rect.left > Src->currentDesc.Width ||
3570 dst_rect.bottom - dst_rect.top > Src->currentDesc.Height) {
3571 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3572 fb_copy_to_texture_direct(This, SrcSurface, &src_rect, &dst_rect, Filter);
3573 } else {
3574 TRACE("Using hardware stretching to flip / stretch the texture\n");
3575 fb_copy_to_texture_hwstretch(This, SrcSurface, &src_rect, &dst_rect, Filter);
3576 }
3577
3578 if(!(This->Flags & SFLAG_DONOTFREE)) {
3579 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3580 This->resource.allocatedMemory = NULL;
3581 This->resource.heapMemory = NULL;
3582 } else {
3583 This->Flags &= ~SFLAG_INSYSMEM;
3584 }
3585
3586 return WINED3D_OK;
3587 } else if(Src) {
3588 /* Blit from offscreen surface to render target */
3589 DWORD oldCKeyFlags = Src->CKeyFlags;
3590 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3591 struct wined3d_context *context;
3592
3593 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3594
3595 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3596 && fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3597 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3598 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3599 {
3600 TRACE("Using stretch_rect_fbo\n");
3601 /* The source is always a texture, but never the currently active render target, and the texture
3602 * contents are never upside down
3603 */
3604 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3605 (IWineD3DSurface *)This, &dst_rect, Filter);
3606 return WINED3D_OK;
3607 }
3608
3609 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3610 && arbfp_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3611 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3612 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3613 {
3614 return arbfp_blit_surface(myDevice, Src, &src_rect, This, &dst_rect, BLIT_OP_BLIT, Filter);
3615 }
3616
3617 /* Color keying: Check if we have to do a color keyed blt,
3618 * and if not check if a color key is activated.
3619 *
3620 * Just modify the color keying parameters in the surface and restore them afterwards
3621 * The surface keeps track of the color key last used to load the opengl surface.
3622 * PreLoad will catch the change to the flags and color key and reload if necessary.
3623 */
3624 if(Flags & WINEDDBLT_KEYSRC) {
3625 /* Use color key from surface */
3626 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3627 /* Use color key from DDBltFx */
3628 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3629 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3630 } else {
3631 /* Do not use color key */
3632 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3633 }
3634
3635 /* Now load the surface */
3636 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3637
3638 /* Activate the destination context, set it up for blitting */
3639 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3640
3641 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3642 * while OpenGL coordinates are window relative.
3643 * Also beware of the origin difference(top left vs bottom left).
3644 * Also beware that the front buffer's surface size is screen width x screen height,
3645 * whereas the real gl drawable size is the size of the window.
3646 */
3647 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3648 RECT windowsize;
3649 POINT offset = {0,0};
3650 UINT h;
3651 ClientToScreen(context->win_handle, &offset);
3652 GetClientRect(context->win_handle, &windowsize);
3653 h = windowsize.bottom - windowsize.top;
3654 dst_rect.left -= offset.x; dst_rect.right -=offset.x;
3655 dst_rect.top -= offset.y; dst_rect.bottom -=offset.y;
3656 dst_rect.top += This->currentDesc.Height - h; dst_rect.bottom += This->currentDesc.Height - h;
3657 }
3658
3659 if (!myDevice->blitter->blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3660 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3661 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3662 {
3663 FIXME("Unsupported blit operation falling back to software\n");
3664 return WINED3DERR_INVALIDCALL;
3665 }
3666
3667 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src);
3668
3669 ENTER_GL();
3670
3671 /* This is for color keying */
3672 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3673 glEnable(GL_ALPHA_TEST);
3674 checkGLcall("glEnable(GL_ALPHA_TEST)");
3675
3676 /* When the primary render target uses P8, the alpha component contains the palette index.
3677 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3678 * should be masked away have alpha set to 0. */
3679 if(primary_render_target_is_p8(myDevice))
3680 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3681 else
3682 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3683 checkGLcall("glAlphaFunc");
3684 } else {
3685 glDisable(GL_ALPHA_TEST);
3686 checkGLcall("glDisable(GL_ALPHA_TEST)");
3687 }
3688
3689 /* Draw a textured quad
3690 */
3691 draw_textured_quad(Src, &src_rect, &dst_rect, Filter);
3692
3693 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3694 glDisable(GL_ALPHA_TEST);
3695 checkGLcall("glDisable(GL_ALPHA_TEST)");
3696 }
3697
3698 /* Restore the color key parameters */
3699 Src->CKeyFlags = oldCKeyFlags;
3700 Src->SrcBltCKey = oldBltCKey;
3701
3702 LEAVE_GL();
3703
3704 /* Leave the opengl state valid for blitting */
3705 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3706
3707 if (wined3d_settings.strict_draw_ordering || (dstSwapchain
3708 && ((IWineD3DSurface *)This == dstSwapchain->frontBuffer
3709 || dstSwapchain->num_contexts > 1)))
3710 wglFlush(); /* Flush to ensure ordering across contexts. */
3711
3712 context_release(context);
3713
3714 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3715 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3716 * is outdated now
3717 */
3718 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3719
3720 return WINED3D_OK;
3721 } else {
3722 /* Source-Less Blit to render target */
3723 if (Flags & WINEDDBLT_COLORFILL) {
3724 DWORD color;
3725
3726 TRACE("Colorfill\n");
3727
3728 /* The color as given in the Blt function is in the format of the frame-buffer...
3729 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3730 */
3731 if (!surface_convert_color_to_argb(This, DDBltFx->u5.dwFillColor, &color))
3732 {
3733 /* The color conversion function already prints an error, so need to do it here */
3734 return WINED3DERR_INVALIDCALL;
3735 }
3736
3737 if (ffp_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_COLOR_FILL,
3738 NULL, 0, 0, NULL,
3739 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3740 {
3741 return ffp_blit.color_fill(myDevice, This, &dst_rect, color);
3742 }
3743 else if (cpu_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_COLOR_FILL,
3744 NULL, 0, 0, NULL,
3745 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3746 {
3747 return cpu_blit.color_fill(myDevice, This, &dst_rect, color);
3748 }
3749 return WINED3DERR_INVALIDCALL;
3750 }
3751 }
3752
3753 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3754 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3755 return WINED3DERR_INVALIDCALL;
3756}
3757
3758static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3759 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3760{
3761 IWineD3DDeviceImpl *myDevice = This->resource.device;
3762 float depth;
3763
3764 if (Flags & WINEDDBLT_DEPTHFILL) {
3765 switch(This->resource.format_desc->format)
3766 {
3767 case WINED3DFMT_D16_UNORM:
3768 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3769 break;
3770 case WINED3DFMT_S1_UINT_D15_UNORM:
3771 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3772 break;
3773 case WINED3DFMT_D24_UNORM_S8_UINT:
3774 case WINED3DFMT_X8D24_UNORM:
3775 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3776 break;
3777 case WINED3DFMT_D32_UNORM:
3778 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3779 break;
3780 default:
3781 depth = 0.0f;
3782 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3783 }
3784
3785 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3786 DestRect == NULL ? 0 : 1,
3787 (const WINED3DRECT *)DestRect,
3788 WINED3DCLEAR_ZBUFFER,
3789 0x00000000,
3790 depth,
3791 0x00000000);
3792 }
3793
3794 FIXME("(%p): Unsupp depthstencil blit\n", This);
3795 return WINED3DERR_INVALIDCALL;
3796}
3797
3798static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3799 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3800 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3801 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3802 IWineD3DDeviceImpl *myDevice = This->resource.device;
3803
3804 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3805 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3806
3807 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3808 {
3809 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3810 return WINEDDERR_SURFACEBUSY;
3811 }
3812
3813 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3814 * except depth blits, which seem to work
3815 */
3816 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3817 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3818 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3819 return WINED3DERR_INVALIDCALL;
3820 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3821 TRACE("Z Blit override handled the blit\n");
3822 return WINED3D_OK;
3823 }
3824 }
3825
3826 /* Special cases for RenderTargets */
3827 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3828 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3829 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3830 }
3831
3832 /* For the rest call the X11 surface implementation.
3833 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3834 * other Blts are rather rare
3835 */
3836 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3837}
3838
3839static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3840 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3841{
3842 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3843 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3844 IWineD3DDeviceImpl *myDevice = This->resource.device;
3845
3846 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3847
3848 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
3849 {
3850 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3851 return WINEDDERR_SURFACEBUSY;
3852 }
3853
3854 if(myDevice->inScene &&
3855 (iface == myDevice->stencilBufferTarget ||
3856 (Source == myDevice->stencilBufferTarget))) {
3857 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3858 return WINED3DERR_INVALIDCALL;
3859 }
3860
3861 /* Special cases for RenderTargets */
3862 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3863 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
3864
3865 RECT SrcRect, DstRect;
3866 DWORD Flags=0;
3867
3868 surface_get_rect(srcImpl, rsrc, &SrcRect);
3869
3870 DstRect.left = dstx;
3871 DstRect.top=dsty;
3872 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3873 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3874
3875 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3876 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3877 Flags |= WINEDDBLT_KEYSRC;
3878 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3879 Flags |= WINEDDBLT_KEYDEST;
3880 if(trans & WINEDDBLTFAST_WAIT)
3881 Flags |= WINEDDBLT_WAIT;
3882 if(trans & WINEDDBLTFAST_DONOTWAIT)
3883 Flags |= WINEDDBLT_DONOTWAIT;
3884
3885 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3886 }
3887
3888
3889 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3890}
3891
3892static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3893{
3894 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3895 RGBQUAD col[256];
3896 IWineD3DPaletteImpl *pal = This->palette;
3897 unsigned int n;
3898 TRACE("(%p)\n", This);
3899
3900 if (!pal) return WINED3D_OK;
3901
3902 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
3903 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
3904 {
3905 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3906 {
3907 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3908 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3909
3910 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3911 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3912 } else {
3913 if(!(This->Flags & SFLAG_INSYSMEM)) {
3914 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3915 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3916 }
3917 TRACE("Dirtifying surface\n");
3918 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3919 }
3920 }
3921
3922 if(This->Flags & SFLAG_DIBSECTION) {
3923 TRACE("(%p): Updating the hdc's palette\n", This);
3924 for (n=0; n<256; n++) {
3925 col[n].rgbRed = pal->palents[n].peRed;
3926 col[n].rgbGreen = pal->palents[n].peGreen;
3927 col[n].rgbBlue = pal->palents[n].peBlue;
3928 col[n].rgbReserved = 0;
3929 }
3930 SetDIBColorTable(This->hDC, 0, 256, col);
3931 }
3932
3933 /* Propagate the changes to the drawable when we have a palette. */
3934 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3935 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3936
3937 return WINED3D_OK;
3938}
3939
3940static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3941 /** Check against the maximum texture sizes supported by the video card **/
3942 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3943 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
3944 unsigned int pow2Width, pow2Height;
3945
3946 This->texture_name = 0;
3947 This->texture_target = GL_TEXTURE_2D;
3948
3949 /* Non-power2 support */
3950 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
3951 {
3952 pow2Width = This->currentDesc.Width;
3953 pow2Height = This->currentDesc.Height;
3954 }
3955 else
3956 {
3957 /* Find the nearest pow2 match */
3958 pow2Width = pow2Height = 1;
3959 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3960 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3961 }
3962 This->pow2Width = pow2Width;
3963 This->pow2Height = pow2Height;
3964
3965 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3966 /** TODO: add support for non power two compressed textures **/
3967 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
3968 {
3969 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3970 This, This->currentDesc.Width, This->currentDesc.Height);
3971 return WINED3DERR_NOTAVAILABLE;
3972 }
3973 }
3974
3975 if(pow2Width != This->currentDesc.Width ||
3976 pow2Height != This->currentDesc.Height) {
3977 This->Flags |= SFLAG_NONPOW2;
3978 }
3979
3980 TRACE("%p\n", This);
3981 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
3982 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
3983 {
3984 /* one of three options
3985 1: Do the same as we do with nonpow 2 and scale the texture, (any texture ops would require the texture to be scaled which is potentially slow)
3986 2: Set the texture to the maximum size (bad idea)
3987 3: WARN and return WINED3DERR_NOTAVAILABLE;
3988 4: Create the surface, but allow it to be used only for DirectDraw Blts. Some apps(e.g. Swat 3) create textures with a Height of 16 and a Width > 3000 and blt 16x16 letter areas from them to the render target.
3989 */
3990 if(This->resource.pool == WINED3DPOOL_DEFAULT || This->resource.pool == WINED3DPOOL_MANAGED)
3991 {
3992 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This);
3993 return WINED3DERR_NOTAVAILABLE;
3994 }
3995
3996 /* We should never use this surface in combination with OpenGL! */
3997 TRACE("(%p) Creating an oversized surface: %ux%u\n", This, This->pow2Width, This->pow2Height);
3998 }
3999 else
4000 {
4001 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4002 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4003 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4004 */
4005 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4006 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4007 && gl_info->supported[EXT_PALETTED_TEXTURE]
4008 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4009 {
4010 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4011 This->pow2Width = This->currentDesc.Width;
4012 This->pow2Height = This->currentDesc.Height;
4013 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4014 }
4015 }
4016
4017 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4018 switch(wined3d_settings.offscreen_rendering_mode) {
4019 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4020 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4021 }
4022 }
4023
4024 This->Flags |= SFLAG_INSYSMEM;
4025
4026 return WINED3D_OK;
4027}
4028
4029/* GL locking is done by the caller */
4030static void surface_depth_blt(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
4031 GLuint texture, GLsizei w, GLsizei h, GLenum target)
4032{
4033 IWineD3DDeviceImpl *device = This->resource.device;
4034 struct blt_info info;
4035 GLint old_binding = 0;
4036
4037 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4038
4039 glDisable(GL_CULL_FACE);
4040 glDisable(GL_BLEND);
4041 glDisable(GL_ALPHA_TEST);
4042 glDisable(GL_SCISSOR_TEST);
4043 glDisable(GL_STENCIL_TEST);
4044 glEnable(GL_DEPTH_TEST);
4045 glDepthFunc(GL_ALWAYS);
4046 glDepthMask(GL_TRUE);
4047 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4048 glViewport(0, 0, w, h);
4049
4050 surface_get_blt_info(target, NULL, w, h, &info);
4051 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4052 glGetIntegerv(info.binding, &old_binding);
4053 glBindTexture(info.bind_target, texture);
4054
4055 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4056
4057 glBegin(GL_TRIANGLE_STRIP);
4058 glTexCoord3fv(info.coords[0]);
4059 glVertex2f(-1.0f, -1.0f);
4060 glTexCoord3fv(info.coords[1]);
4061 glVertex2f(1.0f, -1.0f);
4062 glTexCoord3fv(info.coords[2]);
4063 glVertex2f(-1.0f, 1.0f);
4064 glTexCoord3fv(info.coords[3]);
4065 glVertex2f(1.0f, 1.0f);
4066 glEnd();
4067
4068 glBindTexture(info.bind_target, old_binding);
4069
4070 glPopAttrib();
4071
4072 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4073}
4074
4075void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4076 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4077
4078 TRACE("(%p) New location %#x\n", This, location);
4079
4080 if (location & ~SFLAG_DS_LOCATIONS) {
4081 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4082 }
4083
4084 This->Flags &= ~SFLAG_DS_LOCATIONS;
4085 This->Flags |= location;
4086}
4087
4088/* Context activation is done by the caller. */
4089void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4090{
4091 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4092 IWineD3DDeviceImpl *device = This->resource.device;
4093 const struct wined3d_gl_info *gl_info = context->gl_info;
4094
4095 TRACE("(%p) New location %#x\n", This, location);
4096
4097 /* TODO: Make this work for modes other than FBO */
4098 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4099
4100 if (This->Flags & location) {
4101 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4102 return;
4103 }
4104
4105 if (This->current_renderbuffer) {
4106 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4107 return;
4108 }
4109
4110 if (location == SFLAG_DS_OFFSCREEN) {
4111 if (This->Flags & SFLAG_DS_ONSCREEN) {
4112 GLint old_binding = 0;
4113 GLenum bind_target;
4114
4115 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4116
4117 ENTER_GL();
4118
4119 if (!device->depth_blt_texture) {
4120 glGenTextures(1, &device->depth_blt_texture);
4121 }
4122
4123 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4124 * directly on the FBO texture. That's because we need to flip. */
4125 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4126 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4127 {
4128 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4129 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4130 } else {
4131 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4132 bind_target = GL_TEXTURE_2D;
4133 }
4134 glBindTexture(bind_target, device->depth_blt_texture);
4135 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4136 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4137 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4138 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4139 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4140 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4141 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4142 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4143 glBindTexture(bind_target, old_binding);
4144
4145 /* Setup the destination */
4146 if (!device->depth_blt_rb) {
4147 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4148 checkGLcall("glGenRenderbuffersEXT");
4149 }
4150 if (device->depth_blt_rb_w != This->currentDesc.Width
4151 || device->depth_blt_rb_h != This->currentDesc.Height) {
4152 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4153 checkGLcall("glBindRenderbufferEXT");
4154 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4155 This->currentDesc.Width, This->currentDesc.Height);
4156 checkGLcall("glRenderbufferStorageEXT");
4157 device->depth_blt_rb_w = This->currentDesc.Width;
4158 device->depth_blt_rb_h = This->currentDesc.Height;
4159 }
4160
4161 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4162 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4163 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4164 checkGLcall("glFramebufferRenderbufferEXT");
4165 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, This, FALSE);
4166
4167 /* Do the actual blit */
4168 surface_depth_blt(This, gl_info, device->depth_blt_texture,
4169 This->currentDesc.Width, This->currentDesc.Height, bind_target);
4170 checkGLcall("depth_blt");
4171
4172 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4173 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4174
4175 LEAVE_GL();
4176
4177 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4178 }
4179 else
4180 {
4181 FIXME("No up to date depth stencil location\n");
4182 }
4183 } else if (location == SFLAG_DS_ONSCREEN) {
4184 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4185 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4186
4187 ENTER_GL();
4188
4189 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4190 surface_depth_blt(This, gl_info, This->texture_name,
4191 This->currentDesc.Width, This->currentDesc.Height, This->texture_target);
4192 checkGLcall("depth_blt");
4193
4194 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4195
4196 LEAVE_GL();
4197
4198 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4199 }
4200 else
4201 {
4202 FIXME("No up to date depth stencil location\n");
4203 }
4204 } else {
4205 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4206 }
4207
4208 This->Flags |= location;
4209}
4210
4211static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4212 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4213 IWineD3DBaseTexture *texture;
4214 IWineD3DSurfaceImpl *overlay;
4215
4216 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4217 persistent ? "TRUE" : "FALSE");
4218
4219 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4220 if (surface_is_offscreen(iface))
4221 {
4222 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4223 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4224 }
4225 else
4226 {
4227 TRACE("Surface %p is an onscreen surface\n", iface);
4228 }
4229 }
4230
4231 if(persistent) {
4232 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4233 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4234 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4235 TRACE("Passing to container\n");
4236 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4237 IWineD3DBaseTexture_Release(texture);
4238 }
4239 }
4240 This->Flags &= ~SFLAG_LOCATIONS;
4241 This->Flags |= flag;
4242
4243 /* Redraw emulated overlays, if any */
4244 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4245 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4246 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4247 }
4248 }
4249 } else {
4250 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4251 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4252 TRACE("Passing to container\n");
4253 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4254 IWineD3DBaseTexture_Release(texture);
4255 }
4256 }
4257 This->Flags &= ~flag;
4258 }
4259
4260 if(!(This->Flags & SFLAG_LOCATIONS)) {
4261 ERR("%p: Surface does not have any up to date location\n", This);
4262 }
4263}
4264
4265static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4266{
4267 IWineD3DDeviceImpl *device = This->resource.device;
4268 IWineD3DSwapChainImpl *swapchain;
4269 struct wined3d_context *context;
4270 RECT src_rect, dst_rect;
4271
4272 surface_get_rect(This, rect_in, &src_rect);
4273
4274 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4275 if (context->render_offscreen)
4276 {
4277 dst_rect.left = src_rect.left;
4278 dst_rect.right = src_rect.right;
4279 dst_rect.top = src_rect.bottom;
4280 dst_rect.bottom = src_rect.top;
4281 }
4282 else
4283 {
4284 dst_rect = src_rect;
4285 }
4286
4287 device->blitter->set_shader((IWineD3DDevice *) device, This);
4288
4289 ENTER_GL();
4290 draw_textured_quad(This, &src_rect, &dst_rect, WINED3DTEXF_POINT);
4291 LEAVE_GL();
4292
4293 device->blitter->set_shader((IWineD3DDevice *) device, This);
4294
4295 swapchain = (This->Flags & SFLAG_SWAPCHAIN) ? (IWineD3DSwapChainImpl *)This->container : NULL;
4296 if (wined3d_settings.strict_draw_ordering || (swapchain
4297 && ((IWineD3DSurface *)This == swapchain->frontBuffer
4298 || swapchain->num_contexts > 1)))
4299 wglFlush(); /* Flush to ensure ordering across contexts. */
4300
4301 context_release(context);
4302}
4303
4304/*****************************************************************************
4305 * IWineD3DSurface::LoadLocation
4306 *
4307 * Copies the current surface data from wherever it is to the requested
4308 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4309 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4310 * multiple locations, the gl texture is preferred over the drawable, which is
4311 * preferred over system memory. The PBO counts as system memory. If rect is
4312 * not NULL, only the specified rectangle is copied (only supported for
4313 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4314 * location is marked up to date after the copy.
4315 *
4316 * Parameters:
4317 * flag: Surface location flag to be updated
4318 * rect: rectangle to be copied
4319 *
4320 * Returns:
4321 * WINED3D_OK on success
4322 * WINED3DERR_DEVICELOST on an internal error
4323 *
4324 *****************************************************************************/
4325static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4326 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4327 IWineD3DDeviceImpl *device = This->resource.device;
4328 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4329 struct wined3d_format_desc desc;
4330 CONVERT_TYPES convert;
4331 int width, pitch, outpitch;
4332 BYTE *mem;
4333 BOOL drawable_read_ok = TRUE;
4334 BOOL in_fbo = FALSE;
4335
4336 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4337 if (surface_is_offscreen(iface))
4338 {
4339 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4340 * Prefer SFLAG_INTEXTURE. */
4341 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4342 drawable_read_ok = FALSE;
4343 in_fbo = TRUE;
4344 }
4345 else
4346 {
4347 TRACE("Surface %p is an onscreen surface\n", iface);
4348 }
4349 }
4350
4351 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4352 if(rect) {
4353 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4354 }
4355
4356 if(This->Flags & flag) {
4357 TRACE("Location already up to date\n");
4358 return WINED3D_OK;
4359 }
4360
4361 if(!(This->Flags & SFLAG_LOCATIONS)) {
4362 ERR("%p: Surface does not have any up to date location\n", This);
4363 This->Flags |= SFLAG_LOST;
4364 return WINED3DERR_DEVICELOST;
4365 }
4366
4367 if(flag == SFLAG_INSYSMEM) {
4368 surface_prepare_system_memory(This);
4369
4370 /* Download the surface to system memory */
4371 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4372 {
4373 struct wined3d_context *context = NULL;
4374
4375 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4376
4377 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4378 surface_download_data(This, gl_info);
4379
4380 if (context) context_release(context);
4381 }
4382 else
4383 {
4384 /* Note: It might be faster to download into a texture first. */
4385 read_from_framebuffer(This, rect,
4386 This->resource.allocatedMemory,
4387 IWineD3DSurface_GetPitch(iface));
4388 }
4389 } else if(flag == SFLAG_INDRAWABLE) {
4390 if(This->Flags & SFLAG_INTEXTURE) {
4391 surface_blt_to_drawable(This, rect);
4392 } else {
4393 int byte_count;
4394 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4395 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4396 * values, otherwise we get incorrect values in the target. For now go the slow way
4397 * via a system memory copy
4398 */
4399 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4400 }
4401
4402 d3dfmt_get_conv(This, FALSE /* We need color keying */, FALSE /* We won't use textures */, &desc, &convert);
4403
4404 /* The width is in 'length' not in bytes */
4405 width = This->currentDesc.Width;
4406 pitch = IWineD3DSurface_GetPitch(iface);
4407
4408 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4409 * but it isn't set (yet) in all cases it is getting called. */
4410 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4411 {
4412 struct wined3d_context *context = NULL;
4413
4414 TRACE("Removing the pbo attached to surface %p\n", This);
4415
4416 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4417 surface_remove_pbo(This, gl_info);
4418 if (context) context_release(context);
4419 }
4420
4421 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4422 int height = This->currentDesc.Height;
4423 byte_count = desc.conv_byte_count;
4424
4425 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4426 outpitch = width * byte_count;
4427 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4428
4429 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4430 if(!mem) {
4431 ERR("Out of memory %d, %d!\n", outpitch, height);
4432 return WINED3DERR_OUTOFVIDEOMEMORY;
4433 }
4434 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4435
4436 This->Flags |= SFLAG_CONVERTED;
4437 } else {
4438 This->Flags &= ~SFLAG_CONVERTED;
4439 mem = This->resource.allocatedMemory;
4440 byte_count = desc.byte_count;
4441 }
4442
4443 flush_to_framebuffer_drawpixels(This, desc.glFormat, desc.glType, byte_count, mem);
4444
4445 /* Don't delete PBO memory */
4446 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4447 HeapFree(GetProcessHeap(), 0, mem);
4448 }
4449 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4450 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4451 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4452 }
4453 else
4454 {
4455 /* Upload from system memory */
4456 BOOL srgb = flag == SFLAG_INSRGBTEX;
4457 struct wined3d_context *context = NULL;
4458
4459 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4460 &desc, &convert);
4461
4462 if(srgb) {
4463 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4464 /* Performance warning ... */
4465 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4466 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4467 }
4468 } else {
4469 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4470 /* Performance warning ... */
4471 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4472 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4473 }
4474 }
4475 if(!(This->Flags & SFLAG_INSYSMEM)) {
4476 /* Should not happen */
4477 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4478 /* Lets hope we get it from somewhere... */
4479 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4480 }
4481
4482 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4483
4484 surface_prepare_texture(This, gl_info, srgb);
4485 surface_bind_and_dirtify(This, srgb);
4486
4487 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4488 This->Flags |= SFLAG_GLCKEY;
4489 This->glCKey = This->SrcBltCKey;
4490 }
4491 else This->Flags &= ~SFLAG_GLCKEY;
4492
4493 /* The width is in 'length' not in bytes */
4494 width = This->currentDesc.Width;
4495 pitch = IWineD3DSurface_GetPitch(iface);
4496
4497 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4498 * but it isn't set (yet) in all cases it is getting called. */
4499 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4500 TRACE("Removing the pbo attached to surface %p\n", This);
4501 surface_remove_pbo(This, gl_info);
4502 }
4503
4504 if(desc.convert) {
4505 /* This code is entered for texture formats which need a fixup. */
4506 int height = This->currentDesc.Height;
4507
4508 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4509 outpitch = width * desc.conv_byte_count;
4510 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4511
4512 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4513 if(!mem) {
4514 ERR("Out of memory %d, %d!\n", outpitch, height);
4515 if (context) context_release(context);
4516 return WINED3DERR_OUTOFVIDEOMEMORY;
4517 }
4518 desc.convert(This->resource.allocatedMemory, mem, pitch, width, height);
4519 } else if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4520 /* This code is only entered for color keying fixups */
4521 int height = This->currentDesc.Height;
4522
4523 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4524 outpitch = width * desc.conv_byte_count;
4525 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4526
4527 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4528 if(!mem) {
4529 ERR("Out of memory %d, %d!\n", outpitch, height);
4530 if (context) context_release(context);
4531 return WINED3DERR_OUTOFVIDEOMEMORY;
4532 }
4533 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4534 } else {
4535 mem = This->resource.allocatedMemory;
4536 }
4537
4538 /* Make sure the correct pitch is used */
4539 ENTER_GL();
4540 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4541 LEAVE_GL();
4542
4543 if (mem || (This->Flags & SFLAG_PBO))
4544 surface_upload_data(This, gl_info, &desc, srgb, mem);
4545
4546 /* Restore the default pitch */
4547 ENTER_GL();
4548 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4549 LEAVE_GL();
4550
4551 if (context) context_release(context);
4552
4553 /* Don't delete PBO memory */
4554 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4555 HeapFree(GetProcessHeap(), 0, mem);
4556 }
4557 }
4558
4559 if(rect == NULL) {
4560 This->Flags |= flag;
4561 }
4562
4563 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4564 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4565 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4566 }
4567
4568 return WINED3D_OK;
4569}
4570
4571static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4572{
4573 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4574 IWineD3DSwapChain *swapchain = NULL;
4575
4576 /* Update the drawable size method */
4577 if(container) {
4578 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4579 }
4580 if(swapchain) {
4581 This->get_drawable_size = get_drawable_size_swapchain;
4582 IWineD3DSwapChain_Release(swapchain);
4583 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4584 switch(wined3d_settings.offscreen_rendering_mode) {
4585 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4586 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4587 }
4588 }
4589
4590 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4591}
4592
4593static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4594 return SURFACE_OPENGL;
4595}
4596
4597static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4598 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4599 HRESULT hr;
4600
4601 /* If there's no destination surface there is nothing to do */
4602 if(!This->overlay_dest) return WINED3D_OK;
4603
4604 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4605 * update the overlay. Prevent an endless recursion
4606 */
4607 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4608 return WINED3D_OK;
4609 }
4610 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4611 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4612 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4613 NULL, WINED3DTEXF_LINEAR);
4614 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4615
4616 return hr;
4617}
4618
4619BOOL surface_is_offscreen(IWineD3DSurface *iface)
4620{
4621 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4622 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
4623
4624 /* Not on a swapchain - must be offscreen */
4625 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
4626
4627 /* The front buffer is always onscreen */
4628 if(iface == swapchain->frontBuffer) return FALSE;
4629
4630 /* If the swapchain is rendered to an FBO, the backbuffer is
4631 * offscreen, otherwise onscreen */
4632 return swapchain->render_to_fbo;
4633}
4634
4635const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4636{
4637 /* IUnknown */
4638 IWineD3DBaseSurfaceImpl_QueryInterface,
4639 IWineD3DBaseSurfaceImpl_AddRef,
4640 IWineD3DSurfaceImpl_Release,
4641 /* IWineD3DResource */
4642 IWineD3DBaseSurfaceImpl_GetParent,
4643 IWineD3DBaseSurfaceImpl_SetPrivateData,
4644 IWineD3DBaseSurfaceImpl_GetPrivateData,
4645 IWineD3DBaseSurfaceImpl_FreePrivateData,
4646 IWineD3DBaseSurfaceImpl_SetPriority,
4647 IWineD3DBaseSurfaceImpl_GetPriority,
4648 IWineD3DSurfaceImpl_PreLoad,
4649 IWineD3DSurfaceImpl_UnLoad,
4650 IWineD3DBaseSurfaceImpl_GetType,
4651 /* IWineD3DSurface */
4652 IWineD3DBaseSurfaceImpl_GetContainer,
4653 IWineD3DBaseSurfaceImpl_GetDesc,
4654 IWineD3DSurfaceImpl_LockRect,
4655 IWineD3DSurfaceImpl_UnlockRect,
4656 IWineD3DSurfaceImpl_GetDC,
4657 IWineD3DSurfaceImpl_ReleaseDC,
4658 IWineD3DSurfaceImpl_Flip,
4659 IWineD3DSurfaceImpl_Blt,
4660 IWineD3DBaseSurfaceImpl_GetBltStatus,
4661 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4662 IWineD3DBaseSurfaceImpl_IsLost,
4663 IWineD3DBaseSurfaceImpl_Restore,
4664 IWineD3DSurfaceImpl_BltFast,
4665 IWineD3DBaseSurfaceImpl_GetPalette,
4666 IWineD3DBaseSurfaceImpl_SetPalette,
4667 IWineD3DSurfaceImpl_RealizePalette,
4668 IWineD3DBaseSurfaceImpl_SetColorKey,
4669 IWineD3DBaseSurfaceImpl_GetPitch,
4670 IWineD3DSurfaceImpl_SetMem,
4671 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4672 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4673 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4674 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4675 IWineD3DBaseSurfaceImpl_SetClipper,
4676 IWineD3DBaseSurfaceImpl_GetClipper,
4677 /* Internal use: */
4678 IWineD3DSurfaceImpl_LoadTexture,
4679 IWineD3DSurfaceImpl_BindTexture,
4680 IWineD3DSurfaceImpl_SaveSnapshot,
4681 IWineD3DSurfaceImpl_SetContainer,
4682 IWineD3DBaseSurfaceImpl_GetData,
4683 IWineD3DSurfaceImpl_SetFormat,
4684 IWineD3DSurfaceImpl_PrivateSetup,
4685 IWineD3DSurfaceImpl_ModifyLocation,
4686 IWineD3DSurfaceImpl_LoadLocation,
4687 IWineD3DSurfaceImpl_GetImplType,
4688 IWineD3DSurfaceImpl_DrawOverlay
4689};
4690
4691static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4692/* Context activation is done by the caller. */
4693static void ffp_blit_free(IWineD3DDevice *iface) { }
4694
4695/* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
4696/* Context activation is done by the caller. */
4697static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
4698{
4699 BYTE table[256][4];
4700 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
4701
4702 d3dfmt_p8_init_palette(surface, table, colorkey_active);
4703
4704 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
4705 ENTER_GL();
4706 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
4707 LEAVE_GL();
4708}
4709
4710/* Context activation is done by the caller. */
4711static HRESULT ffp_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4712{
4713 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4714 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4715 enum complex_fixup fixup = get_complex_fixup(surface->resource.format_desc->color_fixup);
4716
4717 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
4718 * else the surface is converted in software at upload time in LoadLocation.
4719 */
4720 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4721 ffp_blit_p8_upload_palette(surface, gl_info);
4722
4723 ENTER_GL();
4724 glEnable(surface->texture_target);
4725 checkGLcall("glEnable(surface->texture_target)");
4726 LEAVE_GL();
4727 return WINED3D_OK;
4728}
4729
4730/* Context activation is done by the caller. */
4731static void ffp_blit_unset(IWineD3DDevice *iface)
4732{
4733 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4734 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4735
4736 ENTER_GL();
4737 glDisable(GL_TEXTURE_2D);
4738 checkGLcall("glDisable(GL_TEXTURE_2D)");
4739 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4740 {
4741 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4742 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4743 }
4744 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4745 {
4746 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4747 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4748 }
4749 LEAVE_GL();
4750}
4751
4752static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4753 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4754 const struct wined3d_format_desc *src_format_desc,
4755 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4756 const struct wined3d_format_desc *dst_format_desc)
4757{
4758 enum complex_fixup src_fixup;
4759
4760 if (blit_op == BLIT_OP_COLOR_FILL)
4761 {
4762 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4763 {
4764 TRACE("Color fill not supported\n");
4765 return FALSE;
4766 }
4767
4768 return TRUE;
4769 }
4770
4771 src_fixup = get_complex_fixup(src_format_desc->color_fixup);
4772 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4773 {
4774 TRACE("Checking support for fixup:\n");
4775 dump_color_fixup_desc(src_format_desc->color_fixup);
4776 }
4777
4778 if (blit_op != BLIT_OP_BLIT)
4779 {
4780 TRACE("Unsupported blit_op=%d\n", blit_op);
4781 return FALSE;
4782 }
4783
4784 if (!is_identity_fixup(dst_format_desc->color_fixup))
4785 {
4786 TRACE("Destination fixups are not supported\n");
4787 return FALSE;
4788 }
4789
4790 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4791 {
4792 TRACE("P8 fixup supported\n");
4793 return TRUE;
4794 }
4795
4796 /* We only support identity conversions. */
4797 if (is_identity_fixup(src_format_desc->color_fixup))
4798 {
4799 TRACE("[OK]\n");
4800 return TRUE;
4801 }
4802
4803 TRACE("[FAILED]\n");
4804 return FALSE;
4805}
4806
4807static HRESULT ffp_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4808{
4809 return IWineD3DDeviceImpl_ClearSurface(device, dst_surface, 1 /* Number of rectangles */,
4810 (const WINED3DRECT*)dst_rect, WINED3DCLEAR_TARGET, fill_color, 0.0f /* Z */, 0 /* Stencil */);
4811}
4812
4813const struct blit_shader ffp_blit = {
4814 ffp_blit_alloc,
4815 ffp_blit_free,
4816 ffp_blit_set,
4817 ffp_blit_unset,
4818 ffp_blit_supported,
4819 ffp_blit_color_fill
4820};
4821
4822static HRESULT cpu_blit_alloc(IWineD3DDevice *iface)
4823{
4824 return WINED3D_OK;
4825}
4826
4827/* Context activation is done by the caller. */
4828static void cpu_blit_free(IWineD3DDevice *iface)
4829{
4830}
4831
4832/* Context activation is done by the caller. */
4833static HRESULT cpu_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4834{
4835 return WINED3D_OK;
4836}
4837
4838/* Context activation is done by the caller. */
4839static void cpu_blit_unset(IWineD3DDevice *iface)
4840{
4841}
4842
4843static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4844 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4845 const struct wined3d_format_desc *src_format_desc,
4846 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4847 const struct wined3d_format_desc *dst_format_desc)
4848{
4849 if (blit_op == BLIT_OP_COLOR_FILL)
4850 {
4851 return TRUE;
4852 }
4853
4854 return FALSE;
4855}
4856
4857static HRESULT cpu_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4858{
4859 WINEDDBLTFX BltFx;
4860 memset(&BltFx, 0, sizeof(BltFx));
4861 BltFx.dwSize = sizeof(BltFx);
4862 BltFx.u5.dwFillColor = color_convert_argb_to_fmt(fill_color, dst_surface->resource.format_desc->format);
4863 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface*)dst_surface, dst_rect, NULL, NULL, WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
4864}
4865
4866const struct blit_shader cpu_blit = {
4867 cpu_blit_alloc,
4868 cpu_blit_free,
4869 cpu_blit_set,
4870 cpu_blit_unset,
4871 cpu_blit_supported,
4872 cpu_blit_color_fill
4873};
4874
4875static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4876 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4877 const struct wined3d_format_desc *src_format_desc,
4878 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4879 const struct wined3d_format_desc *dst_format_desc)
4880{
4881 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
4882 return FALSE;
4883
4884 /* We only support blitting. Things like color keying / color fill should
4885 * be handled by other blitters.
4886 */
4887 if (blit_op != BLIT_OP_BLIT)
4888 return FALSE;
4889
4890 /* Source and/or destination need to be on the GL side */
4891 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
4892 return FALSE;
4893
4894 if(!((src_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET))
4895 && ((dst_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4896 return FALSE;
4897
4898 if (!is_identity_fixup(src_format_desc->color_fixup) ||
4899 !is_identity_fixup(dst_format_desc->color_fixup))
4900 return FALSE;
4901
4902 if (!(src_format_desc->format == dst_format_desc->format
4903 || (is_identity_fixup(src_format_desc->color_fixup)
4904 && is_identity_fixup(dst_format_desc->color_fixup))))
4905 return FALSE;
4906
4907 return TRUE;
4908}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette