jdk/src/windows/native/sun/java2d/d3d/D3DUtils.cpp
author duke
Sat, 01 Dec 2007 00:00:00 +0000
changeset 2 90ce3da70b43
permissions -rw-r--r--
Initial load

/*
 * Copyright 2005-2006 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */


#include "ddrawUtils.h"
#include "D3DUtils.h"
#include "D3DSurfaceData.h"

#ifdef DEBUG
// These strings must be in the same order as pixel
// formats in D3DSurfaceData.java
char * TR_NAMES[] = {
    "TR_OPAQUE",
    "TR_BITMASK",
    "TR_TRANSLUCENT"
};

char * PF_NAMES[] = {
    "PF_INVALID" ,
    "PF_INT_ARGB" ,
    "PF_INT_RGB" ,
    "PF_INT_RGBX",
    "PF_INT_BGR" ,
    "PF_USHORT_565_RGB" ,
    "PF_USHORT_555_RGB" ,
    "PF_USHORT_555_RGBX" ,
    "PF_INT_ARGB_PRE" ,
    "PF_USHORT_4444_ARGB"
};
#endif // DEBUG

/**
 * This structure could be used when searching for a pixel
 * format with preferred bit depth.
 */
typedef struct {
    // Pointer to a DDPIXELFORMAT structure where the found pixel
    // format will be copied to
    DDPIXELFORMAT *pddpf;
    // If TRUE, the search was successful, FALSE otherwise
    BOOL bFoundFormat;
    // Preferred bit depth
    int preferredDepth;
} PixelFormatSearchStruct;

jint D3DUtils_GetPixelFormatType(DDPIXELFORMAT*lpddpf);

HRESULT WINAPI EnumAlphaTextureFormatsCallback(DDPIXELFORMAT* pddpf,
                                               VOID* pContext )
{
    J2dTraceLn(J2D_TRACE_VERBOSE, "EnumAlphaTextureFormatsCallback");
    DDPIXELFORMAT* pddpfOut = (DDPIXELFORMAT*)pContext;

    // Looking for a 8-bit luminance texture (and probably not alpha-luminance)
    if((pddpf->dwFlags & DDPF_ALPHA) && (pddpf->dwAlphaBitDepth == 8))
    {
        memcpy(pddpfOut, pddpf, sizeof(DDPIXELFORMAT));
        return D3DENUMRET_CANCEL;
    }

    return D3DENUMRET_OK;
}

HRESULT CALLBACK
D3DUtils_TextureSearchCallback(DDPIXELFORMAT *lpddpf,
                               void *param)
{
    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DUtils_TextureSearchCallback");
    jint pfType = D3DUtils_GetPixelFormatType(lpddpf);
    if (pfType == PF_INVALID) {
        return DDENUMRET_OK;
    }

    DWORD dwAlphaBitCount = 0;
    if (lpddpf->dwFlags & DDPF_ALPHAPIXELS) {
        DWORD dwMask = lpddpf->dwRGBAlphaBitMask;
        while( dwMask ) {
            dwMask = dwMask & ( dwMask - 1 );
            dwAlphaBitCount++;
        }
    }

    DWORD dwRGBBitCount = lpddpf->dwRGBBitCount;
    WORD wDepthIndex = D3D_DEPTH_IDX(dwRGBBitCount);
    WORD wTransparencyIndex =
        dwAlphaBitCount > 0 ? TR_TRANSLUCENT_IDX : TR_OPAQUE_IDX;

    D3DTextureTable *table = (D3DTextureTable*)param;
    D3DTextureTableCell *cell = &(*table)[wTransparencyIndex][wDepthIndex];
    if (cell->pfType == PF_INVALID || pfType < cell->pfType) {
        // set only if it wasn't set or if current pfType is better than
        // the one found previously: it's better to use 565 than 555
        memcpy(&cell->pddpf, lpddpf, sizeof(DDPIXELFORMAT));
        cell->pfType = pfType;
    }
    // continue for all pixel formats
    return DDENUMRET_OK;
}

HRESULT
WINAPI EnumZBufferFormatsCallback(DDPIXELFORMAT* pddpf,
                                  VOID* pContext )
{
    J2dTraceLn(J2D_TRACE_VERBOSE, "EnumZBufferFormatsCallback");
    PixelFormatSearchStruct *ppfss = (PixelFormatSearchStruct*)pContext;
    DDPIXELFORMAT* pddpfOut = ppfss->pddpf;

    // if found a format with the exact depth, return it
    if (pddpf->dwZBufferBitDepth == (DWORD)ppfss->preferredDepth) {
        ppfss->bFoundFormat = TRUE;
        memcpy(pddpfOut, pddpf, sizeof(DDPIXELFORMAT));
        return D3DENUMRET_CANCEL;
    }
    // If a format with exact depth can't be found, look for the best
    // available, preferring those with the lowest bit depth to save
    // video memory. Also, prefer formats with no stencil bits.
    if (!ppfss->bFoundFormat ||
        (pddpfOut->dwZBufferBitDepth > pddpf->dwZBufferBitDepth &&
         !(pddpf->dwFlags & DDPF_STENCILBUFFER)))
    {
        ppfss->bFoundFormat = TRUE;
        memcpy(pddpfOut, pddpf, sizeof(DDPIXELFORMAT));
    }

    return D3DENUMRET_OK;
}

HRESULT
WINAPI DeviceEnumCallback(LPSTR strDesc, LPSTR strName,
                          LPD3DDEVICEDESC7 pDesc,
                          LPVOID pParentInfo)
{
    J2dTraceLn(J2D_TRACE_VERBOSE, "DeviceEnumCallback");
    DEVICES_INFO *devinfo = (DEVICES_INFO*)pParentInfo;

    if (pDesc->deviceGUID == IID_IDirect3DHALDevice) {
        devinfo->pGUIDs[HAL_IDX] = &IID_IDirect3DHALDevice;
    } else if (pDesc->deviceGUID == IID_IDirect3DTnLHalDevice) {
        devinfo->pGUIDs[TNL_IDX] = &IID_IDirect3DTnLHalDevice;
    } else if (pDesc->deviceGUID == IID_IDirect3DRGBDevice) {
        devinfo->pGUIDs[RGB_IDX] = &IID_IDirect3DRGBDevice;
    } else if (pDesc->deviceGUID == IID_IDirect3DRefDevice) {
        devinfo->pGUIDs[REF_IDX] = &IID_IDirect3DRefDevice;
    }
    return D3DENUMRET_OK;
}

HRESULT
D3DUtils_FindMaskTileTextureFormat(IDirect3DDevice7 *d3dDevice,
                                   DDPIXELFORMAT* pddpf)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_FindMaskTileTextureFormat");
    d3dDevice->EnumTextureFormats(EnumAlphaTextureFormatsCallback,
                                  (void*)pddpf);
    if (pddpf->dwAlphaBitDepth == 8) {
        return D3D_OK;
    }
    return DDERR_GENERIC;
}

HRESULT
D3DUtils_FindDepthBufferFormat(IDirect3D7 *d3dObject,
                               int preferredDepth,
                               DDPIXELFORMAT* pddpf,
                               const GUID *pDeviceGUID)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_FindDepthBufferFormat");
    PixelFormatSearchStruct pfss;
    pfss.pddpf = pddpf;
    pfss.bFoundFormat = FALSE;
    pfss.preferredDepth = preferredDepth;

    d3dObject->EnumZBufferFormats(*pDeviceGUID,
                                  EnumZBufferFormatsCallback,
                                  (void*)&pfss);

    return pfss.bFoundFormat ? D3D_OK : DDERR_GENERIC;
}

jint D3DUtils_GetPixelFormatType(DDPIXELFORMAT*lpddpf)
{
    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DUtils_GetPixelFormatType");

    if (lpddpf == NULL) return PF_INVALID;

    DWORD dwFlags = lpddpf->dwFlags;
    // skip weird formats
    if (lpddpf->dwRGBBitCount < 16   ||
        dwFlags & DDPF_ALPHA         || dwFlags & DDPF_ZBUFFER       ||
        dwFlags & DDPF_ZPIXELS       || dwFlags & DDPF_LUMINANCE     ||
        dwFlags & DDPF_FOURCC        || dwFlags & DDPF_STENCILBUFFER ||
        dwFlags & DDPF_BUMPLUMINANCE || dwFlags & DDPF_BUMPDUDV)
    {
        return PF_INVALID;
    }

    jint pfType = PF_INVALID;
    DWORD aMask = lpddpf->dwRGBAlphaBitMask;
    DWORD rMask = lpddpf->dwRBitMask;
    DWORD gMask = lpddpf->dwGBitMask;
    DWORD bMask = lpddpf->dwBBitMask;

    if (rMask == 0x0000f800 &&
        gMask == 0x000007e0 &&
        bMask == 0x0000001f &&
        aMask == 0x00000000)
    {
        pfType = PF_USHORT_565_RGB;
    } else if (rMask == 0x00007C00 &&
               gMask == 0x000003E0 &&
               bMask == 0x0000001f &&
               aMask == 0x00000000)
    {
        pfType = PF_USHORT_555_RGB;
    } else if (rMask == 0x00000f00 &&
               gMask == 0x000000f0 &&
               bMask == 0x0000000f &&
               aMask == 0x0000f000)
    {
        // REMIND: we currently don't support this
        // pixel format, since we don't have the loops for a
        // premultiplied version of it. So we'll just use INT_ARGB
        // for now
        pfType = PF_INVALID;
        // pfType = PF_USHORT_4444_ARGB;
    } else if (rMask == 0x00ff0000 &&
               gMask == 0x0000ff00 &&
               bMask == 0x000000ff)
    {
        if (lpddpf->dwRGBBitCount == 32) {
            pfType = (dwFlags & DDPF_ALPHAPIXELS) ?
                PF_INT_ARGB : PF_INT_RGB;
        } else {
            // We currently don't support this format.
            // pfType = PF_3BYTE_BGR;
            pfType = PF_INVALID;
        }
    }

    return pfType;
}

void
D3DUtils_SetupTextureFormats(IDirect3DDevice7 *d3dDevice,
                             D3DTextureTable &table)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_SetupTextureFormats");
    if (d3dDevice == NULL || table == NULL) {
        return;
    }

    ZeroMemory(table, sizeof(D3DTextureTable));
    int t;
    for (t = TR_OPAQUE_IDX; t < TR_MAX_IDX; t++) {
        for (int d = DEPTH16_IDX; d < DEPTH_MAX_IDX; d++) {
            table[t][d].pfType = PF_INVALID;
        }
    }
    d3dDevice->EnumTextureFormats(D3DUtils_TextureSearchCallback, table);

    // We've retrieved the pixel formats for this device. The matrix may
    // look something like this, depending on the formats the device supports:
    // Transparency/Depth        Depth 16            Depth 24          Depth 32
    // ------------------------------------------------------------------------
    //      TR_OPAQUE   PF_USHORT_565_RGB          PF_INVALID        PF_INT_RGB
    //     TR_BITMASK          PF_INVALID          PF_INVALID        PF_INVALID
    // TR_TRANSLUCENT          PF_INVALID          PF_INVALID       PF_INT_ARGB


    // we'll be using translucent pixel formats for bitmask images
    // for now, this may change later
    memcpy(&table[TR_BITMASK_IDX], &table[TR_TRANSLUCENT_IDX],
           sizeof(D3DTextureTableCell[DEPTH_MAX_IDX]));
    // Transparency/Depth        Depth 16            Depth 24          Depth 32
    // ------------------------------------------------------------------------
    //      TR_OPAQUE   PF_USHORT_565_RGB          PF_INVALID        PF_INT_RGB
    //     TR_BITMASK          PF_INVALID          PF_INVALID       PF_INT_ARGB
    // TR_TRANSLUCENT          PF_INVALID          PF_INVALID       PF_INT_ARGB

    // REMIND: crude force
    // Find substitutes for pixel formats which we didn't find.
    // For example, if we didn't find a 24-bit format, 32-bit will be
    // a first choice for substitution. But if it wasn't found either,
    // then use 16-bit format
    D3DTextureTableCell *cell16, *cell24, *cell32;
    for (t = TR_OPAQUE_IDX; t < TR_MAX_IDX; t++) {
        cell16 = &table[t][DEPTH16_IDX];
        cell24 = &table[t][DEPTH24_IDX];
        cell32 = &table[t][DEPTH32_IDX];
        if (cell32->pfType == PF_INVALID) {
            if (cell24->pfType != PF_INVALID) {
                memcpy(cell32, cell24, sizeof(D3DTextureTableCell));
            } else if (cell16->pfType != PF_INVALID) {
                memcpy(cell32, cell16, sizeof(D3DTextureTableCell));
            } else {
                // no valid pixel formats for this transparency
                // type were found
                continue;
            }
        }
        // now we know that 32-bit is valid
        if (cell24->pfType == PF_INVALID) {
            // use 32-bit format as a substitution for 24-bit
            memcpy(cell24, cell32, sizeof(D3DTextureTableCell));
        }
        // now we know that 32- and 24-bit are valid
        if (cell16->pfType == PF_INVALID) {
            // use 24-bit format as a substitution for 16-bit
            memcpy(cell16, cell24, sizeof(D3DTextureTableCell));
        }
    }
    // After this loop the matrix may look something like this:
    // Transparency/Depth        Depth 16            Depth 24          Depth 32
    // ------------------------------------------------------------------------
    //      TR_OPAQUE   PF_USHORT_565_RGB          PF_INT_RGB        PF_INT_RGB
    //     TR_BITMASK         PF_INT_ARGB         PF_INT_ARGB       PF_INT_ARGB
    // TR_TRANSLUCENT         PF_INT_ARGB         PF_INT_ARGB       PF_INT_ARGB

#ifdef DEBUG
    // Print out the matrix (should look something like the comment above)
    J2dTraceLn1(J2D_TRACE_INFO,
                "Texutre formats table for device %x", d3dDevice);
    J2dTraceLn(J2D_TRACE_INFO, "Transparency/Depth     Depth 16            "\
               "Depth 24            Depth 32");
    J2dTraceLn(J2D_TRACE_INFO, "-------------------------------------------"\
               "----------------------------");
    for (t = TR_OPAQUE_IDX; t < TR_MAX_IDX; t++) {
        J2dTrace1(J2D_TRACE_INFO, "%15s", TR_NAMES[t]);
        for (int d = DEPTH16_IDX; d < DEPTH_MAX_IDX; d++) {
            J2dTrace1(J2D_TRACE_INFO, "%20s",
                      PF_NAMES[table[t][d].pfType]);
        }
        J2dTrace(J2D_TRACE_INFO, "\n");
    }
#endif // DEBUG
}

const GUID *
D3DUtils_SelectDeviceGUID(IDirect3D7 *d3dObject)
{
    static char * RASTERIZER_NAMES[] = {
        "TNL", "HAL", "REFERENCE", "RGB"
    };
    // try to use TnL rasterizer by default
    int defIndex = TNL_IDX;

    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_SelectDeviceGUID");
    // unless a different one was requested
    char *pRasterizer = getenv("J2D_D3D_RASTERIZER");
    if (pRasterizer != NULL) {
        if (strncmp(pRasterizer, "ref", 3) == 0) {
            defIndex = REF_IDX;
        } else if (strncmp(pRasterizer, "rgb", 3) == 0) {
            defIndex = RGB_IDX;
        } else if (strncmp(pRasterizer, "hal", 3) == 0) {
            defIndex = HAL_IDX;
        } else if (strncmp(pRasterizer, "tnl", 3) == 0) {
            defIndex = TNL_IDX;
        }
        J2dTraceLn1(J2D_TRACE_VERBOSE,
                    "  rasterizer requested: %s",
                    RASTERIZER_NAMES[defIndex]);
    }

    DEVICES_INFO devInfo;
    memset(&devInfo, 0, sizeof(devInfo));
    HRESULT res;
    if (FAILED(res = d3dObject->EnumDevices(DeviceEnumCallback,
                                            (VOID*)&devInfo)))
    {
        DebugPrintDirectDrawError(res, "D3DUtils_SelectDeviceGUID: "\
                                  "EnumDevices failed");
        return NULL;
    }

    // return requested rasterizer's guid if it's present
    if (devInfo.pGUIDs[defIndex] != NULL) {
        J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
                       "D3DUtils_SelectDeviceGUID: using %s rasterizer",
                       RASTERIZER_NAMES[defIndex]);
        return devInfo.pGUIDs[defIndex];
    }
    // if not, try to find one, starting with the best available
    defIndex = TNL_IDX;
    do {
        if (devInfo.pGUIDs[defIndex] != NULL) {
            J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
                           "D3DUtils_SelectDeviceGUID: using %s rasterizer",
                           RASTERIZER_NAMES[defIndex]);
            return devInfo.pGUIDs[defIndex];
        }
        // While we could use the rgb and ref rasterizers if tnl and
        // hal aren't present, it's not practical for performance purposes.
        // so we just leave an opportunity to force them.
    } while (++defIndex < REF_IDX /*DEV_IDX_MAX*/);


    J2dRlsTraceLn(J2D_TRACE_ERROR,
                  "D3DUtils_SelectDeviceGUID: "\
                  "No Accelerated Rasterizers Found");
    return NULL;
}


/*
 * This function sets passed matrix to be a custom left-hand off-center
 * orthogonal matrix. The output is identical to D3DX's function call
 * D3DXMatrixOrthoOffCenterLH((D3DXMATRIX*)&tx,
 *                            0.0, width, height, 0.0, -1.0, 1.0);
 */
void
D3DUtils_SetOrthoMatrixOffCenterLH(D3DMATRIX *m,
                                   float width, float height)
{
    DASSERT((m != NULL) && (width > 0.0f) && (height > 0.0f));
    memset(m, 0, sizeof(D3DMATRIX));
    m->_11 =  2.0f/width;
    m->_22 = -2.0f/height;
    m->_33 =  0.5f;
    m->_44 =  1.0f;

    m->_41 = -1.0f;
    m->_42 =  1.0f;
    m->_43 =  0.5f;
}

void
D3DUtils_SetIdentityMatrix(D3DMATRIX *m, BOOL adjust)
{
    DASSERT(m != NULL);
    m->_12 = m->_13 = m->_14 = m->_21 = m->_23 = m->_24 = 0.0f;
    m->_31 = m->_32 = m->_34 = m->_43 = 0.0f;
    m->_11 = m->_22 = m->_33 = m->_44 = 1.0f;
    if (adjust) {
        // This is required for proper texel alignment
        m->_41 = m->_42 = -0.5f;
    } else {
        m->_41 = m->_42 = 0.0f;
    }
}

DDrawSurface *
D3DUtils_CreatePlainSurface(JNIEnv *env,
                            DDraw *ddObject,
                            D3DContext *d3dContext,
                            int w, int h)
{
    DXSurface *dxSurface;
    jint pType;
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CreatePlainSurface");
    if (FAILED(d3dContext->CreateSurface(env, w, h, 32,
                                         TR_OPAQUE, D3D_PLAIN_SURFACE,
                                         &dxSurface, &pType)))
    {
        return NULL;
    }
    return new DDrawSurface(ddObject, dxSurface);
}

DDrawSurface *
D3DUtils_CreateTexture(JNIEnv *env,
                       DDraw *ddObject,
                       D3DContext *d3dContext,
                       int transparency,
                       int w, int h)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CreateTexture");
    DXSurface *dxSurface;
    jint pType;
    if (FAILED(d3dContext->CreateSurface(env, w, h, 32,
                                         transparency, D3D_TEXTURE_SURFACE,
                                         &dxSurface, &pType)))
    {
        return NULL;
    }
    return new DDrawSurface(ddObject, dxSurface);
}

HRESULT
D3DUtils_UploadIntImageToXRGBTexture(DDrawSurface *lpTexture,
                                     int *pSrc, int width, int height)
{
    HRESULT res;
    int texW = lpTexture->GetDXSurface()->GetWidth();
    int texH = lpTexture->GetDXSurface()->GetHeight();
    int srcStride = width * 4;

    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_UploadIntImageToXRGBTexture");
    if (width > texW) {
        width = texW;
    }
    if (height > texH) {
        height = texH;
    }

    SurfaceDataRasInfo rasInfo;
    if (SUCCEEDED(res = lpTexture->Lock(NULL, &rasInfo,
                                        DDLOCK_WAIT|DDLOCK_NOSYSLOCK, NULL)))
    {
        void *pDstPixels = rasInfo.rasBase;
        void *pSrcPixels = (void*)pSrc;

        // REMIND: clear the dest first
        memset(pDstPixels, 0, texH * rasInfo.scanStride);
        do {
            memcpy(pDstPixels, pSrcPixels, width * 4);
            pSrcPixels = PtrAddBytes(pSrcPixels, srcStride);
            pDstPixels = PtrAddBytes(pDstPixels, rasInfo.scanStride);
        } while (--height > 0);
        res = lpTexture->Unlock(NULL);
    }
    return res;
}

HRESULT
D3DUtils_CheckD3DCaps(LPD3DDEVICEDESC7 lpDesc7)
{
    // The device must support fast rasterization
    static DWORD dwDevCaps =
        (D3DDEVCAPS_DRAWPRIMTLVERTEX | D3DDEVCAPS_HWRASTERIZATION);
    BOOL vt = lpDesc7->dwDevCaps & D3DDEVCAPS_DRAWPRIMTLVERTEX;
    BOOL rz = lpDesc7->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION;

    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CheckD3DCaps");
    return (lpDesc7->dwDevCaps & dwDevCaps) ?
        D3D_OK :
        DDERR_GENERIC;
}

HRESULT
D3DUtils_CheckTextureCaps(LPD3DDEVICEDESC7 lpDesc7)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CheckTextureCaps");
    // REMIND: we should really check both Tri and Lin caps,
    // but hopefully we won't be using line strips soon
    LPD3DPRIMCAPS lpDpcTriCaps = &lpDesc7->dpcTriCaps;
    // Filtering requirements
    static DWORD dwFilterCaps =
        (D3DPTFILTERCAPS_LINEAR | D3DPTFILTERCAPS_NEAREST);
    // Check for caps used for alpha compositing (implementation of
    // Porter-Duff rules)
    static DWORD dwBlendCaps =
        (D3DPBLENDCAPS_ZERO | D3DPBLENDCAPS_ONE |
         D3DPBLENDCAPS_SRCALPHA  | D3DPBLENDCAPS_INVSRCALPHA |
         D3DPBLENDCAPS_DESTALPHA | D3DPBLENDCAPS_INVDESTALPHA);

    if ((lpDesc7->dwTextureOpCaps & D3DTEXOPCAPS_MODULATE) &&
        (lpDpcTriCaps->dwTextureFilterCaps & dwFilterCaps) &&
        (lpDpcTriCaps->dwSrcBlendCaps  & dwBlendCaps) &&
        (lpDpcTriCaps->dwDestBlendCaps & dwBlendCaps))
    {
        return D3D_OK;
    }
    return DDERR_GENERIC;
}

HRESULT
D3DUtils_CheckDeviceCaps(LPD3DDEVICEDESC7 lpDesc7) {
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CheckDeviceCaps");
    if (SUCCEEDED(D3DUtils_CheckD3DCaps(lpDesc7)) &&
        SUCCEEDED(D3DUtils_CheckTextureCaps(lpDesc7)) &&
        SUCCEEDED(D3DUtils_CheckDepthCaps(lpDesc7)))
    {
        return D3D_OK;
    }
    return DDERR_GENERIC;
}

HRESULT
D3DUtils_CheckDepthCaps(LPD3DDEVICEDESC7 lpDesc7)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DUtils_CheckDepthCaps");
    // Check for required depth-buffer operations
    // (see D3DContext::SetClip() for more info).
    static DWORD dwZCmpCaps = (D3DPCMPCAPS_ALWAYS | D3DPCMPCAPS_LESS);
    // D3DPMISCCAPS_MASKZ capability allows enabling/disabling
    // depth buffer updates.
    if ((lpDesc7->dpcTriCaps.dwMiscCaps & D3DPMISCCAPS_MASKZ) &&
        (lpDesc7->dpcTriCaps.dwZCmpCaps & dwZCmpCaps))
    {
        return D3D_OK;
    }
    return DDERR_GENERIC;
}