Separates BindGroupLayoutInternal into it's own file.
- Updates direct decendants to use new header file.
- Follow CL(s) to remove most if not all proxy functions in
BindGroupLayout.h and further header cleanups.
Bug: dawn:1933
Change-Id: I20b1cd0713c4dfd0fab96220f74f27f22d007c5e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/144301
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 2f80783..c3f3e59 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -192,6 +192,8 @@
"BindGroup.h",
"BindGroupLayout.cpp",
"BindGroupLayout.h",
+ "BindGroupLayoutInternal.cpp",
+ "BindGroupLayoutInternal.h",
"BindGroupTracker.h",
"BindingInfo.cpp",
"BindingInfo.h",
diff --git a/src/dawn/native/BindGroupLayout.cpp b/src/dawn/native/BindGroupLayout.cpp
index d7dd201..58ad417 100644
--- a/src/dawn/native/BindGroupLayout.cpp
+++ b/src/dawn/native/BindGroupLayout.cpp
@@ -14,655 +14,10 @@
#include "dawn/native/BindGroupLayout.h"
-#include <algorithm>
-#include <functional>
-#include <limits>
-#include <set>
-#include <vector>
-
-#include "dawn/common/BitSetIterator.h"
-#include "dawn/native/ChainUtils.h"
-#include "dawn/native/Device.h"
-#include "dawn/native/ObjectBase.h"
-#include "dawn/native/ObjectContentHasher.h"
#include "dawn/native/ObjectType_autogen.h"
-#include "dawn/native/PerStage.h"
-#include "dawn/native/ValidationUtils_autogen.h"
namespace dawn::native {
-namespace {
-MaybeError ValidateStorageTextureFormat(DeviceBase* device,
- wgpu::TextureFormat storageTextureFormat) {
- const Format* format = nullptr;
- DAWN_TRY_ASSIGN(format, device->GetInternalFormat(storageTextureFormat));
-
- ASSERT(format != nullptr);
- DAWN_INVALID_IF(!format->supportsStorageUsage,
- "Texture format (%s) does not support storage textures.", storageTextureFormat);
-
- return {};
-}
-
-MaybeError ValidateStorageTextureViewDimension(wgpu::TextureViewDimension dimension) {
- switch (dimension) {
- case wgpu::TextureViewDimension::Cube:
- case wgpu::TextureViewDimension::CubeArray:
- return DAWN_VALIDATION_ERROR("%s texture views cannot be used as storage textures.",
- dimension);
-
- case wgpu::TextureViewDimension::e1D:
- case wgpu::TextureViewDimension::e2D:
- case wgpu::TextureViewDimension::e2DArray:
- case wgpu::TextureViewDimension::e3D:
- return {};
-
- case wgpu::TextureViewDimension::Undefined:
- break;
- }
- UNREACHABLE();
-}
-
-MaybeError ValidateBindGroupLayoutEntry(DeviceBase* device,
- const BindGroupLayoutEntry& entry,
- bool allowInternalBinding) {
- DAWN_TRY(ValidateShaderStage(entry.visibility));
-
- int bindingMemberCount = 0;
-
- if (entry.buffer.type != wgpu::BufferBindingType::Undefined) {
- bindingMemberCount++;
- const BufferBindingLayout& buffer = entry.buffer;
-
- // The kInternalStorageBufferBinding is used internally and not a value
- // in wgpu::BufferBindingType.
- if (buffer.type == kInternalStorageBufferBinding) {
- DAWN_INVALID_IF(!allowInternalBinding, "Internal binding types are disallowed");
- } else {
- DAWN_TRY(ValidateBufferBindingType(buffer.type));
- }
-
- if (buffer.type == wgpu::BufferBindingType::Storage ||
- buffer.type == kInternalStorageBufferBinding) {
- DAWN_INVALID_IF(
- entry.visibility & wgpu::ShaderStage::Vertex,
- "Read-write storage buffer binding is used with a visibility (%s) that contains %s "
- "(note that read-only storage buffer bindings are allowed).",
- entry.visibility, wgpu::ShaderStage::Vertex);
- }
- }
-
- if (entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
- bindingMemberCount++;
- DAWN_TRY(ValidateSamplerBindingType(entry.sampler.type));
- }
-
- if (entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
- bindingMemberCount++;
- const TextureBindingLayout& texture = entry.texture;
- // The kInternalResolveAttachmentSampleType is used internally and not a value
- // in wgpu::TextureSampleType.
- switch (texture.sampleType) {
- case kInternalResolveAttachmentSampleType:
- if (allowInternalBinding) {
- break;
- }
- // should return validation error.
- [[fallthrough]];
- default:
- DAWN_TRY(ValidateTextureSampleType(texture.sampleType));
- break;
- }
-
- // viewDimension defaults to 2D if left undefined, needs validation otherwise.
- wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::e2D;
- if (texture.viewDimension != wgpu::TextureViewDimension::Undefined) {
- DAWN_TRY(ValidateTextureViewDimension(texture.viewDimension));
- viewDimension = texture.viewDimension;
- }
-
- DAWN_INVALID_IF(texture.multisampled && viewDimension != wgpu::TextureViewDimension::e2D,
- "View dimension (%s) for a multisampled texture bindings was not %s.",
- viewDimension, wgpu::TextureViewDimension::e2D);
-
- DAWN_INVALID_IF(
- texture.multisampled && texture.sampleType == wgpu::TextureSampleType::Float,
- "Sample type for multisampled texture binding was %s.", wgpu::TextureSampleType::Float);
- }
-
- if (entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
- bindingMemberCount++;
- const StorageTextureBindingLayout& storageTexture = entry.storageTexture;
- DAWN_TRY(ValidateStorageTextureAccess(storageTexture.access));
- DAWN_TRY(ValidateStorageTextureFormat(device, storageTexture.format));
-
- // viewDimension defaults to 2D if left undefined, needs validation otherwise.
- if (storageTexture.viewDimension != wgpu::TextureViewDimension::Undefined) {
- DAWN_TRY(ValidateTextureViewDimension(storageTexture.viewDimension));
- DAWN_TRY(ValidateStorageTextureViewDimension(storageTexture.viewDimension));
- }
-
- if (storageTexture.access == wgpu::StorageTextureAccess::WriteOnly) {
- DAWN_INVALID_IF(entry.visibility & wgpu::ShaderStage::Vertex,
- "Write-only storage texture binding is used with a visibility (%s) "
- "that contains %s.",
- entry.visibility, wgpu::ShaderStage::Vertex);
- }
- }
-
- const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
- FindInChain(entry.nextInChain, &externalTextureBindingLayout);
- if (externalTextureBindingLayout != nullptr) {
- bindingMemberCount++;
- }
-
- DAWN_INVALID_IF(bindingMemberCount == 0,
- "BindGroupLayoutEntry had none of buffer, sampler, texture, "
- "storageTexture, or externalTexture set");
-
- DAWN_INVALID_IF(bindingMemberCount != 1,
- "BindGroupLayoutEntry had more than one of buffer, sampler, texture, "
- "storageTexture, or externalTexture set");
-
- return {};
-}
-
-BindGroupLayoutEntry CreateSampledTextureBindingForExternalTexture(uint32_t binding,
- wgpu::ShaderStage visibility) {
- BindGroupLayoutEntry entry;
- entry.binding = binding;
- entry.visibility = visibility;
- entry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
- entry.texture.multisampled = false;
- entry.texture.sampleType = wgpu::TextureSampleType::Float;
- return entry;
-}
-
-BindGroupLayoutEntry CreateUniformBindingForExternalTexture(uint32_t binding,
- wgpu::ShaderStage visibility) {
- BindGroupLayoutEntry entry;
- entry.binding = binding;
- entry.visibility = visibility;
- entry.buffer.hasDynamicOffset = false;
- entry.buffer.type = wgpu::BufferBindingType::Uniform;
- return entry;
-}
-
-std::vector<BindGroupLayoutEntry> ExtractAndExpandBglEntries(
- const BindGroupLayoutDescriptor* descriptor,
- BindingCounts* bindingCounts,
- ExternalTextureBindingExpansionMap* externalTextureBindingExpansions) {
- std::vector<BindGroupLayoutEntry> expandedOutput;
-
- // When new bgl entries are created, we use binding numbers larger than
- // kMaxBindingsPerBindGroup to ensure there are no collisions.
- uint32_t nextOpenBindingNumberForNewEntry = kMaxBindingsPerBindGroup;
- for (uint32_t i = 0; i < descriptor->entryCount; i++) {
- const BindGroupLayoutEntry& entry = descriptor->entries[i];
- const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
- FindInChain(entry.nextInChain, &externalTextureBindingLayout);
- // External textures are expanded from a texture_external into two sampled texture
- // bindings and one uniform buffer binding. The original binding number is used
- // for the first sampled texture.
- if (externalTextureBindingLayout != nullptr) {
- for (SingleShaderStage stage : IterateStages(entry.visibility)) {
- // External textures are not fully implemented, which means that expanding
- // the external texture at this time will not occupy the same number of
- // binding slots as defined in the WebGPU specification. Here we prematurely
- // increment the binding counts for an additional sampled textures and a
- // sampler so that an external texture will occupy the correct number of
- // slots for correct validation of shader binding limits.
- // TODO(dawn:1082): Consider removing this and instead making a change to
- // the validation.
- constexpr uint32_t kUnimplementedSampledTexturesPerExternalTexture = 2;
- constexpr uint32_t kUnimplementedSamplersPerExternalTexture = 1;
- bindingCounts->perStage[stage].sampledTextureCount +=
- kUnimplementedSampledTexturesPerExternalTexture;
- bindingCounts->perStage[stage].samplerCount +=
- kUnimplementedSamplersPerExternalTexture;
- }
-
- dawn_native::ExternalTextureBindingExpansion bindingExpansion;
-
- BindGroupLayoutEntry plane0Entry =
- CreateSampledTextureBindingForExternalTexture(entry.binding, entry.visibility);
- bindingExpansion.plane0 = BindingNumber(plane0Entry.binding);
- expandedOutput.push_back(plane0Entry);
-
- BindGroupLayoutEntry plane1Entry = CreateSampledTextureBindingForExternalTexture(
- nextOpenBindingNumberForNewEntry++, entry.visibility);
- bindingExpansion.plane1 = BindingNumber(plane1Entry.binding);
- expandedOutput.push_back(plane1Entry);
-
- BindGroupLayoutEntry paramsEntry = CreateUniformBindingForExternalTexture(
- nextOpenBindingNumberForNewEntry++, entry.visibility);
- bindingExpansion.params = BindingNumber(paramsEntry.binding);
- expandedOutput.push_back(paramsEntry);
-
- externalTextureBindingExpansions->insert(
- {BindingNumber(entry.binding), bindingExpansion});
- } else {
- expandedOutput.push_back(entry);
- }
- }
-
- return expandedOutput;
-}
-} // anonymous namespace
-
-MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor,
- bool allowInternalBinding) {
- DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr");
-
- std::set<BindingNumber> bindingsSet;
- BindingCounts bindingCounts = {};
-
- for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
- const BindGroupLayoutEntry& entry = descriptor->entries[i];
- BindingNumber bindingNumber = BindingNumber(entry.binding);
-
- DAWN_INVALID_IF(bindingNumber >= kMaxBindingsPerBindGroupTyped,
- "Binding number (%u) exceeds the maxBindingsPerBindGroup limit (%u).",
- uint32_t(bindingNumber), kMaxBindingsPerBindGroup);
- DAWN_INVALID_IF(bindingsSet.count(bindingNumber) != 0,
- "On entries[%u]: binding index (%u) was specified by a previous entry.", i,
- entry.binding);
-
- DAWN_TRY_CONTEXT(ValidateBindGroupLayoutEntry(device, entry, allowInternalBinding),
- "validating entries[%u]", i);
-
- IncrementBindingCounts(&bindingCounts, entry);
-
- bindingsSet.insert(bindingNumber);
- }
-
- DAWN_TRY_CONTEXT(ValidateBindingCounts(device->GetLimits(), bindingCounts),
- "validating binding counts");
-
- return {};
-}
-
-namespace {
-
-bool operator!=(const BindingInfo& a, const BindingInfo& b) {
- if (a.visibility != b.visibility || a.bindingType != b.bindingType) {
- return true;
- }
-
- switch (a.bindingType) {
- case BindingInfoType::Buffer:
- return a.buffer.type != b.buffer.type ||
- a.buffer.hasDynamicOffset != b.buffer.hasDynamicOffset ||
- a.buffer.minBindingSize != b.buffer.minBindingSize;
- case BindingInfoType::Sampler:
- return a.sampler.type != b.sampler.type;
- case BindingInfoType::Texture:
- return a.texture.sampleType != b.texture.sampleType ||
- a.texture.viewDimension != b.texture.viewDimension ||
- a.texture.multisampled != b.texture.multisampled;
- case BindingInfoType::StorageTexture:
- return a.storageTexture.access != b.storageTexture.access ||
- a.storageTexture.viewDimension != b.storageTexture.viewDimension ||
- a.storageTexture.format != b.storageTexture.format;
- case BindingInfoType::ExternalTexture:
- return false;
- }
- UNREACHABLE();
-}
-
-bool IsBufferBinding(const BindGroupLayoutEntry& binding) {
- return binding.buffer.type != wgpu::BufferBindingType::Undefined;
-}
-
-bool BindingHasDynamicOffset(const BindGroupLayoutEntry& binding) {
- if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
- return binding.buffer.hasDynamicOffset;
- }
- return false;
-}
-
-BindingInfo CreateBindGroupLayoutInfo(const BindGroupLayoutEntry& binding) {
- BindingInfo bindingInfo;
- bindingInfo.binding = BindingNumber(binding.binding);
- bindingInfo.visibility = binding.visibility;
-
- if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
- bindingInfo.bindingType = BindingInfoType::Buffer;
- bindingInfo.buffer = binding.buffer;
- } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
- bindingInfo.bindingType = BindingInfoType::Sampler;
- bindingInfo.sampler = binding.sampler;
- } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
- bindingInfo.bindingType = BindingInfoType::Texture;
- bindingInfo.texture = binding.texture;
-
- if (binding.texture.viewDimension == wgpu::TextureViewDimension::Undefined) {
- bindingInfo.texture.viewDimension = wgpu::TextureViewDimension::e2D;
- }
- } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
- bindingInfo.bindingType = BindingInfoType::StorageTexture;
- bindingInfo.storageTexture = binding.storageTexture;
-
- if (binding.storageTexture.viewDimension == wgpu::TextureViewDimension::Undefined) {
- bindingInfo.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D;
- }
- } else {
- const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
- FindInChain(binding.nextInChain, &externalTextureBindingLayout);
- if (externalTextureBindingLayout != nullptr) {
- bindingInfo.bindingType = BindingInfoType::ExternalTexture;
- }
- }
-
- return bindingInfo;
-}
-
-bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) {
- const bool aIsBuffer = IsBufferBinding(a);
- const bool bIsBuffer = IsBufferBinding(b);
- if (aIsBuffer != bIsBuffer) {
- // Always place buffers first.
- return aIsBuffer;
- }
-
- if (aIsBuffer) {
- bool aHasDynamicOffset = BindingHasDynamicOffset(a);
- bool bHasDynamicOffset = BindingHasDynamicOffset(b);
- ASSERT(bIsBuffer);
- if (aHasDynamicOffset != bHasDynamicOffset) {
- // Buffers with dynamic offsets should come before those without.
- // This makes it easy to iterate over the dynamic buffer bindings
- // [0, dynamicBufferCount) during validation.
- return aHasDynamicOffset;
- }
- if (aHasDynamicOffset) {
- ASSERT(bHasDynamicOffset);
- ASSERT(a.binding != b.binding);
- // Above, we ensured that dynamic buffers are first. Now, ensure that
- // dynamic buffer bindings are in increasing order. This is because dynamic
- // buffer offsets are applied in increasing order of binding number.
- return a.binding < b.binding;
- }
- }
-
- // This applies some defaults and gives us a single value to check for the binding type.
- BindingInfo aInfo = CreateBindGroupLayoutInfo(a);
- BindingInfo bInfo = CreateBindGroupLayoutInfo(b);
-
- // Sort by type.
- if (aInfo.bindingType != bInfo.bindingType) {
- return aInfo.bindingType < bInfo.bindingType;
- }
-
- if (a.visibility != b.visibility) {
- return a.visibility < b.visibility;
- }
-
- switch (aInfo.bindingType) {
- case BindingInfoType::Buffer:
- if (aInfo.buffer.minBindingSize != bInfo.buffer.minBindingSize) {
- return aInfo.buffer.minBindingSize < bInfo.buffer.minBindingSize;
- }
- break;
- case BindingInfoType::Sampler:
- if (aInfo.sampler.type != bInfo.sampler.type) {
- return aInfo.sampler.type < bInfo.sampler.type;
- }
- break;
- case BindingInfoType::Texture:
- if (aInfo.texture.multisampled != bInfo.texture.multisampled) {
- return aInfo.texture.multisampled < bInfo.texture.multisampled;
- }
- if (aInfo.texture.viewDimension != bInfo.texture.viewDimension) {
- return aInfo.texture.viewDimension < bInfo.texture.viewDimension;
- }
- if (aInfo.texture.sampleType != bInfo.texture.sampleType) {
- return aInfo.texture.sampleType < bInfo.texture.sampleType;
- }
- break;
- case BindingInfoType::StorageTexture:
- if (aInfo.storageTexture.access != bInfo.storageTexture.access) {
- return aInfo.storageTexture.access < bInfo.storageTexture.access;
- }
- if (aInfo.storageTexture.viewDimension != bInfo.storageTexture.viewDimension) {
- return aInfo.storageTexture.viewDimension < bInfo.storageTexture.viewDimension;
- }
- if (aInfo.storageTexture.format != bInfo.storageTexture.format) {
- return aInfo.storageTexture.format < bInfo.storageTexture.format;
- }
- break;
- case BindingInfoType::ExternalTexture:
- break;
- }
- return a.binding < b.binding;
-}
-
-// This is a utility function to help ASSERT that the BGL-binding comparator places buffers
-// first.
-bool CheckBufferBindingsFirst(ityp::span<BindingIndex, const BindingInfo> bindings) {
- BindingIndex lastBufferIndex{0};
- BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
- for (BindingIndex i{0}; i < bindings.size(); ++i) {
- if (bindings[i].bindingType == BindingInfoType::Buffer) {
- lastBufferIndex = std::max(i, lastBufferIndex);
- } else {
- firstNonBufferIndex = std::min(i, firstNonBufferIndex);
- }
- }
-
- // If there are no buffers, then |lastBufferIndex| is initialized to 0 and
- // |firstNonBufferIndex| gets set to 0.
- return firstNonBufferIndex >= lastBufferIndex;
-}
-
-} // namespace
-
-// BindGroupLayoutInternalBase
-
-BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(
- DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor,
- ApiObjectBase::UntrackedByDeviceTag tag)
- : ApiObjectBase(device, descriptor->label), mUnexpandedBindingCount(descriptor->entryCount) {
- std::vector<BindGroupLayoutEntry> sortedBindings = ExtractAndExpandBglEntries(
- descriptor, &mBindingCounts, &mExternalTextureBindingExpansionMap);
-
- std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare);
-
- for (uint32_t i = 0; i < sortedBindings.size(); ++i) {
- const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)];
-
- mBindingInfo.push_back(CreateBindGroupLayoutInfo(binding));
-
- if (IsBufferBinding(binding)) {
- // Buffers must be contiguously packed at the start of the binding info.
- ASSERT(GetBufferCount() == BindingIndex(i));
- }
- IncrementBindingCounts(&mBindingCounts, binding);
-
- const auto& [_, inserted] = mBindingMap.emplace(BindingNumber(binding.binding), i);
- ASSERT(inserted);
- }
- ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), GetBindingCount()}));
- ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped);
-}
-
-BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(
- DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor)
- : BindGroupLayoutInternalBase(device, descriptor, kUntrackedByDevice) {
- GetObjectTrackingList()->Track(this);
-}
-
-BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(DeviceBase* device,
- ObjectBase::ErrorTag tag,
- const char* label)
- : ApiObjectBase(device, tag, label) {}
-
-BindGroupLayoutInternalBase::~BindGroupLayoutInternalBase() = default;
-
-void BindGroupLayoutInternalBase::DestroyImpl() {
- Uncache();
-}
-
-ObjectType BindGroupLayoutInternalBase::GetType() const {
- return ObjectType::BindGroupLayout;
-}
-
-const BindingInfo& BindGroupLayoutInternalBase::GetBindingInfo(BindingIndex bindingIndex) const {
- ASSERT(!IsError());
- ASSERT(bindingIndex < mBindingInfo.size());
- return mBindingInfo[bindingIndex];
-}
-
-const BindGroupLayoutInternalBase::BindingMap& BindGroupLayoutInternalBase::GetBindingMap() const {
- ASSERT(!IsError());
- return mBindingMap;
-}
-
-bool BindGroupLayoutInternalBase::HasBinding(BindingNumber bindingNumber) const {
- return mBindingMap.count(bindingNumber) != 0;
-}
-
-BindingIndex BindGroupLayoutInternalBase::GetBindingIndex(BindingNumber bindingNumber) const {
- ASSERT(!IsError());
- const auto& it = mBindingMap.find(bindingNumber);
- ASSERT(it != mBindingMap.end());
- return it->second;
-}
-
-size_t BindGroupLayoutInternalBase::ComputeContentHash() {
- ObjectContentHasher recorder;
-
- // std::map is sorted by key, so two BGLs constructed in different orders
- // will still record the same.
- for (const auto [id, index] : mBindingMap) {
- recorder.Record(id, index);
-
- const BindingInfo& info = mBindingInfo[index];
- recorder.Record(info.buffer.hasDynamicOffset, info.visibility, info.bindingType,
- info.buffer.type, info.buffer.minBindingSize, info.sampler.type,
- info.texture.sampleType, info.texture.viewDimension,
- info.texture.multisampled, info.storageTexture.access,
- info.storageTexture.format, info.storageTexture.viewDimension);
- }
-
- return recorder.GetContentHash();
-}
-
-bool BindGroupLayoutInternalBase::EqualityFunc::operator()(
- const BindGroupLayoutInternalBase* a,
- const BindGroupLayoutInternalBase* b) const {
- return a->IsLayoutEqual(b);
-}
-
-BindingIndex BindGroupLayoutInternalBase::GetBindingCount() const {
- return mBindingInfo.size();
-}
-
-BindingIndex BindGroupLayoutInternalBase::GetBufferCount() const {
- return BindingIndex(mBindingCounts.bufferCount);
-}
-
-BindingIndex BindGroupLayoutInternalBase::GetDynamicBufferCount() const {
- // This is a binding index because dynamic buffers are packed at the front of the binding
- // info.
- return static_cast<BindingIndex>(mBindingCounts.dynamicStorageBufferCount +
- mBindingCounts.dynamicUniformBufferCount);
-}
-
-uint32_t BindGroupLayoutInternalBase::GetUnverifiedBufferCount() const {
- return mBindingCounts.unverifiedBufferCount;
-}
-
-uint32_t BindGroupLayoutInternalBase::GetExternalTextureBindingCount() const {
- return mExternalTextureBindingExpansionMap.size();
-}
-
-const BindingCounts& BindGroupLayoutInternalBase::GetBindingCountInfo() const {
- return mBindingCounts;
-}
-
-const ExternalTextureBindingExpansionMap&
-BindGroupLayoutInternalBase::GetExternalTextureBindingExpansionMap() const {
- return mExternalTextureBindingExpansionMap;
-}
-
-uint32_t BindGroupLayoutInternalBase::GetUnexpandedBindingCount() const {
- return mUnexpandedBindingCount;
-}
-
-bool BindGroupLayoutInternalBase::IsLayoutEqual(const BindGroupLayoutInternalBase* other) const {
- if (GetBindingCount() != other->GetBindingCount()) {
- return false;
- }
- for (BindingIndex i{0}; i < GetBindingCount(); ++i) {
- if (mBindingInfo[i] != other->mBindingInfo[i]) {
- return false;
- }
- }
- return mBindingMap == other->mBindingMap;
-}
-
-size_t BindGroupLayoutInternalBase::GetBindingDataSize() const {
- // | ------ buffer-specific ----------| ------------ object pointers -------------|
- // | --- offsets + sizes -------------| --------------- Ref<ObjectBase> ----------|
- // Followed by:
- // |---------buffer size array--------|
- // |-uint64_t[mUnverifiedBufferCount]-|
- size_t objectPointerStart = mBindingCounts.bufferCount * sizeof(BufferBindingData);
- ASSERT(IsAligned(objectPointerStart, alignof(Ref<ObjectBase>)));
- size_t bufferSizeArrayStart = Align(
- objectPointerStart + mBindingCounts.totalCount * sizeof(Ref<ObjectBase>), sizeof(uint64_t));
- ASSERT(IsAligned(bufferSizeArrayStart, alignof(uint64_t)));
- return bufferSizeArrayStart + mBindingCounts.unverifiedBufferCount * sizeof(uint64_t);
-}
-
-BindGroupLayoutInternalBase::BindingDataPointers
-BindGroupLayoutInternalBase::ComputeBindingDataPointers(void* dataStart) const {
- BufferBindingData* bufferData = reinterpret_cast<BufferBindingData*>(dataStart);
- auto bindings = reinterpret_cast<Ref<ObjectBase>*>(bufferData + mBindingCounts.bufferCount);
- uint64_t* unverifiedBufferSizes = AlignPtr(
- reinterpret_cast<uint64_t*>(bindings + mBindingCounts.totalCount), sizeof(uint64_t));
-
- ASSERT(IsPtrAligned(bufferData, alignof(BufferBindingData)));
- ASSERT(IsPtrAligned(bindings, alignof(Ref<ObjectBase>)));
- ASSERT(IsPtrAligned(unverifiedBufferSizes, alignof(uint64_t)));
-
- return {{bufferData, GetBufferCount()},
- {bindings, GetBindingCount()},
- {unverifiedBufferSizes, mBindingCounts.unverifiedBufferCount}};
-}
-
-bool BindGroupLayoutInternalBase::IsStorageBufferBinding(BindingIndex bindingIndex) const {
- ASSERT(bindingIndex < GetBufferCount());
- switch (GetBindingInfo(bindingIndex).buffer.type) {
- case wgpu::BufferBindingType::Uniform:
- return false;
- case kInternalStorageBufferBinding:
- case wgpu::BufferBindingType::Storage:
- case wgpu::BufferBindingType::ReadOnlyStorage:
- return true;
- case wgpu::BufferBindingType::Undefined:
- break;
- }
- UNREACHABLE();
-}
-
-std::string BindGroupLayoutInternalBase::EntriesToString() const {
- std::string entries = "[";
- std::string sep = "";
- const BindGroupLayoutInternalBase::BindingMap& bindingMap = GetBindingMap();
- for (const auto [bindingNumber, bindingIndex] : bindingMap) {
- const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
- entries += absl::StrFormat("%s%s", sep, bindingInfo);
- sep = ", ";
- }
- entries += "]";
- return entries;
-}
-
BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device,
const char* label,
Ref<BindGroupLayoutInternalBase> internal,
diff --git a/src/dawn/native/BindGroupLayout.h b/src/dawn/native/BindGroupLayout.h
index 6bf49bb..b0513c2 100644
--- a/src/dawn/native/BindGroupLayout.h
+++ b/src/dawn/native/BindGroupLayout.h
@@ -15,141 +15,13 @@
#ifndef SRC_DAWN_NATIVE_BINDGROUPLAYOUT_H_
#define SRC_DAWN_NATIVE_BINDGROUPLAYOUT_H_
-#include <algorithm>
-#include <bitset>
-#include <map>
#include <string>
-#include "dawn/common/Constants.h"
-#include "dawn/common/ContentLessObjectCacheable.h"
-#include "dawn/common/SlabAllocator.h"
-#include "dawn/common/ityp_span.h"
-#include "dawn/common/ityp_vector.h"
-#include "dawn/native/BindingInfo.h"
-#include "dawn/native/CachedObject.h"
-#include "dawn/native/Error.h"
-#include "dawn/native/Forward.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
+#include "dawn/native/IntegerTypes.h"
#include "dawn/native/ObjectBase.h"
-#include "dawn/native/dawn_platform.h"
-
namespace dawn::native {
-// TODO(dawn:1082): Minor optimization to use BindingIndex instead of BindingNumber
-struct ExternalTextureBindingExpansion {
- BindingNumber plane0;
- BindingNumber plane1;
- BindingNumber params;
-};
-
-using ExternalTextureBindingExpansionMap = std::map<BindingNumber, ExternalTextureBindingExpansion>;
-
-MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor,
- bool allowInternalBinding = false);
-
-// Bindings are specified as a |BindingNumber| in the BindGroupLayoutDescriptor.
-// These numbers may be arbitrary and sparse. Internally, Dawn packs these numbers
-// into a packed range of |BindingIndex| integers.
-class BindGroupLayoutInternalBase : public ApiObjectBase,
- public CachedObject,
- public ContentLessObjectCacheable<BindGroupLayoutInternalBase> {
- public:
- BindGroupLayoutInternalBase(DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor,
- ApiObjectBase::UntrackedByDeviceTag tag);
- BindGroupLayoutInternalBase(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor);
- ~BindGroupLayoutInternalBase() override;
-
- ObjectType GetType() const override;
-
- // A map from the BindingNumber to its packed BindingIndex.
- using BindingMap = std::map<BindingNumber, BindingIndex>;
-
- const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const;
- const BindingMap& GetBindingMap() const;
- bool HasBinding(BindingNumber bindingNumber) const;
- BindingIndex GetBindingIndex(BindingNumber bindingNumber) const;
-
- // Functions necessary for the unordered_set<BGLBase*>-based cache.
- size_t ComputeContentHash() override;
-
- struct EqualityFunc {
- bool operator()(const BindGroupLayoutInternalBase* a,
- const BindGroupLayoutInternalBase* b) const;
- };
-
- BindingIndex GetBindingCount() const;
- // Returns |BindingIndex| because buffers are packed at the front.
- BindingIndex GetBufferCount() const;
- // Returns |BindingIndex| because dynamic buffers are packed at the front.
- BindingIndex GetDynamicBufferCount() const;
- uint32_t GetUnverifiedBufferCount() const;
-
- // Used to get counts and validate them in pipeline layout creation. Other getters
- // should be used to get typed integer counts.
- const BindingCounts& GetBindingCountInfo() const;
-
- uint32_t GetExternalTextureBindingCount() const;
-
- // Used to specify unpacked external texture binding slots when transforming shader modules.
- const ExternalTextureBindingExpansionMap& GetExternalTextureBindingExpansionMap() const;
-
- uint32_t GetUnexpandedBindingCount() const;
-
- // Tests that the BindingInfo of two bind groups are equal.
- bool IsLayoutEqual(const BindGroupLayoutInternalBase* other) const;
-
- struct BufferBindingData {
- uint64_t offset;
- uint64_t size;
- };
-
- struct BindingDataPointers {
- ityp::span<BindingIndex, BufferBindingData> const bufferData = {};
- ityp::span<BindingIndex, Ref<ObjectBase>> const bindings = {};
- ityp::span<uint32_t, uint64_t> const unverifiedBufferSizes = {};
- };
-
- // Compute the amount of space / alignment required to store bindings for a bind group of
- // this layout.
- size_t GetBindingDataSize() const;
- static constexpr size_t GetBindingDataAlignment() {
- static_assert(alignof(Ref<ObjectBase>) <= alignof(BufferBindingData));
- return alignof(BufferBindingData);
- }
-
- BindingDataPointers ComputeBindingDataPointers(void* dataStart) const;
-
- bool IsStorageBufferBinding(BindingIndex bindingIndex) const;
-
- // Returns a detailed string representation of the layout entries for use in error messages.
- std::string EntriesToString() const;
-
- protected:
- void DestroyImpl() override;
-
- template <typename BindGroup>
- SlabAllocator<BindGroup> MakeFrontendBindGroupAllocator(size_t size) {
- return SlabAllocator<BindGroup>(
- size, // bytes
- Align(sizeof(BindGroup), GetBindingDataAlignment()) + GetBindingDataSize(), // size
- std::max(alignof(BindGroup), GetBindingDataAlignment()) // alignment
- );
- }
-
- private:
- BindGroupLayoutInternalBase(DeviceBase* device, ObjectBase::ErrorTag tag, const char* label);
-
- BindingCounts mBindingCounts = {};
- ityp::vector<BindingIndex, BindingInfo> mBindingInfo;
-
- // Map from BindGroupLayoutEntry.binding to packed indices.
- BindingMap mBindingMap;
-
- ExternalTextureBindingExpansionMap mExternalTextureBindingExpansionMap;
-
- uint32_t mUnexpandedBindingCount;
-};
// Wrapper passthrough frontend object that is essentially just a Ref to a backing
// BindGroupLayoutInternalBase and a pipeline compatibility token.
diff --git a/src/dawn/native/BindGroupLayoutInternal.cpp b/src/dawn/native/BindGroupLayoutInternal.cpp
new file mode 100644
index 0000000..eb851f8
--- /dev/null
+++ b/src/dawn/native/BindGroupLayoutInternal.cpp
@@ -0,0 +1,667 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn/native/BindGroupLayoutInternal.h"
+
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "dawn/common/BitSetIterator.h"
+#include "dawn/native/ChainUtils.h"
+#include "dawn/native/Device.h"
+#include "dawn/native/ObjectBase.h"
+#include "dawn/native/ObjectContentHasher.h"
+#include "dawn/native/ObjectType_autogen.h"
+#include "dawn/native/PerStage.h"
+#include "dawn/native/ValidationUtils_autogen.h"
+
+namespace dawn::native {
+
+namespace {
+MaybeError ValidateStorageTextureFormat(DeviceBase* device,
+ wgpu::TextureFormat storageTextureFormat) {
+ const Format* format = nullptr;
+ DAWN_TRY_ASSIGN(format, device->GetInternalFormat(storageTextureFormat));
+
+ ASSERT(format != nullptr);
+ DAWN_INVALID_IF(!format->supportsStorageUsage,
+ "Texture format (%s) does not support storage textures.", storageTextureFormat);
+
+ return {};
+}
+
+MaybeError ValidateStorageTextureViewDimension(wgpu::TextureViewDimension dimension) {
+ switch (dimension) {
+ case wgpu::TextureViewDimension::Cube:
+ case wgpu::TextureViewDimension::CubeArray:
+ return DAWN_VALIDATION_ERROR("%s texture views cannot be used as storage textures.",
+ dimension);
+
+ case wgpu::TextureViewDimension::e1D:
+ case wgpu::TextureViewDimension::e2D:
+ case wgpu::TextureViewDimension::e2DArray:
+ case wgpu::TextureViewDimension::e3D:
+ return {};
+
+ case wgpu::TextureViewDimension::Undefined:
+ break;
+ }
+ UNREACHABLE();
+}
+
+MaybeError ValidateBindGroupLayoutEntry(DeviceBase* device,
+ const BindGroupLayoutEntry& entry,
+ bool allowInternalBinding) {
+ DAWN_TRY(ValidateShaderStage(entry.visibility));
+
+ int bindingMemberCount = 0;
+
+ if (entry.buffer.type != wgpu::BufferBindingType::Undefined) {
+ bindingMemberCount++;
+ const BufferBindingLayout& buffer = entry.buffer;
+
+ // The kInternalStorageBufferBinding is used internally and not a value
+ // in wgpu::BufferBindingType.
+ if (buffer.type == kInternalStorageBufferBinding) {
+ DAWN_INVALID_IF(!allowInternalBinding, "Internal binding types are disallowed");
+ } else {
+ DAWN_TRY(ValidateBufferBindingType(buffer.type));
+ }
+
+ if (buffer.type == wgpu::BufferBindingType::Storage ||
+ buffer.type == kInternalStorageBufferBinding) {
+ DAWN_INVALID_IF(
+ entry.visibility & wgpu::ShaderStage::Vertex,
+ "Read-write storage buffer binding is used with a visibility (%s) that contains %s "
+ "(note that read-only storage buffer bindings are allowed).",
+ entry.visibility, wgpu::ShaderStage::Vertex);
+ }
+ }
+
+ if (entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
+ bindingMemberCount++;
+ DAWN_TRY(ValidateSamplerBindingType(entry.sampler.type));
+ }
+
+ if (entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
+ bindingMemberCount++;
+ const TextureBindingLayout& texture = entry.texture;
+ // The kInternalResolveAttachmentSampleType is used internally and not a value
+ // in wgpu::TextureSampleType.
+ switch (texture.sampleType) {
+ case kInternalResolveAttachmentSampleType:
+ if (allowInternalBinding) {
+ break;
+ }
+ // should return validation error.
+ [[fallthrough]];
+ default:
+ DAWN_TRY(ValidateTextureSampleType(texture.sampleType));
+ break;
+ }
+
+ // viewDimension defaults to 2D if left undefined, needs validation otherwise.
+ wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::e2D;
+ if (texture.viewDimension != wgpu::TextureViewDimension::Undefined) {
+ DAWN_TRY(ValidateTextureViewDimension(texture.viewDimension));
+ viewDimension = texture.viewDimension;
+ }
+
+ DAWN_INVALID_IF(texture.multisampled && viewDimension != wgpu::TextureViewDimension::e2D,
+ "View dimension (%s) for a multisampled texture bindings was not %s.",
+ viewDimension, wgpu::TextureViewDimension::e2D);
+
+ DAWN_INVALID_IF(
+ texture.multisampled && texture.sampleType == wgpu::TextureSampleType::Float,
+ "Sample type for multisampled texture binding was %s.", wgpu::TextureSampleType::Float);
+ }
+
+ if (entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
+ bindingMemberCount++;
+ const StorageTextureBindingLayout& storageTexture = entry.storageTexture;
+ DAWN_TRY(ValidateStorageTextureAccess(storageTexture.access));
+ DAWN_TRY(ValidateStorageTextureFormat(device, storageTexture.format));
+
+ // viewDimension defaults to 2D if left undefined, needs validation otherwise.
+ if (storageTexture.viewDimension != wgpu::TextureViewDimension::Undefined) {
+ DAWN_TRY(ValidateTextureViewDimension(storageTexture.viewDimension));
+ DAWN_TRY(ValidateStorageTextureViewDimension(storageTexture.viewDimension));
+ }
+
+ if (storageTexture.access == wgpu::StorageTextureAccess::WriteOnly) {
+ DAWN_INVALID_IF(entry.visibility & wgpu::ShaderStage::Vertex,
+ "Write-only storage texture binding is used with a visibility (%s) "
+ "that contains %s.",
+ entry.visibility, wgpu::ShaderStage::Vertex);
+ }
+ }
+
+ const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
+ FindInChain(entry.nextInChain, &externalTextureBindingLayout);
+ if (externalTextureBindingLayout != nullptr) {
+ bindingMemberCount++;
+ }
+
+ DAWN_INVALID_IF(bindingMemberCount == 0,
+ "BindGroupLayoutEntry had none of buffer, sampler, texture, "
+ "storageTexture, or externalTexture set");
+
+ DAWN_INVALID_IF(bindingMemberCount != 1,
+ "BindGroupLayoutEntry had more than one of buffer, sampler, texture, "
+ "storageTexture, or externalTexture set");
+
+ return {};
+}
+
+BindGroupLayoutEntry CreateSampledTextureBindingForExternalTexture(uint32_t binding,
+ wgpu::ShaderStage visibility) {
+ BindGroupLayoutEntry entry;
+ entry.binding = binding;
+ entry.visibility = visibility;
+ entry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+ entry.texture.multisampled = false;
+ entry.texture.sampleType = wgpu::TextureSampleType::Float;
+ return entry;
+}
+
+BindGroupLayoutEntry CreateUniformBindingForExternalTexture(uint32_t binding,
+ wgpu::ShaderStage visibility) {
+ BindGroupLayoutEntry entry;
+ entry.binding = binding;
+ entry.visibility = visibility;
+ entry.buffer.hasDynamicOffset = false;
+ entry.buffer.type = wgpu::BufferBindingType::Uniform;
+ return entry;
+}
+
+std::vector<BindGroupLayoutEntry> ExtractAndExpandBglEntries(
+ const BindGroupLayoutDescriptor* descriptor,
+ BindingCounts* bindingCounts,
+ ExternalTextureBindingExpansionMap* externalTextureBindingExpansions) {
+ std::vector<BindGroupLayoutEntry> expandedOutput;
+
+ // When new bgl entries are created, we use binding numbers larger than
+ // kMaxBindingsPerBindGroup to ensure there are no collisions.
+ uint32_t nextOpenBindingNumberForNewEntry = kMaxBindingsPerBindGroup;
+ for (uint32_t i = 0; i < descriptor->entryCount; i++) {
+ const BindGroupLayoutEntry& entry = descriptor->entries[i];
+ const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
+ FindInChain(entry.nextInChain, &externalTextureBindingLayout);
+ // External textures are expanded from a texture_external into two sampled texture
+ // bindings and one uniform buffer binding. The original binding number is used
+ // for the first sampled texture.
+ if (externalTextureBindingLayout != nullptr) {
+ for (SingleShaderStage stage : IterateStages(entry.visibility)) {
+ // External textures are not fully implemented, which means that expanding
+ // the external texture at this time will not occupy the same number of
+ // binding slots as defined in the WebGPU specification. Here we prematurely
+ // increment the binding counts for an additional sampled textures and a
+ // sampler so that an external texture will occupy the correct number of
+ // slots for correct validation of shader binding limits.
+ // TODO(dawn:1082): Consider removing this and instead making a change to
+ // the validation.
+ constexpr uint32_t kUnimplementedSampledTexturesPerExternalTexture = 2;
+ constexpr uint32_t kUnimplementedSamplersPerExternalTexture = 1;
+ bindingCounts->perStage[stage].sampledTextureCount +=
+ kUnimplementedSampledTexturesPerExternalTexture;
+ bindingCounts->perStage[stage].samplerCount +=
+ kUnimplementedSamplersPerExternalTexture;
+ }
+
+ dawn_native::ExternalTextureBindingExpansion bindingExpansion;
+
+ BindGroupLayoutEntry plane0Entry =
+ CreateSampledTextureBindingForExternalTexture(entry.binding, entry.visibility);
+ bindingExpansion.plane0 = BindingNumber(plane0Entry.binding);
+ expandedOutput.push_back(plane0Entry);
+
+ BindGroupLayoutEntry plane1Entry = CreateSampledTextureBindingForExternalTexture(
+ nextOpenBindingNumberForNewEntry++, entry.visibility);
+ bindingExpansion.plane1 = BindingNumber(plane1Entry.binding);
+ expandedOutput.push_back(plane1Entry);
+
+ BindGroupLayoutEntry paramsEntry = CreateUniformBindingForExternalTexture(
+ nextOpenBindingNumberForNewEntry++, entry.visibility);
+ bindingExpansion.params = BindingNumber(paramsEntry.binding);
+ expandedOutput.push_back(paramsEntry);
+
+ externalTextureBindingExpansions->insert(
+ {BindingNumber(entry.binding), bindingExpansion});
+ } else {
+ expandedOutput.push_back(entry);
+ }
+ }
+
+ return expandedOutput;
+}
+} // anonymous namespace
+
+MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ bool allowInternalBinding) {
+ DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr");
+
+ std::set<BindingNumber> bindingsSet;
+ BindingCounts bindingCounts = {};
+
+ for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
+ const BindGroupLayoutEntry& entry = descriptor->entries[i];
+ BindingNumber bindingNumber = BindingNumber(entry.binding);
+
+ DAWN_INVALID_IF(bindingNumber >= kMaxBindingsPerBindGroupTyped,
+ "Binding number (%u) exceeds the maxBindingsPerBindGroup limit (%u).",
+ uint32_t(bindingNumber), kMaxBindingsPerBindGroup);
+ DAWN_INVALID_IF(bindingsSet.count(bindingNumber) != 0,
+ "On entries[%u]: binding index (%u) was specified by a previous entry.", i,
+ entry.binding);
+
+ DAWN_TRY_CONTEXT(ValidateBindGroupLayoutEntry(device, entry, allowInternalBinding),
+ "validating entries[%u]", i);
+
+ IncrementBindingCounts(&bindingCounts, entry);
+
+ bindingsSet.insert(bindingNumber);
+ }
+
+ DAWN_TRY_CONTEXT(ValidateBindingCounts(device->GetLimits(), bindingCounts),
+ "validating binding counts");
+
+ return {};
+}
+
+namespace {
+
+bool operator!=(const BindingInfo& a, const BindingInfo& b) {
+ if (a.visibility != b.visibility || a.bindingType != b.bindingType) {
+ return true;
+ }
+
+ switch (a.bindingType) {
+ case BindingInfoType::Buffer:
+ return a.buffer.type != b.buffer.type ||
+ a.buffer.hasDynamicOffset != b.buffer.hasDynamicOffset ||
+ a.buffer.minBindingSize != b.buffer.minBindingSize;
+ case BindingInfoType::Sampler:
+ return a.sampler.type != b.sampler.type;
+ case BindingInfoType::Texture:
+ return a.texture.sampleType != b.texture.sampleType ||
+ a.texture.viewDimension != b.texture.viewDimension ||
+ a.texture.multisampled != b.texture.multisampled;
+ case BindingInfoType::StorageTexture:
+ return a.storageTexture.access != b.storageTexture.access ||
+ a.storageTexture.viewDimension != b.storageTexture.viewDimension ||
+ a.storageTexture.format != b.storageTexture.format;
+ case BindingInfoType::ExternalTexture:
+ return false;
+ }
+ UNREACHABLE();
+}
+
+bool IsBufferBinding(const BindGroupLayoutEntry& binding) {
+ return binding.buffer.type != wgpu::BufferBindingType::Undefined;
+}
+
+bool BindingHasDynamicOffset(const BindGroupLayoutEntry& binding) {
+ if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
+ return binding.buffer.hasDynamicOffset;
+ }
+ return false;
+}
+
+BindingInfo CreateBindGroupLayoutInfo(const BindGroupLayoutEntry& binding) {
+ BindingInfo bindingInfo;
+ bindingInfo.binding = BindingNumber(binding.binding);
+ bindingInfo.visibility = binding.visibility;
+
+ if (binding.buffer.type != wgpu::BufferBindingType::Undefined) {
+ bindingInfo.bindingType = BindingInfoType::Buffer;
+ bindingInfo.buffer = binding.buffer;
+ } else if (binding.sampler.type != wgpu::SamplerBindingType::Undefined) {
+ bindingInfo.bindingType = BindingInfoType::Sampler;
+ bindingInfo.sampler = binding.sampler;
+ } else if (binding.texture.sampleType != wgpu::TextureSampleType::Undefined) {
+ bindingInfo.bindingType = BindingInfoType::Texture;
+ bindingInfo.texture = binding.texture;
+
+ if (binding.texture.viewDimension == wgpu::TextureViewDimension::Undefined) {
+ bindingInfo.texture.viewDimension = wgpu::TextureViewDimension::e2D;
+ }
+ } else if (binding.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
+ bindingInfo.bindingType = BindingInfoType::StorageTexture;
+ bindingInfo.storageTexture = binding.storageTexture;
+
+ if (binding.storageTexture.viewDimension == wgpu::TextureViewDimension::Undefined) {
+ bindingInfo.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D;
+ }
+ } else {
+ const ExternalTextureBindingLayout* externalTextureBindingLayout = nullptr;
+ FindInChain(binding.nextInChain, &externalTextureBindingLayout);
+ if (externalTextureBindingLayout != nullptr) {
+ bindingInfo.bindingType = BindingInfoType::ExternalTexture;
+ }
+ }
+
+ return bindingInfo;
+}
+
+bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) {
+ const bool aIsBuffer = IsBufferBinding(a);
+ const bool bIsBuffer = IsBufferBinding(b);
+ if (aIsBuffer != bIsBuffer) {
+ // Always place buffers first.
+ return aIsBuffer;
+ }
+
+ if (aIsBuffer) {
+ bool aHasDynamicOffset = BindingHasDynamicOffset(a);
+ bool bHasDynamicOffset = BindingHasDynamicOffset(b);
+ ASSERT(bIsBuffer);
+ if (aHasDynamicOffset != bHasDynamicOffset) {
+ // Buffers with dynamic offsets should come before those without.
+ // This makes it easy to iterate over the dynamic buffer bindings
+ // [0, dynamicBufferCount) during validation.
+ return aHasDynamicOffset;
+ }
+ if (aHasDynamicOffset) {
+ ASSERT(bHasDynamicOffset);
+ ASSERT(a.binding != b.binding);
+ // Above, we ensured that dynamic buffers are first. Now, ensure that
+ // dynamic buffer bindings are in increasing order. This is because dynamic
+ // buffer offsets are applied in increasing order of binding number.
+ return a.binding < b.binding;
+ }
+ }
+
+ // This applies some defaults and gives us a single value to check for the binding type.
+ BindingInfo aInfo = CreateBindGroupLayoutInfo(a);
+ BindingInfo bInfo = CreateBindGroupLayoutInfo(b);
+
+ // Sort by type.
+ if (aInfo.bindingType != bInfo.bindingType) {
+ return aInfo.bindingType < bInfo.bindingType;
+ }
+
+ if (a.visibility != b.visibility) {
+ return a.visibility < b.visibility;
+ }
+
+ switch (aInfo.bindingType) {
+ case BindingInfoType::Buffer:
+ if (aInfo.buffer.minBindingSize != bInfo.buffer.minBindingSize) {
+ return aInfo.buffer.minBindingSize < bInfo.buffer.minBindingSize;
+ }
+ break;
+ case BindingInfoType::Sampler:
+ if (aInfo.sampler.type != bInfo.sampler.type) {
+ return aInfo.sampler.type < bInfo.sampler.type;
+ }
+ break;
+ case BindingInfoType::Texture:
+ if (aInfo.texture.multisampled != bInfo.texture.multisampled) {
+ return aInfo.texture.multisampled < bInfo.texture.multisampled;
+ }
+ if (aInfo.texture.viewDimension != bInfo.texture.viewDimension) {
+ return aInfo.texture.viewDimension < bInfo.texture.viewDimension;
+ }
+ if (aInfo.texture.sampleType != bInfo.texture.sampleType) {
+ return aInfo.texture.sampleType < bInfo.texture.sampleType;
+ }
+ break;
+ case BindingInfoType::StorageTexture:
+ if (aInfo.storageTexture.access != bInfo.storageTexture.access) {
+ return aInfo.storageTexture.access < bInfo.storageTexture.access;
+ }
+ if (aInfo.storageTexture.viewDimension != bInfo.storageTexture.viewDimension) {
+ return aInfo.storageTexture.viewDimension < bInfo.storageTexture.viewDimension;
+ }
+ if (aInfo.storageTexture.format != bInfo.storageTexture.format) {
+ return aInfo.storageTexture.format < bInfo.storageTexture.format;
+ }
+ break;
+ case BindingInfoType::ExternalTexture:
+ break;
+ }
+ return a.binding < b.binding;
+}
+
+// This is a utility function to help ASSERT that the BGL-binding comparator places buffers
+// first.
+bool CheckBufferBindingsFirst(ityp::span<BindingIndex, const BindingInfo> bindings) {
+ BindingIndex lastBufferIndex{0};
+ BindingIndex firstNonBufferIndex = std::numeric_limits<BindingIndex>::max();
+ for (BindingIndex i{0}; i < bindings.size(); ++i) {
+ if (bindings[i].bindingType == BindingInfoType::Buffer) {
+ lastBufferIndex = std::max(i, lastBufferIndex);
+ } else {
+ firstNonBufferIndex = std::min(i, firstNonBufferIndex);
+ }
+ }
+
+ // If there are no buffers, then |lastBufferIndex| is initialized to 0 and
+ // |firstNonBufferIndex| gets set to 0.
+ return firstNonBufferIndex >= lastBufferIndex;
+}
+
+} // namespace
+
+// BindGroupLayoutInternalBase
+
+BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(
+ DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ ApiObjectBase::UntrackedByDeviceTag tag)
+ : ApiObjectBase(device, descriptor->label), mUnexpandedBindingCount(descriptor->entryCount) {
+ std::vector<BindGroupLayoutEntry> sortedBindings = ExtractAndExpandBglEntries(
+ descriptor, &mBindingCounts, &mExternalTextureBindingExpansionMap);
+
+ std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare);
+
+ for (uint32_t i = 0; i < sortedBindings.size(); ++i) {
+ const BindGroupLayoutEntry& binding = sortedBindings[static_cast<uint32_t>(i)];
+
+ mBindingInfo.push_back(CreateBindGroupLayoutInfo(binding));
+
+ if (IsBufferBinding(binding)) {
+ // Buffers must be contiguously packed at the start of the binding info.
+ ASSERT(GetBufferCount() == BindingIndex(i));
+ }
+ IncrementBindingCounts(&mBindingCounts, binding);
+
+ const auto& [_, inserted] = mBindingMap.emplace(BindingNumber(binding.binding), i);
+ ASSERT(inserted);
+ }
+ ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), GetBindingCount()}));
+ ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped);
+}
+
+BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(
+ DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor)
+ : BindGroupLayoutInternalBase(device, descriptor, kUntrackedByDevice) {
+ GetObjectTrackingList()->Track(this);
+}
+
+BindGroupLayoutInternalBase::BindGroupLayoutInternalBase(DeviceBase* device,
+ ObjectBase::ErrorTag tag,
+ const char* label)
+ : ApiObjectBase(device, tag, label) {}
+
+BindGroupLayoutInternalBase::~BindGroupLayoutInternalBase() = default;
+
+void BindGroupLayoutInternalBase::DestroyImpl() {
+ Uncache();
+}
+
+ObjectType BindGroupLayoutInternalBase::GetType() const {
+ return ObjectType::BindGroupLayout;
+}
+
+const BindingInfo& BindGroupLayoutInternalBase::GetBindingInfo(BindingIndex bindingIndex) const {
+ ASSERT(!IsError());
+ ASSERT(bindingIndex < mBindingInfo.size());
+ return mBindingInfo[bindingIndex];
+}
+
+const BindGroupLayoutInternalBase::BindingMap& BindGroupLayoutInternalBase::GetBindingMap() const {
+ ASSERT(!IsError());
+ return mBindingMap;
+}
+
+bool BindGroupLayoutInternalBase::HasBinding(BindingNumber bindingNumber) const {
+ return mBindingMap.count(bindingNumber) != 0;
+}
+
+BindingIndex BindGroupLayoutInternalBase::GetBindingIndex(BindingNumber bindingNumber) const {
+ ASSERT(!IsError());
+ const auto& it = mBindingMap.find(bindingNumber);
+ ASSERT(it != mBindingMap.end());
+ return it->second;
+}
+
+size_t BindGroupLayoutInternalBase::ComputeContentHash() {
+ ObjectContentHasher recorder;
+
+ // std::map is sorted by key, so two BGLs constructed in different orders
+ // will still record the same.
+ for (const auto [id, index] : mBindingMap) {
+ recorder.Record(id, index);
+
+ const BindingInfo& info = mBindingInfo[index];
+ recorder.Record(info.buffer.hasDynamicOffset, info.visibility, info.bindingType,
+ info.buffer.type, info.buffer.minBindingSize, info.sampler.type,
+ info.texture.sampleType, info.texture.viewDimension,
+ info.texture.multisampled, info.storageTexture.access,
+ info.storageTexture.format, info.storageTexture.viewDimension);
+ }
+
+ return recorder.GetContentHash();
+}
+
+bool BindGroupLayoutInternalBase::EqualityFunc::operator()(
+ const BindGroupLayoutInternalBase* a,
+ const BindGroupLayoutInternalBase* b) const {
+ return a->IsLayoutEqual(b);
+}
+
+BindingIndex BindGroupLayoutInternalBase::GetBindingCount() const {
+ return mBindingInfo.size();
+}
+
+BindingIndex BindGroupLayoutInternalBase::GetBufferCount() const {
+ return BindingIndex(mBindingCounts.bufferCount);
+}
+
+BindingIndex BindGroupLayoutInternalBase::GetDynamicBufferCount() const {
+ // This is a binding index because dynamic buffers are packed at the front of the binding
+ // info.
+ return static_cast<BindingIndex>(mBindingCounts.dynamicStorageBufferCount +
+ mBindingCounts.dynamicUniformBufferCount);
+}
+
+uint32_t BindGroupLayoutInternalBase::GetUnverifiedBufferCount() const {
+ return mBindingCounts.unverifiedBufferCount;
+}
+
+uint32_t BindGroupLayoutInternalBase::GetExternalTextureBindingCount() const {
+ return mExternalTextureBindingExpansionMap.size();
+}
+
+const BindingCounts& BindGroupLayoutInternalBase::GetBindingCountInfo() const {
+ return mBindingCounts;
+}
+
+const ExternalTextureBindingExpansionMap&
+BindGroupLayoutInternalBase::GetExternalTextureBindingExpansionMap() const {
+ return mExternalTextureBindingExpansionMap;
+}
+
+uint32_t BindGroupLayoutInternalBase::GetUnexpandedBindingCount() const {
+ return mUnexpandedBindingCount;
+}
+
+bool BindGroupLayoutInternalBase::IsLayoutEqual(const BindGroupLayoutInternalBase* other) const {
+ if (GetBindingCount() != other->GetBindingCount()) {
+ return false;
+ }
+ for (BindingIndex i{0}; i < GetBindingCount(); ++i) {
+ if (mBindingInfo[i] != other->mBindingInfo[i]) {
+ return false;
+ }
+ }
+ return mBindingMap == other->mBindingMap;
+}
+
+size_t BindGroupLayoutInternalBase::GetBindingDataSize() const {
+ // | ------ buffer-specific ----------| ------------ object pointers -------------|
+ // | --- offsets + sizes -------------| --------------- Ref<ObjectBase> ----------|
+ // Followed by:
+ // |---------buffer size array--------|
+ // |-uint64_t[mUnverifiedBufferCount]-|
+ size_t objectPointerStart = mBindingCounts.bufferCount * sizeof(BufferBindingData);
+ ASSERT(IsAligned(objectPointerStart, alignof(Ref<ObjectBase>)));
+ size_t bufferSizeArrayStart = Align(
+ objectPointerStart + mBindingCounts.totalCount * sizeof(Ref<ObjectBase>), sizeof(uint64_t));
+ ASSERT(IsAligned(bufferSizeArrayStart, alignof(uint64_t)));
+ return bufferSizeArrayStart + mBindingCounts.unverifiedBufferCount * sizeof(uint64_t);
+}
+
+BindGroupLayoutInternalBase::BindingDataPointers
+BindGroupLayoutInternalBase::ComputeBindingDataPointers(void* dataStart) const {
+ BufferBindingData* bufferData = reinterpret_cast<BufferBindingData*>(dataStart);
+ auto bindings = reinterpret_cast<Ref<ObjectBase>*>(bufferData + mBindingCounts.bufferCount);
+ uint64_t* unverifiedBufferSizes = AlignPtr(
+ reinterpret_cast<uint64_t*>(bindings + mBindingCounts.totalCount), sizeof(uint64_t));
+
+ ASSERT(IsPtrAligned(bufferData, alignof(BufferBindingData)));
+ ASSERT(IsPtrAligned(bindings, alignof(Ref<ObjectBase>)));
+ ASSERT(IsPtrAligned(unverifiedBufferSizes, alignof(uint64_t)));
+
+ return {{bufferData, GetBufferCount()},
+ {bindings, GetBindingCount()},
+ {unverifiedBufferSizes, mBindingCounts.unverifiedBufferCount}};
+}
+
+bool BindGroupLayoutInternalBase::IsStorageBufferBinding(BindingIndex bindingIndex) const {
+ ASSERT(bindingIndex < GetBufferCount());
+ switch (GetBindingInfo(bindingIndex).buffer.type) {
+ case wgpu::BufferBindingType::Uniform:
+ return false;
+ case kInternalStorageBufferBinding:
+ case wgpu::BufferBindingType::Storage:
+ case wgpu::BufferBindingType::ReadOnlyStorage:
+ return true;
+ case wgpu::BufferBindingType::Undefined:
+ break;
+ }
+ UNREACHABLE();
+}
+
+std::string BindGroupLayoutInternalBase::EntriesToString() const {
+ std::string entries = "[";
+ std::string sep = "";
+ const BindGroupLayoutInternalBase::BindingMap& bindingMap = GetBindingMap();
+ for (const auto [bindingNumber, bindingIndex] : bindingMap) {
+ const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
+ entries += absl::StrFormat("%s%s", sep, bindingInfo);
+ sep = ", ";
+ }
+ entries += "]";
+ return entries;
+}
+
+} // namespace dawn::native
diff --git a/src/dawn/native/BindGroupLayoutInternal.h b/src/dawn/native/BindGroupLayoutInternal.h
new file mode 100644
index 0000000..ec5cf8d
--- /dev/null
+++ b/src/dawn/native/BindGroupLayoutInternal.h
@@ -0,0 +1,156 @@
+// Copyright 2023 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_NATIVE_BINDGROUPLAYOUTINTERNAL_H_
+#define SRC_DAWN_NATIVE_BINDGROUPLAYOUTINTERNAL_H_
+
+#include <algorithm>
+#include <bitset>
+#include <map>
+#include <string>
+
+#include "dawn/common/Constants.h"
+#include "dawn/common/ContentLessObjectCacheable.h"
+#include "dawn/common/SlabAllocator.h"
+#include "dawn/common/ityp_span.h"
+#include "dawn/common/ityp_vector.h"
+#include "dawn/native/BindingInfo.h"
+#include "dawn/native/CachedObject.h"
+#include "dawn/native/Error.h"
+#include "dawn/native/Forward.h"
+#include "dawn/native/ObjectBase.h"
+
+#include "dawn/native/dawn_platform.h"
+
+namespace dawn::native {
+// TODO(dawn:1082): Minor optimization to use BindingIndex instead of BindingNumber
+struct ExternalTextureBindingExpansion {
+ BindingNumber plane0;
+ BindingNumber plane1;
+ BindingNumber params;
+};
+
+using ExternalTextureBindingExpansionMap = std::map<BindingNumber, ExternalTextureBindingExpansion>;
+
+MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ bool allowInternalBinding = false);
+
+// Bindings are specified as a |BindingNumber| in the BindGroupLayoutDescriptor.
+// These numbers may be arbitrary and sparse. Internally, Dawn packs these numbers
+// into a packed range of |BindingIndex| integers.
+class BindGroupLayoutInternalBase : public ApiObjectBase,
+ public CachedObject,
+ public ContentLessObjectCacheable<BindGroupLayoutInternalBase> {
+ public:
+ BindGroupLayoutInternalBase(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ ApiObjectBase::UntrackedByDeviceTag tag);
+ BindGroupLayoutInternalBase(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor);
+ ~BindGroupLayoutInternalBase() override;
+
+ ObjectType GetType() const override;
+
+ // A map from the BindingNumber to its packed BindingIndex.
+ using BindingMap = std::map<BindingNumber, BindingIndex>;
+
+ const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const;
+ const BindingMap& GetBindingMap() const;
+ bool HasBinding(BindingNumber bindingNumber) const;
+ BindingIndex GetBindingIndex(BindingNumber bindingNumber) const;
+
+ // Functions necessary for the unordered_set<BGLBase*>-based cache.
+ size_t ComputeContentHash() override;
+
+ struct EqualityFunc {
+ bool operator()(const BindGroupLayoutInternalBase* a,
+ const BindGroupLayoutInternalBase* b) const;
+ };
+
+ BindingIndex GetBindingCount() const;
+ // Returns |BindingIndex| because buffers are packed at the front.
+ BindingIndex GetBufferCount() const;
+ // Returns |BindingIndex| because dynamic buffers are packed at the front.
+ BindingIndex GetDynamicBufferCount() const;
+ uint32_t GetUnverifiedBufferCount() const;
+
+ // Used to get counts and validate them in pipeline layout creation. Other getters
+ // should be used to get typed integer counts.
+ const BindingCounts& GetBindingCountInfo() const;
+
+ uint32_t GetExternalTextureBindingCount() const;
+
+ // Used to specify unpacked external texture binding slots when transforming shader modules.
+ const ExternalTextureBindingExpansionMap& GetExternalTextureBindingExpansionMap() const;
+
+ uint32_t GetUnexpandedBindingCount() const;
+
+ // Tests that the BindingInfo of two bind groups are equal.
+ bool IsLayoutEqual(const BindGroupLayoutInternalBase* other) const;
+
+ struct BufferBindingData {
+ uint64_t offset;
+ uint64_t size;
+ };
+
+ struct BindingDataPointers {
+ ityp::span<BindingIndex, BufferBindingData> const bufferData = {};
+ ityp::span<BindingIndex, Ref<ObjectBase>> const bindings = {};
+ ityp::span<uint32_t, uint64_t> const unverifiedBufferSizes = {};
+ };
+
+ // Compute the amount of space / alignment required to store bindings for a bind group of
+ // this layout.
+ size_t GetBindingDataSize() const;
+ static constexpr size_t GetBindingDataAlignment() {
+ static_assert(alignof(Ref<ObjectBase>) <= alignof(BufferBindingData));
+ return alignof(BufferBindingData);
+ }
+
+ BindingDataPointers ComputeBindingDataPointers(void* dataStart) const;
+
+ bool IsStorageBufferBinding(BindingIndex bindingIndex) const;
+
+ // Returns a detailed string representation of the layout entries for use in error messages.
+ std::string EntriesToString() const;
+
+ protected:
+ void DestroyImpl() override;
+
+ template <typename BindGroup>
+ SlabAllocator<BindGroup> MakeFrontendBindGroupAllocator(size_t size) {
+ return SlabAllocator<BindGroup>(
+ size, // bytes
+ Align(sizeof(BindGroup), GetBindingDataAlignment()) + GetBindingDataSize(), // size
+ std::max(alignof(BindGroup), GetBindingDataAlignment()) // alignment
+ );
+ }
+
+ private:
+ BindGroupLayoutInternalBase(DeviceBase* device, ObjectBase::ErrorTag tag, const char* label);
+
+ BindingCounts mBindingCounts = {};
+ ityp::vector<BindingIndex, BindingInfo> mBindingInfo;
+
+ // Map from BindGroupLayoutEntry.binding to packed indices.
+ BindingMap mBindingMap;
+
+ ExternalTextureBindingExpansionMap mExternalTextureBindingExpansionMap;
+
+ uint32_t mUnexpandedBindingCount;
+};
+
+} // namespace dawn::native
+
+#endif // SRC_DAWN_NATIVE_BINDGROUPLAYOUT_H_
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 6809257..3c2a231 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -44,6 +44,8 @@
"BindGroup.h"
"BindGroupLayout.cpp"
"BindGroupLayout.h"
+ "BindGroupLayoutInternal.cpp"
+ "BindGroupLayoutInternal.h"
"BindGroupTracker.h"
"BindingInfo.cpp"
"BindingInfo.h"
diff --git a/src/dawn/native/d3d11/BindGroupLayoutD3D11.h b/src/dawn/native/d3d11/BindGroupLayoutD3D11.h
index 74ddf96..59d45d2 100644
--- a/src/dawn/native/d3d11/BindGroupLayoutD3D11.h
+++ b/src/dawn/native/d3d11/BindGroupLayoutD3D11.h
@@ -16,7 +16,7 @@
#define SRC_DAWN_NATIVE_D3D11_BINDGROUPLAYOUTD3D11_H_
#include "dawn/common/SlabAllocator.h"
-#include "dawn/native/BindGroupLayout.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/d3d11/BindGroupD3D11.h"
namespace dawn::native::d3d11 {
diff --git a/src/dawn/native/d3d12/BindGroupLayoutD3D12.h b/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
index 4d8799b..d5df9ff 100644
--- a/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
+++ b/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
@@ -17,10 +17,9 @@
#include <vector>
-#include "dawn/native/BindGroupLayout.h"
-
#include "dawn/common/SlabAllocator.h"
#include "dawn/common/ityp_stack_vec.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/d3d12/BindGroupD3D12.h"
#include "dawn/native/d3d12/d3d12_platform.h"
diff --git a/src/dawn/native/metal/BindGroupLayoutMTL.h b/src/dawn/native/metal/BindGroupLayoutMTL.h
index 1126c1a..8c859a7 100644
--- a/src/dawn/native/metal/BindGroupLayoutMTL.h
+++ b/src/dawn/native/metal/BindGroupLayoutMTL.h
@@ -16,7 +16,7 @@
#define SRC_DAWN_NATIVE_METAL_BINDGROUPLAYOUTMTL_H_
#include "dawn/common/SlabAllocator.h"
-#include "dawn/native/BindGroupLayout.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
namespace dawn::native::metal {
diff --git a/src/dawn/native/null/DeviceNull.h b/src/dawn/native/null/DeviceNull.h
index 3f110ec..97b0bb2 100644
--- a/src/dawn/native/null/DeviceNull.h
+++ b/src/dawn/native/null/DeviceNull.h
@@ -19,7 +19,7 @@
#include <vector>
#include "dawn/native/BindGroup.h"
-#include "dawn/native/BindGroupLayout.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/Buffer.h"
#include "dawn/native/CommandBuffer.h"
#include "dawn/native/CommandEncoder.h"
diff --git a/src/dawn/native/opengl/BindGroupLayoutGL.h b/src/dawn/native/opengl/BindGroupLayoutGL.h
index 18b1d4e..b77229a 100644
--- a/src/dawn/native/opengl/BindGroupLayoutGL.h
+++ b/src/dawn/native/opengl/BindGroupLayoutGL.h
@@ -16,7 +16,7 @@
#define SRC_DAWN_NATIVE_OPENGL_BINDGROUPLAYOUTGL_H_
#include "dawn/common/SlabAllocator.h"
-#include "dawn/native/BindGroupLayout.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/opengl/BindGroupGL.h"
namespace dawn::native::opengl {
diff --git a/src/dawn/native/vulkan/BindGroupLayoutVk.h b/src/dawn/native/vulkan/BindGroupLayoutVk.h
index f78903a..1aa6ff2 100644
--- a/src/dawn/native/vulkan/BindGroupLayoutVk.h
+++ b/src/dawn/native/vulkan/BindGroupLayoutVk.h
@@ -17,10 +17,9 @@
#include <vector>
-#include "dawn/native/BindGroupLayout.h"
-
#include "dawn/common/SlabAllocator.h"
#include "dawn/common/vulkan_platform.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
#include "dawn/native/vulkan/BindGroupVk.h"
namespace dawn::native {