diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index ea2cf2e538..34f7ed6257 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -425,9 +425,11 @@ if (WIN32) # set linker flags to minimize the binary size. if (MSVC) - foreach(type EXE SHARED) - set(CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO} /OPT:REF,ICF,LBR") - set(CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO") + foreach(type EXE STATIC SHARED) + if (NOT type MATCHES STATIC) + set(CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO} /OPT:REF,ICF,LBR") + set(CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO} /INCREMENTAL:NO") + endif() if (NOT onnxruntime_USE_CUDA) set(CMAKE_${type}_LINKER_FLAGS_RELEASE "${CMAKE_${type}_LINKER_FLAGS_RELEASE} /LTCG") set(CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_${type}_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") @@ -694,6 +696,15 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Android") list(APPEND onnxruntime_EXTERNAL_LIBRARIES log) endif() +if (onnxruntime_USE_WINML) + if (NOT onnxruntime_USE_DML) + message( + FATAL_ERROR + "Option onnxruntime_USE_WINML can only be used when onnxruntime_USE_DML is also enabled") + endif() + include(wil.cmake) + include(winml.cmake) +endif() # if(onnxruntime_USE_WINML) #The following files may use the 'onnxruntime_libs' and 'onnxruntime_EXTERNAL_LIBRARIES' vars if (onnxruntime_BUILD_SHARED_LIB) @@ -733,17 +744,4 @@ if (onnxruntime_BUILD_CSHARP) message(STATUS "CSharp Build is enabled") # set_property(GLOBAL PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "netstandard2.0") include(onnxruntime_csharp.cmake) -endif() - -if (onnxruntime_USE_WINML) - - if (NOT onnxruntime_USE_DML) - message( - FATAL_ERROR - "Option onnxruntime_USE_WINML can only be used when onnxruntime_USE_DML is also enabled") - endif() - - include(wil.cmake) - include(winml.cmake) - -endif() # if(onnxruntime_USE_WINML) \ No newline at end of file +endif() \ No newline at end of file diff --git a/cmake/winml.cmake b/cmake/winml.cmake index a56548be7f..8195032640 100644 --- a/cmake/winml.cmake +++ b/cmake/winml.cmake @@ -11,6 +11,7 @@ set(winml_api_root ${REPO_ROOT}/winml/api) get_sdk(sdk_folder sdk_version) set(target_folder ONNXRuntime/winml) set(winml_dll_dir ${REPO_ROOT}/winml/dll) +set(winml_lib_dir ${REPO_ROOT}/winml/lib) set(winml_lib_api_dir ${REPO_ROOT}/winml/lib/api) set(winml_lib_api_core_dir ${REPO_ROOT}/winml/lib/api.core) set(winml_lib_api_image_dir ${REPO_ROOT}/winml/lib/api.image) @@ -126,6 +127,7 @@ add_library(winml_lib_core STATIC ${winml_lib_api_core_dir}/inc/MLValueHelpers.h ${winml_lib_api_core_dir}/inc/ModelInfo.h ${winml_lib_api_core_dir}/inc/TensorBaseHelpers.h + ${winml_lib_api_core_dir}/inc/WinMLAdapter.h ${winml_lib_api_core_dir}/CpuOrtSessionBuilder.h ${winml_lib_api_core_dir}/DmlOrtSessionBuilder.h ${winml_lib_api_core_dir}/FeatureDescriptorFactory.h @@ -138,6 +140,7 @@ add_library(winml_lib_core STATIC ${winml_lib_api_core_dir}/LotusEnvironment.cpp ${winml_lib_api_core_dir}/ModelInfo.cpp ${winml_lib_api_core_dir}/OrtSessionBuilder.cpp + ${winml_lib_api_core_dir}/WinMLAdapter.cpp ${winml_lib_api_core_dir}/ZeroCopyInputStreamWrapper.cpp ) @@ -167,6 +170,7 @@ target_include_directories(winml_lib_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/ex target_include_directories(winml_lib_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/onnx) target_include_directories(winml_lib_core PRIVATE ${winml_lib_api_dir}) # needed for generated headers +target_include_directories(winml_lib_core PRIVATE ${winml_lib_dir}) target_include_directories(winml_lib_core PRIVATE ${winml_lib_api_core_dir}) target_include_directories(winml_lib_core PRIVATE ${winml_lib_common_dir}/inc) target_include_directories(winml_lib_core PRIVATE ${ONNXRUNTIME_INCLUDE_DIR}) @@ -196,6 +200,10 @@ add_dependencies(winml_lib_core winml_api_native_internal) target_link_libraries(winml_lib_core PRIVATE wil) target_link_libraries(winml_lib_core PRIVATE onnxruntime_providers_dml) +# add it to the onnxruntime shared library +list(APPEND onnxruntime_EXTERNAL_LIBRARIES winml_lib_core) +list(APPEND onnxruntime_EXTERNAL_DEPENDENCIES winml_lib_core) + ########################### # Add winml_lib_image ########################### @@ -430,6 +438,7 @@ target_include_directories(winml_dll PRIVATE ${ONNXRUNTIME_ROOT}/core/graph) target_include_directories(winml_dll PRIVATE ${REPO_ROOT}/cmake/external/onnx) target_include_directories(winml_dll PRIVATE ${REPO_ROOT}/cmake/external/protobuf/src) target_include_directories(winml_dll PRIVATE ${REPO_ROOT}/cmake/external/gsl/include) +target_include_directories(winml_dll PRIVATE ${REPO_ROOT}/cmake/external/eigen) # Properties set_target_properties(winml_dll @@ -463,22 +472,22 @@ endif("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") # Link libraries target_link_libraries(winml_dll PRIVATE windowsapp.lib) -target_link_libraries(winml_dll PRIVATE libprotobuf) -target_link_libraries(winml_dll PRIVATE onnx) -target_link_libraries(winml_dll PRIVATE onnxruntime_common) -target_link_libraries(winml_dll PRIVATE onnxruntime_graph) -target_link_libraries(winml_dll PRIVATE onnxruntime_framework) -target_link_libraries(winml_dll PRIVATE onnxruntime_mlas) -target_link_libraries(winml_dll PRIVATE onnxruntime_optimizer) -target_link_libraries(winml_dll PRIVATE onnxruntime_providers) -target_link_libraries(winml_dll PRIVATE onnxruntime_providers_dml) -target_link_libraries(winml_dll PRIVATE onnxruntime_session) -target_link_libraries(winml_dll PRIVATE onnxruntime_util) -target_link_libraries(winml_dll PRIVATE onnx_proto) +# target_link_libraries(winml_dll PRIVATE libprotobuf) +# target_link_libraries(winml_dll PRIVATE onnx) +# target_link_libraries(winml_dll PRIVATE onnxruntime_common) +# target_link_libraries(winml_dll PRIVATE onnxruntime_graph) +# target_link_libraries(winml_dll PRIVATE onnxruntime_framework) +# target_link_libraries(winml_dll PRIVATE onnxruntime_mlas) +# target_link_libraries(winml_dll PRIVATE onnxruntime_optimizer) +# target_link_libraries(winml_dll PRIVATE onnxruntime_providers) +# target_link_libraries(winml_dll PRIVATE onnxruntime_providers_dml) +# target_link_libraries(winml_dll PRIVATE onnxruntime_session) +# target_link_libraries(winml_dll PRIVATE onnxruntime_util) +# target_link_libraries(winml_dll PRIVATE onnx_proto) target_link_libraries(winml_dll PRIVATE re2) target_link_libraries(winml_dll PRIVATE wil) target_link_libraries(winml_dll PRIVATE winml_lib_api) -target_link_libraries(winml_dll PRIVATE winml_lib_core) +# target_link_libraries(winml_dll PRIVATE winml_lib_core) target_link_libraries(winml_dll PRIVATE winml_lib_image) target_link_libraries(winml_dll PRIVATE winml_lib_telemetry) target_link_libraries(winml_dll PRIVATE onecoreuap_apiset.lib) diff --git a/winml/lib/Api.Core/ModelInfo.cpp b/winml/lib/Api.Core/ModelInfo.cpp index b4ea23e581..f436381fb4 100644 --- a/winml/lib/Api.Core/ModelInfo.cpp +++ b/winml/lib/Api.Core/ModelInfo.cpp @@ -133,7 +133,7 @@ void ModelInfo::Initialize( } // factory methods for creating an ort model from a path -std::unique_ptr +onnx::ModelProto* WinML::CreateModelProto( const char* path) { int file_descriptor; @@ -152,7 +152,7 @@ WinML::CreateModelProto( auto stream = google::protobuf::io::FileInputStream(file_descriptor); stream.SetCloseOnDelete(true); - auto model_proto = std::make_unique(); + auto model_proto = new onnx::ModelProto(); WINML_THROW_HR_IF_FALSE_MSG( E_INVALIDARG, model_proto->ParseFromZeroCopyStream(&stream), @@ -162,12 +162,12 @@ WinML::CreateModelProto( } // factory methods for creating an ort model from a stream -std::unique_ptr +onnx::ModelProto* WinML::CreateModelProto( - const wss::IRandomAccessStreamReference& stream_reference) { + const wss::IRandomAccessStreamReference& stream_reference) { ZeroCopyInputStreamWrapper wrapper(stream_reference); - auto model_proto = std::make_unique(); + auto model_proto = new onnx::ModelProto(); WINML_THROW_HR_IF_FALSE_MSG( E_INVALIDARG, model_proto->ParseFromZeroCopyStream(&wrapper), diff --git a/winml/lib/Api.Core/WinMLAdapter.cpp b/winml/lib/Api.Core/WinMLAdapter.cpp new file mode 100644 index 0000000000..7f0c15a6e5 --- /dev/null +++ b/winml/lib/Api.Core/WinMLAdapter.cpp @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "inc/WinMLAdapter.h" +#include "inc/CustomRegistryHelper.h" +#include "inc/LotusEnvironment.h" +#include "core/providers/dml/DmlExecutionProvider/inc/DmlExecutionProvider.h" + +#include "LearningModelDevice.h" +#include "TensorFeatureDescriptor.h" +#include "ImageFeatureDescriptor.h" +#include "api.image/inc/D3DDeviceCache.h" + +using namespace winrt::Windows::AI::MachineLearning; + +namespace Windows::AI::MachineLearning::Adapter { + +HRESULT STDMETHODCALLTYPE +RegisterCustomRegistry( + onnxruntime::InferenceSession* p_session, + IMLOperatorRegistry* registry) { + RETURN_HR_IF(S_OK, registry == nullptr); + RETURN_HR_IF_NULL(E_POINTER, p_session); + + auto custom_registries = WinML::GetLotusCustomRegistries(registry); + + // Register + for (auto& custom_registry : custom_registries) { + WINML_THROW_IF_NOT_OK(p_session->RegisterCustomRegistry(custom_registry)); + } + + return S_OK; +} + +void STDMETHODCALLTYPE EnableDebugOutput() { + WinML::CWinMLLogSink::EnableDebugOutput(); +} + +// ORT intentionally requires callers derive from their session class to access +// the protected Load method used below. +class InferenceSessionProtectedLoadAccessor : public onnxruntime::InferenceSession { + public: + onnxruntime::common::Status + Load(std::unique_ptr p_model_proto) { + return onnxruntime::InferenceSession::Load(std::move(p_model_proto)); + } +}; + +HRESULT STDMETHODCALLTYPE +LoadModel( + onnxruntime::InferenceSession* session, + onnx::ModelProto* model_proto) { + auto session_protected_load_accessor = + static_cast(session); + std::unique_ptr model_proto_ptr(model_proto); + WINML_THROW_IF_NOT_OK(session_protected_load_accessor->Load(std::move(model_proto_ptr))); +} + +static bool +IsFeatureDescriptorFp16( + winml::ILearningModelFeatureDescriptor descriptor) { + if (auto imageFeatureDescriptor = descriptor.try_as()) { + return TensorKind::Float16 == imageFeatureDescriptor->TensorKind(); + } + + if (auto tensorFeatureDescriptor = descriptor.try_as()) { + return TensorKind::Float16 == tensorFeatureDescriptor->TensorKind(); + } + + return false; +} + +HRESULT STDMETHODCALLTYPE +EnsureModelDeviceCompatibility( + winml::LearningModel const& model, + onnx::ModelProto* p_model_proto, + winml::LearningModelDevice const& device) { + auto isFloat16Supported = device.as()->GetD3DDeviceCache()->IsFloat16Supported(); + if (!isFloat16Supported) { + auto& graph = p_model_proto->graph(); + + // The model will not contain fp16 operations if: + // 1. The model has no fp16 inputs + // 2. The model has no fp16 initializers + // 3. The model does not create any fp16 intermediary tensors via the Cast (to float16) operator + // 4. The model does not have any fp16 outputs + + // 1. Ensure that The model has no fp16 inputs + for (auto descriptor : model.InputFeatures()) { + WINML_THROW_HR_IF_TRUE_MSG( + DXGI_ERROR_UNSUPPORTED, + IsFeatureDescriptorFp16(descriptor), + "The model contains a 16-bit input (%ls), but the current device does not support 16-bit float.", + descriptor.Name().c_str()); + } + + // 2. Ensure that the model has no fp16 initializers + for (int i = 0; i < graph.node_size(); i++) { + auto node = graph.node(i); + if (node.op_type() == "Cast" && node.domain().empty()) { + for (int attribIndex = 0; attribIndex < node.attribute_size(); attribIndex++) { + auto attribute = node.attribute(attribIndex); + if (attribute.name() == "to") { + WINML_THROW_HR_IF_TRUE_MSG( + DXGI_ERROR_UNSUPPORTED, + attribute.i() == onnx::TensorProto::DataType::TensorProto_DataType_FLOAT16, + "The model contains a 16-bit float Cast Op (%s), but the current device does not support 16-bit float.", + node.name().c_str()); + } + } + } + } + + // 3. Ensure that the model does not create any fp16 intermediary + // tensors via the Cast (to float16) operator + for (int i = 0; i < graph.initializer_size(); i++) { + auto initializer = graph.initializer(i); + + WINML_THROW_HR_IF_TRUE_MSG( + DXGI_ERROR_UNSUPPORTED, + initializer.data_type() == onnx::TensorProto::DataType::TensorProto_DataType_FLOAT16, + "The model contains a 16-bit float initializer (%s), but the current device does not support 16-bit float.", + initializer.name().c_str()); + } + + // 4. Ensure that the model does not have any fp16 outputs + for (auto descriptor : model.OutputFeatures()) { + WINML_THROW_HR_IF_TRUE_MSG( + DXGI_ERROR_UNSUPPORTED, + IsFeatureDescriptorFp16(descriptor), + "The model contains a 16-bit output (%ls), but the current device does not support 16-bit float.", + descriptor.Name().c_str()); + } + } +} + +HRESULT STDMETHODCALLTYPE ReleaseOrtObject(onnx::ModelProto* model_proto) { + std::unique_ptr ptr(model_proto); +} + +HRESULT STDMETHODCALLTYPE ReleaseOrtObject(onnxruntime::IOBinding* io_binding) { + std::unique_ptr ptr(io_binding); +} + +ID3D12Resource* STDMETHODCALLTYPE +GetD3D12ResourceFromAllocation( + onnxruntime::IExecutionProvider* provider, + void* allocation) { + auto d3dResource = + Dml::GetD3D12ResourceFromAllocation( + provider->GetAllocator(0, ::OrtMemType::OrtMemTypeDefault).get(), + allocation); + return d3dResource; +} + +onnxruntime::MLDataType GetType(winml::TensorKind kind) { + switch (kind) { + case winml::TensorKind::Float: + return onnxruntime::DataTypeImpl::GetType(); + case winml::TensorKind::Float16: + return onnxruntime::DataTypeImpl::GetType(); + }; + return nullptr; +} + +onnxruntime::Tensor* STDMETHODCALLTYPE CreateTensor( + winml::TensorKind kind, + const int64_t* shape, + uint32_t shape_count, + onnxruntime::IExecutionProvider* provider) { + onnxruntime::TensorShape tensor_shape(shape, shape_count); + auto pTensor = new onnxruntime::Tensor( + GetType(kind), + tensor_shape, + provider->GetAllocator(0, ::OrtMemType::OrtMemTypeDefault)); + return pTensor; +} + +} // namespace Windows::AI::MachineLearning::Adapter \ No newline at end of file diff --git a/winml/lib/Api.Core/inc/ModelInfo.h b/winml/lib/Api.Core/inc/ModelInfo.h index b720149f10..63ed7eebd7 100644 --- a/winml/lib/Api.Core/inc/ModelInfo.h +++ b/winml/lib/Api.Core/inc/ModelInfo.h @@ -3,6 +3,8 @@ #pragma once +#include "WinMLAdapter.h" + namespace Windows::AI::MachineLearning { class ModelInfo { @@ -25,9 +27,9 @@ class ModelInfo { }; // factory methods for creating an ort model from a path -std::unique_ptr CreateModelProto(const char* path); +onnx::ModelProto* CreateModelProto(const char* path); // factory methods for creating an ort model from a stream -std::unique_ptr CreateModelProto(const wss::IRandomAccessStreamReference& stream_reference); +onnx::ModelProto* CreateModelProto(const wss::IRandomAccessStreamReference& stream_reference); } // namespace Windows::AI::MachineLearning \ No newline at end of file diff --git a/winml/lib/Api.Core/inc/WinMLAdapter.h b/winml/lib/Api.Core/inc/WinMLAdapter.h new file mode 100644 index 0000000000..94b69bfa1a --- /dev/null +++ b/winml/lib/Api.Core/inc/WinMLAdapter.h @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +namespace Windows::AI::MachineLearning::Adapter { + +__declspec(dllexport) HRESULT STDMETHODCALLTYPE ReleaseOrtObject(onnx::ModelProto* model_proto); +__declspec(dllexport) HRESULT STDMETHODCALLTYPE ReleaseOrtObject(onnxruntime::IOBinding* io_binding); + +__declspec(dllexport) HRESULT STDMETHODCALLTYPE RegisterCustomRegistry( + onnxruntime::InferenceSession* p_session, + IMLOperatorRegistry* registry); + +__declspec(dllexport) void STDMETHODCALLTYPE EnableDebugOutput(); + +__declspec(dllexport) HRESULT STDMETHODCALLTYPE LoadModel(onnxruntime::InferenceSession* session, onnx::ModelProto* model); + +__declspec(dllexport) HRESULT STDMETHODCALLTYPE EnsureModelDeviceCompatibility( + winml::LearningModel const& model, + onnx::ModelProto* p_model_proto, + winml::LearningModelDevice const& device); + +__declspec(dllexport) ID3D12Resource* STDMETHODCALLTYPE GetD3D12ResourceFromAllocation(onnxruntime::IExecutionProvider* provider, void* allocation); + +__declspec(dllexport) onnxruntime::Tensor* STDMETHODCALLTYPE CreateTensor( + winml::TensorKind kind, + const int64_t * shape, + uint32_t shape_count, + onnxruntime::IExecutionProvider* provider); + +// header only code to enable smart pointers on abstract ort objects +template +class OrtObject { + public: + OrtObject() { + p_ = nullptr; + } + + OrtObject(T* m) { + p_ = m; + } + + virtual ~OrtObject() { + if (p_ != nullptr) { + ReleaseOrtObject(p_); + } + } + + T* p_; +}; +using ModelProto = OrtObject; +using IOBinding = OrtObject; + +} // namespace Windows::AI::MachineLearning::Adapter \ No newline at end of file diff --git a/winml/lib/Api.Core/pch.h b/winml/lib/Api.Core/pch.h index 535e5b2638..6c66eeaf65 100644 --- a/winml/lib/Api.Core/pch.h +++ b/winml/lib/Api.Core/pch.h @@ -3,4 +3,5 @@ #pragma once -#include "cppwinrt_onnx.h" \ No newline at end of file +#include "cppwinrt_onnx.h" +#include "dx.h" diff --git a/winml/lib/Api.Image/inc/ImageConversionTypes.h b/winml/lib/Api.Image/inc/ImageConversionTypes.h index 7f64fa743a..74d541fdc6 100644 --- a/winml/lib/Api.Image/inc/ImageConversionTypes.h +++ b/winml/lib/Api.Image/inc/ImageConversionTypes.h @@ -28,6 +28,6 @@ enum ImageTensorChannelType { struct ImageTensorDescription { ImageTensorDataType dataType; ImageTensorChannelType channelType; - UINT sizes[kImageTensorDimensionCountMax]; + int64_t sizes[kImageTensorDimensionCountMax]; }; } // namespace Windows::AI::MachineLearning::Internal \ No newline at end of file diff --git a/winml/lib/Api/ImageFeatureValue.cpp b/winml/lib/Api/ImageFeatureValue.cpp index 8e79c26d3a..cb947ddea8 100644 --- a/winml/lib/Api/ImageFeatureValue.cpp +++ b/winml/lib/Api/ImageFeatureValue.cpp @@ -347,8 +347,8 @@ static void GPUTensorize( void* pAllocatedResource, WinML::BindingContext& context) { auto d3dResource = - Dml::GetD3D12ResourceFromAllocation( - spSession->GetExecutionProvider()->GetAllocator(0, ::OrtMemType::OrtMemTypeDefault).get(), + _winmla::GetD3D12ResourceFromAllocation( + spSession->GetExecutionProvider(), pAllocatedResource); auto spDevice = spSession->Device().as(); @@ -391,14 +391,11 @@ static void GPUTensorize( static OrtValue CreateMLValue( com_ptr spSession, const ImageTensorDescription& tensorDescriptor) { - auto shape = - std::vector( - std::begin(tensorDescriptor.sizes), - std::end(tensorDescriptor.sizes)); - auto pTensor = new onnxruntime::Tensor( - tensorDescriptor.dataType == kImageTensorDataTypeFloat32 ? onnxruntime::DataTypeImpl::GetType() : onnxruntime::DataTypeImpl::GetType(), - onnxruntime::TensorShape(shape), - spSession->GetExecutionProvider()->GetAllocator(0, ::OrtMemType::OrtMemTypeDefault)); + auto pTensor = _winmla::CreateTensor( + tensorDescriptor.dataType == kImageTensorDataTypeFloat32 ? TensorKind::Float : TensorKind::Float16, + &(tensorDescriptor.sizes[0]), + sizeof(tensorDescriptor.sizes) / sizeof(tensorDescriptor.sizes[0]), + spSession->GetExecutionProvider()); OrtValue ort_value; ort_value.Init(pTensor, @@ -591,7 +588,7 @@ HRESULT ImageFeatureValue::UpdateSourceResourceData(BindingContext& context, Ort auto pooledConverter = PoolObjectWrapper::Create(spDevice->DetensorizerStore()->Fetch(descriptor)); auto pProvider = spSession->GetExecutionProvider(); - auto d3dResource = Dml::GetD3D12ResourceFromAllocation(pProvider->GetAllocator(0, ::OrtMemType::OrtMemTypeDefault).get(), pAllocatedResource); + auto d3dResource = _winmla::GetD3D12ResourceFromAllocation(pProvider, pAllocatedResource); for (uint32_t batchIdx = 0; batchIdx < m_batchSize; ++batchIdx) { auto videoFrame = m_videoFrames.GetAt(batchIdx); diff --git a/winml/lib/Api/LearningModel.cpp b/winml/lib/Api/LearningModel.cpp index 0d6dc8174e..5aea902822 100644 --- a/winml/lib/Api/LearningModel.cpp +++ b/winml/lib/Api/LearningModel.cpp @@ -17,8 +17,6 @@ #include "SequenceFeatureDescriptor.h" #include "TensorFeatureDescriptor.h" -using namespace OperatorHelper; - namespace winrt::Windows::AI::MachineLearning::implementation { LearningModel::LearningModel( const hstring& path, @@ -35,7 +33,7 @@ LearningModel::LearningModel( OverrideShapeInferenceMethods(); - model_proto_ = WinML::CreateModelProto(path.c_str()); + model_proto_ = std::make_unique<_winmla::ModelProto>(WinML::CreateModelProto(path.c_str())); Initialize(); @@ -52,7 +50,7 @@ LearningModel::LearningModel( OverrideShapeInferenceMethods(); - model_proto_ = WinML::CreateModelProto(stream); + model_proto_ = std::make_unique<_winmla::ModelProto>(WinML::CreateModelProto(stream)); Initialize(); @@ -62,7 +60,7 @@ WINML_CATCH_ALL void LearningModel::Initialize() { model_info_ = std::make_unique( - model_proto_.get()); + model_proto_.get()->p_); } void LearningModel::LogCreationEvent(bool fromStream) { @@ -257,9 +255,9 @@ LearningModel::LoadFromStream( } WINML_CATCH_ALL -std::unique_ptr +std::unique_ptr<_winmla::ModelProto> LearningModel::DetachModelProto() { - std::unique_ptr detached_model_proto; + std::unique_ptr<_winmla::ModelProto> detached_model_proto; if (model_proto_ != nullptr) { detached_model_proto = std::move(model_proto_); @@ -269,13 +267,13 @@ LearningModel::DetachModelProto() { return detached_model_proto; } -std::unique_ptr +std::unique_ptr<_winmla::ModelProto> LearningModel::CopyModelProto() { if (model_proto_ == nullptr) { return nullptr; } - return std::make_unique(*model_proto_); + return std::make_unique<_winmla::ModelProto>(*model_proto_); } static std::once_flag g_schema_override_once_flag; diff --git a/winml/lib/Api/LearningModel.h b/winml/lib/Api/LearningModel.h index 919de796f5..9a8ec81c59 100644 --- a/winml/lib/Api/LearningModel.h +++ b/winml/lib/Api/LearningModel.h @@ -4,10 +4,11 @@ #pragma once #include "LearningModel.g.h" +#include "WinMLAdapter.h" -namespace Windows::AI::MachineLearning { -class LotusEnvironment; -class ModelInfo; + namespace Windows::AI::MachineLearning { + class LotusEnvironment; + class ModelInfo; } // namespace Windows::AI::MachineLearning namespace winrt::Windows::AI::MachineLearning::implementation { @@ -101,10 +102,10 @@ struct LearningModel : LearningModelT { IMLOperatorRegistry* GetOperatorRegistry(); - std::unique_ptr + std::unique_ptr<_winmla::ModelProto> DetachModelProto(); - std::unique_ptr + std::unique_ptr<_winmla::ModelProto> CopyModelProto(); private: @@ -125,7 +126,7 @@ struct LearningModel : LearningModelT { private: std::shared_ptr lotus_environment_; - std::unique_ptr model_proto_; + std::unique_ptr model_proto_; std::unique_ptr model_info_; ILearningModelOperatorProvider operator_provider_; }; diff --git a/winml/lib/Api/LearningModelSession.cpp b/winml/lib/Api/LearningModelSession.cpp index cbd5bfa4c6..0be664c595 100644 --- a/winml/lib/Api/LearningModelSession.cpp +++ b/winml/lib/Api/LearningModelSession.cpp @@ -5,9 +5,9 @@ #include "LearningModelSession.h" -#include "CustomRegistryHelper.h" #include "ImageFeatureDescriptor.h" #include "IOrtSessionBuilder.h" +#include "WinMLAdapter.h" #include "LearningModel.h" #include "LearningModelBinding.h" #include "LearningModelEvaluationResult.h" @@ -39,110 +39,6 @@ struct __declspec(uuid("D113B493-BBA2-4993-8608-D706A73B91CE")) __declspec(novta static const GUID WINML_PIX_EVAL_CAPTURABLE_WORK_GUID = __uuidof(guid_details::WINML_PIX_EVAL_CAPTURABLE_WORK_GUID); namespace winrt::Windows::AI::MachineLearning::implementation { -// ORT intentionally requires callers derive from their session class to access -// the protected Load method used below. -class InferenceSessionProtectedLoadAccessor : public onnxruntime::InferenceSession { - public: - onnxruntime::common::Status - Load(std::unique_ptr p_model_proto) { - return onnxruntime::InferenceSession::Load(std::move(p_model_proto)); - } -}; - -static bool -IsFeatureDescriptorFp16( - winml::ILearningModelFeatureDescriptor descriptor) { - if (auto imageFeatureDescriptor = descriptor.try_as()) { - return TensorKind::Float16 == imageFeatureDescriptor->TensorKind(); - } - - if (auto tensorFeatureDescriptor = descriptor.try_as()) { - return TensorKind::Float16 == tensorFeatureDescriptor->TensorKind(); - } - - return false; -} - -static void -EnsureModelDeviceCompatibility( - winml::LearningModel const& model, - onnx::ModelProto* p_model_proto, - winml::LearningModelDevice const& device) { - auto isFloat16Supported = device.as()->GetD3DDeviceCache()->IsFloat16Supported(); - if (!isFloat16Supported) { - auto& graph = p_model_proto->graph(); - - // The model will not contain fp16 operations if: - // 1. The model has no fp16 inputs - // 2. The model has no fp16 initializers - // 3. The model does not create any fp16 intermediary tensors via the Cast (to float16) operator - // 4. The model does not have any fp16 outputs - - // 1. Ensure that The model has no fp16 inputs - for (auto descriptor : model.InputFeatures()) { - WINML_THROW_HR_IF_TRUE_MSG( - DXGI_ERROR_UNSUPPORTED, - IsFeatureDescriptorFp16(descriptor), - "The model contains a 16-bit input (%ls), but the current device does not support 16-bit float.", - descriptor.Name().c_str()); - } - - // 2. Ensure that the model has no fp16 initializers - for (int i = 0; i < graph.node_size(); i++) { - auto node = graph.node(i); - if (node.op_type() == "Cast" && node.domain().empty()) { - for (int attribIndex = 0; attribIndex < node.attribute_size(); attribIndex++) { - auto attribute = node.attribute(attribIndex); - if (attribute.name() == "to") { - WINML_THROW_HR_IF_TRUE_MSG( - DXGI_ERROR_UNSUPPORTED, - attribute.i() == onnx::TensorProto::DataType::TensorProto_DataType_FLOAT16, - "The model contains a 16-bit float Cast Op (%s), but the current device does not support 16-bit float.", - node.name().c_str()); - } - } - } - } - - // 3. Ensure that the model does not create any fp16 intermediary - // tensors via the Cast (to float16) operator - for (int i = 0; i < graph.initializer_size(); i++) { - auto initializer = graph.initializer(i); - - WINML_THROW_HR_IF_TRUE_MSG( - DXGI_ERROR_UNSUPPORTED, - initializer.data_type() == onnx::TensorProto::DataType::TensorProto_DataType_FLOAT16, - "The model contains a 16-bit float initializer (%s), but the current device does not support 16-bit float.", - initializer.name().c_str()); - } - - // 4. Ensure that the model does not have any fp16 outputs - for (auto descriptor : model.OutputFeatures()) { - WINML_THROW_HR_IF_TRUE_MSG( - DXGI_ERROR_UNSUPPORTED, - IsFeatureDescriptorFp16(descriptor), - "The model contains a 16-bit output (%ls), but the current device does not support 16-bit float.", - descriptor.Name().c_str()); - } - } -} - -static HRESULT -RegisterCustomRegistry( - onnxruntime::InferenceSession* p_session, - IMLOperatorRegistry* registry) { - RETURN_HR_IF(S_OK, registry == nullptr); - RETURN_HR_IF_NULL(E_POINTER, p_session); - - auto custom_registries = WinML::GetLotusCustomRegistries(registry); - - // Register - for (auto& custom_registry : custom_registries) { - WINML_THROW_IF_NOT_OK(p_session->RegisterCustomRegistry(custom_registry)); - } - - return S_OK; -} LearningModelSession::LearningModelSession( winml::LearningModel const& model) try : LearningModelSession(model, @@ -166,7 +62,7 @@ LearningModelSession::LearningModelSession( } WINML_CATCH_ALL -std::unique_ptr +std::unique_ptr<_winmla::ModelProto> LearningModelSession::GetOptimizedModel() { // Get the model proto auto should_close_model = @@ -176,9 +72,9 @@ LearningModelSession::GetOptimizedModel() { return GetOptimizedModel(should_close_model); } -std::unique_ptr +std::unique_ptr<_winmla::ModelProto> LearningModelSession::GetOptimizedModel(bool should_close_model) { - std::unique_ptr model_proto; + std::unique_ptr<_winmla::ModelProto> model_proto; { // Lock the model detach/copy since multiple threads can access concurrently @@ -196,7 +92,7 @@ LearningModelSession::GetOptimizedModel(bool should_close_model) { } // Ensure that the model is runnable on the device - EnsureModelDeviceCompatibility(model_, model_proto.get(), device_); + WINML_THROW_IF_FAILED(_winmla::EnsureModelDeviceCompatibility(model_, model_proto.get()->p_, device_)); return model_proto; } @@ -230,16 +126,16 @@ void LearningModelSession::Initialize() { // Register the custom operator registry auto model = model_.as(); - RegisterCustomRegistry(session.get(), model->GetOperatorRegistry()); + WINML_THROW_IF_FAILED(_winmla::RegisterCustomRegistry(session.get(), model->GetOperatorRegistry())); // Register only the transformers not already in ORT const bool registerLotusTransformers = false; GraphTransformerHelpers::RegisterGraphTransformers(session.get(), registerLotusTransformers); // Load the model into the session - auto session_protected_load_accessor = - static_cast(session.get()); - WINML_THROW_IF_NOT_OK(session_protected_load_accessor->Load(std::move(model_proto))); + WINML_THROW_IF_FAILED(_winmla::LoadModel(session.get(), model_proto.get()->p_)); + // the session owns the model_proto now + model_proto.release(); // Initialize the session session_builder->Initialize(session.get(), p_cached_execution_provider); @@ -525,7 +421,7 @@ void LearningModelSession::ApplyEvaluationProperties() try { if (evaluation_properties_) { auto is_debug_output_enabled = evaluation_properties_.HasKey(c_enable_debug_output); if (is_debug_output_enabled) { - WinML::CWinMLLogSink::EnableDebugOutput(); + _winmla::EnableDebugOutput(); } } } diff --git a/winml/lib/Api/LearningModelSession.h b/winml/lib/Api/LearningModelSession.h index 0b37f03ec4..552dafc03c 100644 --- a/winml/lib/Api/LearningModelSession.h +++ b/winml/lib/Api/LearningModelSession.h @@ -7,6 +7,7 @@ #include "LearningModelBinding.h" #include "WinML_Lock.h" +#include "WinMLAdapter.h" namespace winrt::Windows::AI::MachineLearning::implementation { @@ -74,10 +75,10 @@ struct LearningModelSession : LearningModelSessionT { void Initialize(); - std::unique_ptr + std::unique_ptr<_winmla::ModelProto> GetOptimizedModel(); - std::unique_ptr + std::unique_ptr<_winmla::ModelProto> GetOptimizedModel(bool should_close_model); uint64_t diff --git a/winml/lib/Common/inc/NamespaceAliases.h b/winml/lib/Common/inc/NamespaceAliases.h index 4da87337c8..a09ae6b698 100644 --- a/winml/lib/Common/inc/NamespaceAliases.h +++ b/winml/lib/Common/inc/NamespaceAliases.h @@ -34,4 +34,7 @@ namespace Windows::AI::MachineLearning {} namespace WinML = ::Windows::AI::MachineLearning; namespace Windows::AI::MachineLearning::Telemetry {} -namespace _winmlt = ::Windows::AI::MachineLearning::Telemetry; \ No newline at end of file +namespace _winmlt = ::Windows::AI::MachineLearning::Telemetry; + +namespace Windows::AI::MachineLearning::Adapter {} +namespace _winmla = ::Windows::AI::MachineLearning::Adapter; \ No newline at end of file