VirtualBox

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

Last change on this file since 22415 was 22415, checked in by vboxsync, 16 years ago

crOpenGL: fix random crashes/memory corruption when running java apps

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