Hi all,
A week or so ago I tried to get a game working in WINE (Titan Quest),
however it seems it really needs a proper DIB engine before it'll be
playable.
Anyways, in my attempt to get it working I fixed up some issues w/
"IWineD3DDeviceImpl_UpdateSurface.c" in dlls/wined3d, not entirely sure
how you guys do things around here so I thought i'd just post my changes
to the function in this mailing list and you can do with it what you
want. (Note: it actually has a few different methods of doing one
thing, enclosed in macro blocks to toggle between them. Technically the
first one should work (i think :\) once you guys properly implement
surface locking/unlocking, and it's 'simplest', but the last one is the
only one that actually works properly (using standard OpenGL calls).
You'll probably have to clean up the code to meet your coding standards
/ etc, maybe remove the custom byte size calculation of compressed
images, and some other things - but the functionality is there... i
also fixed the existing code that didn't actually do what it was
supposed to, heh.
Regards,
Mitchell Wheeler
static HRESULT WINAPI IWineD3DDeviceImpl_UpdateSurface(IWineD3DDevice
*iface, IWineD3DSurface *pSourceSurface, CONST RECT* pSourceRect,
IWineD3DSurface *pDestinationSurface, CONST POINT* pDestPoint) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *) iface;
/** TODO: remove casts to IWineD3DSurfaceImpl
* NOTE: move code to surface to accomplish this
****************************************/
IWineD3DSurfaceImpl *pSrcSurface = (IWineD3DSurfaceImpl
*)pSourceSurface;
IWineD3DSurfaceImpl *pDestSurface =
(IWineD3DSurfaceImpl*)pDestinationSurface;
int srcWidth, srcHeight;
unsigned int srcSurfaceWidth, srcSurfaceHeight, destSurfaceWidth,
destSurfaceHeight;
WINED3DFORMAT destFormat, srcFormat;
int srcLeft, srcTop, destLeft, destTop;
WINED3DPOOL srcPool, destPool;
int offset = 0;
int rowoffset = 0; /* how many bytes to add onto the end of a row to
wraparound to the beginning of the next */
glDescriptor *glDescription = NULL, *glSrcDescription = NULL;
GLenum dummy;
int bpp;
UINT destByteSize = 0, srcByteSize = 0;
int destPixelByteSize = 0, srcPixelByteSize = 0;
CONVERT_TYPES convert = NO_CONVERSION;
WINED3DSURFACE_DESC winedesc;
TRACE("(%p) : Source (%p) Rect (%p) Destination (%p) Point(%p)\n",
This, pSourceSurface, pSourceRect, pDestinationSurface, pDestPoint);
memset(&winedesc, 0, sizeof(winedesc));
winedesc.Width = &srcSurfaceWidth;
winedesc.Height = &srcSurfaceHeight;
winedesc.Pool = &srcPool;
winedesc.Format = &srcFormat;
winedesc.Size = &srcByteSize;
IWineD3DSurface_GetDesc(pSourceSurface, &winedesc);
winedesc.Width = &destSurfaceWidth;
winedesc.Height = &destSurfaceHeight;
winedesc.Pool = &destPool;
winedesc.Format = &destFormat;
winedesc.Size = &destByteSize;
IWineD3DSurface_GetDesc(pDestinationSurface, &winedesc);
if(srcPool != WINED3DPOOL_SYSTEMMEM || destPool !=
WINED3DPOOL_DEFAULT){
WARN("source %p must be SYSTEMMEM and dest %p must be DEFAULT,
returning WINED3DERR_INVALIDCALL\n", pSourceSurface, pDestinationSurface);
return WINED3DERR_INVALIDCALL;
}
/* This call loads the opengl surface directly, instead of copying
the surface to the
* destination's sysmem copy. If surface conversion is needed, use
BltFast instead to
* copy in sysmem and use regular surface loading.
*/
d3dfmt_get_conv((IWineD3DSurfaceImpl *) pDestinationSurface, FALSE,
TRUE,
&dummy, &dummy, &dummy, &convert, &bpp, FALSE);
if(convert != NO_CONVERSION) {
return IWineD3DSurface_BltFast(pDestinationSurface,
pDestPoint ? pDestPoint->x : 0,
pDestPoint ? pDestPoint->y : 0,
pSourceSurface, (RECT *)
pSourceRect, 0);
}
if (destFormat == WINED3DFMT_UNKNOWN) {
TRACE("(%p) : Converting destination surface from
WINED3DFMT_UNKNOWN to the source format\n", This);
IWineD3DSurface_SetFormat(pDestinationSurface, srcFormat);
/* Get the update surface description */
IWineD3DSurface_GetDesc(pDestinationSurface, &winedesc);
}
ActivateContext(This, This->lastActiveRenderTarget,
CTXUSAGE_RESOURCELOAD);
ENTER_GL();
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
checkGLcall("glActiveTextureARB");
}
/* Make sure the surface is loaded and up to date */
IWineD3DSurface_PreLoad(pDestinationSurface);
IWineD3DSurface_GetGlDesc(pDestinationSurface, &glDescription);
IWineD3DSurface_GetGlDesc(pDestinationSurface, &glSrcDescription);
/* this needs to be done in lines if the sourceRect != the
sourceWidth */
srcWidth = pSourceRect ? pSourceRect->right -
pSourceRect->left : srcSurfaceWidth;
srcHeight = pSourceRect ? pSourceRect->bottom -
pSourceRect->top : srcSurfaceHeight;
srcLeft = pSourceRect ? pSourceRect->left
: 0;
srcTop = pSourceRect ? pSourceRect->top
: 0;
destLeft = pDestPoint ? pDestPoint->x
: 0;
destTop = pDestPoint ? pDestPoint->y
: 0;
// Calculate the rowOffset / offset values, for copying
#define ISDXTFORMAT(format) \
(format == WINED3DFMT_DXT1 || format == WINED3DFMT_DXT2 ||
format == WINED3DFMT_DXT3 || format == WINED3DFMT_DXT4 || format ==
WINED3DFMT_DXT5)
// Calculating this for our selves because I don't have faith in how
surface->bytesPerPixel is calculated...
destPixelByteSize = destByteSize / (int)(destSurfaceWidth *
destSurfaceHeight);
srcPixelByteSize = srcByteSize / (int)(srcSurfaceWidth *
srcSurfaceHeight);
if(pSourceRect != NULL)
{
// Irregular surface formats
if (destFormat != srcFormat) {
FIXME("Can not convert between surface pixel formats...");
// Would require some relatively big code restructuring for
rare occurances (that shouldn't even be supported?)
return WINED3DERR_INVALIDCALL;
}
// Uniform surface formats
else
{
if(srcWidth != srcSurfaceWidth || srcLeft > 0)
{
rowoffset = srcSurfaceWidth * srcPixelByteSize;
offset += (srcLeft * srcPixelByteSize);
}
if(srcTop > 0)
offset += srcTop * srcSurfaceWidth * srcPixelByteSize;
}
}
/* Sanity check */
if (IWineD3DSurface_GetData(pSourceSurface) == NULL) {
/* need to lock the surface to get the data */
FIXME("Surfaces has no allocated memory, but should be an in
memory only surface\n");
}
/* TODO: Cube and volume support */
if(rowoffset != 0) {
// Compressed texture
if (ISDXTFORMAT(destFormat))
{
if (GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC))
{
#if 0
RECT rect;
rect.left = destLeft;
rect.top = destTop;
rect.right = destLeft + srcWidth;
rect.bottom = destTop + srcHeight;
// Lock source/destination regions
WINED3DLOCKED_RECT destRect, srcRect;
printf("Locking surfaces...\n");
if(
FAILED(IWineD3DSurface_LockRect(pDestinationSurface, &destRect, &rect,
0)) ||
FAILED(IWineD3DSurface_LockRect(pSourceSurface,
&srcRect, pSourceRect, WINED3DLOCK_READONLY)))
{
// How should we handle this case? Just returning
an invalid call for now...
return WINED3DERR_INVALIDCALL;
}
printf("Surfaces locked, copying contents now... ");
if(destFormat == WINED3DFMT_DXT1) {
printf("DXT1\n");
//memcpy(destRect.pBits, srcRect.pBits,
srcRect.Pitch * (srcHeight / 2));
//memcpy(destRect.pBits, srcRect.pBits,
srcRect.Pitch * (srcHeight / 2));
memcpy(destRect.pBits, srcRect.pBits,
ceil(srcWidth/4) * ceil(srcHeight/4) * 8);
} else {
printf("DXT2-5\n");
//memcpy(destRect.pBits, srcRect.pBits,
srcRect.Pitch * (srcHeight / 4));
memcpy(destRect.pBits, srcRect.pBits,
ceil(srcWidth/4) * ceil(srcHeight/4) * 16);
}
// Unlock source/destination regions
IWineD3DSurface_UnlockRect(pSourceSurface);
IWineD3DSurface_UnlockRect(pDestinationSurface);
printf("Unlocking surfaces...\n");
#elif 0
// Get pointers to the source/destination buffers
unsigned char *dest_data = (unsigned
char*)IWineD3DSurface_GetData(pDestinationSurface);
const unsigned char *src_data = (unsigned
char*)IWineD3DSurface_GetData(pSourceSurface);
// Copy subsection of source buffer, in to destination
buffer.
unsigned char *dest_ptr = dest_data + offset;
const unsigned char *src_ptr = src_data + offset;
size_t i;
for(i = 0; i < srcHeight; ++i) {
memcpy(dest_ptr, src_ptr, srcWidth * srcPixelByteSize);
dest_ptr += rowoffset;
src_ptr += rowoffset;
}
// Upload destination buffer to server
GL_EXTCALL(glCompressedTexImage2DARB(glDescription->target,
glDescription->level,
glDescription->glFormatInternal,
srcWidth,
srcHeight,
0,
destByteSize,
dest_data));
checkGLcall("glCompressedTexImage2DARB");
#elif 1
int blocksize = 16;
if(destFormat == WINED3DFMT_DXT1)
blocksize = 8;
#define max(a, b) (a > b? a : b)
srcHeight = max(srcHeight, 4);
srcWidth = max(srcWidth, 4);
#undef max
int data_size = (srcWidth / 4) * (srcHeight / 4) *
blocksize;
int row_size = data_size / srcHeight;
int pixel_size = row_size / srcWidth;
int stride = srcSurfaceWidth * pixel_size;
unsigned char *temp_buffer = HeapAlloc(GetProcessHeap(),
0, data_size);
unsigned char *temp_ptr = temp_buffer;
// Debug info
//printf(" > BytesTotal: %i | BytesPerPixel: %i |
RowSize: %i\n", data_size, pixel_size, row_size);
//printf(" > Source: %i -> %i, %i -> %i | Dest: %i,
%i\n", srcLeft, srcWidth, srcTop, srcHeight, destLeft, destTop);
//printf(" > Offset: %i\n", (int)((row_size * srcTop) +
srcLeft * pixel_size));
// Get pointers to the source buffer
const unsigned char *src_data = (unsigned
char*)IWineD3DSurface_GetData(pSourceSurface);
src_data += (stride * srcTop) + srcLeft * pixel_size;
size_t i;
for(i = 0; i < srcHeight; ++i) {
memcpy(temp_ptr, src_data, row_size);
temp_ptr += row_size;
src_data += stride;
}
GL_EXTCALL(glCompressedTexSubImage2DARB(
glDescription->target,
glDescription->level,
destLeft,
destTop,
srcWidth,
srcHeight,
glDescription->glFormatInternal,
data_size,
temp_buffer
));
//checkGLcall("glCompressedTexSubImage2DARB");
HeapFree(GetProcessHeap(), 0, temp_buffer);
#endif
}
else
{
FIXME("TODO: Need to update a DXT compressed texture
without hardware support\n");
}
}
// Uncompressed texture
else
{
/* not a whole row so we have to do it a line at a time */
int j;
/* hopefully using pointer addtion will be quicker than
using a point + j * rowoffset */
const unsigned char* data =((const unsigned char
*)IWineD3DSurface_GetData(pSourceSurface)) + offset;
for(j = destTop; j < (srcHeight + destTop); ++j)
{
glTexSubImage2D(glDescription->target
,glDescription->level
,destLeft
,j
,srcWidth
,1
,glDescription->glFormat
,glDescription->glType
,data /* could be quicker using */
);
data += rowoffset;
}
}
} else {
// Compressed texture
if (ISDXTFORMAT(destFormat))
{
if (GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC))
{
int blocksize = 16;
if(destFormat == WINED3DFMT_DXT1)
blocksize = 8;
#define max(a, b) (a > b? a : b)
int data_size = (max(srcWidth, 4) / 4) * (max(srcHeight,
4) / 4) * blocksize;
#undef max
//printf(" -> S = %i | CompressedTexImage2D(?, %i, %i,
%i, %i, 0, %i, ?)\n", destByteSize, glDescription->level,
glDescription->glFormatInternal, srcWidth, srcHeight, data_size);
GL_EXTCALL(glCompressedTexSubImage2DARB(glDescription->target,
glDescription->level,
destLeft,
destTop,
srcWidth,
srcHeight,
glDescription->glFormatInternal,
data_size,
IWineD3DSurface_GetData(pSourceSurface)));
checkGLcall("glCompressedTexImage2DARB");
}
else
{
FIXME("TODO: Need to update a DXT compressed texture
without hardware support\n");
}
}
// Uncompressed Texture
else
{
glTexSubImage2D(
glDescription->target,
glDescription->level,
destLeft,
destTop,
srcWidth,
srcHeight,
glDescription->glFormat,
glDescription->glType,
IWineD3DSurface_GetData(pSourceSurface)
);
checkGLcall("glTexSubImage2D");
}
}
LEAVE_GL();
((IWineD3DSurfaceImpl *)pDestinationSurface)->Flags &= ~SFLAG_INSYSMEM;
((IWineD3DSurfaceImpl *)pDestinationSurface)->Flags |= SFLAG_INTEXTURE;
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_SAMPLER(0));
return WINED3D_OK;
}