2 * IWineD3DSurface Implementation
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
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
35 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
38 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
39 static void surface_remove_pbo(IWineD3DSurfaceImpl *This);
41 void surface_force_reload(IWineD3DSurface *iface)
43 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
45 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
48 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
50 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
56 name = &This->glDescription.srgbTextureName;
57 flag = SFLAG_INSRGBTEX;
61 name = &This->glDescription.textureName;
62 flag = SFLAG_INTEXTURE;
65 TRACE("(%p) : setting texture name %u\n", This, new_name);
67 if (!*name && new_name)
69 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
70 * surface has no texture name yet. See if we can get rid of this. */
71 if (This->Flags & flag)
72 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
73 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
77 surface_force_reload(iface);
80 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
82 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
84 TRACE("(%p) : setting target %#x\n", This, target);
86 if (This->glDescription.target != target)
88 if (target == GL_TEXTURE_RECTANGLE_ARB)
90 This->Flags &= ~SFLAG_NORMCOORD;
92 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
94 This->Flags |= SFLAG_NORMCOORD;
97 This->glDescription.target = target;
98 surface_force_reload(iface);
101 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
104 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
105 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
106 * gl states. The current texture unit should always be a valid one.
108 * To be more specific, this is tricky because we can implicitly be called
109 * from sampler() in state.c. This means we can't touch anything other than
110 * whatever happens to be the currently active texture, or we would risk
111 * marking already applied sampler states dirty again.
113 * TODO: Track the current active texture per GL context instead of using glGet
115 GLint active_texture;
117 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
119 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
121 if (active_sampler != -1) {
122 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
124 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
127 /* This function checks if the primary render target uses the 8bit paletted format. */
128 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
130 if (device->render_targets && device->render_targets[0]) {
131 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
132 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
133 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
139 /* This call just downloads data, the caller is responsible for activating the
140 * right context and binding the correct texture. */
141 static void surface_download_data(IWineD3DSurfaceImpl *This) {
142 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
144 /* Only support read back of converted P8 surfaces */
145 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
147 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
153 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
154 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
155 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
157 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
158 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
160 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n",
161 This, This->glDescription.level, format_desc->glFormat, format_desc->glType,
162 This->resource.allocatedMemory);
164 if(This->Flags & SFLAG_PBO) {
165 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
166 checkGLcall("glBindBufferARB");
167 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
168 checkGLcall("glGetCompressedTexImageARB()");
169 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
170 checkGLcall("glBindBufferARB");
172 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
173 checkGLcall("glGetCompressedTexImageARB()");
179 GLenum format = format_desc->glFormat;
180 GLenum type = format_desc->glType;
184 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
185 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
188 type = GL_UNSIGNED_BYTE;
191 if (This->Flags & SFLAG_NONPOW2) {
192 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
193 src_pitch = format_desc->byte_count * This->pow2Width;
194 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
195 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
196 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
198 mem = This->resource.allocatedMemory;
201 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
204 if(This->Flags & SFLAG_PBO) {
205 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
206 checkGLcall("glBindBufferARB");
208 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
210 checkGLcall("glGetTexImage()");
212 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
213 checkGLcall("glBindBufferARB");
215 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
217 checkGLcall("glGetTexImage()");
221 if (This->Flags & SFLAG_NONPOW2) {
222 const BYTE *src_data;
226 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
227 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
228 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
230 * We're doing this...
232 * instead of boxing the texture :
233 * |<-texture width ->| -->pow2width| /\
234 * |111111111111111111| | |
235 * |222 Texture 222222| boxed empty | texture height
236 * |3333 Data 33333333| | |
237 * |444444444444444444| | \/
238 * ----------------------------------- |
239 * | boxed empty | boxed empty | pow2height
241 * -----------------------------------
244 * we're repacking the data to the expected texture width
246 * |<-texture width ->| -->pow2width| /\
247 * |111111111111111111222222222222222| |
248 * |222333333333333333333444444444444| texture height
252 * | empty | pow2height
254 * -----------------------------------
258 * |<-texture width ->| /\
259 * |111111111111111111|
260 * |222222222222222222|texture height
261 * |333333333333333333|
262 * |444444444444444444| \/
263 * --------------------
265 * this also means that any references to allocatedMemory should work with the data as if were a
266 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
268 * internally the texture is still stored in a boxed format so any references to textureName will
269 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
271 * Performance should not be an issue, because applications normally do not lock the surfaces when
272 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
273 * and doesn't have to be re-read.
276 dst_data = This->resource.allocatedMemory;
277 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
278 for (y = 1 ; y < This->currentDesc.Height; y++) {
279 /* skip the first row */
280 src_data += src_pitch;
281 dst_data += dst_pitch;
282 memcpy(dst_data, src_data, dst_pitch);
285 HeapFree(GetProcessHeap(), 0, mem);
289 /* Surface has now been downloaded */
290 This->Flags |= SFLAG_INSYSMEM;
293 /* This call just uploads data, the caller is responsible for activating the
294 * right context and binding the correct texture. */
295 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
296 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
298 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
300 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
301 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
302 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
304 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
305 FIXME("Using DXT1/3/5 without advertized support\n");
307 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
308 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
309 * function uses glCompressedTexImage2D instead of the SubImage call
311 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
314 if(This->Flags & SFLAG_PBO) {
315 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
316 checkGLcall("glBindBufferARB");
317 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
319 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
320 width, height, 0 /* border */, This->resource.size, NULL));
321 checkGLcall("glCompressedTexSubImage2D");
323 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
324 checkGLcall("glBindBufferARB");
326 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
327 width, height, 0 /* border */, This->resource.size, data));
328 checkGLcall("glCompressedTexSubImage2D");
333 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
336 if(This->Flags & SFLAG_PBO) {
337 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
338 checkGLcall("glBindBufferARB");
339 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
341 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
342 checkGLcall("glTexSubImage2D");
344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
345 checkGLcall("glBindBufferARB");
348 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
349 checkGLcall("glTexSubImage2D");
356 /* This call just allocates the texture, the caller is responsible for
357 * activating the right context and binding the correct texture. */
358 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
359 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
360 BOOL enable_client_storage = FALSE;
361 const BYTE *mem = NULL;
363 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
365 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",
366 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
367 internal, width, height, format, type);
369 if (format_desc->format == WINED3DFMT_DXT1 || format_desc->format == WINED3DFMT_DXT2
370 || format_desc->format == WINED3DFMT_DXT3 || format_desc->format == WINED3DFMT_DXT4
371 || format_desc->format == WINED3DFMT_DXT5 || format_desc->format == WINED3DFMT_ATI2N)
373 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
374 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
376 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
377 * once, unfortunately
379 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
380 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
381 This->Flags |= SFLAG_CLIENT;
382 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
384 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
385 width, height, 0 /* border */, This->resource.size, mem));
394 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
395 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
396 /* In some cases we want to disable client storage.
397 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
398 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
399 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
400 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
401 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
403 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
404 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
405 This->Flags &= ~SFLAG_CLIENT;
406 enable_client_storage = TRUE;
408 This->Flags |= SFLAG_CLIENT;
410 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
411 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
413 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
416 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
417 checkGLcall("glTexImage2D");
419 if(enable_client_storage) {
420 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
421 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
426 /* In D3D the depth stencil dimensions have to be greater than or equal to the
427 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
428 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
429 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
430 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
431 renderbuffer_entry_t *entry;
432 GLuint renderbuffer = 0;
433 unsigned int src_width, src_height;
435 src_width = This->pow2Width;
436 src_height = This->pow2Height;
438 /* A depth stencil smaller than the render target is not valid */
439 if (width > src_width || height > src_height) return;
441 /* Remove any renderbuffer set if the sizes match */
442 if (width == src_width && height == src_height) {
443 This->current_renderbuffer = NULL;
447 /* Look if we've already got a renderbuffer of the correct dimensions */
448 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
449 if (entry->width == width && entry->height == height) {
450 renderbuffer = entry->id;
451 This->current_renderbuffer = entry;
457 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
458 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
459 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
460 This->resource.format_desc->glInternal, width, height));
462 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
463 entry->width = width;
464 entry->height = height;
465 entry->id = renderbuffer;
466 list_add_head(&This->renderbuffers, &entry->entry);
468 This->current_renderbuffer = entry;
471 checkGLcall("set_compatible_renderbuffer");
474 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
475 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
476 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
478 TRACE("(%p) : swapchain %p\n", This, swapchain);
480 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
481 TRACE("Returning GL_BACK\n");
483 } else if (swapchain_impl->frontBuffer == iface) {
484 TRACE("Returning GL_FRONT\n");
488 FIXME("Higher back buffer, returning GL_BACK\n");
492 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
493 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
496 IWineD3DBaseTexture *baseTexture = NULL;
498 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
499 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
501 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
504 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
505 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
506 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
507 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
511 This->dirtyRect.left = 0;
512 This->dirtyRect.top = 0;
513 This->dirtyRect.right = This->currentDesc.Width;
514 This->dirtyRect.bottom = This->currentDesc.Height;
517 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
518 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
520 /* if the container is a basetexture then mark it dirty. */
521 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
523 TRACE("Passing to container\n");
524 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
525 IWineD3DBaseTexture_Release(baseTexture);
529 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
531 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
532 ULONG ref = InterlockedDecrement(&This->resource.ref);
533 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
535 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
536 renderbuffer_entry_t *entry, *entry2;
537 TRACE("(%p) : cleaning up\n", This);
539 /* Need a context to destroy the texture. Use the currently active render target, but only if
540 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
541 * When destroying the primary rt, Uninit3D will activate a context before doing anything
543 if(device->render_targets && device->render_targets[0]) {
544 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
548 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
549 TRACE("Deleting texture %d\n", This->glDescription.textureName);
550 glDeleteTextures(1, &This->glDescription.textureName);
553 if(This->Flags & SFLAG_PBO) {
555 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
558 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
559 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
560 HeapFree(GetProcessHeap(), 0, entry);
564 if(This->Flags & SFLAG_DIBSECTION) {
566 SelectObject(This->hDC, This->dib.holdbitmap);
568 /* Release the DIB section */
569 DeleteObject(This->dib.DIBsection);
570 This->dib.bitmap_data = NULL;
571 This->resource.allocatedMemory = NULL;
573 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
575 HeapFree(GetProcessHeap(), 0, This->palette9);
577 resource_cleanup((IWineD3DResource *)iface);
579 if(This->overlay_dest) {
580 list_remove(&This->overlay_entry);
583 TRACE("(%p) Released\n", This);
584 HeapFree(GetProcessHeap(), 0, This);
590 /* ****************************************************
591 IWineD3DSurface IWineD3DResource parts follow
592 **************************************************** */
594 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
596 /* TODO: check for locks */
597 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
598 IWineD3DBaseTexture *baseTexture = NULL;
599 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
601 TRACE("(%p)Checking to see if the container is a base texture\n", This);
602 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
603 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
604 TRACE("Passing to container\n");
605 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
606 IWineD3DBaseTexture_Release(baseTexture);
608 TRACE("(%p) : About to load surface\n", This);
610 if(!device->isInDraw) {
611 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
614 if (This->resource.format_desc->format == WINED3DFMT_P8
615 || This->resource.format_desc->format == WINED3DFMT_A8P8)
617 if(palette9_changed(This)) {
618 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
619 /* TODO: This is not necessarily needed with hw palettized texture support */
620 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
621 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
622 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
626 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
628 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
629 /* Tell opengl to try and keep this texture in video ram (well mostly) */
633 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
640 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
641 surface_internal_preload(iface, SRGB_ANY);
644 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
645 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
646 This->resource.allocatedMemory =
647 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
650 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
651 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
652 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
653 checkGLcall("glGetBufferSubData");
654 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
655 checkGLcall("glDeleteBuffers");
659 This->Flags &= ~SFLAG_PBO;
662 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
663 IWineD3DBaseTexture *texture = NULL;
664 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
665 renderbuffer_entry_t *entry, *entry2;
666 TRACE("(%p)\n", iface);
668 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
669 /* Default pool resources are supposed to be destroyed before Reset is called.
670 * Implicit resources stay however. So this means we have an implicit render target
671 * or depth stencil. The content may be destroyed, but we still have to tear down
672 * opengl resources, so we cannot leave early.
674 * Put the most up to date surface location into the drawable. D3D-wise this content
675 * is undefined, so it would be nowhere, but that would make the location management
676 * more complicated. The drawable is a sane location, because if we mark sysmem or
677 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
678 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
681 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
682 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
684 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
687 /* Load the surface into system memory */
688 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
689 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
691 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
692 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
694 /* Destroy PBOs, but load them into real sysmem before */
695 if(This->Flags & SFLAG_PBO) {
696 surface_remove_pbo(This);
699 /* Destroy fbo render buffers. This is needed for implicit render targets, for
700 * all application-created targets the application has to release the surface
701 * before calling _Reset
703 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
705 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
707 list_remove(&entry->entry);
708 HeapFree(GetProcessHeap(), 0, entry);
710 list_init(&This->renderbuffers);
711 This->current_renderbuffer = NULL;
713 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
716 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
719 glDeleteTextures(1, &This->glDescription.textureName);
720 This->glDescription.textureName = 0;
723 IWineD3DBaseTexture_Release(texture);
728 /* ******************************************************
729 IWineD3DSurface IWineD3DSurface parts follow
730 ****************************************************** */
732 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
734 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
735 TRACE("(%p) : returning %p\n", This, &This->glDescription);
736 *glDescription = &This->glDescription;
739 /* Read the framebuffer back into the surface */
740 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
741 IWineD3DSwapChainImpl *swapchain;
742 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
746 BYTE *row, *top, *bottom;
750 BOOL srcIsUpsideDown;
755 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
756 static BOOL warned = FALSE;
758 ERR("The application tries to lock the render target, but render target locking is disabled\n");
764 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
765 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
766 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
767 * context->last_was_blit set on the unlock.
769 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
772 /* Select the correct read buffer, and give some debug output.
773 * There is no need to keep track of the current read buffer or reset it, every part of the code
774 * that reads sets the read buffer as desired.
776 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
778 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
779 TRACE("Locking %#x buffer\n", buffer);
780 glReadBuffer(buffer);
781 checkGLcall("glReadBuffer");
783 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
784 srcIsUpsideDown = FALSE;
786 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
787 * Read from the back buffer
789 TRACE("Locking offscreen render target\n");
790 glReadBuffer(myDevice->offscreenBuffer);
791 srcIsUpsideDown = TRUE;
794 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
798 local_rect.right = This->currentDesc.Width;
799 local_rect.bottom = This->currentDesc.Height;
803 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
805 switch(This->resource.format_desc->format)
809 if(primary_render_target_is_p8(myDevice)) {
810 /* In case of P8 render targets the index is stored in the alpha component */
812 type = GL_UNSIGNED_BYTE;
814 bpp = This->resource.format_desc->byte_count;
816 /* GL can't return palettized data, so read ARGB pixels into a
817 * separate block of memory and convert them into palettized format
818 * in software. Slow, but if the app means to use palettized render
819 * targets and locks it...
821 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
822 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
823 * for the color channels when palettizing the colors.
826 type = GL_UNSIGNED_BYTE;
828 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
830 ERR("Out of memory\n");
834 bpp = This->resource.format_desc->byte_count * 3;
841 fmt = This->resource.format_desc->glFormat;
842 type = This->resource.format_desc->glType;
843 bpp = This->resource.format_desc->byte_count;
846 if(This->Flags & SFLAG_PBO) {
847 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
848 checkGLcall("glBindBufferARB");
850 ERR("mem not null for pbo -- unexpected\n");
855 /* Save old pixel store pack state */
856 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
857 checkGLcall("glIntegerv");
858 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
859 checkGLcall("glIntegerv");
860 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
861 checkGLcall("glIntegerv");
863 /* Setup pixel store pack state -- to glReadPixels into the correct place */
864 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
865 checkGLcall("glPixelStorei");
866 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
867 checkGLcall("glPixelStorei");
868 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
869 checkGLcall("glPixelStorei");
871 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
872 local_rect.right - local_rect.left,
873 local_rect.bottom - local_rect.top,
875 checkGLcall("glReadPixels");
877 /* Reset previous pixel store pack state */
878 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
879 checkGLcall("glPixelStorei");
880 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
881 checkGLcall("glPixelStorei");
882 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
883 checkGLcall("glPixelStorei");
885 if(This->Flags & SFLAG_PBO) {
886 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
887 checkGLcall("glBindBufferARB");
889 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
890 * to get a pointer to it and perform the flipping in software. This is a lot
891 * faster than calling glReadPixels for each line. In case we want more speed
892 * we should rerender it flipped in a FBO and read the data back from the FBO. */
893 if(!srcIsUpsideDown) {
894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
895 checkGLcall("glBindBufferARB");
897 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
898 checkGLcall("glMapBufferARB");
902 /* TODO: Merge this with the palettization loop below for P8 targets */
903 if(!srcIsUpsideDown) {
905 /* glReadPixels returns the image upside down, and there is no way to prevent this.
906 Flip the lines in software */
907 len = (local_rect.right - local_rect.left) * bpp;
908 off = local_rect.left * bpp;
910 row = HeapAlloc(GetProcessHeap(), 0, len);
912 ERR("Out of memory\n");
913 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
918 top = mem + pitch * local_rect.top;
919 bottom = mem + pitch * (local_rect.bottom - 1);
920 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
921 memcpy(row, top + off, len);
922 memcpy(top + off, bottom + off, len);
923 memcpy(bottom + off, row, len);
927 HeapFree(GetProcessHeap(), 0, row);
929 /* Unmap the temp PBO buffer */
930 if(This->Flags & SFLAG_PBO) {
931 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
932 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
938 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
939 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
940 * the same color but we have no choice.
941 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
943 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
945 const PALETTEENTRY *pal = NULL;
946 DWORD width = pitch / 3;
950 pal = This->palette->palents;
952 ERR("Palette is missing, cannot perform inverse palette lookup\n");
953 HeapFree(GetProcessHeap(), 0, mem);
957 for(y = local_rect.top; y < local_rect.bottom; y++) {
958 for(x = local_rect.left; x < local_rect.right; x++) {
959 /* start lines pixels */
960 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
961 const BYTE *green = blue + 1;
962 const BYTE *red = green + 1;
964 for(c = 0; c < 256; c++) {
965 if(*red == pal[c].peRed &&
966 *green == pal[c].peGreen &&
967 *blue == pal[c].peBlue)
969 *((BYTE *) dest + y * width + x) = c;
975 HeapFree(GetProcessHeap(), 0, mem);
979 /* Read the framebuffer contents into a texture */
980 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
982 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
983 IWineD3DSwapChainImpl *swapchain;
985 GLenum format, internal, type;
986 CONVERT_TYPES convert;
988 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
990 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
992 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
993 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
994 * states in the stateblock, and no driver was found yet that had bugs in that regard.
996 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
997 surface_bind_and_dirtify(This, srgb);
1000 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1003 /* Select the correct read buffer, and give some debug output.
1004 * There is no need to keep track of the current read buffer or reset it, every part of the code
1005 * that reads sets the read buffer as desired.
1007 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1009 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1010 TRACE("Locking %#x buffer\n", buffer);
1013 glReadBuffer(buffer);
1014 checkGLcall("glReadBuffer");
1017 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1019 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1020 * Read from the back buffer
1022 TRACE("Locking offscreen render target\n");
1024 glReadBuffer(device->offscreenBuffer);
1025 checkGLcall("glReadBuffer");
1029 if(!(This->Flags & alloc_flag)) {
1030 surface_allocate_surface(This, internal, This->pow2Width,
1031 This->pow2Height, format, type);
1032 This->Flags |= alloc_flag;
1036 /* If !SrcIsUpsideDown we should flip the surface.
1037 * This can be done using glCopyTexSubImage2D but this
1038 * is VERY slow, so don't do that. We should prevent
1039 * this code from getting called in such cases or perhaps
1040 * we can use FBOs */
1042 glCopyTexSubImage2D(This->glDescription.target,
1043 This->glDescription.level,
1045 This->currentDesc.Width,
1046 This->currentDesc.Height);
1047 checkGLcall("glCopyTexSubImage2D");
1049 glReadBuffer(prevRead);
1050 checkGLcall("glReadBuffer");
1053 TRACE("Updated target %d\n", This->glDescription.target);
1056 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1057 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1058 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1061 if(!(This->Flags & SFLAG_DYNLOCK)) {
1063 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1064 if(This->lockCount > MAXLOCKCOUNT) {
1065 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1066 This->Flags |= SFLAG_DYNLOCK;
1070 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1071 * Also don't create a PBO for systemmem surfaces.
1073 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1075 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1077 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1080 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1081 error = glGetError();
1082 if(This->pbo == 0 || error != GL_NO_ERROR) {
1083 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1086 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1088 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1089 checkGLcall("glBindBufferARB");
1091 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1092 checkGLcall("glBufferDataARB");
1094 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1095 checkGLcall("glBindBufferARB");
1097 /* We don't need the system memory anymore and we can't even use it for PBOs */
1098 if(!(This->Flags & SFLAG_CLIENT)) {
1099 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1100 This->resource.heapMemory = NULL;
1102 This->resource.allocatedMemory = NULL;
1103 This->Flags |= SFLAG_PBO;
1105 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1106 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1109 if(!This->resource.heapMemory) {
1110 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1112 This->resource.allocatedMemory =
1113 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1114 if(This->Flags & SFLAG_INSYSMEM) {
1115 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1120 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1121 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1122 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1124 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1126 /* This is also done in the base class, but we have to verify this before loading any data from
1127 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1128 * may interfere, and all other bad things may happen
1130 if (This->Flags & SFLAG_LOCKED) {
1131 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1132 return WINED3DERR_INVALIDCALL;
1134 This->Flags |= SFLAG_LOCKED;
1136 if (!(This->Flags & SFLAG_LOCKABLE))
1138 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1141 if (Flags & WINED3DLOCK_DISCARD) {
1142 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1143 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1144 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1145 This->Flags |= SFLAG_INSYSMEM;
1149 if (This->Flags & SFLAG_INSYSMEM) {
1150 TRACE("Local copy is up to date, not downloading data\n");
1151 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1155 /* Now download the surface content from opengl
1156 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
1157 * Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
1159 if ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])
1161 const RECT *pass_rect = pRect;
1163 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
1164 * because most caller functions do not need that. So do that here
1169 pRect->right == This->currentDesc.Width &&
1170 pRect->bottom == This->currentDesc.Height) {
1174 switch(wined3d_settings.rendertargetlock_mode) {
1177 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
1179 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
1180 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
1181 * This may be faster on some cards
1183 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
1190 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1196 } else if(iface == myDevice->stencilBufferTarget) {
1197 /** the depth stencil in openGL has a format of GL_FLOAT
1198 * which should be good for WINED3DFMT_D16_LOCKABLE
1199 * and WINED3DFMT_D16
1200 * it is unclear what format the stencil buffer is in except.
1201 * 'Each index is converted to fixed point...
1202 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
1203 * mappings in the table GL_PIXEL_MAP_S_TO_S.
1204 * glReadPixels(This->lockedRect.left,
1205 * This->lockedRect.bottom - j - 1,
1206 * This->lockedRect.right - This->lockedRect.left,
1208 * GL_DEPTH_COMPONENT,
1210 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
1212 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
1213 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
1214 * none of that is the case the problem is not in this function :-)
1215 ********************************************/
1216 FIXME("Depth stencil locking not supported yet\n");
1218 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
1219 TRACE("locking an ordinary surface\n");
1220 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
1224 if(This->Flags & SFLAG_PBO) {
1225 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1227 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1228 checkGLcall("glBindBufferARB");
1230 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1231 if(This->resource.allocatedMemory) {
1232 ERR("The surface already has PBO memory allocated!\n");
1235 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1236 checkGLcall("glMapBufferARB");
1238 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1240 checkGLcall("glBindBufferARB");
1245 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1248 IWineD3DBaseTexture *pBaseTexture;
1251 * as seen in msdn docs
1253 surface_add_dirty_rect(iface, pRect);
1255 /** Dirtify Container if needed */
1256 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1257 TRACE("Making container dirty\n");
1258 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1259 IWineD3DBaseTexture_Release(pBaseTexture);
1261 TRACE("Surface is standalone, no need to dirty the container\n");
1265 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1268 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1270 GLint prev_rasterpos[4];
1271 GLint skipBytes = 0;
1272 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1273 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1274 IWineD3DSwapChainImpl *swapchain;
1276 /* Activate the correct context for the render target */
1277 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1280 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1281 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1282 TRACE("Unlocking %#x buffer\n", buffer);
1283 glDrawBuffer(buffer);
1284 checkGLcall("glDrawBuffer");
1286 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1288 /* Primary offscreen render target */
1289 TRACE("Offscreen render target\n");
1290 glDrawBuffer(myDevice->offscreenBuffer);
1291 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1294 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1295 checkGLcall("glIntegerv");
1296 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1297 checkGLcall("glIntegerv");
1298 glPixelZoom(1.0, -1.0);
1299 checkGLcall("glPixelZoom");
1301 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1302 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1303 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1305 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1306 checkGLcall("glRasterPos2f");
1308 /* Some drivers(radeon dri, others?) don't like exceptions during
1309 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1310 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1311 * catch to put the dib section in InSync mode, which leads to a crash
1312 * and a blocked x server on my radeon card.
1314 * The following lines read the dib section so it is put in InSync mode
1315 * before glDrawPixels is called and the crash is prevented. There won't
1316 * be any interfering gdi accesses, because UnlockRect is called from
1317 * ReleaseDC, and the app won't use the dc any more afterwards.
1319 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1321 read = This->resource.allocatedMemory[0];
1324 if(This->Flags & SFLAG_PBO) {
1325 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1326 checkGLcall("glBindBufferARB");
1329 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1330 if(This->Flags & SFLAG_LOCKED) {
1331 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1332 (This->lockedRect.bottom - This->lockedRect.top)-1,
1334 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1335 checkGLcall("glDrawPixels");
1337 glDrawPixels(This->currentDesc.Width,
1338 This->currentDesc.Height,
1340 checkGLcall("glDrawPixels");
1343 if(This->Flags & SFLAG_PBO) {
1344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1345 checkGLcall("glBindBufferARB");
1348 glPixelZoom(1.0,1.0);
1349 checkGLcall("glPixelZoom");
1351 glRasterPos3iv(&prev_rasterpos[0]);
1352 checkGLcall("glRasterPos3iv");
1354 /* Reset to previous pack row length */
1355 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1356 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1359 glDrawBuffer(myDevice->offscreenBuffer);
1360 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1361 } else if(swapchain->backBuffer) {
1362 glDrawBuffer(GL_BACK);
1363 checkGLcall("glDrawBuffer(GL_BACK)");
1365 glDrawBuffer(GL_FRONT);
1366 checkGLcall("glDrawBuffer(GL_FRONT)");
1373 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1374 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1375 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1378 if (!(This->Flags & SFLAG_LOCKED)) {
1379 WARN("trying to Unlock an unlocked surf@%p\n", This);
1380 return WINEDDERR_NOTLOCKED;
1383 if (This->Flags & SFLAG_PBO) {
1384 TRACE("Freeing PBO memory\n");
1385 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1387 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1388 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1389 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1390 checkGLcall("glUnmapBufferARB");
1392 This->resource.allocatedMemory = NULL;
1395 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1397 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1398 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1402 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1404 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1405 static BOOL warned = FALSE;
1407 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1413 if(This->dirtyRect.left == 0 &&
1414 This->dirtyRect.top == 0 &&
1415 This->dirtyRect.right == This->currentDesc.Width &&
1416 This->dirtyRect.bottom == This->currentDesc.Height) {
1419 /* TODO: Proper partial rectangle tracking */
1420 fullsurface = FALSE;
1421 This->Flags |= SFLAG_INSYSMEM;
1424 switch(wined3d_settings.rendertargetlock_mode) {
1427 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1428 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1434 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1439 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1440 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1441 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1442 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1443 * not fully up to date because only a subrectangle was read in LockRect.
1445 This->Flags &= ~SFLAG_INSYSMEM;
1446 This->Flags |= SFLAG_INDRAWABLE;
1449 This->dirtyRect.left = This->currentDesc.Width;
1450 This->dirtyRect.top = This->currentDesc.Height;
1451 This->dirtyRect.right = 0;
1452 This->dirtyRect.bottom = 0;
1453 } else if(iface == myDevice->stencilBufferTarget) {
1454 FIXME("Depth Stencil buffer locking is not implemented\n");
1456 /* The rest should be a normal texture */
1457 IWineD3DBaseTextureImpl *impl;
1458 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1459 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1460 * states need resetting
1462 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1463 if(impl->baseTexture.bindCount) {
1464 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1466 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1471 This->Flags &= ~SFLAG_LOCKED;
1472 memset(&This->lockedRect, 0, sizeof(RECT));
1474 /* Overlays have to be redrawn manually after changes with the GL implementation */
1475 if(This->overlay_dest) {
1476 IWineD3DSurface_DrawOverlay(iface);
1481 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1483 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1484 WINED3DLOCKED_RECT lock;
1488 TRACE("(%p)->(%p)\n",This,pHDC);
1490 if(This->Flags & SFLAG_USERPTR) {
1491 ERR("Not supported on surfaces with an application-provided surfaces\n");
1492 return WINEDDERR_NODC;
1495 /* Give more detailed info for ddraw */
1496 if (This->Flags & SFLAG_DCINUSE)
1497 return WINEDDERR_DCALREADYCREATED;
1499 /* Can't GetDC if the surface is locked */
1500 if (This->Flags & SFLAG_LOCKED)
1501 return WINED3DERR_INVALIDCALL;
1503 /* According to Direct3D9 docs, only these formats are supported */
1504 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1505 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1506 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1507 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1508 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1509 return WINED3DERR_INVALIDCALL;
1512 memset(&lock, 0, sizeof(lock)); /* To be sure */
1514 /* Create a DIB section if there isn't a hdc yet */
1516 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1517 if(This->Flags & SFLAG_CLIENT) {
1518 surface_internal_preload(iface, SRGB_RGB);
1521 /* Use the dib section from now on if we are not using a PBO */
1522 if(!(This->Flags & SFLAG_PBO))
1523 This->resource.allocatedMemory = This->dib.bitmap_data;
1526 /* Lock the surface */
1527 hr = IWineD3DSurface_LockRect(iface,
1532 if(This->Flags & SFLAG_PBO) {
1533 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1534 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1538 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1539 /* keep the dib section */
1543 if (This->resource.format_desc->format == WINED3DFMT_P8
1544 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1546 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1547 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1549 const PALETTEENTRY *pal = NULL;
1552 pal = This->palette->palents;
1554 IWineD3DSurfaceImpl *dds_primary;
1555 IWineD3DSwapChainImpl *swapchain;
1556 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1557 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1558 if (dds_primary && dds_primary->palette)
1559 pal = dds_primary->palette->palents;
1563 for (n=0; n<256; n++) {
1564 col[n].rgbRed = pal[n].peRed;
1565 col[n].rgbGreen = pal[n].peGreen;
1566 col[n].rgbBlue = pal[n].peBlue;
1567 col[n].rgbReserved = 0;
1569 SetDIBColorTable(This->hDC, 0, 256, col);
1574 TRACE("returning %p\n",*pHDC);
1575 This->Flags |= SFLAG_DCINUSE;
1580 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1582 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1584 TRACE("(%p)->(%p)\n",This,hDC);
1586 if (!(This->Flags & SFLAG_DCINUSE))
1587 return WINEDDERR_NODC;
1589 if (This->hDC !=hDC) {
1590 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1591 return WINEDDERR_NODC;
1594 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1595 /* Copy the contents of the DIB over to the PBO */
1596 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1599 /* we locked first, so unlock now */
1600 IWineD3DSurface_UnlockRect(iface);
1602 This->Flags &= ~SFLAG_DCINUSE;
1607 /* ******************************************************
1608 IWineD3DSurface Internal (No mapping to directx api) parts follow
1609 ****************************************************** */
1611 HRESULT 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) {
1612 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1613 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1614 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1616 /* Default values: From the surface */
1617 *format = glDesc->glFormat;
1618 *type = glDesc->glType;
1619 *convert = NO_CONVERSION;
1620 *target_bpp = glDesc->byte_count;
1623 *internal = glDesc->glGammaInternal;
1625 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1626 && !(This->Flags & SFLAG_SWAPCHAIN))
1628 *internal = glDesc->rtInternal;
1630 *internal = glDesc->glInternal;
1633 /* Ok, now look if we have to do any conversion */
1634 switch(This->resource.format_desc->format)
1641 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1642 * of the two is available make sure texturing is requested as neither of the two works in
1643 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1644 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1645 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1646 * conflicts with this.
1648 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1649 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1650 device->render_targets &&
1651 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1652 colorkey_active || !use_texturing ) {
1654 *internal = GL_RGBA;
1655 *type = GL_UNSIGNED_BYTE;
1657 if(colorkey_active) {
1658 *convert = CONVERT_PALETTED_CK;
1660 *convert = CONVERT_PALETTED;
1663 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1665 *internal = GL_RGBA;
1666 *type = GL_UNSIGNED_BYTE;
1672 case WINED3DFMT_R3G3B2:
1673 /* **********************
1674 GL_UNSIGNED_BYTE_3_3_2
1675 ********************** */
1676 if (colorkey_active) {
1677 /* This texture format will never be used.. So do not care about color keying
1678 up until the point in time it will be needed :-) */
1679 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1683 case WINED3DFMT_R5G6B5:
1684 if (colorkey_active) {
1685 *convert = CONVERT_CK_565;
1687 *internal = GL_RGBA;
1688 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1692 case WINED3DFMT_X1R5G5B5:
1693 if (colorkey_active) {
1694 *convert = CONVERT_CK_5551;
1696 *internal = GL_RGBA;
1697 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1701 case WINED3DFMT_R8G8B8:
1702 if (colorkey_active) {
1703 *convert = CONVERT_CK_RGB24;
1705 *internal = GL_RGBA;
1706 *type = GL_UNSIGNED_INT_8_8_8_8;
1711 case WINED3DFMT_X8R8G8B8:
1712 if (colorkey_active) {
1713 *convert = CONVERT_RGB32_888;
1715 *internal = GL_RGBA;
1716 *type = GL_UNSIGNED_INT_8_8_8_8;
1720 case WINED3DFMT_R8G8_SNORM:
1721 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1722 *convert = CONVERT_V8U8;
1724 *internal = GL_RGB8;
1725 *type = GL_UNSIGNED_BYTE;
1729 case WINED3DFMT_L6V5U5:
1730 *convert = CONVERT_L6V5U5;
1731 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1733 /* Use format and types from table */
1735 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1738 *internal = GL_RGB5;
1739 *type = GL_UNSIGNED_SHORT_5_6_5;
1743 case WINED3DFMT_X8L8V8U8:
1744 *convert = CONVERT_X8L8V8U8;
1746 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1747 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1748 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1749 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1750 * the needed type and format parameter, so the internal format contains a
1751 * 4th component, which is returned as alpha
1755 *internal = GL_RGB8;
1756 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1760 case WINED3DFMT_R8G8B8A8_SNORM:
1761 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1762 *convert = CONVERT_Q8W8V8U8;
1764 *internal = GL_RGBA8;
1765 *type = GL_UNSIGNED_BYTE;
1769 case WINED3DFMT_R16G16_SNORM:
1770 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1771 *convert = CONVERT_V16U16;
1773 *internal = GL_RGB16_EXT;
1774 *type = GL_UNSIGNED_SHORT;
1778 case WINED3DFMT_A4L4:
1779 /* A4L4 exists as an internal gl format, but for some reason there is not
1780 * format+type combination to load it. Thus convert it to A8L8, then load it
1781 * with A4L4 internal, but A8L8 format+type
1783 *convert = CONVERT_A4L4;
1784 *format = GL_LUMINANCE_ALPHA;
1785 *internal = GL_LUMINANCE4_ALPHA4;
1786 *type = GL_UNSIGNED_BYTE;
1790 case WINED3DFMT_R16G16_UNORM:
1791 *convert = CONVERT_G16R16;
1793 *internal = GL_RGB16_EXT;
1794 *type = GL_UNSIGNED_SHORT;
1798 case WINED3DFMT_R16G16_FLOAT:
1799 *convert = CONVERT_R16G16F;
1801 *internal = GL_RGB16F_ARB;
1802 *type = GL_HALF_FLOAT_ARB;
1806 case WINED3DFMT_R32G32_FLOAT:
1807 *convert = CONVERT_R32G32F;
1809 *internal = GL_RGB32F_ARB;
1821 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1822 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
1826 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1831 memcpy(dst, src, pitch * height);
1834 case CONVERT_PALETTED:
1835 case CONVERT_PALETTED_CK:
1837 IWineD3DPaletteImpl* pal = This->palette;
1842 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1845 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1847 for (y = 0; y < height; y++)
1849 source = src + pitch * y;
1850 dest = dst + outpitch * y;
1851 /* This is an 1 bpp format, using the width here is fine */
1852 for (x = 0; x < width; x++) {
1853 BYTE color = *source++;
1854 *dest++ = table[color][0];
1855 *dest++ = table[color][1];
1856 *dest++ = table[color][2];
1857 *dest++ = table[color][3];
1863 case CONVERT_CK_565:
1865 /* Converting the 565 format in 5551 packed to emulate color-keying.
1867 Note : in all these conversion, it would be best to average the averaging
1868 pixels to get the color of the pixel that will be color-keyed to
1869 prevent 'color bleeding'. This will be done later on if ever it is
1872 Note2: Nvidia documents say that their driver does not support alpha + color keying
1873 on the same surface and disables color keying in such a case
1879 TRACE("Color keyed 565\n");
1881 for (y = 0; y < height; y++) {
1882 Source = (const WORD *)(src + y * pitch);
1883 Dest = (WORD *) (dst + y * outpitch);
1884 for (x = 0; x < width; x++ ) {
1885 WORD color = *Source++;
1886 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1887 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1888 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1897 case CONVERT_CK_5551:
1899 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1903 TRACE("Color keyed 5551\n");
1904 for (y = 0; y < height; y++) {
1905 Source = (const WORD *)(src + y * pitch);
1906 Dest = (WORD *) (dst + y * outpitch);
1907 for (x = 0; x < width; x++ ) {
1908 WORD color = *Source++;
1910 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1911 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1915 *Dest &= ~(1 << 15);
1923 case CONVERT_CK_RGB24:
1925 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
1927 for (y = 0; y < height; y++)
1929 source = src + pitch * y;
1930 dest = dst + outpitch * y;
1931 for (x = 0; x < width; x++) {
1932 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
1933 DWORD dstcolor = color << 8;
1934 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1935 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1938 *(DWORD*)dest = dstcolor;
1946 case CONVERT_RGB32_888:
1948 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
1950 for (y = 0; y < height; y++)
1952 source = src + pitch * y;
1953 dest = dst + outpitch * y;
1954 for (x = 0; x < width; x++) {
1955 DWORD color = 0xffffff & *(const DWORD*)source;
1956 DWORD dstcolor = color << 8;
1957 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1958 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1961 *(DWORD*)dest = dstcolor;
1972 const short *Source;
1973 unsigned char *Dest;
1974 for(y = 0; y < height; y++) {
1975 Source = (const short *)(src + y * pitch);
1976 Dest = dst + y * outpitch;
1977 for (x = 0; x < width; x++ ) {
1978 long color = (*Source++);
1979 /* B */ Dest[0] = 0xff;
1980 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1981 /* R */ Dest[2] = (color) + 128; /* U */
1988 case CONVERT_V16U16:
1991 const DWORD *Source;
1992 unsigned short *Dest;
1993 for(y = 0; y < height; y++) {
1994 Source = (const DWORD *)(src + y * pitch);
1995 Dest = (unsigned short *) (dst + y * outpitch);
1996 for (x = 0; x < width; x++ ) {
1997 DWORD color = (*Source++);
1998 /* B */ Dest[0] = 0xffff;
1999 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2000 /* R */ Dest[2] = (color ) + 32768; /* U */
2007 case CONVERT_Q8W8V8U8:
2010 const DWORD *Source;
2011 unsigned char *Dest;
2012 for(y = 0; y < height; y++) {
2013 Source = (const DWORD *)(src + y * pitch);
2014 Dest = dst + y * outpitch;
2015 for (x = 0; x < width; x++ ) {
2016 long color = (*Source++);
2017 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2018 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2019 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2020 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2027 case CONVERT_L6V5U5:
2031 unsigned char *Dest;
2033 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2034 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2035 * fixed function and shaders without further conversion once the surface is
2038 for(y = 0; y < height; y++) {
2039 Source = (const WORD *)(src + y * pitch);
2040 Dest = dst + y * outpitch;
2041 for (x = 0; x < width; x++ ) {
2042 short color = (*Source++);
2043 unsigned char l = ((color >> 10) & 0xfc);
2044 char v = ((color >> 5) & 0x3e);
2045 char u = ((color ) & 0x1f);
2047 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2048 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2049 * shift. GL reads a signed value and converts it into an unsigned value.
2051 /* M */ Dest[2] = l << 1;
2053 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2054 * from 5 bit values to 8 bit values.
2056 /* V */ Dest[1] = v << 3;
2057 /* U */ Dest[0] = u << 3;
2062 for(y = 0; y < height; y++) {
2063 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2064 Source = (const WORD *)(src + y * pitch);
2065 for (x = 0; x < width; x++ ) {
2066 short color = (*Source++);
2067 unsigned char l = ((color >> 10) & 0xfc);
2068 short v = ((color >> 5) & 0x3e);
2069 short u = ((color ) & 0x1f);
2070 short v_conv = v + 16;
2071 short u_conv = u + 16;
2073 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2081 case CONVERT_X8L8V8U8:
2084 const DWORD *Source;
2085 unsigned char *Dest;
2087 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2088 /* This implementation works with the fixed function pipeline and shaders
2089 * without further modification after converting the surface.
2091 for(y = 0; y < height; y++) {
2092 Source = (const DWORD *)(src + y * pitch);
2093 Dest = dst + y * outpitch;
2094 for (x = 0; x < width; x++ ) {
2095 long color = (*Source++);
2096 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2097 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2098 /* U */ Dest[0] = (color & 0xff); /* U */
2099 /* I */ Dest[3] = 255; /* X */
2104 /* Doesn't work correctly with the fixed function pipeline, but can work in
2105 * shaders if the shader is adjusted. (There's no use for this format in gl's
2106 * standard fixed function pipeline anyway).
2108 for(y = 0; y < height; y++) {
2109 Source = (const DWORD *)(src + y * pitch);
2110 Dest = dst + y * outpitch;
2111 for (x = 0; x < width; x++ ) {
2112 long color = (*Source++);
2113 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2114 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2115 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2126 const unsigned char *Source;
2127 unsigned char *Dest;
2128 for(y = 0; y < height; y++) {
2129 Source = src + y * pitch;
2130 Dest = dst + y * outpitch;
2131 for (x = 0; x < width; x++ ) {
2132 unsigned char color = (*Source++);
2133 /* A */ Dest[1] = (color & 0xf0) << 0;
2134 /* L */ Dest[0] = (color & 0x0f) << 4;
2141 case CONVERT_G16R16:
2142 case CONVERT_R16G16F:
2148 for(y = 0; y < height; y++) {
2149 Source = (const WORD *)(src + y * pitch);
2150 Dest = (WORD *) (dst + y * outpitch);
2151 for (x = 0; x < width; x++ ) {
2152 WORD green = (*Source++);
2153 WORD red = (*Source++);
2156 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2157 * shader overwrites it anyway
2166 case CONVERT_R32G32F:
2169 const float *Source;
2171 for(y = 0; y < height; y++) {
2172 Source = (const float *)(src + y * pitch);
2173 Dest = (float *) (dst + y * outpitch);
2174 for (x = 0; x < width; x++ ) {
2175 float green = (*Source++);
2176 float red = (*Source++);
2187 ERR("Unsupported conversation type %d\n", convert);
2192 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
2193 IWineD3DPaletteImpl* pal = This->palette;
2194 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2195 BOOL index_in_alpha = FALSE;
2196 int dxVersion = ( (IWineD3DImpl *) device->wineD3D)->dxVersion;
2199 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2200 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2201 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2202 * duplicate entries. Store the color key in the unused alpha component to speed the
2203 * download up and to make conversion unneeded. */
2204 index_in_alpha = primary_render_target_is_p8(device);
2207 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2208 if(dxVersion <= 7) {
2209 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2210 if(index_in_alpha) {
2211 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2212 there's no palette at this time. */
2213 for (i = 0; i < 256; i++) table[i][3] = i;
2216 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2217 alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2218 capability flag is present (wine does advertise this capability) */
2219 for (i = 0; i < 256; i++) {
2220 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2221 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2222 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2223 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2227 TRACE("Using surface palette %p\n", pal);
2228 /* Get the surface's palette */
2229 for (i = 0; i < 256; i++) {
2230 table[i][0] = pal->palents[i].peRed;
2231 table[i][1] = pal->palents[i].peGreen;
2232 table[i][2] = pal->palents[i].peBlue;
2234 /* When index_in_alpha is the palette index is stored in the alpha component. In case of a readback
2235 we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
2236 In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
2237 of pixels that should be masked away is set to 0. */
2238 if(index_in_alpha) {
2240 } else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2242 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2243 table[i][3] = pal->palents[i].peFlags;
2251 /* This function is used in case of 8bit paletted textures to upload the palette.
2252 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2253 extensions like ATI_fragment_shaders is possible.
2255 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2256 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2258 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2260 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2262 /* Try to use the paletted texture extension */
2263 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2265 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2266 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2270 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2271 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2272 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2274 /* Create the fragment program if we don't have it */
2275 if(!device->paletteConversionShader)
2277 const char *fragment_palette_conversion =
2280 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2281 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2282 /* The alpha-component contains the palette index */
2283 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2284 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2285 "MAD index.a, index.a, constants.x, constants.y;\n"
2286 /* Use the alpha-component as an index in the palette to get the final color */
2287 "TEX result.color, index.a, texture[1], 1D;\n"
2290 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2291 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2292 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2293 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2294 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2297 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2298 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2300 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2301 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2303 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2304 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2305 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2306 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2308 /* Switch back to unit 0 in which the 2D texture will be stored. */
2309 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2311 /* Rebind the texture because it isn't bound anymore */
2312 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2316 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2317 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2319 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2320 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2322 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2323 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2328 if(This->palette9) {
2329 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2333 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2335 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2339 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2340 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2341 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2343 if (!(This->Flags & flag)) {
2344 TRACE("Reloading because surface is dirty\n");
2345 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2346 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2347 /* Reload: vice versa OR */
2348 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2349 /* Also reload: Color key is active AND the color key has changed */
2350 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2351 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2352 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2353 TRACE("Reloading because of color keying\n");
2354 /* To perform the color key conversion we need a sysmem copy of
2355 * the surface. Make sure we have it
2358 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2359 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2360 /* TODO: This is not necessarily needed with hw palettized texture support */
2361 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2363 TRACE("surface is already in texture\n");
2367 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2368 * These resources are not bound by device size or format restrictions. Because of this,
2369 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2370 * However, these resources can always be created, locked, and copied.
2372 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2374 FIXME("(%p) Operation not supported for scratch textures\n",This);
2375 return WINED3DERR_INVALIDCALL;
2378 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2382 static unsigned int gen = 0;
2385 if ((gen % 10) == 0) {
2386 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2387 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2390 * debugging crash code
2399 if (!(This->Flags & SFLAG_DONOTFREE)) {
2400 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2401 This->resource.allocatedMemory = NULL;
2402 This->resource.heapMemory = NULL;
2403 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2409 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2410 /* TODO: check for locks */
2411 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2412 IWineD3DBaseTexture *baseTexture = NULL;
2413 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2415 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2416 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2417 TRACE("Passing to container\n");
2418 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2419 IWineD3DBaseTexture_Release(baseTexture);
2422 TRACE("(%p) : Binding surface\n", This);
2424 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2425 if(!device->isInDraw) {
2426 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2431 if (!This->glDescription.level) {
2433 glGenTextures(1, name);
2434 checkGLcall("glGenTextures");
2435 TRACE("Surface %p given name %d\n", This, *name);
2437 glBindTexture(This->glDescription.target, *name);
2438 checkGLcall("glBindTexture");
2439 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2440 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2441 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2442 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2443 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2444 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2445 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2446 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2447 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2448 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2450 /* This is where we should be reducing the amount of GLMemoryUsed */
2452 /* Mipmap surfaces should have a base texture container */
2453 ERR("Mipmap surface has a glTexture bound to it!\n");
2456 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2457 checkGLcall("glBindTexture");
2466 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2469 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2470 char *allocatedMemory;
2471 const char *textureRow;
2472 IWineD3DSwapChain *swapChain = NULL;
2473 int width, height, i, y;
2474 GLuint tmpTexture = 0;
2477 Textures may not be stored in ->allocatedgMemory and a GlTexture
2478 so we should lock the surface before saving a snapshot, or at least check that
2480 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2481 by calling GetTexImage and in compressed form by calling
2482 GetCompressedTexImageARB. Queried compressed images can be saved and
2483 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2484 texture images do not need to be processed by the GL and should
2485 significantly improve texture loading performance relative to uncompressed
2488 /* Setup the width and height to be the internal texture width and height. */
2489 width = This->pow2Width;
2490 height = This->pow2Height;
2491 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2492 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2494 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2495 /* if were not a real texture then read the back buffer into a real texture */
2496 /* we don't want to interfere with the back buffer so read the data into a temporary
2497 * texture and then save the data out of the temporary texture
2501 TRACE("(%p) Reading render target into texture\n", This);
2503 glGenTextures(1, &tmpTexture);
2504 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2506 glTexImage2D(GL_TEXTURE_2D,
2513 GL_UNSIGNED_INT_8_8_8_8_REV,
2516 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2517 checkGLcall("glGetIntegerv");
2518 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2519 checkGLcall("glReadBuffer");
2520 glCopyTexImage2D(GL_TEXTURE_2D,
2529 checkGLcall("glCopyTexImage2D");
2530 glReadBuffer(prevRead);
2533 } else { /* bind the real texture, and make sure it up to date */
2534 surface_internal_preload(iface, SRGB_RGB);
2535 surface_bind_and_dirtify(This, FALSE);
2537 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2539 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2540 glGetTexImage(GL_TEXTURE_2D,
2541 This->glDescription.level,
2543 GL_UNSIGNED_INT_8_8_8_8_REV,
2545 checkGLcall("glTexImage2D");
2547 glBindTexture(GL_TEXTURE_2D, 0);
2548 glDeleteTextures(1, &tmpTexture);
2552 f = fopen(filename, "w+");
2554 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2555 return WINED3DERR_INVALIDCALL;
2557 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2558 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2573 fwrite(&width,2,1,f);
2575 fwrite(&height,2,1,f);
2580 /* 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 */
2582 textureRow = allocatedMemory + (width * (height - 1) *4);
2584 textureRow = allocatedMemory;
2585 for (y = 0 ; y < height; y++) {
2586 for (i = 0; i < width; i++) {
2587 color = *((const DWORD*)textureRow);
2588 fputc((color >> 16) & 0xFF, f); /* B */
2589 fputc((color >> 8) & 0xFF, f); /* G */
2590 fputc((color >> 0) & 0xFF, f); /* R */
2591 fputc((color >> 24) & 0xFF, f); /* A */
2594 /* take two rows of the pointer to the texture memory */
2596 (textureRow-= width << 3);
2599 TRACE("Closing file\n");
2603 IWineD3DSwapChain_Release(swapChain);
2605 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2609 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2610 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2613 TRACE("(%p) : Calling base function first\n", This);
2614 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2616 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2617 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2618 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2623 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2624 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2626 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2627 WARN("Surface is locked or the HDC is in use\n");
2628 return WINED3DERR_INVALIDCALL;
2631 if(Mem && Mem != This->resource.allocatedMemory) {
2632 void *release = NULL;
2634 /* Do I have to copy the old surface content? */
2635 if(This->Flags & SFLAG_DIBSECTION) {
2636 /* Release the DC. No need to hold the critical section for the update
2637 * Thread because this thread runs only on front buffers, but this method
2638 * fails for render targets in the check above.
2640 SelectObject(This->hDC, This->dib.holdbitmap);
2641 DeleteDC(This->hDC);
2642 /* Release the DIB section */
2643 DeleteObject(This->dib.DIBsection);
2644 This->dib.bitmap_data = NULL;
2645 This->resource.allocatedMemory = NULL;
2647 This->Flags &= ~SFLAG_DIBSECTION;
2648 } else if(!(This->Flags & SFLAG_USERPTR)) {
2649 release = This->resource.heapMemory;
2650 This->resource.heapMemory = NULL;
2652 This->resource.allocatedMemory = Mem;
2653 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2655 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2656 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2658 /* For client textures opengl has to be notified */
2659 if(This->Flags & SFLAG_CLIENT) {
2660 DWORD oldFlags = This->Flags;
2661 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2662 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2663 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2664 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2667 /* Now free the old memory if any */
2668 HeapFree(GetProcessHeap(), 0, release);
2669 } else if(This->Flags & SFLAG_USERPTR) {
2670 /* LockRect and GetDC will re-create the dib section and allocated memory */
2671 This->resource.allocatedMemory = NULL;
2672 /* HeapMemory should be NULL already */
2673 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2674 This->Flags &= ~SFLAG_USERPTR;
2676 if(This->Flags & SFLAG_CLIENT) {
2677 DWORD oldFlags = This->Flags;
2678 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2679 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2680 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2681 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2687 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2689 /* Flip the surface contents */
2694 front->hDC = back->hDC;
2698 /* Flip the DIBsection */
2701 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2702 tmp = front->dib.DIBsection;
2703 front->dib.DIBsection = back->dib.DIBsection;
2704 back->dib.DIBsection = tmp;
2706 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2707 else front->Flags &= ~SFLAG_DIBSECTION;
2708 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2709 else back->Flags &= ~SFLAG_DIBSECTION;
2712 /* Flip the surface data */
2716 tmp = front->dib.bitmap_data;
2717 front->dib.bitmap_data = back->dib.bitmap_data;
2718 back->dib.bitmap_data = tmp;
2720 tmp = front->resource.allocatedMemory;
2721 front->resource.allocatedMemory = back->resource.allocatedMemory;
2722 back->resource.allocatedMemory = tmp;
2724 tmp = front->resource.heapMemory;
2725 front->resource.heapMemory = back->resource.heapMemory;
2726 back->resource.heapMemory = tmp;
2731 GLuint tmp_pbo = front->pbo;
2732 front->pbo = back->pbo;
2733 back->pbo = tmp_pbo;
2736 /* client_memory should not be different, but just in case */
2739 tmp = front->dib.client_memory;
2740 front->dib.client_memory = back->dib.client_memory;
2741 back->dib.client_memory = tmp;
2744 /* Flip the opengl texture */
2746 glDescriptor tmp_desc = back->glDescription;
2747 back->glDescription = front->glDescription;
2748 front->glDescription = tmp_desc;
2752 DWORD tmp_flags = back->Flags;
2753 back->Flags = front->Flags;
2754 front->Flags = tmp_flags;
2758 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2759 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2760 IWineD3DSwapChainImpl *swapchain = NULL;
2762 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2764 /* Flipping is only supported on RenderTargets and overlays*/
2765 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2766 WARN("Tried to flip a non-render target, non-overlay surface\n");
2767 return WINEDDERR_NOTFLIPPABLE;
2770 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2771 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2773 /* Update the overlay if it is visible */
2774 if(This->overlay_dest) {
2775 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2782 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2783 * FIXME("(%p) Target override is not supported by now\n", This);
2784 * Additionally, it isn't really possible to support triple-buffering
2785 * properly on opengl at all
2789 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2791 ERR("Flipped surface is not on a swapchain\n");
2792 return WINEDDERR_NOTFLIPPABLE;
2795 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2796 * and only d3d8 and d3d9 apps specify the presentation interval
2798 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2799 /* Most common case first to avoid wasting time on all the other cases */
2800 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2801 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2802 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2803 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2804 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2805 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2806 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2808 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2811 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2812 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2813 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2817 /* Does a direct frame buffer -> texture copy. Stretching is done
2818 * with single pixel copy calls
2820 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2821 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2822 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2824 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2827 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2830 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2831 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2834 /* Bind the target texture */
2835 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2836 checkGLcall("glBindTexture");
2838 TRACE("Reading from an offscreen target\n");
2839 upsidedown = !upsidedown;
2840 glReadBuffer(myDevice->offscreenBuffer);
2842 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2843 glReadBuffer(buffer);
2845 checkGLcall("glReadBuffer");
2847 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2848 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2850 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2851 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2853 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2854 ERR("Texture filtering not supported in direct blit\n");
2856 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2857 ERR("Texture filtering not supported in direct blit\n");
2861 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2862 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2863 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2865 glCopyTexSubImage2D(This->glDescription.target,
2866 This->glDescription.level,
2867 drect->x1, drect->y1, /* xoffset, yoffset */
2868 srect->x1, Src->currentDesc.Height - srect->y2,
2869 drect->x2 - drect->x1, drect->y2 - drect->y1);
2871 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2872 /* I have to process this row by row to swap the image,
2873 * otherwise it would be upside down, so stretching in y direction
2874 * doesn't cost extra time
2876 * However, stretching in x direction can be avoided if not necessary
2878 for(row = drect->y1; row < drect->y2; row++) {
2879 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2880 /* Well, that stuff works, but it's very slow.
2881 * find a better way instead
2885 for(col = drect->x1; col < drect->x2; col++) {
2886 glCopyTexSubImage2D(This->glDescription.target,
2887 This->glDescription.level,
2888 drect->x1 + col, row, /* xoffset, yoffset */
2889 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2893 glCopyTexSubImage2D(This->glDescription.target,
2894 This->glDescription.level,
2895 drect->x1, row, /* xoffset, yoffset */
2896 srect->x1, yoffset - (int) (row * yrel),
2897 drect->x2-drect->x1, 1);
2901 checkGLcall("glCopyTexSubImage2D");
2906 /* Uses the hardware to stretch and flip the image */
2907 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2908 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
2909 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
2911 GLuint src, backup = 0;
2912 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2913 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2914 float left, right, top, bottom; /* Texture coordinates */
2915 UINT fbwidth = Src->currentDesc.Width;
2916 UINT fbheight = Src->currentDesc.Height;
2917 GLenum drawBuffer = GL_BACK;
2918 GLenum texture_target;
2919 BOOL noBackBufferBackup;
2921 TRACE("Using hwstretch blit\n");
2922 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2923 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2924 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2926 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2927 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
2928 /* Get it a description */
2929 surface_internal_preload(SrcSurface, SRGB_RGB);
2933 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2934 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2936 if(myDevice->activeContext->aux_buffers >= 2) {
2937 /* Got more than one aux buffer? Use the 2nd aux buffer */
2938 drawBuffer = GL_AUX1;
2939 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
2940 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2941 drawBuffer = GL_AUX0;
2944 if(noBackBufferBackup) {
2945 glGenTextures(1, &backup);
2946 checkGLcall("glGenTextures\n");
2947 glBindTexture(GL_TEXTURE_2D, backup);
2948 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2949 texture_target = GL_TEXTURE_2D;
2951 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2952 * we are reading from the back buffer, the backup can be used as source texture
2954 texture_target = Src->glDescription.target;
2955 glBindTexture(texture_target, Src->glDescription.textureName);
2956 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2957 glEnable(texture_target);
2958 checkGLcall("glEnable(texture_target)");
2960 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2961 Src->Flags &= ~SFLAG_INTEXTURE;
2965 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
2967 TRACE("Reading from an offscreen target\n");
2968 upsidedown = !upsidedown;
2969 glReadBuffer(myDevice->offscreenBuffer);
2972 /* TODO: Only back up the part that will be overwritten */
2973 glCopyTexSubImage2D(texture_target, 0,
2974 0, 0 /* read offsets */,
2979 checkGLcall("glCopyTexSubImage2D");
2981 /* No issue with overriding these - the sampler is dirty due to blit usage */
2982 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2983 magLookup[Filter - WINED3DTEXF_NONE]);
2984 checkGLcall("glTexParameteri");
2985 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2986 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
2987 checkGLcall("glTexParameteri");
2989 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2990 src = backup ? backup : Src->glDescription.textureName;
2992 glReadBuffer(GL_FRONT);
2993 checkGLcall("glReadBuffer(GL_FRONT)");
2995 glGenTextures(1, &src);
2996 checkGLcall("glGenTextures(1, &src)");
2997 glBindTexture(GL_TEXTURE_2D, src);
2998 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3000 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3001 * out for power of 2 sizes
3003 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3004 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3005 checkGLcall("glTexImage2D");
3006 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3007 0, 0 /* read offsets */,
3012 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3013 checkGLcall("glTexParameteri");
3014 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3015 checkGLcall("glTexParameteri");
3017 glReadBuffer(GL_BACK);
3018 checkGLcall("glReadBuffer(GL_BACK)");
3020 if(texture_target != GL_TEXTURE_2D) {
3021 glDisable(texture_target);
3022 glEnable(GL_TEXTURE_2D);
3023 texture_target = GL_TEXTURE_2D;
3026 checkGLcall("glEnd and previous");
3032 top = Src->currentDesc.Height - srect->y1;
3033 bottom = Src->currentDesc.Height - srect->y2;
3035 top = Src->currentDesc.Height - srect->y2;
3036 bottom = Src->currentDesc.Height - srect->y1;
3039 if(Src->Flags & SFLAG_NORMCOORD) {
3040 left /= Src->pow2Width;
3041 right /= Src->pow2Width;
3042 top /= Src->pow2Height;
3043 bottom /= Src->pow2Height;
3046 /* draw the source texture stretched and upside down. The correct surface is bound already */
3047 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3048 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3050 glDrawBuffer(drawBuffer);
3051 glReadBuffer(drawBuffer);
3055 glTexCoord2f(left, bottom);
3056 glVertex2i(0, fbheight);
3059 glTexCoord2f(left, top);
3060 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3063 glTexCoord2f(right, top);
3064 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3067 glTexCoord2f(right, bottom);
3068 glVertex2i(drect->x2 - drect->x1, fbheight);
3070 checkGLcall("glEnd and previous");
3072 if(texture_target != This->glDescription.target) {
3073 glDisable(texture_target);
3074 glEnable(This->glDescription.target);
3075 texture_target = This->glDescription.target;
3078 /* Now read the stretched and upside down image into the destination texture */
3079 glBindTexture(texture_target, This->glDescription.textureName);
3080 checkGLcall("glBindTexture");
3081 glCopyTexSubImage2D(texture_target,
3083 drect->x1, drect->y1, /* xoffset, yoffset */
3084 0, 0, /* We blitted the image to the origin */
3085 drect->x2 - drect->x1, drect->y2 - drect->y1);
3086 checkGLcall("glCopyTexSubImage2D");
3088 if(drawBuffer == GL_BACK) {
3089 /* Write the back buffer backup back */
3091 if(texture_target != GL_TEXTURE_2D) {
3092 glDisable(texture_target);
3093 glEnable(GL_TEXTURE_2D);
3094 texture_target = GL_TEXTURE_2D;
3096 glBindTexture(GL_TEXTURE_2D, backup);
3097 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3099 if(texture_target != Src->glDescription.target) {
3100 glDisable(texture_target);
3101 glEnable(Src->glDescription.target);
3102 texture_target = Src->glDescription.target;
3104 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3105 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3110 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3114 glTexCoord2f(0.0, 0.0);
3115 glVertex2i(0, fbheight);
3118 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3119 glVertex2i(fbwidth, Src->currentDesc.Height);
3122 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3123 glVertex2i(fbwidth, 0);
3126 /* Restore the old draw buffer */
3127 glDrawBuffer(GL_BACK);
3129 glDisable(texture_target);
3130 checkGLcall("glDisable(texture_target)");
3133 if(src != Src->glDescription.textureName && src != backup) {
3134 glDeleteTextures(1, &src);
3135 checkGLcall("glDeleteTextures(1, &src)");
3138 glDeleteTextures(1, &backup);
3139 checkGLcall("glDeleteTextures(1, &backup)");
3145 /* Not called from the VTable */
3146 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3147 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3148 WINED3DTEXTUREFILTERTYPE Filter)
3151 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3152 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3153 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3155 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3157 /* Get the swapchain. One of the surfaces has to be a primary surface */
3158 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3159 WARN("Destination is in sysmem, rejecting gl blt\n");
3160 return WINED3DERR_INVALIDCALL;
3162 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3163 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3165 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3166 WARN("Src is in sysmem, rejecting gl blt\n");
3167 return WINED3DERR_INVALIDCALL;
3169 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3170 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3173 /* Early sort out of cases where no render target is used */
3174 if(!dstSwapchain && !srcSwapchain &&
3175 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3176 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3177 return WINED3DERR_INVALIDCALL;
3180 /* No destination color keying supported */
3181 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3182 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3183 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3184 return WINED3DERR_INVALIDCALL;
3188 rect.x1 = DestRect->left;
3189 rect.y1 = DestRect->top;
3190 rect.x2 = DestRect->right;
3191 rect.y2 = DestRect->bottom;
3195 rect.x2 = This->currentDesc.Width;
3196 rect.y2 = This->currentDesc.Height;
3199 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3200 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3201 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3202 /* Half-life does a Blt from the back buffer to the front buffer,
3203 * Full surface size, no flags... Use present instead
3205 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3208 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3212 TRACE("Looking if a Present can be done...\n");
3213 /* Source Rectangle must be full surface */
3215 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3216 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3217 TRACE("No, Source rectangle doesn't match\n");
3223 mySrcRect.right = Src->currentDesc.Width;
3224 mySrcRect.bottom = Src->currentDesc.Height;
3226 /* No stretching may occur */
3227 if(mySrcRect.right != rect.x2 - rect.x1 ||
3228 mySrcRect.bottom != rect.y2 - rect.y1) {
3229 TRACE("No, stretching is done\n");
3233 /* Destination must be full surface or match the clipping rectangle */
3234 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3238 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3243 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3246 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3247 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3249 TRACE("No, dest rectangle doesn't match(clipper)\n");
3250 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3251 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3257 if(rect.x1 != 0 || rect.y1 != 0 ||
3258 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3259 TRACE("No, dest rectangle doesn't match(surface size)\n");
3266 /* These flags are unimportant for the flag check, remove them */
3267 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3268 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3270 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3271 * take very long, while a flip is fast.
3272 * This applies to Half-Life, which does such Blts every time it finished
3273 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3274 * menu. This is also used by all apps when they do windowed rendering
3276 * The problem is that flipping is not really the same as copying. After a
3277 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3278 * untouched. Therefore it's necessary to override the swap effect
3279 * and to set it back after the flip.
3281 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3285 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3286 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3288 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3289 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3291 dstSwapchain->presentParms.SwapEffect = orig_swap;
3298 TRACE("Unsupported blit between buffers on the same swapchain\n");
3299 return WINED3DERR_INVALIDCALL;
3300 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3301 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3302 return WINED3DERR_INVALIDCALL;
3303 } else if(dstSwapchain && srcSwapchain) {
3304 FIXME("Implement hardware blit between two different swapchains\n");
3305 return WINED3DERR_INVALIDCALL;
3306 } else if(dstSwapchain) {
3307 if(SrcSurface == myDevice->render_targets[0]) {
3308 TRACE("Blit from active render target to a swapchain\n");
3309 /* Handled with regular texture -> swapchain blit */
3311 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3312 FIXME("Implement blit from a swapchain to the active render target\n");
3313 return WINED3DERR_INVALIDCALL;
3316 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3317 /* Blit from render target to texture */
3319 BOOL upsideDown, stretchx;
3320 BOOL paletteOverride = FALSE;
3322 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3323 TRACE("Color keying not supported by frame buffer to texture blit\n");
3324 return WINED3DERR_INVALIDCALL;
3325 /* Destination color key is checked above */
3328 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3329 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3332 if(SrcRect->top < SrcRect->bottom) {
3333 srect.y1 = SrcRect->top;
3334 srect.y2 = SrcRect->bottom;
3337 srect.y1 = SrcRect->bottom;
3338 srect.y2 = SrcRect->top;
3341 srect.x1 = SrcRect->left;
3342 srect.x2 = SrcRect->right;
3346 srect.x2 = Src->currentDesc.Width;
3347 srect.y2 = Src->currentDesc.Height;
3350 if(rect.x1 > rect.x2) {
3354 upsideDown = !upsideDown;
3357 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3363 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3364 * In this case grab the palette from the render target. */
3365 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3367 paletteOverride = TRUE;
3368 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3369 This->palette = Src->palette;
3372 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3373 * flip the image nor scale it.
3375 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3376 * -> If the app wants a image width an unscaled width, copy it line per line
3377 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3378 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3379 * back buffer. This is slower than reading line per line, thus not used for flipping
3380 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3383 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3384 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3387 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3388 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3389 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3390 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3391 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3392 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3393 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3395 TRACE("Using hardware stretching to flip / stretch the texture\n");
3396 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3399 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3401 This->palette = NULL;
3403 if(!(This->Flags & SFLAG_DONOTFREE)) {
3404 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3405 This->resource.allocatedMemory = NULL;
3406 This->resource.heapMemory = NULL;
3408 This->Flags &= ~SFLAG_INSYSMEM;
3410 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3411 * path is never entered
3413 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3417 /* Blit from offscreen surface to render target */
3418 float glTexCoord[4];
3419 DWORD oldCKeyFlags = Src->CKeyFlags;
3420 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3421 RECT SourceRectangle;
3422 BOOL paletteOverride = FALSE;
3424 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3427 SourceRectangle.left = SrcRect->left;
3428 SourceRectangle.right = SrcRect->right;
3429 SourceRectangle.top = SrcRect->top;
3430 SourceRectangle.bottom = SrcRect->bottom;
3432 SourceRectangle.left = 0;
3433 SourceRectangle.right = Src->currentDesc.Width;
3434 SourceRectangle.top = 0;
3435 SourceRectangle.bottom = Src->currentDesc.Height;
3438 /* When blitting from an offscreen surface to a rendertarget, the source
3439 * surface is not required to have a palette. Our rendering / conversion
3440 * code further down the road retrieves the palette from the surface, so
3441 * it must have a palette set. */
3442 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3444 paletteOverride = TRUE;
3445 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3446 Src->palette = This->palette;
3449 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3450 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3451 TRACE("Using stretch_rect_fbo\n");
3452 /* The source is always a texture, but never the currently active render target, and the texture
3453 * contents are never upside down
3455 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3456 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3458 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3460 Src->palette = NULL;
3464 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3465 /* Fall back to software */
3466 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3467 SourceRectangle.left, SourceRectangle.top,
3468 SourceRectangle.right, SourceRectangle.bottom);
3469 return WINED3DERR_INVALIDCALL;
3472 /* Color keying: Check if we have to do a color keyed blt,
3473 * and if not check if a color key is activated.
3475 * Just modify the color keying parameters in the surface and restore them afterwards
3476 * The surface keeps track of the color key last used to load the opengl surface.
3477 * PreLoad will catch the change to the flags and color key and reload if necessary.
3479 if(Flags & WINEDDBLT_KEYSRC) {
3480 /* Use color key from surface */
3481 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3482 /* Use color key from DDBltFx */
3483 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3484 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3486 /* Do not use color key */
3487 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3490 /* Now load the surface */
3491 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3493 /* Activate the destination context, set it up for blitting */
3494 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3496 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3497 * while OpenGL coordinates are window relative.
3498 * Also beware of the origin difference(top left vs bottom left).
3499 * Also beware that the front buffer's surface size is screen width x screen height,
3500 * whereas the real gl drawable size is the size of the window.
3502 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3504 POINT offset = {0,0};
3506 ClientToScreen(dstSwapchain->win_handle, &offset);
3507 GetClientRect(dstSwapchain->win_handle, &windowsize);
3508 h = windowsize.bottom - windowsize.top;
3509 rect.x1 -= offset.x; rect.x2 -=offset.x;
3510 rect.y1 -= offset.y; rect.y2 -=offset.y;
3511 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3514 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3515 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3519 /* Bind the texture */
3520 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3521 checkGLcall("glBindTexture");
3523 /* Filtering for StretchRect */
3524 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3525 magLookup[Filter - WINED3DTEXF_NONE]);
3526 checkGLcall("glTexParameteri");
3527 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3528 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3529 checkGLcall("glTexParameteri");
3530 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3531 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3532 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3533 checkGLcall("glTexEnvi");
3535 /* This is for color keying */
3536 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3537 glEnable(GL_ALPHA_TEST);
3538 checkGLcall("glEnable GL_ALPHA_TEST");
3540 /* When the primary render target uses P8, the alpha component contains the palette index.
3541 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3542 * should be masked away have alpha set to 0. */
3543 if(primary_render_target_is_p8(myDevice))
3544 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3546 glAlphaFunc(GL_NOTEQUAL, 0.0);
3547 checkGLcall("glAlphaFunc\n");
3549 glDisable(GL_ALPHA_TEST);
3550 checkGLcall("glDisable GL_ALPHA_TEST");
3553 /* Draw a textured quad
3557 glColor3d(1.0f, 1.0f, 1.0f);
3558 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3563 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3564 glVertex3f(rect.x1, rect.y2, 0.0);
3566 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3571 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3576 checkGLcall("glEnd");
3578 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3579 glDisable(GL_ALPHA_TEST);
3580 checkGLcall("glDisable(GL_ALPHA_TEST)");
3583 glBindTexture(Src->glDescription.target, 0);
3584 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3586 /* Restore the color key parameters */
3587 Src->CKeyFlags = oldCKeyFlags;
3588 Src->SrcBltCKey = oldBltCKey;
3590 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3592 Src->palette = NULL;
3596 /* Leave the opengl state valid for blitting */
3597 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3599 /* Flush in case the drawable is used by multiple GL contexts */
3600 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3603 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3604 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3607 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3611 /* Source-Less Blit to render target */
3612 if (Flags & WINEDDBLT_COLORFILL) {
3613 /* This is easy to handle for the D3D Device... */
3616 TRACE("Colorfill\n");
3618 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3619 must be true if we are here */
3620 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3621 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3622 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3623 TRACE("Surface is higher back buffer, falling back to software\n");
3624 return WINED3DERR_INVALIDCALL;
3627 /* The color as given in the Blt function is in the format of the frame-buffer...
3628 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3630 if (This->resource.format_desc->format == WINED3DFMT_P8)
3634 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3635 else alpha = 0xFF000000;
3637 if (This->palette) {
3639 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3640 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3641 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3646 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3648 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3651 color = ((0xFF000000) |
3652 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3653 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3654 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3657 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3658 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3660 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3662 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3664 color = DDBltFx->u5.dwFillColor;
3667 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3668 return WINED3DERR_INVALIDCALL;
3671 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3672 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3673 1, /* Number of rectangles */
3674 &rect, WINED3DCLEAR_TARGET, color,
3681 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3682 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3683 return WINED3DERR_INVALIDCALL;
3686 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3687 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3689 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3692 if (Flags & WINEDDBLT_DEPTHFILL) {
3693 switch(This->resource.format_desc->format)
3695 case WINED3DFMT_D16_UNORM:
3696 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3698 case WINED3DFMT_D15S1:
3699 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3701 case WINED3DFMT_D24S8:
3702 case WINED3DFMT_D24X8:
3703 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3705 case WINED3DFMT_D32:
3706 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3710 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3713 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3714 DestRect == NULL ? 0 : 1,
3715 (const WINED3DRECT *)DestRect,
3716 WINED3DCLEAR_ZBUFFER,
3722 FIXME("(%p): Unsupp depthstencil blit\n", This);
3723 return WINED3DERR_INVALIDCALL;
3726 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3727 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3728 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3729 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3730 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3731 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3732 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3734 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3736 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3737 return WINEDDERR_SURFACEBUSY;
3740 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3741 * except depth blits, which seem to work
3743 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3744 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3745 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3746 return WINED3DERR_INVALIDCALL;
3747 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3748 TRACE("Z Blit override handled the blit\n");
3753 /* Special cases for RenderTargets */
3754 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3755 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3756 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3759 /* For the rest call the X11 surface implementation.
3760 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3761 * other Blts are rather rare
3763 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3766 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3767 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3769 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3770 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3771 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3772 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3774 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3776 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3777 return WINEDDERR_SURFACEBUSY;
3780 if(myDevice->inScene &&
3781 (iface == myDevice->stencilBufferTarget ||
3782 (Source && Source == myDevice->stencilBufferTarget))) {
3783 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3784 return WINED3DERR_INVALIDCALL;
3787 /* Special cases for RenderTargets */
3788 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3789 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3791 RECT SrcRect, DstRect;
3795 SrcRect.left = rsrc->left;
3796 SrcRect.top= rsrc->top;
3797 SrcRect.bottom = rsrc->bottom;
3798 SrcRect.right = rsrc->right;
3802 SrcRect.right = srcImpl->currentDesc.Width;
3803 SrcRect.bottom = srcImpl->currentDesc.Height;
3806 DstRect.left = dstx;
3808 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3809 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3811 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3812 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3813 Flags |= WINEDDBLT_KEYSRC;
3814 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3815 Flags |= WINEDDBLT_KEYDEST;
3816 if(trans & WINEDDBLTFAST_WAIT)
3817 Flags |= WINEDDBLT_WAIT;
3818 if(trans & WINEDDBLTFAST_DONOTWAIT)
3819 Flags |= WINEDDBLT_DONOTWAIT;
3821 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3825 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3828 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3830 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3832 IWineD3DPaletteImpl *pal = This->palette;
3834 TRACE("(%p)\n", This);
3836 if (!pal) return WINED3D_OK;
3838 if (This->resource.format_desc->format == WINED3DFMT_P8
3839 || This->resource.format_desc->format == WINED3DFMT_A8P8)
3842 GLenum format, internal, type;
3843 CONVERT_TYPES convert;
3845 /* Check if we are using a RTL mode which uses texturing for uploads */
3846 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
3848 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
3849 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
3851 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
3853 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3854 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3856 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3857 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3859 /* Re-upload the palette */
3860 d3dfmt_p8_upload_palette(iface, convert);
3862 if(!(This->Flags & SFLAG_INSYSMEM)) {
3863 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3864 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3866 TRACE("Dirtifying surface\n");
3867 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3871 if(This->Flags & SFLAG_DIBSECTION) {
3872 TRACE("(%p): Updating the hdc's palette\n", This);
3873 for (n=0; n<256; n++) {
3874 col[n].rgbRed = pal->palents[n].peRed;
3875 col[n].rgbGreen = pal->palents[n].peGreen;
3876 col[n].rgbBlue = pal->palents[n].peBlue;
3877 col[n].rgbReserved = 0;
3879 SetDIBColorTable(This->hDC, 0, 256, col);
3882 /* Propagate the changes to the drawable when we have a palette. */
3883 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3884 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3889 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3890 /** Check against the maximum texture sizes supported by the video card **/
3891 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3892 unsigned int pow2Width, pow2Height;
3894 This->glDescription.textureName = 0;
3895 This->glDescription.target = GL_TEXTURE_2D;
3897 /* Non-power2 support */
3898 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
3899 pow2Width = This->currentDesc.Width;
3900 pow2Height = This->currentDesc.Height;
3902 /* Find the nearest pow2 match */
3903 pow2Width = pow2Height = 1;
3904 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3905 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3907 This->pow2Width = pow2Width;
3908 This->pow2Height = pow2Height;
3910 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3911 WINED3DFORMAT Format = This->resource.format_desc->format;
3912 /** TODO: add support for non power two compressed textures **/
3913 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3914 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5
3915 || Format == WINED3DFMT_ATI2N)
3917 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3918 This, This->currentDesc.Width, This->currentDesc.Height);
3919 return WINED3DERR_NOTAVAILABLE;
3923 if(pow2Width != This->currentDesc.Width ||
3924 pow2Height != This->currentDesc.Height) {
3925 This->Flags |= SFLAG_NONPOW2;
3928 TRACE("%p\n", This);
3929 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3930 /* one of three options
3931 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)
3932 2: Set the texture to the maximum size (bad idea)
3933 3: WARN and return WINED3DERR_NOTAVAILABLE;
3934 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.
3936 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
3937 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
3938 This->Flags |= SFLAG_OVERSIZE;
3940 /* This will be initialized on the first blt */
3941 This->glRect.left = 0;
3942 This->glRect.top = 0;
3943 This->glRect.right = 0;
3944 This->glRect.bottom = 0;
3946 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3947 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3948 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3949 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3951 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
3952 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
3953 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
3954 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3956 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3957 This->pow2Width = This->currentDesc.Width;
3958 This->pow2Height = This->currentDesc.Height;
3959 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3962 /* No oversize, gl rect is the full texture size */
3963 This->Flags &= ~SFLAG_OVERSIZE;
3964 This->glRect.left = 0;
3965 This->glRect.top = 0;
3966 This->glRect.right = This->pow2Width;
3967 This->glRect.bottom = This->pow2Height;
3970 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3971 switch(wined3d_settings.offscreen_rendering_mode) {
3972 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3973 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3974 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3978 This->Flags |= SFLAG_INSYSMEM;
3983 struct depth_blt_info
3987 enum tex_types tex_type;
3988 GLfloat coords[4][3];
3991 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
3993 GLfloat (*coords)[3] = info->coords;
3998 FIXME("Unsupported texture target %#x\n", target);
3999 /* Fall back to GL_TEXTURE_2D */
4001 info->binding = GL_TEXTURE_BINDING_2D;
4002 info->bind_target = GL_TEXTURE_2D;
4003 info->tex_type = tex_2d;
4004 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4005 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4006 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4007 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4010 case GL_TEXTURE_RECTANGLE_ARB:
4011 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4012 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4013 info->tex_type = tex_rect;
4014 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4015 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4016 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4017 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4020 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4021 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4022 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4023 info->tex_type = tex_cube;
4024 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4025 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4026 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4027 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4029 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4030 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4031 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4032 info->tex_type = tex_cube;
4033 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4034 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4035 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4036 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4038 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4039 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4040 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4041 info->tex_type = tex_cube;
4042 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4043 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4044 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4045 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4047 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4048 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4049 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4050 info->tex_type = tex_cube;
4051 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4052 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4053 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4054 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4056 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4057 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4058 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4059 info->tex_type = tex_cube;
4060 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4061 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4062 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4063 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4065 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4066 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4067 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4068 info->tex_type = tex_cube;
4069 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4070 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4071 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4072 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4076 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4078 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4079 struct depth_blt_info info;
4080 GLint old_binding = 0;
4082 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4084 glDisable(GL_CULL_FACE);
4086 glDisable(GL_ALPHA_TEST);
4087 glDisable(GL_SCISSOR_TEST);
4088 glDisable(GL_STENCIL_TEST);
4089 glEnable(GL_DEPTH_TEST);
4090 glDepthFunc(GL_ALWAYS);
4091 glDepthMask(GL_TRUE);
4092 glBlendFunc(GL_ZERO, GL_ONE);
4093 glViewport(0, 0, w, h);
4095 surface_get_depth_blt_info(target, w, h, &info);
4096 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4097 glGetIntegerv(info.binding, &old_binding);
4098 glBindTexture(info.bind_target, texture);
4100 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4102 glBegin(GL_TRIANGLE_STRIP);
4103 glTexCoord3fv(info.coords[0]);
4104 glVertex2f(-1.0f, -1.0f);
4105 glTexCoord3fv(info.coords[1]);
4106 glVertex2f(1.0f, -1.0f);
4107 glTexCoord3fv(info.coords[2]);
4108 glVertex2f(-1.0f, 1.0f);
4109 glTexCoord3fv(info.coords[3]);
4110 glVertex2f(1.0f, 1.0f);
4113 glBindTexture(info.bind_target, old_binding);
4117 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4120 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4121 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4123 TRACE("(%p) New location %#x\n", This, location);
4125 if (location & ~SFLAG_DS_LOCATIONS) {
4126 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4129 This->Flags &= ~SFLAG_DS_LOCATIONS;
4130 This->Flags |= location;
4133 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4134 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4135 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4137 TRACE("(%p) New location %#x\n", This, location);
4139 /* TODO: Make this work for modes other than FBO */
4140 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4142 if (This->Flags & location) {
4143 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4147 if (This->current_renderbuffer) {
4148 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4152 if (location == SFLAG_DS_OFFSCREEN) {
4153 if (This->Flags & SFLAG_DS_ONSCREEN) {
4154 GLint old_binding = 0;
4157 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4161 if (!device->depth_blt_texture) {
4162 glGenTextures(1, &device->depth_blt_texture);
4165 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4166 * directly on the FBO texture. That's because we need to flip. */
4167 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4168 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4169 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4170 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4172 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4173 bind_target = GL_TEXTURE_2D;
4175 glBindTexture(bind_target, device->depth_blt_texture);
4176 glCopyTexImage2D(bind_target,
4177 This->glDescription.level,
4178 This->resource.format_desc->glInternal,
4181 This->currentDesc.Width,
4182 This->currentDesc.Height,
4184 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4185 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4186 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4187 glBindTexture(bind_target, old_binding);
4189 /* Setup the destination */
4190 if (!device->depth_blt_rb) {
4191 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4192 checkGLcall("glGenRenderbuffersEXT");
4194 if (device->depth_blt_rb_w != This->currentDesc.Width
4195 || device->depth_blt_rb_h != This->currentDesc.Height) {
4196 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4197 checkGLcall("glBindRenderbufferEXT");
4198 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4199 checkGLcall("glRenderbufferStorageEXT");
4200 device->depth_blt_rb_w = This->currentDesc.Width;
4201 device->depth_blt_rb_h = This->currentDesc.Height;
4204 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4205 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4206 checkGLcall("glFramebufferRenderbufferEXT");
4207 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4209 /* Do the actual blit */
4210 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4211 checkGLcall("depth_blt");
4213 if (device->activeContext->current_fbo) {
4214 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4216 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4217 checkGLcall("glBindFramebuffer()");
4222 FIXME("No up to date depth stencil location\n");
4224 } else if (location == SFLAG_DS_ONSCREEN) {
4225 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4226 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4230 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4231 checkGLcall("glBindFramebuffer()");
4232 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4233 checkGLcall("depth_blt");
4235 if (device->activeContext->current_fbo) {
4236 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4237 checkGLcall("glBindFramebuffer()");
4242 FIXME("No up to date depth stencil location\n");
4245 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4248 This->Flags |= location;
4251 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4252 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4253 IWineD3DBaseTexture *texture;
4254 IWineD3DSurfaceImpl *overlay;
4256 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4257 persistent ? "TRUE" : "FALSE");
4259 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4260 if (This->Flags & SFLAG_SWAPCHAIN)
4262 TRACE("Surface %p is an onscreen surface\n", iface);
4264 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4265 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4270 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4271 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4272 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4273 TRACE("Passing to container\n");
4274 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4275 IWineD3DBaseTexture_Release(texture);
4278 This->Flags &= ~SFLAG_LOCATIONS;
4279 This->Flags |= flag;
4281 /* Redraw emulated overlays, if any */
4282 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4283 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4284 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4288 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4289 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4290 TRACE("Passing to container\n");
4291 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4292 IWineD3DBaseTexture_Release(texture);
4295 This->Flags &= ~flag;
4298 if(!(This->Flags & SFLAG_LOCATIONS)) {
4299 ERR("%p: Surface does not have any up to date location\n", This);
4315 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4317 f->l = ((r->left * 2.0f) / w) - 1.0f;
4318 f->t = ((r->top * 2.0f) / h) - 1.0f;
4319 f->r = ((r->right * 2.0f) / w) - 1.0f;
4320 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4323 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4324 struct coords coords[4];
4326 IWineD3DSwapChain *swapchain;
4327 IWineD3DBaseTexture *texture;
4328 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4330 struct float_rect f;
4337 rect.right = This->currentDesc.Width;
4338 rect.bottom = This->currentDesc.Height;
4341 switch(This->glDescription.target)
4344 bind_target = GL_TEXTURE_2D;
4346 coords[0].x = (float)rect.left / This->pow2Width;
4347 coords[0].y = (float)rect.top / This->pow2Height;
4350 coords[1].x = (float)rect.left / This->pow2Width;
4351 coords[1].y = (float)rect.bottom / This->pow2Height;
4354 coords[2].x = (float)rect.right / This->pow2Width;
4355 coords[2].y = (float)rect.bottom / This->pow2Height;
4358 coords[3].x = (float)rect.right / This->pow2Width;
4359 coords[3].y = (float)rect.top / This->pow2Height;
4363 case GL_TEXTURE_RECTANGLE_ARB:
4364 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4365 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4366 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4367 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4368 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4371 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4372 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4373 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4374 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4375 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4376 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4377 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4380 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4381 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4382 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4383 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4384 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4385 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4386 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4389 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4390 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4391 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4392 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4393 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4394 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4395 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4398 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4399 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4400 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4401 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4402 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4403 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4404 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4407 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4408 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4409 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4410 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4411 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4412 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4413 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4416 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4417 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4418 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4419 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4420 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4421 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4422 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4426 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4430 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4433 glEnable(bind_target);
4434 checkGLcall("glEnable(bind_target)");
4435 glBindTexture(bind_target, This->glDescription.textureName);
4436 checkGLcall("bind_target, This->glDescription.textureName)");
4437 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4438 checkGLcall("glTexParameteri");
4439 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4440 checkGLcall("glTexParameteri");
4442 if (device->render_offscreen)
4444 LONG tmp = rect.top;
4445 rect.top = rect.bottom;
4450 glTexCoord3fv(&coords[0].x);
4451 glVertex2i(rect.left, rect.top);
4453 glTexCoord3fv(&coords[1].x);
4454 glVertex2i(rect.left, rect.bottom);
4456 glTexCoord3fv(&coords[2].x);
4457 glVertex2i(rect.right, rect.bottom);
4459 glTexCoord3fv(&coords[3].x);
4460 glVertex2i(rect.right, rect.top);
4462 checkGLcall("glEnd");
4464 glDisable(bind_target);
4465 checkGLcall("glDisable(bind_target)");
4469 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4471 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4472 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4473 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4476 IWineD3DSwapChain_Release(swapchain);
4478 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4479 * reset properly next draw
4481 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4483 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4484 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4485 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4486 IWineD3DBaseTexture_Release(texture);
4491 /*****************************************************************************
4492 * IWineD3DSurface::LoadLocation
4494 * Copies the current surface data from wherever it is to the requested
4495 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4496 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4497 * multiple locations, the gl texture is preferred over the drawable, which is
4498 * preferred over system memory. The PBO counts as system memory. If rect is
4499 * not NULL, only the specified rectangle is copied (only supported for
4500 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4501 * location is marked up to date after the copy.
4504 * flag: Surface location flag to be updated
4505 * rect: rectangle to be copied
4508 * WINED3D_OK on success
4509 * WINED3DERR_DEVICELOST on an internal error
4511 *****************************************************************************/
4512 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4513 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4514 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4515 GLenum format, internal, type;
4516 CONVERT_TYPES convert;
4518 int width, pitch, outpitch;
4520 BOOL drawable_read_ok = TRUE;
4522 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4523 if (This->Flags & SFLAG_SWAPCHAIN)
4525 TRACE("Surface %p is an onscreen surface\n", iface);
4527 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4528 * Prefer SFLAG_INTEXTURE. */
4529 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4530 drawable_read_ok = FALSE;
4534 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4536 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4539 if(This->Flags & flag) {
4540 TRACE("Location already up to date\n");
4544 if(!(This->Flags & SFLAG_LOCATIONS)) {
4545 ERR("%p: Surface does not have any up to date location\n", This);
4546 This->Flags |= SFLAG_LOST;
4547 return WINED3DERR_DEVICELOST;
4550 if(flag == SFLAG_INSYSMEM) {
4551 surface_prepare_system_memory(This);
4553 /* Download the surface to system memory */
4554 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4555 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4556 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4558 surface_download_data(This);
4560 read_from_framebuffer(This, rect,
4561 This->resource.allocatedMemory,
4562 IWineD3DSurface_GetPitch(iface));
4564 } else if(flag == SFLAG_INDRAWABLE) {
4565 if(This->Flags & SFLAG_INTEXTURE) {
4566 surface_blt_to_drawable(This, rect);
4568 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4569 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4570 * values, otherwise we get incorrect values in the target. For now go the slow way
4571 * via a system memory copy
4573 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4576 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4578 /* The width is in 'length' not in bytes */
4579 width = This->currentDesc.Width;
4580 pitch = IWineD3DSurface_GetPitch(iface);
4582 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4583 * but it isn't set (yet) in all cases it is getting called. */
4584 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4585 TRACE("Removing the pbo attached to surface %p\n", This);
4586 surface_remove_pbo(This);
4589 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4590 int height = This->currentDesc.Height;
4592 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4593 outpitch = width * bpp;
4594 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4596 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4598 ERR("Out of memory %d, %d!\n", outpitch, height);
4599 return WINED3DERR_OUTOFVIDEOMEMORY;
4601 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4603 This->Flags |= SFLAG_CONVERTED;
4605 This->Flags &= ~SFLAG_CONVERTED;
4606 mem = This->resource.allocatedMemory;
4609 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4611 /* Don't delete PBO memory */
4612 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4613 HeapFree(GetProcessHeap(), 0, mem);
4615 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4616 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4617 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4618 } else { /* Upload from system memory */
4619 BOOL srgb = flag == SFLAG_INSRGBTEX;
4620 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4621 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4624 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4625 /* Performance warning ... */
4626 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4627 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4630 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4631 /* Performance warning ... */
4632 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4633 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4637 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4638 surface_bind_and_dirtify(This, srgb);
4640 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4641 This->Flags |= SFLAG_GLCKEY;
4642 This->glCKey = This->SrcBltCKey;
4644 else This->Flags &= ~SFLAG_GLCKEY;
4646 /* The width is in 'length' not in bytes */
4647 width = This->currentDesc.Width;
4648 pitch = IWineD3DSurface_GetPitch(iface);
4650 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4651 * but it isn't set (yet) in all cases it is getting called. */
4652 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4653 TRACE("Removing the pbo attached to surface %p\n", This);
4654 surface_remove_pbo(This);
4657 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4658 int height = This->currentDesc.Height;
4660 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4661 outpitch = width * bpp;
4662 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4664 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4666 ERR("Out of memory %d, %d!\n", outpitch, height);
4667 return WINED3DERR_OUTOFVIDEOMEMORY;
4669 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4671 This->Flags |= SFLAG_CONVERTED;
4673 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4674 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4676 d3dfmt_p8_upload_palette(iface, convert);
4677 This->Flags &= ~SFLAG_CONVERTED;
4678 mem = This->resource.allocatedMemory;
4680 This->Flags &= ~SFLAG_CONVERTED;
4681 mem = This->resource.allocatedMemory;
4684 /* Make sure the correct pitch is used */
4686 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4689 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4690 TRACE("non power of two support\n");
4691 if(!(This->Flags & alloc_flag)) {
4692 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4693 This->Flags |= alloc_flag;
4695 if (mem || (This->Flags & SFLAG_PBO)) {
4696 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4699 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4700 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4702 if(!(This->Flags & alloc_flag)) {
4703 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4704 This->Flags |= alloc_flag;
4706 if (mem || (This->Flags & SFLAG_PBO)) {
4707 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4711 /* Restore the default pitch */
4713 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4716 /* Don't delete PBO memory */
4717 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4718 HeapFree(GetProcessHeap(), 0, mem);
4723 This->Flags |= flag;
4726 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4727 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4728 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4729 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4735 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4737 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4738 IWineD3DSwapChain *swapchain = NULL;
4740 /* Update the drawable size method */
4742 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4745 This->get_drawable_size = get_drawable_size_swapchain;
4746 IWineD3DSwapChain_Release(swapchain);
4747 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4748 switch(wined3d_settings.offscreen_rendering_mode) {
4749 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4750 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4751 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4755 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4758 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4759 return SURFACE_OPENGL;
4762 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4763 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4766 /* If there's no destination surface there is nothing to do */
4767 if(!This->overlay_dest) return WINED3D_OK;
4769 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4770 * update the overlay. Prevent an endless recursion
4772 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4775 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4776 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4777 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4778 NULL, WINED3DTEXF_LINEAR);
4779 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4784 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4787 IWineD3DBaseSurfaceImpl_QueryInterface,
4788 IWineD3DBaseSurfaceImpl_AddRef,
4789 IWineD3DSurfaceImpl_Release,
4790 /* IWineD3DResource */
4791 IWineD3DBaseSurfaceImpl_GetParent,
4792 IWineD3DBaseSurfaceImpl_GetDevice,
4793 IWineD3DBaseSurfaceImpl_SetPrivateData,
4794 IWineD3DBaseSurfaceImpl_GetPrivateData,
4795 IWineD3DBaseSurfaceImpl_FreePrivateData,
4796 IWineD3DBaseSurfaceImpl_SetPriority,
4797 IWineD3DBaseSurfaceImpl_GetPriority,
4798 IWineD3DSurfaceImpl_PreLoad,
4799 IWineD3DSurfaceImpl_UnLoad,
4800 IWineD3DBaseSurfaceImpl_GetType,
4801 /* IWineD3DSurface */
4802 IWineD3DBaseSurfaceImpl_GetContainer,
4803 IWineD3DBaseSurfaceImpl_GetDesc,
4804 IWineD3DSurfaceImpl_LockRect,
4805 IWineD3DSurfaceImpl_UnlockRect,
4806 IWineD3DSurfaceImpl_GetDC,
4807 IWineD3DSurfaceImpl_ReleaseDC,
4808 IWineD3DSurfaceImpl_Flip,
4809 IWineD3DSurfaceImpl_Blt,
4810 IWineD3DBaseSurfaceImpl_GetBltStatus,
4811 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4812 IWineD3DBaseSurfaceImpl_IsLost,
4813 IWineD3DBaseSurfaceImpl_Restore,
4814 IWineD3DSurfaceImpl_BltFast,
4815 IWineD3DBaseSurfaceImpl_GetPalette,
4816 IWineD3DBaseSurfaceImpl_SetPalette,
4817 IWineD3DSurfaceImpl_RealizePalette,
4818 IWineD3DBaseSurfaceImpl_SetColorKey,
4819 IWineD3DBaseSurfaceImpl_GetPitch,
4820 IWineD3DSurfaceImpl_SetMem,
4821 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4822 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4823 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4824 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4825 IWineD3DBaseSurfaceImpl_SetClipper,
4826 IWineD3DBaseSurfaceImpl_GetClipper,
4828 IWineD3DSurfaceImpl_LoadTexture,
4829 IWineD3DSurfaceImpl_BindTexture,
4830 IWineD3DSurfaceImpl_SaveSnapshot,
4831 IWineD3DSurfaceImpl_SetContainer,
4832 IWineD3DSurfaceImpl_GetGlDesc,
4833 IWineD3DBaseSurfaceImpl_GetData,
4834 IWineD3DSurfaceImpl_SetFormat,
4835 IWineD3DSurfaceImpl_PrivateSetup,
4836 IWineD3DSurfaceImpl_ModifyLocation,
4837 IWineD3DSurfaceImpl_LoadLocation,
4838 IWineD3DSurfaceImpl_GetImplType,
4839 IWineD3DSurfaceImpl_DrawOverlay
4841 #undef GLINFO_LOCATION
4843 #define GLINFO_LOCATION device->adapter->gl_info
4844 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4845 static void ffp_blit_free(IWineD3DDevice *iface) { }
4847 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
4848 GLenum textype, UINT width, UINT height)
4851 checkGLcall("glEnable(textype)");
4855 static void ffp_blit_unset(IWineD3DDevice *iface) {
4856 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4857 glDisable(GL_TEXTURE_2D);
4858 checkGLcall("glDisable(GL_TEXTURE_2D)");
4859 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
4860 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4861 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4863 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
4864 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4865 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4869 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
4871 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4873 TRACE("Checking support for fixup:\n");
4874 dump_color_fixup_desc(fixup);
4877 /* We only support identity conversions. */
4878 if (is_identity_fixup(fixup))
4884 TRACE("[FAILED]\n");
4888 const struct blit_shader ffp_blit = {
4893 ffp_blit_color_fixup_supported