From 26412d4c05046bb8f23c13c1c29196a93694e8c5 Mon Sep 17 00:00:00 2001 From: Tiago Koji Castro Shibata Date: Thu, 16 Jan 2020 14:37:59 -0800 Subject: [PATCH] Remove internal libs from tests --- cmake/winml.cmake | 30 ++- cmake/winml_unittests.cmake | 3 +- winml/lib/Api.Image/D3DDeviceCache.cpp | 9 +- winml/lib/Api.Image/DeviceHelpers.cpp | 209 +---------------- winml/lib/Api.Image/inc/DeviceHelpers.h | 19 +- winml/lib/Common/CommonDeviceHelpers.cpp | 215 ++++++++++++++++++ winml/lib/Common/inc/CommonDeviceHelpers.h | 50 ++++ .../test/api/LearningModelBindingAPITest.cpp | 3 +- .../test/api/LearningModelSessionAPITest.cpp | 8 +- winml/test/common/std.h | 3 +- .../cppwinrt/scenariotestscppwinrt.cpp | 17 +- 11 files changed, 318 insertions(+), 248 deletions(-) create mode 100644 winml/lib/Common/CommonDeviceHelpers.cpp create mode 100644 winml/lib/Common/inc/CommonDeviceHelpers.h diff --git a/cmake/winml.cmake b/cmake/winml.cmake index f9c02ddf0e9e5..2958a16681793 100644 --- a/cmake/winml.cmake +++ b/cmake/winml.cmake @@ -258,7 +258,7 @@ add_dependencies(winml_lib_image winml_api_native) add_dependencies(winml_lib_image winml_api_native_internal) # Link libraries -target_link_libraries(winml_lib_image PRIVATE wil) +target_link_libraries(winml_lib_image PRIVATE wil winml_lib_common) if (onnxruntime_USE_DML) target_add_dml(winml_lib_image) endif(onnxruntime_USE_DML) @@ -360,11 +360,37 @@ add_dependencies(winml_lib_api winml_api_native) add_dependencies(winml_lib_api winml_api_native_internal) # Link libraries -target_link_libraries(winml_lib_api PRIVATE wil) +target_link_libraries(winml_lib_api PRIVATE wil winml_lib_telemetry) if (onnxruntime_USE_DML) target_add_dml(winml_lib_api) endif(onnxruntime_USE_DML) +########################### +# Add winml_lib_common +########################### + +add_library(winml_lib_common STATIC + ${winml_lib_common_dir}/CommonDeviceHelpers.cpp +) + +set_target_properties(winml_lib_common PROPERTIES CXX_STANDARD 17) +set_target_properties(winml_lib_common PROPERTIES CXX_STANDARD_REQUIRED ON) +target_compile_options(winml_lib_common PRIVATE /GR- /await /bigobj /wd4238) +target_link_libraries(winml_lib_common PRIVATE wil) +target_include_directories(winml_lib_common PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/winml_api) +target_compile_definitions(winml_lib_common PRIVATE + ONNX_NAMESPACE=onnx + ONNX_ML + LOTUS_LOG_THRESHOLD=2 + LOTUS_ENABLE_STDERR_LOGGING + PLATFORM_WINDOWS + _SCL_SECURE_NO_WARNINGS) +add_dependencies(winml_lib_common winml_sdk_cppwinrt) + +target_include_directories(winml_lib_common PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/winml_api) # windows machine learning generated component headers +target_include_directories(winml_lib_common PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/winml_api/comp_generated) # windows machine learning generated component headers +target_include_directories(winml_lib_common PRIVATE ${winml_lib_api_dir}) +target_include_directories(winml_lib_common PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) ########################### # Add winml_dll diff --git a/cmake/winml_unittests.cmake b/cmake/winml_unittests.cmake index 0596c711dca1a..29ee21ce742c4 100644 --- a/cmake/winml_unittests.cmake +++ b/cmake/winml_unittests.cmake @@ -4,7 +4,6 @@ set(WINML_TEST_SRC_DIR ${REPO_ROOT}/winml/test) set(WINML_TEST_INC_DIR ${REPO_ROOT}/winml/test/common - ${REPO_ROOT}/winml/lib/Api.Image/inc ${REPO_ROOT}/winml/lib/Common/inc ${REPO_ROOT}/onnxruntime ${REPO_ROOT}/onnxruntime/core/providers/dml/DmlExecutionProvider/src/External/D3DX12 @@ -44,7 +43,7 @@ function(add_winml_test) if (_UT_DEPENDS) add_dependencies(${_UT_TARGET} ${_UT_DEPENDS}) endif() - target_link_libraries(${_UT_TARGET} PRIVATE ${_UT_LIBS} gtest winml_lib_image ${onnxruntime_EXTERNAL_LIBRARIES} winml_lib_telemetry winml_lib_api onnxruntime) + target_link_libraries(${_UT_TARGET} PRIVATE ${_UT_LIBS} gtest ${onnxruntime_EXTERNAL_LIBRARIES} winml_lib_common onnxruntime) add_test(NAME ${_UT_TARGET} COMMAND ${_UT_TARGET} diff --git a/winml/lib/Api.Image/D3DDeviceCache.cpp b/winml/lib/Api.Image/D3DDeviceCache.cpp index a551bb96562c5..1b6314cd8a0bb 100644 --- a/winml/lib/Api.Image/D3DDeviceCache.cpp +++ b/winml/lib/Api.Image/D3DDeviceCache.cpp @@ -6,6 +6,7 @@ #include #include #include "inc/DeviceHelpers.h" +#include "CommonDeviceHelpers.h" namespace float32 { #include "shaders\SurfaceToTensor-SurfaceToTensorBGR8.h" @@ -50,8 +51,8 @@ D3DDeviceCache::D3DDeviceCache(Windows::AI::MachineLearning::LearningModelDevice DXGI_GPU_PREFERENCE preference; WINML_THROW_IF_FAILED(DeviceHelpers::GetGPUPreference(deviceKind, &preference)); - DeviceHelpers::AdapterEnumerationSupport support; - WINML_THROW_IF_FAILED(DeviceHelpers::GetAdapterEnumerationSupport(&support)); + CommonDeviceHelpers::AdapterEnumerationSupport support; + WINML_THROW_IF_FAILED(CommonDeviceHelpers::GetAdapterEnumerationSupport(&support)); const char errStr[] = "No hardware adapters available"; if (support.has_dxgi) { @@ -130,7 +131,7 @@ D3DDeviceCache::~D3DDeviceCache() { bool D3DDeviceCache::IsFloat16Supported() { if (device_ != nullptr) { - return DeviceHelpers::IsFloat16Supported(device_.get()); + return CommonDeviceHelpers::IsFloat16Supported(device_.get()); } return true; @@ -671,4 +672,4 @@ void D3DDeviceCache::SyncD3D11DeviceToConverter(_In_ ID3D11Fence* pD3D11Fence) { bool D3DDeviceCache::SharedHandleInitialized() { return d3d11_fence_ != nullptr; } -} // namespace winrt::Windows::AI::MachineLearning::implementation \ No newline at end of file +} // namespace winrt::Windows::AI::MachineLearning::implementation diff --git a/winml/lib/Api.Image/DeviceHelpers.cpp b/winml/lib/Api.Image/DeviceHelpers.cpp index 3174f61fad619..6e0b5a7ffd946 100644 --- a/winml/lib/Api.Image/DeviceHelpers.cpp +++ b/winml/lib/Api.Image/DeviceHelpers.cpp @@ -9,215 +9,10 @@ #include #include #include "inc/DeviceHelpers.h" +#include "CommonDeviceHelpers.h" #include "LearningModelDevice.h" namespace DeviceHelpers { -constexpr uint32_t c_intelVendorId = 0x8086; -constexpr uint32_t c_nvidiaVendorId = 0x10DE; -constexpr uint32_t c_amdVendorId = 0x1002; -static std::optional s_adapterEnumerationSupport; - -static bool CheckAdapterFP16Blocked(bool isMcdmAdapter, uint32_t vendorId, uint32_t majorVersion, uint32_t minorVersion) { - switch (vendorId) { - case c_intelVendorId: { - if (isMcdmAdapter) { - return false; - } - - // Check Intel GPU driver version - return (majorVersion < 25) || (majorVersion == 25 && minorVersion < 6574) || (majorVersion == 26 && minorVersion < 6572); - } - } - return false; -} - -static void ParseDriverVersion(LARGE_INTEGER& version, uint32_t& majorVersion, uint32_t& minorVersion) { - majorVersion = HIWORD(version.HighPart); - minorVersion = LOWORD(version.LowPart); -} - -static HRESULT GetDXGIAdapterMetadata(ID3D12Device& device, uint32_t& vendorId, uint32_t& majorVersion, uint32_t& minorVersion) { - winrt::com_ptr spFactory; - RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(spFactory.put()))); - - winrt::com_ptr spAdapter; - RETURN_IF_FAILED(spFactory->EnumAdapterByLuid(device.GetAdapterLuid(), IID_PPV_ARGS(spAdapter.put()))); - - DXGI_ADAPTER_DESC adapterDesc = {}; - RETURN_IF_FAILED(spAdapter->GetDesc(&adapterDesc)); - - LARGE_INTEGER driverVersion; - RETURN_IF_FAILED(spAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &driverVersion)); - - vendorId = adapterDesc.VendorId; - ParseDriverVersion(driverVersion, majorVersion, minorVersion); - return S_OK; -} - -#ifdef ENABLE_DXCORE -static HRESULT GetDXCoreAdapterMetadata(ID3D12Device& device, bool& isMcdmAdapter, uint32_t& vendorId, uint32_t& majorVersion, uint32_t& minorVersion) { - winrt::com_ptr spFactory; - RETURN_IF_FAILED(DXCoreCreateAdapterFactory(IID_PPV_ARGS(spFactory.put()))); - - winrt::com_ptr spAdapter; - RETURN_IF_FAILED(spFactory->GetAdapterByLuid(device.GetAdapterLuid(), IID_PPV_ARGS(spAdapter.put()))); - - if (spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE) && - (!(spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS) || - spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS)))) { - isMcdmAdapter = true; - } else { - isMcdmAdapter = false; - } - - DXCoreHardwareID hardwareId; - RETURN_IF_FAILED(spAdapter->GetProperty(DXCoreAdapterProperty::HardwareID, &hardwareId)); - vendorId = hardwareId.vendorID; - - uint64_t rawDriverVersion; - RETURN_IF_FAILED(spAdapter->GetProperty(DXCoreAdapterProperty::DriverVersion, &rawDriverVersion)); - - LARGE_INTEGER driverVersion; - driverVersion.QuadPart = static_cast(rawDriverVersion); - ParseDriverVersion(driverVersion, majorVersion, minorVersion); - return S_OK; -} -#endif - -static HRESULT GetD3D12Device(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device, ID3D12Device** outDevice) { - _LUID id; - id.LowPart = device.AdapterId().LowPart; - id.HighPart = device.AdapterId().HighPart; - AdapterEnumerationSupport support; - RETURN_IF_FAILED(GetAdapterEnumerationSupport(&support)); - - if (support.has_dxgi) { - winrt::com_ptr spFactory; - RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(spFactory.put()))); - - winrt::com_ptr spAdapter; - RETURN_IF_FAILED(spFactory->EnumAdapterByLuid(id, IID_PPV_ARGS(spAdapter.put()))); - RETURN_IF_FAILED(D3D12CreateDevice(spAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(outDevice))); - } -#ifdef ENABLE_DXCORE - if (support.has_dxgi == false) { - winrt::com_ptr spFactory; - RETURN_IF_FAILED(DXCoreCreateAdapterFactory(IID_PPV_ARGS(spFactory.put()))); - - winrt::com_ptr spAdapter; - RETURN_IF_FAILED(spFactory->GetAdapterByLuid(id, IID_PPV_ARGS(spAdapter.put()))); - RETURN_IF_FAILED(D3D12CreateDevice(spAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(outDevice))); - } -#endif - return S_OK; -} - -static HRESULT IsFloat16Blocked(ID3D12Device& device, bool* isBlocked) { - uint32_t vendorId; - uint32_t majorVersion; - uint32_t minorVersion; - bool isMcdmAdapter; - *isBlocked = true; - AdapterEnumerationSupport support; - RETURN_IF_FAILED(GetAdapterEnumerationSupport(&support)); -#ifdef ENABLE_DXCORE - if (support.has_dxcore) { - RETURN_IF_FAILED(GetDXCoreAdapterMetadata(device, isMcdmAdapter, vendorId, majorVersion, minorVersion)); - *isBlocked = CheckAdapterFP16Blocked(isMcdmAdapter, vendorId, majorVersion, minorVersion); - return S_OK; - } -#endif - RETURN_IF_FAILED(GetDXGIAdapterMetadata(device, vendorId, majorVersion, minorVersion)); - isMcdmAdapter = false; - *isBlocked = CheckAdapterFP16Blocked(isMcdmAdapter, vendorId, majorVersion, minorVersion); - return S_OK; -} - -bool IsFloat16Supported(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device) { - auto modelImpl = device.as(); - if (modelImpl->IsCpuDevice()) { - return true; - } - winrt::com_ptr d3d12Device; - if (FAILED(GetD3D12Device(device, d3d12Device.put()))) { - return false; - } - return IsFloat16Supported(d3d12Device.get()); -} - -bool IsFloat16Supported(ID3D12Device* device) { -#ifndef USE_DML - WINML_THROW_HR_IF_TRUE_MSG(ERROR_NOT_SUPPORTED, true, "IsFloat16Supported is not implemented for WinML only build."); - return false; -#else - bool isBlocked; - if (FAILED(IsFloat16Blocked(*device, &isBlocked)) || isBlocked) { - return false; - } - winrt::com_ptr dmlDevice; - winrt::check_hresult(DMLCreateDevice( - device, - DML_CREATE_DEVICE_FLAG_NONE, - IID_PPV_ARGS(dmlDevice.put()))); - - DML_FEATURE_QUERY_TENSOR_DATA_TYPE_SUPPORT float16Query = {DML_TENSOR_DATA_TYPE_FLOAT16}; - DML_FEATURE_DATA_TENSOR_DATA_TYPE_SUPPORT float16Data = {}; - - winrt::check_hresult(dmlDevice->CheckFeatureSupport( - DML_FEATURE_TENSOR_DATA_TYPE_SUPPORT, - sizeof(float16Query), - &float16Query, - sizeof(float16Data), - &float16Data)); - return float16Data.IsSupported; -#endif USE_DML -} - -// uses Structured Exception Handling (SEH) to detect for delay load failures of target API. -// You cannot mix and match SEH with C++ exception and object unwinding -// In this case we will catch it, and report up to the caller via HRESULT so our callers can use -// C++ exceptions -template -static HRESULT RunDelayLoadedApi(TFunc& tfunc, TArgs&&... args) { - __try { - return tfunc(std::forward(args)...); - } __except (GetExceptionCode() == VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { - // this could be ok, just let people know that it failed to load - return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND); - } -} - -HRESULT GetAdapterEnumerationSupport(AdapterEnumerationSupport* support) { - if (!s_adapterEnumerationSupport.has_value()) { - // check for support, starting with DXGI - winrt::com_ptr dxgiFactory; -#ifdef ENABLE_DXCORE - winrt::com_ptr dxcoreFactory; - // necessary because DXCoreCreateAdapterFactory is overloaded - HRESULT(WINAPI * pDxCoreTestFunc) - (REFIID, void**) = DXCoreCreateAdapterFactory; -#endif - AdapterEnumerationSupport adapterEnumerationSupport = {}; - - if (SUCCEEDED(RunDelayLoadedApi(CreateDXGIFactory1, IID_PPV_ARGS(dxgiFactory.put())))) { - adapterEnumerationSupport.has_dxgi = true; - } -#ifdef ENABLE_DXCORE - if (SUCCEEDED(RunDelayLoadedApi(pDxCoreTestFunc, IID_PPV_ARGS(dxcoreFactory.put())))) { - adapterEnumerationSupport.has_dxcore = true; - } -#endif - - s_adapterEnumerationSupport = adapterEnumerationSupport; - - if (!(adapterEnumerationSupport.has_dxgi || adapterEnumerationSupport.has_dxcore)) { - return TYPE_E_CANTLOADLIBRARY; - } - } - *support = s_adapterEnumerationSupport.value(); - return S_OK; -} - HRESULT GetDXGIHardwareAdapterWithPreference(DXGI_GPU_PREFERENCE preference, IDXGIAdapter1** ppAdapter) { winrt::com_ptr spFactory; RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(spFactory.put()))); @@ -304,7 +99,7 @@ HRESULT GetDXCoreHardwareAdapterWithPreference(DXGI_GPU_PREFERENCE preference, I #endif HRESULT CreateD3D11On12Device(ID3D12Device* device12, ID3D11Device** device11) { - return DeviceHelpers::RunDelayLoadedApi( + return CommonDeviceHelpers::RunDelayLoadedApi( D3D11On12CreateDevice, device12, // pointer to d3d12 device D3D11_CREATE_DEVICE_BGRA_SUPPORT, // required in order to interop with Direct2D diff --git a/winml/lib/Api.Image/inc/DeviceHelpers.h b/winml/lib/Api.Image/inc/DeviceHelpers.h index 2a86c7d40cd96..d5c409c02b680 100644 --- a/winml/lib/Api.Image/inc/DeviceHelpers.h +++ b/winml/lib/Api.Image/inc/DeviceHelpers.h @@ -14,28 +14,11 @@ #include #endif -// -// Exception information -// -#ifndef FACILITY_VISUALCPP -#define FACILITY_VISUALCPP ((LONG)0x6d) -#endif - -#define VcppException(sev, err) ((sev) | (FACILITY_VISUALCPP << 16) | err) - namespace DeviceHelpers { -struct AdapterEnumerationSupport { - bool has_dxgi; - bool has_dxcore; -}; - -HRESULT GetAdapterEnumerationSupport(AdapterEnumerationSupport* support); -bool IsFloat16Supported(ID3D12Device* device); -bool IsFloat16Supported(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device); HRESULT CreateD3D11On12Device(ID3D12Device* device12, ID3D11Device** device11); #ifdef ENABLE_DXCORE HRESULT GetDXCoreHardwareAdapterWithPreference(DXGI_GPU_PREFERENCE preference, _COM_Outptr_ IDXCoreAdapter** ppAdapter); #endif HRESULT GetDXGIHardwareAdapterWithPreference(DXGI_GPU_PREFERENCE preference, _COM_Outptr_ IDXGIAdapter1** adapter); HRESULT GetGPUPreference(winrt::Windows::AI::MachineLearning::LearningModelDeviceKind deviceKind, DXGI_GPU_PREFERENCE* preference) noexcept; -} // namespace DeviceHelpers \ No newline at end of file +} // namespace DeviceHelpers diff --git a/winml/lib/Common/CommonDeviceHelpers.cpp b/winml/lib/Common/CommonDeviceHelpers.cpp new file mode 100644 index 0000000000000..292d665ffdf1b --- /dev/null +++ b/winml/lib/Common/CommonDeviceHelpers.cpp @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// #include "dx.h" +// #include + +#if USE_DML +#include +#endif USE_DML +#include "inc/CommonDeviceHelpers.h" +#include +#include +#include "LearningModelDevice.h" + +namespace { +constexpr uint32_t c_intelVendorId = 0x8086; +constexpr uint32_t c_nvidiaVendorId = 0x10DE; +constexpr uint32_t c_amdVendorId = 0x1002; + +bool CheckAdapterFP16Blocked(bool isMcdmAdapter, uint32_t vendorId, uint32_t majorVersion, uint32_t minorVersion) { + switch (vendorId) { + case c_intelVendorId: { + if (isMcdmAdapter) { + return false; + } + + // Check Intel GPU driver version + return (majorVersion < 25) || (majorVersion == 25 && minorVersion < 6574) || (majorVersion == 26 && minorVersion < 6572); + } + } + return false; +} + +void ParseDriverVersion(LARGE_INTEGER& version, uint32_t& majorVersion, uint32_t& minorVersion) { + majorVersion = HIWORD(version.HighPart); + minorVersion = LOWORD(version.LowPart); +} + +HRESULT GetDXGIAdapterMetadata(ID3D12Device& device, uint32_t& vendorId, uint32_t& majorVersion, uint32_t& minorVersion) { + winrt::com_ptr spFactory; + RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(spFactory.put()))); + + winrt::com_ptr spAdapter; + RETURN_IF_FAILED(spFactory->EnumAdapterByLuid(device.GetAdapterLuid(), IID_PPV_ARGS(spAdapter.put()))); + + DXGI_ADAPTER_DESC adapterDesc = {}; + RETURN_IF_FAILED(spAdapter->GetDesc(&adapterDesc)); + + LARGE_INTEGER driverVersion; + RETURN_IF_FAILED(spAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &driverVersion)); + + vendorId = adapterDesc.VendorId; + ParseDriverVersion(driverVersion, majorVersion, minorVersion); + return S_OK; +} + +#ifdef ENABLE_DXCORE +HRESULT GetDXCoreAdapterMetadata(ID3D12Device& device, bool& isMcdmAdapter, uint32_t& vendorId, uint32_t& majorVersion, uint32_t& minorVersion) { + winrt::com_ptr spFactory; + RETURN_IF_FAILED(DXCoreCreateAdapterFactory(IID_PPV_ARGS(spFactory.put()))); + + winrt::com_ptr spAdapter; + RETURN_IF_FAILED(spFactory->GetAdapterByLuid(device.GetAdapterLuid(), IID_PPV_ARGS(spAdapter.put()))); + + if (spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE) && + (!(spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS) || + spAdapter->IsAttributeSupported(DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS)))) { + isMcdmAdapter = true; + } else { + isMcdmAdapter = false; + } + + DXCoreHardwareID hardwareId; + RETURN_IF_FAILED(spAdapter->GetProperty(DXCoreAdapterProperty::HardwareID, &hardwareId)); + vendorId = hardwareId.vendorID; + + uint64_t rawDriverVersion; + RETURN_IF_FAILED(spAdapter->GetProperty(DXCoreAdapterProperty::DriverVersion, &rawDriverVersion)); + + LARGE_INTEGER driverVersion; + driverVersion.QuadPart = static_cast(rawDriverVersion); + ParseDriverVersion(driverVersion, majorVersion, minorVersion); + return S_OK; +} +#endif + +HRESULT GetD3D12Device(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device, ID3D12Device** outDevice) { + _LUID id; + id.LowPart = device.AdapterId().LowPart; + id.HighPart = device.AdapterId().HighPart; + CommonDeviceHelpers::AdapterEnumerationSupport support; + RETURN_IF_FAILED(GetAdapterEnumerationSupport(&support)); + + if (support.has_dxgi) { + winrt::com_ptr spFactory; + RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(spFactory.put()))); + + winrt::com_ptr spAdapter; + RETURN_IF_FAILED(spFactory->EnumAdapterByLuid(id, IID_PPV_ARGS(spAdapter.put()))); + RETURN_IF_FAILED(D3D12CreateDevice(spAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(outDevice))); + } +#ifdef ENABLE_DXCORE + if (support.has_dxgi == false) { + winrt::com_ptr spFactory; + RETURN_IF_FAILED(DXCoreCreateAdapterFactory(IID_PPV_ARGS(spFactory.put()))); + + winrt::com_ptr spAdapter; + RETURN_IF_FAILED(spFactory->GetAdapterByLuid(id, IID_PPV_ARGS(spAdapter.put()))); + RETURN_IF_FAILED(D3D12CreateDevice(spAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(outDevice))); + } +#endif + return S_OK; +} + +HRESULT IsFloat16Blocked(ID3D12Device& device, bool* isBlocked) { + uint32_t vendorId; + uint32_t majorVersion; + uint32_t minorVersion; + bool isMcdmAdapter; + *isBlocked = true; + CommonDeviceHelpers::AdapterEnumerationSupport support; + RETURN_IF_FAILED(CommonDeviceHelpers::GetAdapterEnumerationSupport(&support)); +#ifdef ENABLE_DXCORE + if (support.has_dxcore) { + RETURN_IF_FAILED(GetDXCoreAdapterMetadata(device, isMcdmAdapter, vendorId, majorVersion, minorVersion)); + *isBlocked = CheckAdapterFP16Blocked(isMcdmAdapter, vendorId, majorVersion, minorVersion); + return S_OK; + } +#endif + RETURN_IF_FAILED(GetDXGIAdapterMetadata(device, vendorId, majorVersion, minorVersion)); + isMcdmAdapter = false; + *isBlocked = CheckAdapterFP16Blocked(isMcdmAdapter, vendorId, majorVersion, minorVersion); + return S_OK; +} +} + +namespace CommonDeviceHelpers { +constexpr uint32_t c_intelVendorId = 0x8086; +constexpr uint32_t c_nvidiaVendorId = 0x10DE; +constexpr uint32_t c_amdVendorId = 0x1002; + +bool IsFloat16Supported(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device) { + auto adapterId = device.AdapterId(); + if (!adapterId.HighPart && !adapterId.LowPart) { + // CPU device + return true; + } + winrt::com_ptr d3d12Device; + if (FAILED(GetD3D12Device(device, d3d12Device.put()))) { + return false; + } + return IsFloat16Supported(d3d12Device.get()); +} + +bool IsFloat16Supported(ID3D12Device* device) { +#ifndef USE_DML + WINML_THROW_HR_IF_TRUE_MSG(ERROR_NOT_SUPPORTED, true, "IsFloat16Supported is not implemented for WinML only build."); + return false; +#else + bool isBlocked; + if (FAILED(IsFloat16Blocked(*device, &isBlocked)) || isBlocked) { + return false; + } + winrt::com_ptr dmlDevice; + winrt::check_hresult(DMLCreateDevice( + device, + DML_CREATE_DEVICE_FLAG_NONE, + IID_PPV_ARGS(dmlDevice.put()))); + + DML_FEATURE_QUERY_TENSOR_DATA_TYPE_SUPPORT float16Query = {DML_TENSOR_DATA_TYPE_FLOAT16}; + DML_FEATURE_DATA_TENSOR_DATA_TYPE_SUPPORT float16Data = {}; + + winrt::check_hresult(dmlDevice->CheckFeatureSupport( + DML_FEATURE_TENSOR_DATA_TYPE_SUPPORT, + sizeof(float16Query), + &float16Query, + sizeof(float16Data), + &float16Data)); + return float16Data.IsSupported; +#endif +} + +HRESULT GetAdapterEnumerationSupport(AdapterEnumerationSupport* support) { + static std::optional s_adapterEnumerationSupport; + if (!s_adapterEnumerationSupport.has_value()) { + // check for support, starting with DXGI + winrt::com_ptr dxgiFactory; +#ifdef ENABLE_DXCORE + winrt::com_ptr dxcoreFactory; + // necessary because DXCoreCreateAdapterFactory is overloaded + HRESULT(WINAPI * pDxCoreTestFunc) + (REFIID, void**) = DXCoreCreateAdapterFactory; +#endif + AdapterEnumerationSupport adapterEnumerationSupport = {}; + + if (SUCCEEDED(RunDelayLoadedApi(CreateDXGIFactory1, IID_PPV_ARGS(dxgiFactory.put())))) { + adapterEnumerationSupport.has_dxgi = true; + } +#ifdef ENABLE_DXCORE + if (SUCCEEDED(RunDelayLoadedApi(pDxCoreTestFunc, IID_PPV_ARGS(dxcoreFactory.put())))) { + adapterEnumerationSupport.has_dxcore = true; + } +#endif + + s_adapterEnumerationSupport = adapterEnumerationSupport; + + if (!(adapterEnumerationSupport.has_dxgi || adapterEnumerationSupport.has_dxcore)) { + return TYPE_E_CANTLOADLIBRARY; + } + } + *support = s_adapterEnumerationSupport.value(); + return S_OK; +} + +} // namespace CommonDeviceHelpers diff --git a/winml/lib/Common/inc/CommonDeviceHelpers.h b/winml/lib/Common/inc/CommonDeviceHelpers.h new file mode 100644 index 0000000000000..38f1aa1e8a13c --- /dev/null +++ b/winml/lib/Common/inc/CommonDeviceHelpers.h @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "winrt_headers.h" +#include +#include +#include + +#if __has_include("dxcore.h") +#define ENABLE_DXCORE 1 +#endif +#ifdef ENABLE_DXCORE +#include +#endif + +// +// Exception information +// +#ifndef FACILITY_VISUALCPP +#define FACILITY_VISUALCPP ((LONG)0x6d) +#endif + +#define VcppException(sev, err) ((sev) | (FACILITY_VISUALCPP << 16) | err) + +namespace CommonDeviceHelpers { +struct AdapterEnumerationSupport { + bool has_dxgi; + bool has_dxcore; +}; + +// uses Structured Exception Handling (SEH) to detect for delay load failures of target API. +// You cannot mix and match SEH with C++ exception and object unwinding +// In this case we will catch it, and report up to the caller via HRESULT so our callers can use +// C++ exceptions +template +HRESULT RunDelayLoadedApi(TFunc& tfunc, TArgs&&... args) { + __try { + return tfunc(std::forward(args)...); + } __except (GetExceptionCode() == VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + // this could be ok, just let people know that it failed to load + return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND); + } +} + +HRESULT GetAdapterEnumerationSupport(AdapterEnumerationSupport* support); +bool IsFloat16Supported(ID3D12Device* device); +bool IsFloat16Supported(const winrt::Windows::AI::MachineLearning::LearningModelDevice& device); +} // namespace CommonDeviceHelpers diff --git a/winml/test/api/LearningModelBindingAPITest.cpp b/winml/test/api/LearningModelBindingAPITest.cpp index 99d9dc6e9b251..883eeb3cfc326 100644 --- a/winml/test/api/LearningModelBindingAPITest.cpp +++ b/winml/test/api/LearningModelBindingAPITest.cpp @@ -7,7 +7,6 @@ #include #include #include "winrt/Windows.Storage.h" -#include "DeviceHelpers.h" #include using namespace winrt; using namespace winrt::Windows::AI::MachineLearning; @@ -655,4 +654,4 @@ const LearningModelBindingAPITestApi& getapi() { VerifyOutputAfterImageBindCalledTwice }; return api; -} \ No newline at end of file +} diff --git a/winml/test/api/LearningModelSessionAPITest.cpp b/winml/test/api/LearningModelSessionAPITest.cpp index 908d13532e44d..45af7cbd831b1 100644 --- a/winml/test/api/LearningModelSessionAPITest.cpp +++ b/winml/test/api/LearningModelSessionAPITest.cpp @@ -1,10 +1,10 @@ #include "testPch.h" + #include "APITest.h" +#include "CommonDeviceHelpers.h" #include "LearningModelSessionAPITest.h" -#include "winrt/Windows.Storage.h" - -#include "DeviceHelpers.h" #include "protobufHelpers.h" +#include "winrt/Windows.Storage.h" #include #include @@ -231,7 +231,7 @@ static LearningModelSession CreateSession(LearningModel model) WINML_EXPECT_NO_THROW(device = LearningModelDevice(LearningModelDeviceKind::DirectX)); LearningModelSession session(nullptr); - if (DeviceHelpers::IsFloat16Supported(device)) + if (CommonDeviceHelpers::IsFloat16Supported(device)) { WINML_EXPECT_NO_THROW(session = LearningModelSession(model, device)); } diff --git a/winml/test/common/std.h b/winml/test/common/std.h index d6eab75f645f8..99a3af8e5943f 100644 --- a/winml/test/common/std.h +++ b/winml/test/common/std.h @@ -24,9 +24,10 @@ // IUnknown must be declared before winrt/base.h is included to light up support for native COM // interfaces with C++/WinRT types (e.g. winrt::com_ptr). #include +#include #include "winrt/base.h" #include "winrt/Windows.Foundation.Collections.h" #include "comp_generated/winrt/windows.ai.machinelearning.h" // WinML -#include "Windows.AI.MachineLearning.Native.h" \ No newline at end of file +#include "Windows.AI.MachineLearning.Native.h" diff --git a/winml/test/scenario/cppwinrt/scenariotestscppwinrt.cpp b/winml/test/scenario/cppwinrt/scenariotestscppwinrt.cpp index 1420b0c05e425..e38f5c29b3079 100644 --- a/winml/test/scenario/cppwinrt/scenariotestscppwinrt.cpp +++ b/winml/test/scenario/cppwinrt/scenariotestscppwinrt.cpp @@ -15,20 +15,21 @@ #ifdef GetCurrentTime #undef GetCurrentTime #endif +#include "CommonDeviceHelpers.h" #include "CustomOperatorProvider.h" -#include "DeviceHelpers.h" #include "filehelpers.h" #include "robuffer.h" +#include "scenariotestscppwinrt.h" #include "Windows.AI.MachineLearning.Native.h" #include "Windows.Graphics.DirectX.Direct3D11.interop.h" #include "windows.ui.xaml.media.dxinterop.h" #include "winrt/Windows.UI.Xaml.Controls.h" #include "winrt/Windows.UI.Xaml.Media.Imaging.h" + #include #include #include #include -#include "scenariotestscppwinrt.h" #include #if __has_include("dxcore.h") #define ENABLE_DXCORE 1 @@ -408,8 +409,8 @@ static void Scenario8SetDeviceSampleCustomCommandQueue() { LearningModel model = LearningModel::LoadFromFilePath(filePath); com_ptr pD3D12Device = nullptr; - DeviceHelpers::AdapterEnumerationSupport support; - if (FAILED(DeviceHelpers::GetAdapterEnumerationSupport(&support))) { + CommonDeviceHelpers::AdapterEnumerationSupport support; + if (FAILED(CommonDeviceHelpers::GetAdapterEnumerationSupport(&support))) { WINML_LOG_ERROR("Unable to load DXGI or DXCore"); return; } @@ -696,7 +697,7 @@ static void Scenario17DevDiagnostics() { WINML_EXPECT_NO_THROW(session.Evaluate(binding, L"")); } -/** +/** * Custom Operator Tests are labeled as GPU tests because the DML code is interlaced with the custom op code * even though CPU custom ops shouldn't be dependent on GPU functionality. * These should be reclassed to ScenarioCppWinrt once the DML code is decoupled from the custom op code. @@ -1242,8 +1243,8 @@ static void ReuseVideoFrame() { std::vector deviceKinds = {LearningModelDeviceKind::Cpu, LearningModelDeviceKind::DirectX}; std::vector videoFrameSources; - DeviceHelpers::AdapterEnumerationSupport support; - DeviceHelpers::GetAdapterEnumerationSupport(&support); + CommonDeviceHelpers::AdapterEnumerationSupport support; + CommonDeviceHelpers::GetAdapterEnumerationSupport(&support); if (support.has_dxgi) { videoFrameSources = {"SoftwareBitmap", "Direct3DSurface"}; } else { @@ -1449,4 +1450,4 @@ const ScenarioTestApi& getapi() { D2DInterop, }; return api; -} \ No newline at end of file +}