WineD3D patch submission

Mitchell Wheeler mitchell.wheeler at gmail.com
Fri Sep 21 22:58:38 CDT 2007


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;
}



More information about the wine-devel mailing list