surface.c revision 589fd26cedb2b4ebbed14f2964cad03cc8ebbca2
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * IWineD3DSurface Implementation
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 1998 Lionel Ulmer
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2000-2001 TransGaming Technologies Inc.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2002-2005 Jason Edmeades
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2002-2003 Raphael Junqueira
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2004 Christian Costa
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2005 Oliver Stieber
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2007-2008 Henri Verbeet
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright 2006-2008 Roderick Colenbrander
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * This library is free software; you can redistribute it and/or
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * modify it under the terms of the GNU Lesser General Public
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * License as published by the Free Software Foundation; either
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * version 2.1 of the License, or (at your option) any later version.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * This library is distributed in the hope that it will be useful,
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * but WITHOUT ANY WARRANTY; without even the implied warranty of
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Lesser General Public License for more details.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * You should have received a copy of the GNU Lesser General Public
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * License along with this library; if not, write to the Free Software
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Sun LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * other than GPL or LGPL is available it will apply instead, Sun elects to use only
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * a choice of LGPL license versions is made available with the language indicating
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * that LGPLv2 or any later version may be used, or where a choice of which version
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * of the LGPL is applied is otherwise unspecified.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync#define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void surface_remove_pbo(IWineD3DSurfaceImpl *This);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncvoid surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync TRACE("(%p) : setting texture name %u\n", This, new_name);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * surface has no texture name yet. See if we can get rid of this. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncvoid surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync TRACE("(%p) : setting target %#x\n", This, target);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * gl states. The current texture unit should always be a valid one.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * To be more specific, this is tricky because we can implicitly be called
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * from sampler() in state.c. This means we can't touch anything other than
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * whatever happens to be the currently active texture, or we would risk
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * marking already applied sampler states dirty again.
ENTER_GL();
LEAVE_GL();
return TRUE;
return FALSE;
FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
ENTER_GL();
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);
GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
LEAVE_GL();
void *mem;
int src_pitch = 0;
int dst_pitch = 0;
if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
LEAVE_GL();
UINT y;
* rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
/* 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
ENTER_GL();
GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
LEAVE_GL();
ENTER_GL();
glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
LEAVE_GL();
static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
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",
/* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
/* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
ENTER_GL();
GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
LEAVE_GL();
ENTER_GL();
if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
* allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
if(enable_client_storage) {
LEAVE_GL();
void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
if (!renderbuffer) {
return GL_BACK;
return GL_FRONT;
return GL_BACK;
IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
if (dirty_rect)
if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
if (ref == 0) {
ENTER_GL();
LEAVE_GL();
return ref;
if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
/* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
ENTER_GL();
LEAVE_GL();
This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
(BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
ENTER_GL();
GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
LEAVE_GL();
ENTER_GL();
LEAVE_GL();
if(!texture) {
ENTER_GL();
LEAVE_GL();
static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
if(!warned) {
* Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
* should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
ENTER_GL();
if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
if(!rect) {
case WINED3DFMT_P8:
if(!mem) {
LEAVE_GL();
glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
if(!srcIsUpsideDown) {
if(!srcIsUpsideDown) {
if(!row) {
LEAVE_GL();
LEAVE_GL();
/* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
* index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
* In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
int bpp;
d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
/* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
* locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
ENTER_GL();
LEAVE_GL();
if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
ENTER_GL();
LEAVE_GL();
ENTER_GL();
LEAVE_GL();
ENTER_GL();
LEAVE_GL();
/* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
* This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
/* MAXLOCKCOUNT is defined in wined3d_private.h */
if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
ENTER_GL();
GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
LEAVE_GL();
This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
(BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
return WINED3DERR_INVALIDCALL;
goto lock_end;
goto lock_end;
* Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
* Offscreen targets which are not active at the moment or are higher targets(FBOs) can be locked with the texture path
if(pRect &&
case RTL_TEXDRAW:
case RTL_TEXTEX:
FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
/* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
* texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
case RTL_AUTO:
case RTL_READDRAW:
case RTL_READTEX:
case RTL_DISABLE:
* (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
/* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
ENTER_GL();
This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
LEAVE_GL();
if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
ENTER_GL();
if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
/* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
if(!swapchain) {
LEAVE_GL();
return WINEDDERR_NOTLOCKED;
ENTER_GL();
LEAVE_GL();
goto unlock_end;
if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
if(!warned) {
ERR("The application tries to write to the render target, but render target locking is disabled\n");
goto unlock_end;
case RTL_READTEX:
case RTL_TEXTEX:
IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
case RTL_AUTO:
case RTL_READDRAW:
case RTL_TEXDRAW:
if(!fullsurface) {
/* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
* the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
* to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
* are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
return WINED3D_OK;
return WINEDDERR_NODC;
return WINEDDERR_DCALREADYCREATED;
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
&lock,
NULL,
/* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
return hr;
if (pal) {
return WINED3D_OK;
return WINEDDERR_NODC;
return WINEDDERR_NODC;
return WINED3D_OK;
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) {
if(srgb_mode) {
case WINED3DFMT_P8:
/* Use conversion when the paletted texture extension OR fragment shaders are available. When either
* conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
if(colorkey_active) {
case WINED3DFMT_R3G3B2:
if (colorkey_active) {
case WINED3DFMT_R5G6B5:
if (colorkey_active) {
case WINED3DFMT_X1R5G5B5:
if (colorkey_active) {
case WINED3DFMT_R8G8B8:
if (colorkey_active) {
case WINED3DFMT_X8R8G8B8:
if (colorkey_active) {
case WINED3DFMT_R8G8_SNORM:
case WINED3DFMT_L6V5U5:
case WINED3DFMT_X8L8V8U8:
case WINED3DFMT_R16G16_SNORM:
case WINED3DFMT_A4L4:
case WINED3DFMT_R16G16_UNORM:
case WINED3DFMT_R16G16_FLOAT:
case WINED3DFMT_R32G32_FLOAT:
return WINED3D_OK;
switch (convert) {
case NO_CONVERSION:
case CONVERT_PALETTED:
case CONVERT_PALETTED_CK:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
case CONVERT_CK_565:
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
Dest++;
case CONVERT_CK_5551:
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
Dest++;
case CONVERT_CK_RGB24:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
case CONVERT_RGB32_888:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
case CONVERT_V8U8:
const short *Source;
unsigned char *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_V16U16:
unsigned short *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_Q8W8V8U8:
unsigned char *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_L6V5U5:
unsigned char *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_X8L8V8U8:
unsigned char *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_A4L4:
const unsigned char *Source;
unsigned char *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_G16R16:
case CONVERT_R16G16F:
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
case CONVERT_R32G32F:
const float *Source;
float *Dest;
for(y = 0; y < height; y++) {
for (x = 0; x < width; x++ ) {
return WINED3D_OK;
/* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
if(index_in_alpha) {
/* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
we can then read GL_ALPHA. Color keying is handled in BltOverride using a GL_ALPHA_TEST using GL_NOT_EQUAL.
In case of index_in_alpha the color key itself is passed to glAlphaFunc in other cases the alpha component
if(index_in_alpha) {
} else if(colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue) && (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
* The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
const char *fragment_palette_conversion =
"TEX index, fragment.texcoord[0], texture[0], 2D;\n"
"TEX result.color, index.a, texture[1], 1D;\n"
GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
return FALSE;
if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
return FALSE;
return TRUE;
/* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
return WINED3D_OK;
return WINED3DERR_INVALIDCALL;
static unsigned int gen = 0;
++gen;
snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
return WINED3D_OK;
if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
ENTER_GL();
if (!*name) {
} else if (*name) {
LEAVE_GL();
#include <errno.h>
#include <stdio.h>
static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
char *allocatedMemory;
const char *textureRow;
/* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
ENTER_GL();
NULL);
LEAVE_GL();
ENTER_GL();
if (tmpTexture) {
LEAVE_GL();
if (NULL == f) {
return WINED3DERR_INVALIDCALL;
/* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
fputc(0,f);
/* 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 */
if(swapChain)
for (y = 0 ; y < height; y++) {
for (i = 0; i < width; i++) {
if(swapChain)
fclose(f);
if(swapChain) {
return WINED3D_OK;
TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
return hr;
return WINED3DERR_INVALIDCALL;
/* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
return WINED3D_OK;
void* tmp;
static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
return WINEDDERR_NOTFLIPPABLE;
return WINED3D_OK;
if(override) {
if(!swapchain) {
return WINEDDERR_NOTFLIPPABLE;
/* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
return hr;
static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
ENTER_GL();
if(!swapchain) {
FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
} else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
if(upsidedown &&
LEAVE_GL();
static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
ENTER_GL();
} else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
if(noBackBufferBackup) {
/* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
/* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
if(swapchain) {
fbheight);
/* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
fbheight);
if(upsidedown) {
glEnd();
if(backup) {
glVertex2i(0, 0);
glEnd();
if(backup) {
LEAVE_GL();
return WINED3DERR_INVALIDCALL;
IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
if(Src) {
return WINED3DERR_INVALIDCALL;
IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
if (DestRect) {
/* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
* This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
if( SrcRect ) {
TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
return WINED3D_OK;
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
} else if(dstSwapchain) {
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
/* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
if(SrcRect) {
TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
* -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
* -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
* If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
* FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
/* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
if(paletteOverride)
return WINED3D_OK;
} else if(Src) {
if(SrcRect) {
TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
/* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
if(paletteOverride)
return WINED3D_OK;
return WINED3DERR_INVALIDCALL;
UINT h;
ENTER_GL();
glEnd();
/* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
if(paletteOverride)
LEAVE_GL();
if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
glFlush();
return WINED3D_OK;
return WINED3DERR_INVALIDCALL;
return WINED3DERR_INVALIDCALL;
return WINED3D_OK;
return WINED3DERR_INVALIDCALL;
float depth;
case WINED3DFMT_D16_UNORM:
case WINED3DFMT_D15S1:
case WINED3DFMT_D24S8:
case WINED3DFMT_D24X8:
case WINED3DFMT_D32:
return WINED3DERR_INVALIDCALL;
static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
return WINEDDERR_SURFACEBUSY;
if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
return WINED3DERR_INVALIDCALL;
} else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
return WINED3D_OK;
if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
return WINEDDERR_SURFACEBUSY;
TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
return WINED3DERR_INVALIDCALL;
if(rsrc) {
if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
int bpp;
BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
/* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
return WINED3D_OK;
return WINED3DERR_NOTAVAILABLE;
if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
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)
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.
Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
return WINED3D_OK;
struct depth_blt_info
static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
switch (target)
case GL_TEXTURE_2D:
case GL_TEXTURE_RECTANGLE_ARB:
static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
glViewport(0, 0, w, h);
glEnd();
glPopAttrib();
ENTER_GL();
GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
LEAVE_GL();
ENTER_GL();
surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
LEAVE_GL();
static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
if(persistent) {
if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
struct coords {
GLfloat x, y, z;
struct float_rect
struct float_rect f;
if(rect_in) {
case GL_TEXTURE_2D:
coords[0].z = 0;
case GL_TEXTURE_RECTANGLE_ARB:
ENTER_GL();
glEnd();
LEAVE_GL();
if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
/* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
glFlush();
/* We changed the filtering settings on the texture. Inform the container about this to get the filters
if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
int bpp;
if(rect) {
return WINED3D_OK;
return WINED3DERR_DEVICELOST;
if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
if(!mem) {
return WINED3DERR_OUTOFVIDEOMEMORY;
d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
if(srgb) {
if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
if(!mem) {
return WINED3DERR_OUTOFVIDEOMEMORY;
d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
ENTER_GL();
LEAVE_GL();
surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
/* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
ENTER_GL();
LEAVE_GL();
return WINED3D_OK;
static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
if(container) {
if(swapchain) {
return SURFACE_OPENGL;
return WINED3D_OK;
return hr;
return WINED3D_OK;
return TRUE;
return FALSE;