view src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp @ 4305:7181441faf72

7003962: AWT: securely load DLLs and launch executables using fully qualified path Reviewed-by: art, bae, alanb
author dcherepanov
date Fri, 08 Apr 2011 16:44:14 +0400
parents a06412e13bf7
children 92b5197e9ff5
line wrap: on
line source

/*
 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "D3DBadHardware.h"
#include "D3DPipelineManager.h"
#include "D3DRenderQueue.h"
#include "WindowsFlags.h"
#include "awt_Win32GraphicsDevice.h"

// state of the adapter prior to initialization
#define CONTEXT_NOT_INITED 0
// this state is set if adapter initialization had failed
#define CONTEXT_INIT_FAILED (-1)
// this state is set if adapter was successfully created
#define CONTEXT_CREATED 1

static BOOL bNoHwCheck = (getenv("J2D_D3D_NO_HWCHECK") != NULL);

D3DPipelineManager *D3DPipelineManager::pMgr = NULL;


D3DPipelineManager * D3DPipelineManager::CreateInstance(void)
{
    if (!IsD3DEnabled() ||
        FAILED((D3DPipelineManager::CheckOSVersion())) ||
        FAILED((D3DPipelineManager::GDICheckForBadHardware())))
    {
        return NULL;
    }

    if (pMgr == NULL) {
        pMgr = new D3DPipelineManager();
        if (FAILED(pMgr->InitD3D())) {
            SAFE_DELETE(pMgr);
        }
    } else {
        // this should never happen so to be on the safe side do not
        // use this unexpected pointer, do not try to release it, just null
        // it out and fail safely
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
                       "D3DPPLM::CreateInstance: unexpected instance: 0x%x,"\
                       " abort.", pMgr);
        pMgr = NULL;
    }
    return pMgr;
}

void D3DPipelineManager::DeleteInstance()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::DeleteInstance()");
    SAFE_DELETE(pMgr);
}

D3DPipelineManager * D3DPipelineManager::GetInstance(void)
{
    return pMgr;
}

D3DPipelineManager::D3DPipelineManager(void)
{
    pd3d9 = NULL;
    hLibD3D9 = NULL;
    pAdapters = NULL;
    adapterCount = 0;
    currentFSFocusAdapter = -1;
    defaultFocusWindow = 0;
    devType = SelectDeviceType();
}

D3DPipelineManager::~D3DPipelineManager(void)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::~D3DPipelineManager()");
    ReleaseD3D();
}

HRESULT D3DPipelineManager::ReleaseD3D(void)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::ReleaseD3D()");

    ReleaseAdapters();

    SAFE_RELEASE(pd3d9);

    if (hLibD3D9 != NULL) {
        ::FreeLibrary(hLibD3D9);
        hLibD3D9 = NULL;
    }

    return S_OK;
}

// Creates a Direct3D9 object and initializes adapters.
// If succeeded, returns S_OK, otherwise returns the error code.
HRESULT D3DPipelineManager::InitD3D(void)
{
    typedef IDirect3D9 * WINAPI FnDirect3DCreate9(UINT SDKVersion);

    hLibD3D9 = JDK_LoadSystemLibrary("d3d9.dll");
    if (hLibD3D9 == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "InitD3D: no d3d9.dll");
        return E_FAIL;
    }

    FnDirect3DCreate9 *d3dcreate9 = NULL;
    d3dcreate9 = (FnDirect3DCreate9*)
        ::GetProcAddress(hLibD3D9, "Direct3DCreate9");
    if (d3dcreate9 == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "InitD3D: no Direct3DCreate9");
        ::FreeLibrary(hLibD3D9);
        return E_FAIL;
    }

    pd3d9 = d3dcreate9(D3D_SDK_VERSION);
    if (pd3d9 == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
            "InitD3D: unable to create IDirect3D9 object");
        ::FreeLibrary(hLibD3D9);
        return E_FAIL;
    }

    HRESULT res;
    if (FAILED(res = InitAdapters())) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "InitD3D: failed to init adapters");
        ReleaseD3D();
        return res;
    }

    return S_OK;
}

HRESULT D3DPipelineManager::ReleaseAdapters()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::ReleaseAdapters()");

    D3DRQ_ResetCurrentContextAndDestination();
    if (pAdapters != NULL) {
        for (UINT i = 0; i < adapterCount; i++) {
            if (pAdapters[i].pd3dContext != NULL) {
                delete pAdapters[i].pd3dContext;
            }
        }
        delete[] pAdapters;
        pAdapters = NULL;
    }
    if (defaultFocusWindow != 0) {
        DestroyWindow(defaultFocusWindow);
        UnregisterClass(L"D3DFocusWindow", GetModuleHandle(NULL));
        defaultFocusWindow = 0;
    }
    currentFSFocusAdapter = -1;
    return S_OK;
}

// static
void D3DPipelineManager::NotifyAdapterEventListeners(UINT adapter,
                                                     jint eventType)
{
    HMONITOR hMon;
    int gdiScreen;
    D3DPipelineManager *pMgr;

    // fix for 6946559: if d3d preloading fails jmv may be NULL
    if (jvm == NULL) {
        return;
    }

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    pMgr = D3DPipelineManager::GetInstance();
    RETURN_IF_NULL(pMgr);
    hMon = pMgr->pd3d9->GetAdapterMonitor(adapter);
    gdiScreen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hMon);

    JNU_CallStaticMethodByName(env, NULL,
        "sun/java2d/pipe/hw/AccelDeviceEventNotifier",
        "eventOccured", "(II)V",
        gdiScreen, eventType);
}

UINT D3DPipelineManager::GetAdapterOrdinalForScreen(jint gdiScreen)
{
    HMONITOR mHnd = AwtWin32GraphicsDevice::GetMonitor(gdiScreen);
    if (mHnd == (HMONITOR)0) {
        return D3DADAPTER_DEFAULT;
    }
    return GetAdapterOrdinalByHmon((HMONITOR)mHnd);
}

// static
HRESULT D3DPipelineManager::HandleAdaptersChange(HMONITOR *pHMONITORs, UINT monNum)
{
    HRESULT res = S_OK;
    BOOL bResetD3D = FALSE, bFound;

    D3DPipelineManager *pMgr = D3DPipelineManager::GetInstance();
    RETURN_STATUS_IF_NULL(pHMONITORs, E_FAIL);
    if (pMgr == NULL) {
        // NULL pMgr is valid when the pipeline is not enabled or if it hasn't
        // been created yet
        return S_OK;
    }
    RETURN_STATUS_IF_NULL(pMgr->pAdapters, E_FAIL);
    RETURN_STATUS_IF_NULL(pMgr->pd3d9, E_FAIL);

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::HandleAdaptersChange");

    if (monNum != pMgr->adapterCount) {
        J2dTraceLn2(J2D_TRACE_VERBOSE,
                   "  number of adapters changed (old=%d, new=%d)",
                   pMgr->adapterCount, monNum);
        bResetD3D = TRUE;
    } else {
        for (UINT i = 0; i < pMgr->adapterCount; i++) {
            HMONITOR hMon = pMgr->pd3d9->GetAdapterMonitor(i);
            if (hMon == (HMONITOR)0x0) {
                J2dTraceLn1(J2D_TRACE_VERBOSE, "  adapter %d: removed", i);
                bResetD3D = TRUE;
                break;
            }
            bFound = FALSE;
            for (UINT mon = 0; mon < monNum; mon++) {
                if (pHMONITORs[mon] == hMon) {
                    J2dTraceLn3(J2D_TRACE_VERBOSE,
                            "  adapter %d: found hmnd[%d]=0x%x", i, mon, hMon);
                    bFound = TRUE;
                    break;
                }
            }
            if (!bFound) {
                J2dTraceLn2(J2D_TRACE_VERBOSE,
                            "  adapter %d: could not find hmnd=0x%x "\
                            "in the list of new hmnds", i, hMon);
                bResetD3D = TRUE;
                break;
            }
        }
    }

    if (bResetD3D) {
        J2dTraceLn(J2D_TRACE_VERBOSE, "  adapters changed: resetting d3d");
        pMgr->ReleaseD3D();
        res = pMgr->InitD3D();
    }
    return res;
}

HRESULT D3DPipelineManager::HandleLostDevices()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::HandleLostDevices()");
    BOOL bAllClear = TRUE;

    HWND hwnd = GetCurrentFocusWindow();
    if (hwnd != defaultFocusWindow) {
        // we're in full-screen mode
        WINDOWPLACEMENT wp;
        ::ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
        wp.length = sizeof(WINDOWPLACEMENT);
        ::GetWindowPlacement(hwnd, &wp);

        // Only attempt to restore the devices if we're in full-screen mode
        // and the fs window is active; sleep otherwise.
        // Restoring a window while minimized causes problems on Vista:
        // sometimes we restore the window too quickly and it pops up back from
        // minimized state when the device is restored.
        //
        // WARNING: this is a sleep on the Toolkit thread! We may reconsider
        // this if we find any issues later.
        if ((wp.showCmd & SW_SHOWMINNOACTIVE) && !(wp.showCmd & SW_SHOWNORMAL)){
            static DWORD prevCallTime = 0;
            J2dTraceLn(J2D_TRACE_VERBOSE, "  fs focus window is minimized");
            DWORD currentTime = ::GetTickCount();
            if ((currentTime - prevCallTime) < 100) {
                J2dTraceLn(J2D_TRACE_VERBOSE, "  tight loop detected, sleep");
                ::Sleep(100);
            }
            prevCallTime = currentTime;
            return D3DERR_DEVICELOST;
        }
    }
    if (pAdapters != NULL) {
        for (UINT i = 0; i < adapterCount; i++) {
            if (pAdapters[i].pd3dContext != NULL) {
                J2dTraceLn1(J2D_TRACE_VERBOSE,
                            "  HandleLostDevices: checking adapter %d", i);
                D3DContext *d3dc = pAdapters[i].pd3dContext;
                if (FAILED(d3dc->CheckAndResetDevice())) {
                    bAllClear = FALSE;
                }
            }
        }
    }
    return bAllClear ? S_OK : D3DERR_DEVICELOST;
}

HRESULT D3DPipelineManager::InitAdapters()
{
    HRESULT res = E_FAIL;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::InitAdapters()");
    if (pAdapters != NULL) {
        ReleaseAdapters();
    }

    adapterCount = pd3d9->GetAdapterCount();
    pAdapters = new D3DAdapter[adapterCount];
    if (pAdapters == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "InitAdapters: out of memory");
        adapterCount = 0;
        return E_FAIL;
    }
    ZeroMemory(pAdapters, adapterCount * sizeof(D3DAdapter));

    res = CheckAdaptersInfo();
    RETURN_STATUS_IF_FAILED(res);

    currentFSFocusAdapter = -1;
    if (CreateDefaultFocusWindow() == 0) {
        return E_FAIL;
    }

    return S_OK;
}

// static
HRESULT
D3DPipelineManager::CheckOSVersion()
{
    // require Windows XP or newer client-class OS
    if (IS_WINVER_ATLEAST(5, 1) &&
        !D3DPPLM_OsVersionMatches(OS_WINSERV_2008R2|OS_WINSERV_2008|
                                  OS_WINSERV_2003))
    {
        J2dTraceLn(J2D_TRACE_INFO,
                   "D3DPPLM::CheckOSVersion: Windows XP or newer client-classs"\
                   " OS detected, passed");
        return S_OK;
    }
    J2dRlsTraceLn(J2D_TRACE_ERROR,
                  "D3DPPLM::CheckOSVersion: Windows 2000 or earlier (or a "\
                  "server) OS detected, failed");
    if (bNoHwCheck) {
        J2dRlsTraceLn(J2D_TRACE_WARNING,
                      "  OS check overridden via J2D_D3D_NO_HWCHECK");
        return S_OK;
    }
    return E_FAIL;
}

// static
HRESULT
D3DPipelineManager::GDICheckForBadHardware()
{
    DISPLAY_DEVICE dd;
    dd.cb = sizeof(DISPLAY_DEVICE);

    int failedDevices = 0;
    int attachedDevices = 0;
    int i = 0;
    WCHAR *id;
    WCHAR vendorId[5];
    WCHAR deviceId[5];
    DWORD dwDId, dwVId;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::GDICheckForBadHardware");

    // i<20 is to guard against buggy drivers
    while (EnumDisplayDevices(NULL, i, &dd, 0) && i < 20) {
        if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) {
            attachedDevices++;
            id = dd.DeviceID;
            if (wcslen(id) > 21) {
                // get vendor ID
                wcsncpy(vendorId, id+8, 4);
                int args1 = swscanf(vendorId, L"%X", &dwVId);

                // get device ID
                wcsncpy(deviceId, id+17, 4);
                int args2 = swscanf(deviceId, L"%X", &dwDId);

                if (args1 == 1 && args2 == 1) {
                    J2dTraceLn2(J2D_TRACE_VERBOSE,
                                "  device: vendorID=0x%04x, deviceId=0x%04x",
                                dwVId, dwDId);
                    // since we don't have a driver version here we will
                    // just ask to ignore the version for now; bad hw
                    // entries with specific drivers information will be
                    // processed later when d3d is initialized and we can
                    // obtain a driver version
                    if (FAILED(CheckForBadHardware(dwVId, dwDId, MAX_VERSION))){
                        failedDevices++;
                    }
                }
            }
        }

        i++;
    }

    if (failedDevices == attachedDevices) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
            "D3DPPLM::GDICheckForBadHardware: no suitable devices found");
        return E_FAIL;
    }

    return S_OK;
}

BOOL D3DPPLM_OsVersionMatches(USHORT osInfo) {
    static USHORT currentOS = OS_UNDEFINED;

    if (currentOS == OS_UNDEFINED) {
        BOOL bVersOk;
        OSVERSIONINFOEX osvi;

        ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

        bVersOk = GetVersionEx((OSVERSIONINFO *) &osvi);

        J2dRlsTrace(J2D_TRACE_INFO, "[I] OS Version = ");
        if (bVersOk && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
            osvi.dwMajorVersion > 4)
        {
            if (osvi.dwMajorVersion >= 6 && osvi.dwMinorVersion == 0) {
                if (osvi.wProductType == VER_NT_WORKSTATION) {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_VISTA\n");
                    currentOS = OS_VISTA;
                } else {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_WINSERV_2008\n");
                    currentOS = OS_WINSERV_2008;
                }
            } else if (osvi.dwMajorVersion >= 6 && osvi.dwMinorVersion >= 1) {
                if (osvi.wProductType == VER_NT_WORKSTATION) {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_WINDOWS7 or newer\n");
                    currentOS = OS_WINDOWS7;
                } else {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_WINSERV_2008R2 or newer\n");
                    currentOS = OS_WINSERV_2008R2;
                }
            } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
                if (osvi.wProductType == VER_NT_WORKSTATION) {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_WINXP_64\n");
                    currentOS = OS_WINXP_64;
                } else {
                    J2dRlsTrace(J2D_TRACE_INFO, "OS_WINSERV_2003\n");
                    currentOS = OS_WINSERV_2003;
                }
            } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
                J2dRlsTrace(J2D_TRACE_INFO, "OS_WINXP ");
                currentOS = OS_WINXP;
                if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
                    J2dRlsTrace(J2D_TRACE_INFO, "Home\n");
                } else {
                    J2dRlsTrace(J2D_TRACE_INFO, "Pro\n");
                }
            } else {
                J2dRlsTrace2(J2D_TRACE_INFO,
                            "OS_UNKNOWN: dwMajorVersion=%d dwMinorVersion=%d\n",
                             osvi.dwMajorVersion, osvi.dwMinorVersion);
                currentOS = OS_UNKNOWN;
            }
        } else {
            if (bVersOk) {
                J2dRlsTrace2(J2D_TRACE_INFO,
                             "OS_UNKNOWN: dwPlatformId=%d dwMajorVersion=%d\n",
                             osvi.dwPlatformId, osvi.dwMajorVersion);
            } else {
                J2dRlsTrace(J2D_TRACE_INFO,"OS_UNKNOWN: GetVersionEx failed\n");
            }
            currentOS = OS_UNKNOWN;
        }
    }
    return (currentOS & osInfo);
}

// static
HRESULT
D3DPipelineManager::CheckForBadHardware(DWORD vId, DWORD dId, LONGLONG version)
{
    DWORD vendorId, deviceId;
    UINT adapterInfo = 0;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::CheckForBadHardware");

    while ((vendorId = badHardware[adapterInfo].VendorId) != 0x0000 &&
           (deviceId = badHardware[adapterInfo].DeviceId) != 0x0000)
    {
        if (vendorId == vId && (deviceId == dId || deviceId == ALL_DEVICEIDS)) {
            LONGLONG goodVersion = badHardware[adapterInfo].DriverVersion;
            USHORT osInfo = badHardware[adapterInfo].OsInfo;
            // the hardware check fails if:
            // - we have an entry for this OS and
            // - hardware is bad for all driver versions (NO_VERSION), or
            //   we have a driver version which is older than the
            //   minimum required for this OS
            if (D3DPPLM_OsVersionMatches(osInfo) &&
                (goodVersion == NO_VERSION || version < goodVersion))
            {
                J2dRlsTraceLn2(J2D_TRACE_ERROR,
                    "D3DPPLM::CheckForBadHardware: found matching "\
                    "hardware: VendorId=0x%04x DeviceId=0x%04x",
                    vendorId, deviceId);
                if (goodVersion != NO_VERSION) {
                    // this was a match by the driver version
                    LARGE_INTEGER li;
                    li.QuadPart = goodVersion;
                    J2dRlsTraceLn(J2D_TRACE_ERROR,
                                  "  bad driver found, device disabled");
                    J2dRlsTraceLn4(J2D_TRACE_ERROR,
                                   "  update your driver to at "\
                                   "least version %d.%d.%d.%d",
                                   HIWORD(li.HighPart), LOWORD(li.HighPart),
                                   HIWORD(li.LowPart),  LOWORD(li.LowPart));
                } else {
                    // this was a match by the device (no good driver for this
                    // device)
                    J2dRlsTraceLn(J2D_TRACE_ERROR,
                                  "D3DPPLM::CheckForBadHardware: bad hardware "\
                                  "found, device disabled");
                }
                if (!bNoHwCheck) {
                    return D3DERR_INVALIDDEVICE;
                }
                J2dRlsTraceLn(J2D_TRACE_WARNING, "  Warning: hw/driver match "\
                              "overridden (via J2D_D3D_NO_HWCHECK)");
            }
        }
        adapterInfo++;
    }

    return S_OK;
}

HRESULT D3DPipelineManager::CheckAdaptersInfo()
{
    D3DADAPTER_IDENTIFIER9 aid;
    UINT failedAdaptersCount = 0;

    J2dRlsTraceLn(J2D_TRACE_INFO, "CheckAdaptersInfo");
    J2dRlsTraceLn(J2D_TRACE_INFO, "------------------");
    for (UINT Adapter = 0; Adapter < adapterCount; Adapter++) {

        if (FAILED(pd3d9->GetAdapterIdentifier(Adapter, 0, &aid))) {
            pAdapters[Adapter].state = CONTEXT_INIT_FAILED;
            failedAdaptersCount++;
            continue;
        }

        J2dRlsTraceLn1(J2D_TRACE_INFO, "Adapter Ordinal  : %d", Adapter);
        J2dRlsTraceLn1(J2D_TRACE_INFO, "Adapter Handle   : 0x%x",
                       pd3d9->GetAdapterMonitor(Adapter));
        J2dRlsTraceLn1(J2D_TRACE_INFO, "Description      : %s",
                       aid.Description);
        J2dRlsTraceLn2(J2D_TRACE_INFO, "GDI Name, Driver : %s, %s",
                       aid.DeviceName, aid.Driver);
        J2dRlsTraceLn1(J2D_TRACE_INFO, "Vendor Id        : 0x%04x",
                       aid.VendorId);
        J2dRlsTraceLn1(J2D_TRACE_INFO, "Device Id        : 0x%04x",
                       aid.DeviceId);
        J2dRlsTraceLn1(J2D_TRACE_INFO, "SubSys Id        : 0x%x",
                       aid.SubSysId);
        J2dRlsTraceLn4(J2D_TRACE_INFO, "Driver Version   : %d.%d.%d.%d",
                       HIWORD(aid.DriverVersion.HighPart),
                       LOWORD(aid.DriverVersion.HighPart),
                       HIWORD(aid.DriverVersion.LowPart),
                       LOWORD(aid.DriverVersion.LowPart));
        J2dRlsTrace3(J2D_TRACE_INFO,
                     "[I] GUID             : {%08X-%04X-%04X-",
                       aid.DeviceIdentifier.Data1,
                       aid.DeviceIdentifier.Data2,
                       aid.DeviceIdentifier.Data3);
        J2dRlsTrace4(J2D_TRACE_INFO, "%02X%02X-%02X%02X",
                       aid.DeviceIdentifier.Data4[0],
                       aid.DeviceIdentifier.Data4[1],
                       aid.DeviceIdentifier.Data4[2],
                       aid.DeviceIdentifier.Data4[3]);
        J2dRlsTrace4(J2D_TRACE_INFO, "%02X%02X%02X%02X}\n",
                       aid.DeviceIdentifier.Data4[4],
                       aid.DeviceIdentifier.Data4[5],
                       aid.DeviceIdentifier.Data4[6],
                       aid.DeviceIdentifier.Data4[7]);

        if (FAILED(CheckForBadHardware(aid.VendorId, aid.DeviceId,
                                       aid.DriverVersion.QuadPart)) ||
            FAILED(CheckDeviceCaps(Adapter))  ||
            FAILED(D3DEnabledOnAdapter(Adapter)))
        {
            pAdapters[Adapter].state = CONTEXT_INIT_FAILED;
            failedAdaptersCount++;
        }
        J2dRlsTraceLn(J2D_TRACE_INFO, "------------------");
    }

    if (failedAdaptersCount == adapterCount) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "D3DPPLM::CheckAdaptersInfo: no suitable adapters found");
        return E_FAIL;
    }

    return S_OK;
}

D3DDEVTYPE D3DPipelineManager::SelectDeviceType()
{
    char *pRas = getenv("J2D_D3D_RASTERIZER");
    D3DDEVTYPE dtype = D3DDEVTYPE_HAL;
    if (pRas != NULL) {
        J2dRlsTrace(J2D_TRACE_WARNING, "[W] D3DPPLM::SelectDeviceType: ");
        if (strncmp(pRas, "ref", 3) == 0 || strncmp(pRas, "rgb", 3) == 0) {
            J2dRlsTrace(J2D_TRACE_WARNING, "ref rasterizer selected");
            dtype = D3DDEVTYPE_REF;
        } else if (strncmp(pRas, "hal",3) == 0 || strncmp(pRas, "tnl",3) == 0) {
            J2dRlsTrace(J2D_TRACE_WARNING, "hal rasterizer selected");
            dtype = D3DDEVTYPE_HAL;
        } else if (strncmp(pRas, "nul", 3) == 0) {
            J2dRlsTrace(J2D_TRACE_WARNING, "nullref rasterizer selected");
            dtype = D3DDEVTYPE_NULLREF;
        } else {
            J2dRlsTrace1(J2D_TRACE_WARNING,
                "unknown rasterizer: %s, only (ref|hal|nul) "\
                "supported, hal selected instead", pRas);
        }
        J2dRlsTrace(J2D_TRACE_WARNING, "\n");
    }
    return dtype;
}

#define CHECK_CAP(FLAG, CAP) \
    do {    \
        if (!((FLAG)&CAP)) { \
            J2dRlsTraceLn2(J2D_TRACE_ERROR, \
                           "D3DPPLM::CheckDeviceCaps: adapter %d: Failed "\
                           "(cap %s not supported)", \
                           adapter, #CAP); \
            return E_FAIL; \
        } \
    } while (0)

HRESULT D3DPipelineManager::CheckDeviceCaps(UINT adapter)
{
    HRESULT res;
    D3DCAPS9 d3dCaps;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::CheckDeviceCaps");

    res = pd3d9->GetDeviceCaps(adapter, devType, &d3dCaps);
    RETURN_STATUS_IF_FAILED(res);

    CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_DRAWPRIMTLVERTEX);

    // by requiring hardware tnl we are hoping for better drivers quality
    if (!IsD3DForced()) {
        // fail if not hw tnl unless d3d was forced
        CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_HWTRANSFORMANDLIGHT);
    }
    if (d3dCaps.DeviceType == D3DDEVTYPE_HAL) {
        CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_HWRASTERIZATION);
    }

    CHECK_CAP(d3dCaps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST);

    CHECK_CAP(d3dCaps.Caps3, D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD);

    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE);
    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_BLENDOP);
    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_MASKZ);

    CHECK_CAP(d3dCaps.ZCmpCaps, D3DPCMPCAPS_ALWAYS);
    CHECK_CAP(d3dCaps.ZCmpCaps, D3DPCMPCAPS_LESS);

    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_ZERO);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_ONE);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_SRCALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_DESTALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_INVSRCALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_INVDESTALPHA);

    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_ZERO);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_ONE);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_SRCALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_DESTALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_INVDESTALPHA);

    CHECK_CAP(d3dCaps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP);
    CHECK_CAP(d3dCaps.TextureAddressCaps, D3DPTADDRESSCAPS_WRAP);

    CHECK_CAP(d3dCaps.TextureOpCaps, D3DTEXOPCAPS_MODULATE);

    if (d3dCaps.PixelShaderVersion < D3DPS_VERSION(2,0) && !IsD3DForced()) {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
                       "D3DPPLM::CheckDeviceCaps: adapter %d: Failed "\
                       "(pixel shaders 2.0 required)", adapter);
        return E_FAIL;
    }

    J2dRlsTraceLn1(J2D_TRACE_INFO,
                   "D3DPPLM::CheckDeviceCaps: adapter %d: Passed", adapter);
    return S_OK;
}


HRESULT D3DPipelineManager::D3DEnabledOnAdapter(UINT adapter)
{
    HRESULT res;
    D3DDISPLAYMODE dm;

    res = pd3d9->GetAdapterDisplayMode(adapter, &dm);
    RETURN_STATUS_IF_FAILED(res);

    res = pd3d9->CheckDeviceType(adapter, devType, dm.Format, dm.Format, TRUE);
    if (FAILED(res)) {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
                "D3DPPLM::D3DEnabledOnAdapter: no " \
                "suitable d3d device on adapter %d", adapter);
    }

    return res;
}

UINT D3DPipelineManager::GetAdapterOrdinalByHmon(HMONITOR hMon)
{
    UINT ret = D3DADAPTER_DEFAULT;

    if (pd3d9 != NULL) {
        UINT adapterCount = pd3d9->GetAdapterCount();
        for (UINT adapter = 0; adapter < adapterCount; adapter++) {
            HMONITOR hm = pd3d9->GetAdapterMonitor(adapter);
            if (hm == hMon) {
                ret = adapter;
                break;
            }
        }
    }
    return ret;
}

D3DFORMAT
D3DPipelineManager::GetMatchingDepthStencilFormat(UINT adapterOrdinal,
                                                  D3DFORMAT adapterFormat,
                                                  D3DFORMAT renderTargetFormat)
{
    static D3DFORMAT formats[] =
        { D3DFMT_D16, D3DFMT_D32, D3DFMT_D24S8, D3DFMT_D24X8 };
    D3DFORMAT newFormat = D3DFMT_UNKNOWN;
    HRESULT res;
    for (int i = 0; i < 4; i++) {
        res = pd3d9->CheckDeviceFormat(adapterOrdinal,
                devType, adapterFormat, D3DUSAGE_DEPTHSTENCIL,
                D3DRTYPE_SURFACE, formats[i]);
        if (FAILED(res)) continue;

        res = pd3d9->CheckDepthStencilMatch(adapterOrdinal,
                devType, adapterFormat, renderTargetFormat, formats[i]);
        if (FAILED(res)) continue;
        newFormat = formats[i];
        break;
    }
    return newFormat;
}

HWND D3DPipelineManager::CreateDefaultFocusWindow()
{
    UINT adapterOrdinal = D3DADAPTER_DEFAULT;

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DPPLM::CreateDefaultFocusWindow: adapter=%d",
                adapterOrdinal);

    if (defaultFocusWindow != 0) {
        J2dRlsTraceLn(J2D_TRACE_WARNING,
                      "D3DPPLM::CreateDefaultFocusWindow: "\
                      "existing default focus window!");
        return defaultFocusWindow;
    }

    WNDCLASS wc;
    ZeroMemory(&wc, sizeof(WNDCLASS));
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = DefWindowProc;
    wc.lpszClassName = L"D3DFocusWindow";
    if (RegisterClass(&wc) == 0) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "D3DPPLM::CreateDefaultFocusWindow: "\
                      "error registering window class");
        return 0;
    }

    MONITORINFO mi;
    ZeroMemory(&mi, sizeof(MONITORINFO));
    mi.cbSize = sizeof(MONITORINFO);
    HMONITOR hMon = pd3d9->GetAdapterMonitor(adapterOrdinal);
    if (hMon == 0 || !GetMonitorInfo(hMon, (LPMONITORINFO)&mi)) {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
            "D3DPPLM::CreateDefaultFocusWindow: "\
            "error getting monitor info for adapter=%d", adapterOrdinal);
        return 0;
    }

    HWND hWnd = CreateWindow(L"D3DFocusWindow", L"D3DFocusWindow", 0,
        mi.rcMonitor.left, mi.rcMonitor.top, 1, 1,
        NULL, NULL, GetModuleHandle(NULL), NULL);
    if (hWnd == 0) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
            "D3DPPLM::CreateDefaultFocusWindow: CreateWindow failed");
    } else {
        J2dTraceLn2(J2D_TRACE_INFO,
            "  Created default focus window %x for adapter %d",
            hWnd, adapterOrdinal);
        defaultFocusWindow = hWnd;
    }
    return hWnd;
}

HWND D3DPipelineManager::GetCurrentFocusWindow()
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::GetCurrentFocusWindow");
    if (currentFSFocusAdapter < 0) {
        J2dTraceLn1(J2D_TRACE_VERBOSE,
                    "  no fs windows, using default focus window=0x%x",
                    defaultFocusWindow);
        return defaultFocusWindow;
    }
    J2dTraceLn1(J2D_TRACE_VERBOSE, "  using fs window=0x%x",
                pAdapters[currentFSFocusAdapter].fsFocusWindow);
    return pAdapters[currentFSFocusAdapter].fsFocusWindow;
}

HWND D3DPipelineManager::SetFSFocusWindow(UINT adapterOrdinal, HWND hWnd)
{
    J2dTraceLn2(J2D_TRACE_INFO,"D3DPPLM::SetFSFocusWindow hwnd=0x%x adapter=%d",
                hWnd, adapterOrdinal);

    HWND prev = pAdapters[adapterOrdinal].fsFocusWindow;
    pAdapters[adapterOrdinal].fsFocusWindow = hWnd;
    if (currentFSFocusAdapter < 0) {
        J2dTraceLn(J2D_TRACE_VERBOSE, "  first full-screen window");
        // first fs window
        currentFSFocusAdapter = adapterOrdinal;
        // REMIND: we might want to reset the rest of the context here as well
        // like we do when the an adapter exits fs mode; currently they will
        // be reset sometime later
    } else {
        // there's already a fs window
        if (currentFSFocusAdapter == adapterOrdinal) {
            // it's current fs window => we're exiting fs mode on this adapter;
            // look for a new fs focus window
            if (hWnd == 0) {
                UINT i;
                currentFSFocusAdapter = -1;
                for (i = 0; i < adapterCount; i++) {
                    if (pAdapters[i].fsFocusWindow != 0) {
                        J2dTraceLn1(J2D_TRACE_VERBOSE,
                                    "  adapter %d is still in fs mode", i);
                        currentFSFocusAdapter = i;
                        break;
                    }
                }
                // we have to reset all devices any time current focus device
                // exits fs mode, and also to prevent some of them being left in
                // a lost state when the last device exits fs - when non-last
                // adapters exit fs mode they would not be able to create the
                // device and will be put in a lost state forever
                HRESULT res;
                J2dTraceLn(J2D_TRACE_VERBOSE,
                           "  adapter exited full-screen, reset all adapters");
                for (i = 0; i < adapterCount; i++) {
                    if (pAdapters[i].pd3dContext != NULL) {
                        res = pAdapters[i].pd3dContext->ResetContext();
                        D3DRQ_MarkLostIfNeeded(res,
                            D3DRQ_GetCurrentDestination());
                    }
                }
            } else {
                J2dTraceLn1(J2D_TRACE_WARNING,
                            "D3DPM::SetFSFocusWindow: setting the fs "\
                            "window again for adapter %d", adapterOrdinal);
            }
        }
    }
    return prev;
}

HRESULT D3DPipelineManager::GetD3DContext(UINT adapterOrdinal,
                                          D3DContext **ppd3dContext)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::GetD3DContext");

    HRESULT res = S_OK;
    if (adapterOrdinal < 0 || adapterOrdinal >= adapterCount ||
        pAdapters == NULL ||
        pAdapters[adapterOrdinal].state == CONTEXT_INIT_FAILED)
    {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
            "D3DPPLM::GetD3DContext: invalid parameters or "\
            "failed init for adapter %d", adapterOrdinal);
        *ppd3dContext = NULL;
        return E_FAIL;
    }

    if (pAdapters[adapterOrdinal].state == CONTEXT_NOT_INITED) {
        D3DContext *pCtx = NULL;

        if (pAdapters[adapterOrdinal].pd3dContext != NULL) {
            J2dTraceLn1(J2D_TRACE_ERROR, "  non-null context in "\
                        "uninitialized adapter %d", adapterOrdinal);
            res = E_FAIL;
        } else {
            J2dTraceLn1(J2D_TRACE_VERBOSE,
                        "  initializing context for adapter %d",adapterOrdinal);

            if (SUCCEEDED(res = D3DEnabledOnAdapter(adapterOrdinal))) {
                res = D3DContext::CreateInstance(pd3d9, adapterOrdinal, &pCtx);
                if (FAILED(res)) {
                    J2dRlsTraceLn1(J2D_TRACE_ERROR,
                        "D3DPPLM::GetD3DContext: failed to create context "\
                        "for adapter=%d", adapterOrdinal);
                }
            } else {
                J2dRlsTraceLn1(J2D_TRACE_ERROR,
                    "D3DPPLM::GetContext: no d3d on adapter %d",adapterOrdinal);
            }
        }
        pAdapters[adapterOrdinal].state =
            SUCCEEDED(res) ? CONTEXT_CREATED : CONTEXT_INIT_FAILED;
        pAdapters[adapterOrdinal].pd3dContext = pCtx;
    }
    *ppd3dContext = pAdapters[adapterOrdinal].pd3dContext;
    return res;
}


//==============================================================
// D3DInitializer
//==============================================================

D3DInitializer D3DInitializer::theInstance;

D3DInitializer::D3DInitializer()
    : bComInitialized(false), pAdapterIniters(NULL)
{
}

D3DInitializer::~D3DInitializer()
{
    if (pAdapterIniters) {
        delete[] pAdapterIniters;
    }
}

void D3DInitializer::InitImpl()
{
    J2dRlsTraceLn(J2D_TRACE_INFO, "D3DInitializer::InitImpl");
    if (SUCCEEDED(::CoInitialize(NULL))) {
        bComInitialized = true;
    }
    D3DPipelineManager *pMgr = D3DPipelineManager::CreateInstance();
    if (pMgr != NULL) {
        // init adapters if we are preloading
        if (AwtToolkit::GetInstance().GetPreloadThread().OnPreloadThread()) {
            UINT adapterCount = pMgr->adapterCount;

            pAdapterIniters = new D3DAdapterInitializer[adapterCount];
            for (UINT i=0; i<adapterCount; i++) {
                pAdapterIniters[i].setAdapter(i);
                AwtToolkit::GetInstance().GetPreloadThread().AddAction(&pAdapterIniters[i]);
            }
        }
    }
}

void D3DInitializer::CleanImpl(bool reInit)
{
    J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DInitializer::CleanImpl (%s)",
                                    reInit ? "RELAUNCH" : "normal");
    D3DPipelineManager::DeleteInstance();
    if (bComInitialized) {
        CoUninitialize();
    }
}


void D3DInitializer::D3DAdapterInitializer::InitImpl()
{
    J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DAdapterInitializer::InitImpl(%d) started", adapter);

    D3DPipelineManager *pMgr = D3DPipelineManager::GetInstance();
    if (pMgr == NULL) {
        return;
    }

    D3DContext *pd3dContext;
    pMgr->GetD3DContext(adapter, &pd3dContext);

    J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DAdapterInitializer::InitImpl(%d) finished", adapter);
}

void D3DInitializer::D3DAdapterInitializer::CleanImpl(bool reInit)
{
    // nothing to do - D3DPipelineManager cleans adapters
}


extern "C" {
/*
 * Export function to start D3D preloading
 * (called from java/javaw - see src/windows/bin/java-md.c)
 */
__declspec(dllexport) int preloadD3D()
{
    J2dRlsTraceLn(J2D_TRACE_INFO, "AWT warmup: preloadD3D");
    AwtToolkit::GetInstance().GetPreloadThread().AddAction(&D3DInitializer::GetInstance());
    return 1;
}

}