[dawn][native] Add BGL extension and validation for bindless. Bug: 435251399 Change-Id: I822a830e4b5d4ee76bb475e3be2d47e557703ae7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/256215 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Loko Kung <lokokung@google.com> Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json index 0d0d96b..c9798dc 100644 --- a/src/dawn/dawn.json +++ b/src/dawn/dawn.json
@@ -512,12 +512,39 @@ {"name": "storage texture", "type": "storage texture binding layout", "default": "zero"} ] }, + + "dynamic binding kind": { + "category": "enum", + "tags": ["dawn"], + "values": [ + {"value": 0, "name": "undefined", "jsrepr": "undefined", "valid": false}, + {"value": 1, "name": "sampled texture"} + ] + }, + "dynamic binding array layout": { + "category": "structure", + "tags": ["dawn"], + "extensible": "in", + "members": [ + {"name": "start", "type": "uint32_t", "default": 0}, + {"name": "kind", "type": "dynamic binding kind"} + ] + }, + "bind group layout dynamic binding array": { + "category": "structure", + "tags": ["dawn"], + "chained": "in", + "chain roots": ["bind group layout descriptor"], + "members": [ + {"name": "dynamic array", "type": "dynamic binding array layout"} + ] + }, "bind group layout descriptor": { "category": "structure", "extensible": "in", "members": [ {"name": "label", "type": "string view", "optional": true}, - {"name": "entry count", "type": "size_t"}, + {"name": "entry count", "type": "size_t", "default": 0}, {"name": "entries", "type": "bind group layout entry", "annotation": "const*", "length": "entry count"} ] }, @@ -3902,7 +3929,8 @@ {"value": 70, "name": "dawn fake device initialize error for testing", "tags": ["dawn"]}, {"value": 71, "name": "texture component swizzle descriptor", "tags": ["dawn"]}, {"value": 72, "name": "shared texture memory D3D11 begin state", "tags": ["dawn", "native"]}, - {"value": 73, "name": "dawn consume adapter descriptor", "tags": ["dawn"]} + {"value": 73, "name": "dawn consume adapter descriptor", "tags": ["dawn"]}, + {"value": 74, "name": "bind group layout dynamic binding array", "tags": ["dawn"]} ] }, "texture": {
diff --git a/src/dawn/native/BindGroupLayoutInternal.cpp b/src/dawn/native/BindGroupLayoutInternal.cpp index 1c454a1..92c135f 100644 --- a/src/dawn/native/BindGroupLayoutInternal.cpp +++ b/src/dawn/native/BindGroupLayoutInternal.cpp
@@ -280,7 +280,7 @@ MaybeError ValidateStaticSamplersWithTextureBindings( DeviceBase* device, - const BindGroupLayoutDescriptor* descriptor, + const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor, const std::map<BindingNumber, uint32_t>& bindingNumberToIndexMap) { // Map of texture binding number to static sampler binding number. std::map<BindingNumber, BindingNumber> textureToStaticSamplerBindingMap; @@ -322,9 +322,28 @@ } // anonymous namespace MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device, - const BindGroupLayoutDescriptor* descriptor, + const BindGroupLayoutDescriptor* descriptorChain, bool allowInternalBinding) { - DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr"); + UnpackedPtr<BindGroupLayoutDescriptor> descriptor; + DAWN_TRY_ASSIGN(descriptor, ValidateAndUnpack(descriptorChain)); + + // Handle the dynamic binding array first to also extract information needed to validate the + // rest of the bindings. + std::optional<BindingNumber> startOfDynamicArray = {}; + if (auto* dynamic = descriptor.Get<BindGroupLayoutDynamicBindingArray>()) { + DAWN_INVALID_IF(!device->HasFeature(Feature::ChromiumExperimentalBindless), + "Dynamic binding array used without the %s feature enabled.", + wgpu::FeatureName::ChromiumExperimentalBindless); + DAWN_INVALID_IF(dynamic->dynamicArray.nextInChain != nullptr, + "DynamicBindingArrayLayout::nextInChain must be nullptr"); + + startOfDynamicArray = BindingNumber(dynamic->dynamicArray.start); + DAWN_TRY(ValidateDynamicBindingKind(dynamic->dynamicArray.kind)); + + DAWN_INVALID_IF(startOfDynamicArray >= kMaxBindingsPerBindGroupTyped, + "dynamic array start (%u) exceeds the maxBindingsPerBindGroup limit (%u).", + startOfDynamicArray.value(), kMaxBindingsPerBindGroup); + } // Map of binding number to entry index. std::map<BindingNumber, uint32_t> bindingMap; @@ -338,7 +357,7 @@ DAWN_INVALID_IF( bindingNumber >= kMaxBindingsPerBindGroupTyped, "On entries[%u]: binding number (%u) exceeds the maxBindingsPerBindGroup limit (%u).", - i, uint32_t(bindingNumber), kMaxBindingsPerBindGroup); + i, bindingNumber, kMaxBindingsPerBindGroup); BindingNumber arraySize{1}; if (entry->bindingArraySize > 1) { @@ -357,6 +376,12 @@ kMaxBindingsPerBindGroupTyped); } + DAWN_INVALID_IF(startOfDynamicArray.has_value() && + bindingNumber + arraySize > startOfDynamicArray.value(), + "On entries[%u]: the range of binding used [%u, %u) conflicts with the " + "dynamic binding array that starts at binding %u.", + i, bindingNumber, bindingNumber + arraySize, startOfDynamicArray.value()); + // Check that the same binding is not set twice. bindingNumber + arraySize cannot overflow // as they are both smaller than kMaxBindingsPerBindGroupTyped. static_assert(kMaxBindingsPerBindGroup < std::numeric_limits<uint32_t>::max() / 2); @@ -448,7 +473,8 @@ ityp::vector<BindingIndex, BindingInfo> entries; ExternalTextureBindingExpansionMap externalTextureBindingExpansions; }; -ExpandedBindingInfo ConvertAndExpandBGLEntries(const BindGroupLayoutDescriptor* descriptor) { +ExpandedBindingInfo ConvertAndExpandBGLEntries( + const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor) { ExpandedBindingInfo result; // When new bgl entries are created, we use binding numbers larger than kMaxBindingsPerBindGroup @@ -550,9 +576,11 @@ BindGroupLayoutInternalBase::BindGroupLayoutInternalBase( DeviceBase* device, - const BindGroupLayoutDescriptor* descriptor, + const BindGroupLayoutDescriptor* descriptorChain, ApiObjectBase::UntrackedByDeviceTag tag) - : ApiObjectBase(device, descriptor->label) { + : ApiObjectBase(device, descriptorChain->label) { + UnpackedPtr<BindGroupLayoutDescriptor> descriptor = Unpack(descriptorChain); + ExpandedBindingInfo unpackedBindings = ConvertAndExpandBGLEntries(descriptor); mExternalTextureBindingExpansionMap = std::move(unpackedBindings.externalTextureBindingExpansions); @@ -611,6 +639,13 @@ UnpackedPtr<BindGroupLayoutEntry> entry = Unpack(&descriptor->entries[i]); IncrementBindingCounts(&mValidationBindingCounts, entry); } + + // Handle the dynamic binding array if there is one. + if (auto* dynamic = descriptor.Get<BindGroupLayoutDynamicBindingArray>()) { + mHasDynamicArray = true; + mDynamicArrayStart = BindingNumber(dynamic->dynamicArray.start); + mDynamicArrayKind = dynamic->dynamicArray.kind; + } } BindGroupLayoutInternalBase::BindGroupLayoutInternalBase( @@ -658,6 +693,18 @@ return it->second; } +bool BindGroupLayoutInternalBase::HasDynamicArray() const { + return mHasDynamicArray; +} + +BindingNumber BindGroupLayoutInternalBase::GetDynamicArrayStart() const { + return mDynamicArrayStart; +} + +wgpu::DynamicBindingKind BindGroupLayoutInternalBase::GetDynamicArrayKind() const { + return mDynamicArrayKind; +} + void BindGroupLayoutInternalBase::ReduceMemoryUsage() {} size_t BindGroupLayoutInternalBase::ComputeContentHash() { @@ -698,6 +745,8 @@ }); } + recorder.Record(mHasDynamicArray, mDynamicArrayStart, mDynamicArrayKind); + return recorder.GetContentHash(); } @@ -712,12 +761,21 @@ return false; } } - return a->mBindingMap == b->mBindingMap; + if (a->mBindingMap != b->mBindingMap) { + return false; + } + + if (a->mHasDynamicArray != b->mHasDynamicArray || + a->mDynamicArrayKind != b->mDynamicArrayKind || + a->mDynamicArrayStart != b->mDynamicArrayStart) { + return false; + } + return true; } bool BindGroupLayoutInternalBase::IsEmpty() const { DAWN_ASSERT(!IsError()); - return mBindingInfo.empty(); + return mBindingInfo.empty() && !mHasDynamicArray; } BindingIndex BindGroupLayoutInternalBase::GetBindingCount() const {
diff --git a/src/dawn/native/BindGroupLayoutInternal.h b/src/dawn/native/BindGroupLayoutInternal.h index 91a0211..65c6208 100644 --- a/src/dawn/native/BindGroupLayoutInternal.h +++ b/src/dawn/native/BindGroupLayoutInternal.h
@@ -80,11 +80,17 @@ // A map from the BindingNumber to its packed BindingIndex. using BindingMap = std::map<BindingNumber, BindingIndex>; + // Getters for static bindings const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const; const BindingMap& GetBindingMap() const; bool HasBinding(BindingNumber bindingNumber) const; BindingIndex GetBindingIndex(BindingNumber bindingNumber) const; + // Getters for the dynamic binding array. + bool HasDynamicArray() const; + BindingNumber GetDynamicArrayStart() const; + wgpu::DynamicBindingKind GetDynamicArrayKind() const; + // Signals it's an appropriate time to free unused memory. BindGroupLayout implementations often // have SlabAllocator<BindGroup> that need an external signal. virtual void ReduceMemoryUsage(); @@ -175,6 +181,11 @@ BindingCounts mValidationBindingCounts = {}; bool mNeedsCrossBindingValidation = false; + + // Information about the dynamic binding array part of the BGL. + bool mHasDynamicArray = false; + BindingNumber mDynamicArrayStart{0}; + wgpu::DynamicBindingKind mDynamicArrayKind = wgpu::DynamicBindingKind::Undefined; }; } // namespace dawn::native
diff --git a/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp b/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp index 284e3e5..176bed8 100644 --- a/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp +++ b/src/dawn/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -4344,5 +4344,109 @@ } } +class BindGroupValidationTest_ChromiumExperimentalBindless : public BindGroupValidationTest { + protected: + std::vector<wgpu::FeatureName> GetRequiredFeatures() override { + return {wgpu::FeatureName::ChromiumExperimentalBindless}; + } +}; + +// Control case where creating a dynamic binding array with the feature enabled is valid. +TEST_F(BindGroupValidationTest_ChromiumExperimentalBindless, SuccessWithFeatureEnabled) { + wgpu::BindGroupLayoutDynamicBindingArray dynamic; + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::SampledTexture; + + wgpu::BindGroupLayoutDescriptor desc; + desc.nextInChain = &dynamic; + + // No error is produced. + device.CreateBindGroupLayout(&desc); +} + +// Error case where creating a dynamic binding array with the feature disabled is an error. +TEST_F(BindGroupValidationTest, ErrorWithFeatureDisabled) { + wgpu::BindGroupLayoutDynamicBindingArray dynamic; + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::SampledTexture; + + wgpu::BindGroupLayoutDescriptor desc; + desc.nextInChain = &dynamic; + + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); +} + +// Check that using DynamicArrayKind::Undefined is an error. +TEST_F(BindGroupValidationTest_ChromiumExperimentalBindless, UndefinedArrayKind) { + wgpu::BindGroupLayoutDynamicBindingArray dynamic; + + wgpu::BindGroupLayoutDescriptor desc; + desc.nextInChain = &dynamic; + + // Control case: SampledTexture is a valid kind. + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::SampledTexture; + device.CreateBindGroupLayout(&desc); + + // Error case: Undefined is invalid. + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::Undefined; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); +} + +// Check that the start of the binding array must be less than maxBindingsPerBindGroup +TEST_F(BindGroupValidationTest_ChromiumExperimentalBindless, DynamicArrayStartLimit) { + wgpu::BindGroupLayoutDynamicBindingArray dynamic; + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::SampledTexture; + + wgpu::BindGroupLayoutDescriptor desc; + desc.nextInChain = &dynamic; + + // No error is produced if we are under the limit. + dynamic.dynamicArray.start = kMaxBindingsPerBindGroup - 1; + device.CreateBindGroupLayout(&desc); + + // Error case if we are at the limit. + dynamic.dynamicArray.start = kMaxBindingsPerBindGroup; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); + + // Error case if we are above the limit. + dynamic.dynamicArray.start = kMaxBindingsPerBindGroup + 1; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); +} + +// Check that conflicts of binding number are not allowed between dynamic and static bindings. +TEST_F(BindGroupValidationTest_ChromiumExperimentalBindless, ConflictWithStaticBindings) { + wgpu::BindGroupLayoutEntry entry; + entry.binding = 0; + entry.bindingArraySize = 0; + entry.texture.sampleType = wgpu::TextureSampleType::Float; + + wgpu::BindGroupLayoutDynamicBindingArray dynamic; + dynamic.dynamicArray.kind = wgpu::DynamicBindingKind::SampledTexture; + dynamic.dynamicArray.start = 3; + + wgpu::BindGroupLayoutDescriptor desc; + desc.nextInChain = &dynamic; + desc.entryCount = 1; + desc.entries = &entry; + + // Control case: the non-arrayed static binding is before the dynamic array. + entry.binding = 2; + entry.bindingArraySize = 1; + device.CreateBindGroupLayout(&desc); + + // Error case: the non-arrayed static binding is after the dynamic array. + entry.binding = 3; + entry.bindingArraySize = 1; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); + + // Control case: the arrayed static binding is before the dynamic array. + entry.binding = 0; + entry.bindingArraySize = 3; + device.CreateBindGroupLayout(&desc); + + // Error case: the arrayed static binding is after the dynamic array. + entry.binding = 0; + entry.bindingArraySize = 4; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&desc)); +} + } // anonymous namespace } // namespace dawn