📄 surface.c
字号:
/*
* IWineD3DSurface Implementation
*
* Copyright 1998 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
* Copyright 2002-2005 Jason Edmeades
* Copyright 2002-2003 Raphael Junqueira
* Copyright 2004 Christian Costa
* Copyright 2005 Oliver Stieber
* Copyright 2006 Stefan D鰏inger for CodeWeavers
* Copyright 2007 Henri Verbeet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include "wined3d_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
#define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
static void surface_download_data(IWineD3DSurfaceImpl *This) {
if (!This->resource.allocatedMemory) This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), 0, This->resource.size + 4);
if (This->resource.format == WINED3DFMT_DXT1 ||
This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
} else {
TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
checkGLcall("glGetCompressedTexImageARB()");
}
} else {
void *mem;
int src_pitch = 0;
int dst_pitch = 0;
if(This->Flags & SFLAG_CONVERTED) {
FIXME("Read back converted textures unsupported\n");
return;
}
if (This->Flags & SFLAG_NONPOW2) {
unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
src_pitch = This->bytesPerPixel * This->pow2Width;
dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
} else {
mem = This->resource.allocatedMemory;
}
TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
This->glDescription.glFormat, This->glDescription.glType, mem);
glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
This->glDescription.glType, mem);
checkGLcall("glGetTexImage()");
if (This->Flags & SFLAG_NONPOW2) {
LPBYTE src_data, dst_data;
int y;
/*
* Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
* the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
* repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
*
* We're doing this...
*
* instead of boxing the texture :
* |<-texture width ->| -->pow2width| /\
* |111111111111111111| | |
* |222 Texture 222222| boxed empty | texture height
* |3333 Data 33333333| | |
* |444444444444444444| | \/
* ----------------------------------- |
* | boxed empty | boxed empty | pow2height
* | | | \/
* -----------------------------------
*
*
* we're repacking the data to the expected texture width
*
* |<-texture width ->| -->pow2width| /\
* |111111111111111111222222222222222| |
* |222333333333333333333444444444444| texture height
* |444444 | |
* | | \/
* | | |
* | empty | pow2height
* | | \/
* -----------------------------------
*
* == is the same as
*
* |<-texture width ->| /\
* |111111111111111111|
* |222222222222222222|texture height
* |333333333333333333|
* |444444444444444444| \/
* --------------------
*
* this also means that any references to allocatedMemory should work with the data as if were a
* standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
*
* internally the texture is still stored in a boxed format so any references to textureName will
* get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
*
* Performance should not be an issue, because applications normally do not lock the surfaces when
* rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
* and doesn't have to be re-read.
*/
src_data = mem;
dst_data = This->resource.allocatedMemory;
TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
for (y = 1 ; y < This->currentDesc.Height; y++) {
/* skip the first row */
src_data += src_pitch;
dst_data += dst_pitch;
memcpy(dst_data, src_data, dst_pitch);
}
HeapFree(GetProcessHeap(), 0, mem);
}
}
/* Surface has now been downloaded */
This->Flags |= SFLAG_INSYSMEM;
}
static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
if (This->resource.format == WINED3DFMT_DXT1 ||
This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
FIXME("Using DXT1/3/5 without advertized support\n");
} else {
if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
/* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
This->Flags |= SFLAG_CLIENT;
}
TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
ENTER_GL();
/* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
* glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
* function uses glCompressedTexImage2D instead of the SubImage call
*/
GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
width, height, 0 /* border */, This->resource.size, data));
checkGLcall("glCompressedTexSubImage2D");
LEAVE_GL();
}
} else {
TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
ENTER_GL();
glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
checkGLcall("glTexSubImage2D");
LEAVE_GL();
}
}
static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
BOOL enable_client_storage = FALSE;
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", This,
This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
if (This->resource.format == WINED3DFMT_DXT1 ||
This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
/* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
return;
}
ENTER_GL();
if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
/* In some cases we want to disable client storage.
* SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
* SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
* SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
* SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
* allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
*/
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
This->Flags &= SFLAG_CLIENT;
enable_client_storage = TRUE;
} else {
This->Flags |= SFLAG_CLIENT;
/* Below point opengl to our allocated texture memory */
}
}
glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
checkGLcall("glTexImage2D");
if(enable_client_storage) {
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
}
LEAVE_GL();
This->Flags |= SFLAG_ALLOCATED;
}
/* In D3D the depth stencil dimensions have to be greater than or equal to the
* render target dimensions. With FBOs, the dimensions have to be an exact match. */
/* TODO: We should synchronize the renderbuffer's content with the texture's content. */
void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
renderbuffer_entry_t *entry;
GLuint renderbuffer = 0;
unsigned int src_width, src_height;
src_width = This->pow2Width;
src_height = This->pow2Height;
/* A depth stencil smaller than the render target is not valid */
if (width > src_width || height > src_height) return;
/* Remove any renderbuffer set if the sizes match */
if (width == src_width && height == src_height) {
This->current_renderbuffer = NULL;
return;
}
/* Look if we've already got a renderbuffer of the correct dimensions */
LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
if (entry->width == width && entry->height == height) {
renderbuffer = entry->id;
This->current_renderbuffer = entry;
break;
}
}
if (!renderbuffer) {
const GlPixelFormatDesc *glDesc;
getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
entry->width = width;
entry->height = height;
entry->id = renderbuffer;
list_add_head(&This->renderbuffers, &entry->entry);
This->current_renderbuffer = entry;
}
checkGLcall("set_compatible_renderbuffer");
}
GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
TRACE("(%p) : swapchain %p\n", This, swapchain);
if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
TRACE("Returning GL_BACK\n");
return GL_BACK;
} else if (swapchain_impl->frontBuffer == iface) {
TRACE("Returning GL_FRONT\n");
return GL_FRONT;
}
FIXME("Higher back buffer, returning GL_BACK\n");
return GL_BACK;
}
/* *******************************************
IWineD3DSurface IUnknown parts follow
******************************************* */
HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
{
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -