diff --git a/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/DmlOperatorSlice.cpp b/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/DmlOperatorSlice.cpp index 0e9d0feb5a..fb82f7708c 100644 --- a/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/DmlOperatorSlice.cpp +++ b/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/DmlOperatorSlice.cpp @@ -13,49 +13,36 @@ public: : DmlOperator(kernelInfo), SliceHelperBase(kernelInfo, kernelInfo.GetTensorShapeDescription(), opsetVersion) { - uint32_t minInputCount = (opsetVersion < 10) ? 1 : 3; - ML_CHECK_VALID_ARGUMENT(kernelInfo.GetInputCount() >= minInputCount); + const uint32_t inputCount = kernelInfo.GetInputCount(); + ML_CHECK_VALID_ARGUMENT((opsetVersion < 10 && inputCount == 1) + || (opsetVersion == 10 && inputCount >= 3 && inputCount <= 5)); ML_CHECK_VALID_ARGUMENT(kernelInfo.GetOutputCount() == 1); - // TODO (23108599): Slice V10 introduces an optional "Steps" input which the kernel does not yet support. - THROW_HR_IF(E_NOTIMPL, kernelInfo.GetInputCount() > 4); - - std::vector> kernelInputIndices = { 0 }; + std::vector> kernelInputIndices = { 0 }; // Only bind GPU to first 'data' tensor. DmlOperator::Initialize(kernelInfo, kernelInputIndices); - assert(m_inputTensorDescs[0].GetDimensionCount() >= gsl::narrow_cast(m_offsets.size())); - assert(m_inputTensorDescs[0].GetDimensionCount() >= gsl::narrow_cast(m_sizes.size())); - assert(m_inputTensorDescs[0].GetDimensionCount() >= gsl::narrow_cast(m_strides.size())); + const uint32_t inputTensorRank = m_inputTensorDescs[0].GetDimensionCount(); + assert(inputTensorRank >= gsl::narrow_cast(m_offsets.size())); + assert(inputTensorRank >= gsl::narrow_cast(m_sizes.size())); + assert(inputTensorRank >= gsl::narrow_cast(m_strides.size())); // Pad the parameters to respect DML's requirements - m_offsets.insert( - m_offsets.begin(), - m_inputTensorDescs[0].GetDimensionCount() - gsl::narrow_cast(m_offsets.size()), - 0); - - m_sizes.insert( - m_sizes.begin(), - m_inputTensorDescs[0].GetDimensionCount() - gsl::narrow_cast(m_sizes.size()), - 1); - - m_strides.insert( - m_strides.begin(), - m_inputTensorDescs[0].GetDimensionCount() - gsl::narrow_cast(m_strides.size()), - 1); + FillWithLeadingValues(/*inout*/ m_offsets, m_inputTensorDescs[0].GetDimensionCount(), 0u); + FillWithLeadingValues(/*inout*/ m_sizes, m_inputTensorDescs[0].GetDimensionCount(), 1u); + FillWithLeadingValues(/*inout*/ m_strides, m_inputTensorDescs[0].GetDimensionCount(), 1); std::vector inputDescs = GetDmlInputDescs(); std::vector outputDescs = GetDmlOutputDescs(); - DML_SLICE_OPERATOR_DESC sliceDesc = {}; + DML_SLICE1_OPERATOR_DESC sliceDesc = {}; sliceDesc.InputTensor = inputDescs.data(); sliceDesc.OutputTensor = outputDescs.data(); sliceDesc.DimensionCount = gsl::narrow_cast(m_offsets.size()); - sliceDesc.Offsets = m_offsets.data(); - sliceDesc.Sizes = m_sizes.data(); - sliceDesc.Strides = m_strides.data(); + sliceDesc.InputWindowOffsets = m_offsets.data(); + sliceDesc.InputWindowSizes = m_sizes.data(); + sliceDesc.InputWindowStrides = m_strides.data(); - DML_OPERATOR_DESC opDesc = { DML_OPERATOR_SLICE, &sliceDesc }; - + DML_OPERATOR_DESC opDesc = { DML_OPERATOR_SLICE1, &sliceDesc }; SetDmlOperatorDesc(opDesc, kernelInfo); } }; @@ -73,9 +60,10 @@ public: void CALLBACK QuerySlice(IMLOperatorSupportQueryContextPrivate* context, bool *isSupported) { - *isSupported = (context->GetInputCount() <= 4); + *isSupported = (context->GetInputCount() <= 5); } DML_OP_DEFINE_CREATION_FUNCTION(Slice7, DmlOperatorSliceTemplate<7>); DML_OP_DEFINE_CREATION_FUNCTION(Slice10, DmlOperatorSliceTemplate<10>); +DML_OP_DEFINE_CREATION_FUNCTION(Slice11, DmlOperatorSliceTemplate<10>); } // namespace Dml diff --git a/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/OperatorRegistration.cpp b/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/OperatorRegistration.cpp index f19bf8d6a9..8be3774de1 100644 --- a/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/OperatorRegistration.cpp +++ b/onnxruntime/core/providers/dml/DmlExecutionProvider/src/Operators/OperatorRegistration.cpp @@ -101,6 +101,7 @@ DML_OP_EXTERN_CREATION_FUNCTION(Tile); DML_OP_EXTERN_CREATION_FUNCTION(Concat); DML_OP_EXTERN_CREATION_FUNCTION(Slice7); DML_OP_EXTERN_CREATION_FUNCTION(Slice10); +DML_OP_EXTERN_CREATION_FUNCTION(Slice11); DML_OP_EXTERN_CREATION_FUNCTION(Pad); DML_OP_EXTERN_CREATION_FUNCTION(SpaceToDepth); DML_OP_EXTERN_CREATION_FUNCTION(DepthToSpace); @@ -214,6 +215,7 @@ const static char* const typeNameListCast[2] = { "T1", "T2" }; const static char* const typeNameListIsNan[2] = { "T1", "T2" }; const static char* const typeNameListConstantOfShape[2] = { "T1", "T2" }; const static char* const typeNameListScatterGather[2] = { "T", "Tind" }; +const static char* const typeNameListSlice10[2] = { "T", "Tind" }; const static char* const typeNameListQuantize[2] = { "T1", "T2" }; const static char* const typeNameListWhere[2] = { "B", "T" }; const static char* const typeNameListOneHot[3] = { "T1", "T2", "T3" }; @@ -228,6 +230,7 @@ const static SupportedTensorDataTypes supportedTypeListTopK[2] = {SupportedTenso const static SupportedTensorDataTypes supportedTypeListIndices[1] = { SupportedTensorDataTypes::Int32|SupportedTensorDataTypes::Int64 }; const static SupportedTensorDataTypes supportedTypeListCast[2] = { SupportedTensorDataTypes::AllScalars, SupportedTensorDataTypes::Scalars8to32 }; const static SupportedTensorDataTypes supportedTypeListScatterGather[2] = { SupportedTensorDataTypes::NumericDefault, SupportedTensorDataTypes::Int32 | SupportedTensorDataTypes::Int64 }; +const static SupportedTensorDataTypes supportedTypeListSlice10[2] = { SupportedTensorDataTypes::NumericDefault, SupportedTensorDataTypes::Int32 | SupportedTensorDataTypes::Int64 }; const static SupportedTensorDataTypes supportedTypeListQuantizeLinear[2] = { SupportedTensorDataTypes::Float32 | SupportedTensorDataTypes::Int32, SupportedTensorDataTypes::UInt8 | SupportedTensorDataTypes::Int8 }; const static SupportedTensorDataTypes supportedTypeListDequantizeLinear[2] = { SupportedTensorDataTypes::Float32, SupportedTensorDataTypes::UInt8 | SupportedTensorDataTypes::Int8 | SupportedTensorDataTypes::Int32 }; const static SupportedTensorDataTypes supportedTypeListQuantize[2] = { SupportedTensorDataTypes::Float32, SupportedTensorDataTypes::UInt8 }; @@ -297,7 +300,8 @@ const static OperatorRegistrationInformation operatorRegistrationInformationTabl {REG_INFO( 7, Transpose, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 7, Concat, typeNameListDefault, supportedTypeListNumericDefault, DmGraphSupport::Supported)}, {REG_INFO_VER( 7, Slice, typeNameListDefault, supportedTypeListNumericDefault, DmGraphSupport::Supported)}, - {REG_INFO_VER( 10, Slice, typeNameListDefault, supportedTypeListNumericDefault, DmGraphSupport::Supported, {1, 2, 3}, std::nullopt, QuerySlice)}, + {REG_INFO_VER( 10, Slice, typeNameListSlice10, supportedTypeListSlice10, DmGraphSupport::Supported, {1, 2, 3, 4}, std::nullopt, QuerySlice)}, + {REG_INFO_VER( 11, Slice, typeNameListSlice10, supportedTypeListSlice10, DmGraphSupport::Supported, {1, 2, 3, 4}, std::nullopt, QuerySlice)}, {REG_INFO( 7, Pad, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 7, SpaceToDepth, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 7, DepthToSpace, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, @@ -473,7 +477,6 @@ const static OperatorRegistrationInformation operatorRegistrationInformationTabl {REG_INFO( 11, Scan, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 11, ScatterElements, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 11, ScatterND, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, - {REG_INFO( 11, Slice, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 11, Softmax, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 11, Split, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, {REG_INFO( 11, Squeeze, typeNameListDefault, supportedTypeListFloat16to32, DmGraphSupport::Supported)}, diff --git a/onnxruntime/core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h b/onnxruntime/core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h index 7fee23b8d0..a6615bd815 100644 --- a/onnxruntime/core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h +++ b/onnxruntime/core/providers/dml/OperatorAuthorHelper/MLOperatorAuthorHelper.h @@ -563,6 +563,20 @@ public: return m_impl->GetOutputCount(); } + // Returns true if an input to the operator is valid. + // This returns false for optional omitted inputs and invalid indices. + bool IsInputValid(uint32_t inputIndex) const noexcept + { + return m_impl->IsInputValid(inputIndex); + } + + // Returns true if an output to the operator is valid. + // This returns false for optional omitted inputs and invalid indices. + bool IsOutputValid(uint32_t inputIndex) const noexcept + { + return m_impl->IsOutputValid(inputIndex); + } + MLOperatorEdgeDescription GetInputEdgeDescription(uint32_t inputIndex) const { MLOperatorEdgeDescription ret; diff --git a/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.cpp b/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.cpp index 2d4526d71c..924df87537 100644 --- a/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.cpp +++ b/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.cpp @@ -32,21 +32,51 @@ void HandleNegativeAxes(gsl::span onnxAxes, uint32_t dimCount) } } -void FillWithLeadingValues(/*inout*/ std::vector& values, uint32_t minimumElementCount, uint32_t fillValue) +void ReadCpuLocalTensorIntoInt32( + const MLOperatorTensor& tensor, + std::vector& result + ) { - // e.g. - // input = [6,7] - // elementCount = 4 - // fillValue = 1 - // output = [1,1,6,7] + result.clear(); + ML_CHECK_VALID_ARGUMENT(tensor.IsCpuData(), "Tensor must be CPU Tensor."); - const size_t oldElementCount = values.size(); - const size_t newElementCount = std::max(size_t(minimumElementCount), oldElementCount); - const size_t fillCount = newElementCount - oldElementCount; + const std::vector& tensorDimensions = tensor.GetShape(); + const uint32_t elementCount = ComputeElementCountFromDimensions(tensorDimensions); - values.resize(newElementCount); - std::copy_backward(values.data(), values.data() + oldElementCount, values.data() + fillCount); - std::fill_n(values.data(), fillCount, fillValue); + switch (tensor.GetTensorDataType()) + { + case MLOperatorTensorDataType::Int32: + { + const int32_t* data = tensor.GetData(); + result.assign(data, data + elementCount); + } + break; + + case MLOperatorTensorDataType::Int64: + { + const int64_t* data = tensor.GetData(); + for (auto d : gsl::make_span(data, data + elementCount)) + { + result.push_back(gsl::narrow_cast(d)); + } + } + break; + + default: + ML_INVALID_ARGUMENT("Expecting CPU local tensor of type int32 or int64."); + break; + } +} + +void DowncastDimensions(gsl::span inputDimensions, std::vector& outputDimensions) +{ + outputDimensions.reserve(inputDimensions.size()); + outputDimensions.clear(); + + for (int64_t dim : inputDimensions) + { + outputDimensions.push_back(gsl::narrow_cast(std::clamp(dim, INT32_MIN, INT32_MAX))); + } } int64_t ReadAsInt64(MLOperatorTensorDataType tensorDataType, const void* p) @@ -1070,10 +1100,7 @@ int64_t ReadAsInt64(MLOperatorTensorDataType tensorDataType, const void* p) // First element of shape tensor is how many dims to expand to. std::vector desiredTensorShape; - for (int64_t dim : gsl::make_span(shapeData, dimCount)) - { - desiredTensorShape.push_back(gsl::narrow_cast(dim)); - } + DowncastDimensions(gsl::make_span(shapeData, dimCount), /*out*/ desiredTensorShape); // Determine the broadcasted input shape. outputDimensions = OperatorHelper::BroadcastTensorShape(actualInputTensorShape, desiredTensorShape); @@ -1098,10 +1125,7 @@ int64_t ReadAsInt64(MLOperatorTensorDataType tensorDataType, const void* p) // First element of shape tensor is how many dims to expand to. std::vector desiredTensorShape; - for (int64_t dim : gsl::make_span(shapeData, dimCount)) - { - desiredTensorShape.push_back(gsl::narrow_cast(dim)); - } + DowncastDimensions(gsl::make_span(shapeData, dimCount), /*out*/ desiredTensorShape); return { std::move(EdgeShapes(desiredTensorShape)) }; } diff --git a/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.h b/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.h index 6940ed3a85..e593c39cb2 100644 --- a/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.h +++ b/onnxruntime/core/providers/dml/OperatorAuthorHelper/OperatorHelper.h @@ -75,10 +75,28 @@ void RemoveValuesByIndex(gsl::span indices, bool keepOneValue, / values.resize(newValuesCount); } -void FillWithLeadingValues(/*inout*/ std::vector& values, uint32_t minimumElementCount, uint32_t fillValue); +template +void FillWithLeadingValues(/*inout*/ std::vector& values, uint32_t minimumElementCount, T fillValue) +{ + // e.g. + // input = [6,7] + // elementCount = 4 + // fillValue = 1 + // output = [1,1,6,7] + + const size_t oldElementCount = values.size(); + const size_t newElementCount = std::max(size_t(minimumElementCount), oldElementCount); + const size_t fillCount = newElementCount - oldElementCount; + + values.resize(newElementCount); + std::copy_backward(values.data(), values.data() + oldElementCount, values.data() + fillCount); + std::fill_n(values.data(), fillCount, fillValue); +} int64_t ReadAsInt64(MLOperatorTensorDataType tensorDataType, const void* p); +void ReadCpuLocalTensorIntoInt32(const MLOperatorTensor& tensor, std::vector& result); + class EdgeShapes { public: EdgeShapes() = default; @@ -503,56 +521,25 @@ class SplitHelper { class SliceHelperBase { public: - template + template void ReadIndexTensors( const Info_t& operatorInfo, - std::vector& starts, - std::vector& ends, - std::vector& axes, - std::vector& steps - ) + /*out*/ std::vector& starts, + /*out*/ std::vector& ends, + /*out*/ std::vector& axes, + /*out*/ std::vector& steps + ) { - // Get starts, ends, optional axes and optional steps from constant inputs. - MLOperatorTensor startsTensor = operatorInfo.GetConstantInputTensor(1); - const std::vector& startsTensorDimensions = startsTensor.GetShape(); - size_t dimCount = startsTensorDimensions[0]; - const Index_t* startsData = startsTensor.GetData(); - for (size_t i = 0; i < dimCount; ++i) + // Get starts, ends, optional axes, and optional steps from constant inputs. + ReadCpuLocalTensorIntoInt32(operatorInfo.GetConstantInputTensor(1), /*out*/ starts); + ReadCpuLocalTensorIntoInt32(operatorInfo.GetConstantInputTensor(2), /*out*/ ends); + if (operatorInfo.IsInputValid(3)) { - starts.push_back(gsl::narrow_cast(startsData[i])); + ReadCpuLocalTensorIntoInt32(operatorInfo.GetConstantInputTensor(3), /*out*/ axes); } - - MLOperatorTensor endsTensor = operatorInfo.GetConstantInputTensor(2); - const std::vector& endsTensorDimensions = endsTensor.GetShape(); - dimCount = endsTensorDimensions[0]; - const Index_t* endsData = endsTensor.GetData(); - for (size_t i = 0; i < dimCount; ++i) + if (operatorInfo.IsInputValid(4)) { - ends.push_back(gsl::narrow_cast(endsData[i])); - } - uint32_t inputCount = operatorInfo.GetInputCount(); - if (inputCount > 3) - { - MLOperatorTensor axesTensor = operatorInfo.GetConstantInputTensor(3); - const std::vector& axesTensorDimensions = axesTensor.GetShape(); - dimCount = axesTensorDimensions[0]; - const Index_t* axesData = axesTensor.GetData(); - for (size_t i = 0; i < dimCount; ++i) - { - axes.push_back(gsl::narrow_cast(axesData[i])); - } - } - - if (inputCount > 4) - { - MLOperatorTensor stepsTensor = operatorInfo.GetConstantInputTensor(4); - const std::vector& stepsTensorDimensions = stepsTensor.GetShape(); - dimCount = stepsTensorDimensions[0]; - const Index_t* stepsData = stepsTensor.GetData(); - for (size_t i = 0; i < dimCount; ++i) - { - steps.push_back(gsl::narrow_cast(stepsData[i])); - } + ReadCpuLocalTensorIntoInt32(operatorInfo.GetConstantInputTensor(4), /*out*/ steps); } } @@ -567,29 +554,23 @@ public: std::vector ends; std::vector axes; std::vector steps; + if (opsetVersion == 7) { - // Get starts, ends and axes from attributes + // Read starts, ends, and axes from attributes. starts = operatorInfo.GetOptionalAttributeVectorInt32(AttrName::Starts); ends = operatorInfo.GetOptionalAttributeVectorInt32(AttrName::Ends); axes = operatorInfo.GetOptionalAttributeVectorInt32(AttrName::Axes); } else if (opsetVersion == 10) { - if (operatorInfo.GetConstantInputTensor(1).GetTensorDataType() == MLOperatorTensorDataType::Int32) - { - ReadIndexTensors(operatorInfo, starts, ends, axes, steps); - } - else - { - THROW_HR_IF(E_INVALIDARG, operatorInfo.GetConstantInputTensor(1).GetTensorDataType() != MLOperatorTensorDataType::Int64); - ReadIndexTensors(operatorInfo, starts, ends, axes, steps); - } + // Read starts, ends, and axes from tensors. + ReadIndexTensors(operatorInfo, /*out*/ starts, /*out*/ ends, /*out*/ axes, /*out*/ steps); } - const uint32_t dimCount = gsl::narrow_cast(inputDimensions.size()); - HandleNegativeAxes(/*inout*/ axes, dimCount); - + const uint32_t inputDimensionCount = gsl::narrow_cast(inputDimensions.size()); + HandleNegativeAxes(/*inout*/ axes, inputDimensionCount); + ML_CHECK_VALID_ARGUMENT(starts.size() == ends.size(), "'starts' must equal 'ends' in size."); ML_CHECK_VALID_ARGUMENT(axes.empty() || starts.size() == axes.size(), "'axes' must equal 'starts' in size, or 'axes' must be empty."); @@ -604,18 +585,14 @@ public: // Clamp selected dimensions to given 'starts' and 'ends'. for (int i = 0, ci = gsl::narrow_cast(starts.size()); i < ci; ++i) { - int dimIndex = i; - if (!axes.empty()) - { - dimIndex = axes[i]; - } + int dimIndex = axes.empty() ? i : axes[i]; ML_CHECK_VALID_ARGUMENT(dimIndex < inputDimensions.size(), "'axes' must be valid with within actual input dimensions."); // Positive values are offsets from 0. // Negative values are offsets from the dimension's size. int dim = gsl::narrow_cast(inputDimensions[dimIndex]); - int start = (starts[i] < 0) ? (starts[i] + dim) : starts[i]; - int end = (ends[i] < 0) ? (ends[i] + dim) : ends[i]; + int start = (starts[i] < 0 && starts[i] > INT_MIN) ? (starts[i] + dim) : starts[i]; + int end = (ends[i] < 0 && ends[i] < INT_MAX) ? (ends[i] + dim) : ends[i]; // Clamp the dimensions to the slice extents. // Clamp negative numbers to 0, per case test_slice_start_out_of_bounds. @@ -1195,6 +1172,7 @@ using ShapeInferenceHelper_Transpose = TransposeHelper; using ShapeInferenceHelper_Concat = ConcatHelper; using ShapeInferenceHelper_Slice7 = SliceHelper; using ShapeInferenceHelper_Slice10 = Slice10Helper; +using ShapeInferenceHelper_Slice11 = Slice10Helper; // No functional change from 10. using ShapeInferenceHelper_Pad = PaddingHelper; using ShapeInferenceHelper_SpaceToDepth = SpaceToDepthHelper; using ShapeInferenceHelper_DepthToSpace = DepthToSpaceHelper;