| // Copyright 2020 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include "src/dawn/native/BindingInfo.h" | |
| #include <algorithm> | |
| #include "src/dawn/common/MatchVariant.h" | |
| #include "src/dawn/native/Adapter.h" | |
| #include "src/dawn/native/ChainUtils.h" | |
| #include "src/dawn/native/Limits.h" | |
| #include "src/dawn/native/Sampler.h" | |
| namespace dawn::native { | |
| BindingInfoType GetBindingInfoType(const BindingInfo& info) { | |
| return MatchVariant( | |
| info.bindingLayout, | |
| [](const BufferBindingInfo&) -> BindingInfoType { return BindingInfoType::Buffer; }, | |
| [](const SamplerBindingInfo&) -> BindingInfoType { return BindingInfoType::Sampler; }, | |
| [](const TextureBindingInfo&) -> BindingInfoType { return BindingInfoType::Texture; }, | |
| [](const StorageTextureBindingInfo&) -> BindingInfoType { | |
| return BindingInfoType::StorageTexture; | |
| }, | |
| [](const TexelBufferBindingInfo&) -> BindingInfoType { | |
| return BindingInfoType::TexelBuffer; | |
| }, | |
| [](const StaticSamplerBindingInfo&) -> BindingInfoType { | |
| return BindingInfoType::StaticSampler; | |
| }, | |
| [](const InputAttachmentBindingInfo&) -> BindingInfoType { | |
| return BindingInfoType::InputAttachment; | |
| }, | |
| [](const ExternalTextureBindingInfo&) -> BindingInfoType { | |
| return BindingInfoType::ExternalTexture; | |
| }); | |
| } | |
| BindingInfoType GetBindingInfoType(const BindGroupLayoutEntry* entry) { | |
| if (entry->buffer.type != wgpu::BufferBindingType::BindingNotUsed) { | |
| return BindingInfoType::Buffer; | |
| } else if (entry->texture.sampleType != wgpu::TextureSampleType::BindingNotUsed) { | |
| return BindingInfoType::Texture; | |
| } else if (entry->storageTexture.access != wgpu::StorageTextureAccess::BindingNotUsed) { | |
| return BindingInfoType::StorageTexture; | |
| } else if (entry->sampler.type != wgpu::SamplerBindingType::BindingNotUsed) { | |
| return BindingInfoType::Sampler; | |
| } | |
| UnpackedPtr<BindGroupLayoutEntry> unpacked = Unpack(entry); | |
| if (unpacked.Has<TexelBufferBindingLayout>()) { | |
| return BindingInfoType::TexelBuffer; | |
| } else if (unpacked.Has<ExternalTextureBindingLayout>()) { | |
| return BindingInfoType::ExternalTexture; | |
| } else if (unpacked.Has<StaticSamplerBindingLayout>()) { | |
| return BindingInfoType::StaticSampler; | |
| } | |
| DAWN_UNREACHABLE(); | |
| } | |
| void IncrementBindingCounts(BindingCounts* bindingCounts, | |
| const UnpackedPtr<BindGroupLayoutEntry>& entry) { | |
| uint32_t arraySize = std::max(1u, entry->bindingArraySize); | |
| bindingCounts->totalCount += arraySize; | |
| uint32_t PerStageBindingCounts::* perStageBindingCountMember = nullptr; | |
| if (entry->buffer.type != wgpu::BufferBindingType::BindingNotUsed) { | |
| bindingCounts->bufferCount += arraySize; | |
| const BufferBindingLayout& buffer = entry->buffer; | |
| if (buffer.minBindingSize == 0) { | |
| bindingCounts->unverifiedBufferCount += arraySize; | |
| } | |
| switch (buffer.type) { | |
| case wgpu::BufferBindingType::Uniform: | |
| if (buffer.hasDynamicOffset) { | |
| bindingCounts->dynamicUniformBufferCount += arraySize; | |
| } | |
| perStageBindingCountMember = &PerStageBindingCounts::uniformBufferCount; | |
| break; | |
| case wgpu::BufferBindingType::Storage: | |
| case kInternalStorageBufferBinding: | |
| case kInternalReadOnlyStorageBufferBinding: | |
| case wgpu::BufferBindingType::ReadOnlyStorage: | |
| if (buffer.hasDynamicOffset) { | |
| bindingCounts->dynamicStorageBufferCount += arraySize; | |
| } | |
| perStageBindingCountMember = &PerStageBindingCounts::storageBufferCount; | |
| break; | |
| case wgpu::BufferBindingType::BindingNotUsed: | |
| // Can't get here due to the enclosing if statement. | |
| case wgpu::BufferBindingType::Undefined: | |
| DAWN_UNREACHABLE(); | |
| break; | |
| } | |
| } else if (entry->sampler.type != wgpu::SamplerBindingType::BindingNotUsed) { | |
| perStageBindingCountMember = &PerStageBindingCounts::samplerCount; | |
| } else if (entry->texture.sampleType != wgpu::TextureSampleType::BindingNotUsed) { | |
| if (entry->texture.viewDimension == kInternalInputAttachmentDim) { | |
| // Internal use only. | |
| return; | |
| } else { | |
| perStageBindingCountMember = &PerStageBindingCounts::sampledTextureCount; | |
| } | |
| } else if (entry->storageTexture.access != wgpu::StorageTextureAccess::BindingNotUsed) { | |
| perStageBindingCountMember = &PerStageBindingCounts::storageTextureCount; | |
| } else if (entry.Has<TexelBufferBindingLayout>()) { | |
| perStageBindingCountMember = &PerStageBindingCounts::texelBufferCount; | |
| } else if (entry.Has<ExternalTextureBindingLayout>()) { | |
| perStageBindingCountMember = &PerStageBindingCounts::externalTextureCount; | |
| } else if (entry.Has<StaticSamplerBindingLayout>()) { | |
| ++bindingCounts->staticSamplerCount; | |
| perStageBindingCountMember = &PerStageBindingCounts::staticSamplerCount; | |
| } | |
| DAWN_ASSERT(perStageBindingCountMember != nullptr); | |
| for (SingleShaderStage stage : IterateStages(entry->visibility)) { | |
| (bindingCounts->perStage[stage].*perStageBindingCountMember) += arraySize; | |
| } | |
| } | |
| void AccumulateBindingCounts(BindingCounts* bindingCounts, const BindingCounts& rhs) { | |
| bindingCounts->totalCount += rhs.totalCount; | |
| bindingCounts->bufferCount += rhs.bufferCount; | |
| bindingCounts->unverifiedBufferCount += rhs.unverifiedBufferCount; | |
| bindingCounts->dynamicUniformBufferCount += rhs.dynamicUniformBufferCount; | |
| bindingCounts->dynamicStorageBufferCount += rhs.dynamicStorageBufferCount; | |
| for (SingleShaderStage stage : IterateStages(kAllStages)) { | |
| bindingCounts->perStage[stage].sampledTextureCount += | |
| rhs.perStage[stage].sampledTextureCount; | |
| bindingCounts->perStage[stage].samplerCount += rhs.perStage[stage].samplerCount; | |
| bindingCounts->perStage[stage].storageBufferCount += rhs.perStage[stage].storageBufferCount; | |
| bindingCounts->perStage[stage].storageTextureCount += | |
| rhs.perStage[stage].storageTextureCount; | |
| bindingCounts->perStage[stage].texelBufferCount += rhs.perStage[stage].texelBufferCount; | |
| bindingCounts->perStage[stage].uniformBufferCount += rhs.perStage[stage].uniformBufferCount; | |
| bindingCounts->perStage[stage].externalTextureCount += | |
| rhs.perStage[stage].externalTextureCount; | |
| bindingCounts->perStage[stage].staticSamplerCount += rhs.perStage[stage].staticSamplerCount; | |
| } | |
| } | |
| MaybeError ValidateBindingCounts(const CombinedLimits& limits, | |
| const BindingCounts& bindingCounts, | |
| const AdapterBase* adapter) { | |
| // Prevent combinations that are not supported (static sampler + external texture). | |
| for (SingleShaderStage stage : IterateStages(kAllStages)) { | |
| DAWN_INVALID_IF(bindingCounts.staticSamplerCount != 0 && | |
| bindingCounts.perStage[stage].externalTextureCount != 0, | |
| "Static samplers and external textures are used in the same pipeline."); | |
| } | |
| // Validation against the device limits. | |
| uint32_t maxDynamicUniformBuffersPerPipelineLayout = | |
| limits.v1.maxDynamicUniformBuffersPerPipelineLayout; | |
| DAWN_INVALID_IF( | |
| bindingCounts.dynamicUniformBufferCount > maxDynamicUniformBuffersPerPipelineLayout, | |
| "The number of dynamic uniform buffers (%u) exceeds the maximum per-pipeline-layout " | |
| "limit (%u).%s", | |
| bindingCounts.dynamicUniformBufferCount, maxDynamicUniformBuffersPerPipelineLayout, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, | |
| maxDynamicUniformBuffersPerPipelineLayout, | |
| bindingCounts.dynamicUniformBufferCount)); | |
| uint32_t maxDynamicStorageBuffersPerPipelineLayout = | |
| limits.v1.maxDynamicStorageBuffersPerPipelineLayout; | |
| DAWN_INVALID_IF( | |
| bindingCounts.dynamicStorageBufferCount > maxDynamicStorageBuffersPerPipelineLayout, | |
| "The number of dynamic storage buffers (%u) exceeds the maximum per-pipeline-layout " | |
| "limit (%u).%s", | |
| bindingCounts.dynamicStorageBufferCount, maxDynamicStorageBuffersPerPipelineLayout, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, | |
| maxDynamicStorageBuffersPerPipelineLayout, | |
| bindingCounts.dynamicStorageBufferCount)); | |
| uint32_t maxSampledTexturesPerShaderStage = limits.v1.maxSampledTexturesPerShaderStage; | |
| uint32_t maxSamplersPerShaderStage = limits.v1.maxSamplersPerShaderStage; | |
| uint32_t maxStorageBuffersInFragmentStage = limits.compat.maxStorageBuffersInFragmentStage; | |
| uint32_t maxStorageBuffersInVertexStage = limits.compat.maxStorageBuffersInVertexStage; | |
| uint32_t maxStorageBuffersPerShaderStage = limits.v1.maxStorageBuffersPerShaderStage; | |
| uint32_t maxUniformBuffersPerShaderStage = limits.v1.maxUniformBuffersPerShaderStage; | |
| uint32_t maxStorageTexturesInFragmentStage = limits.compat.maxStorageTexturesInFragmentStage; | |
| uint32_t maxStorageTexturesInVertexStage = limits.compat.maxStorageTexturesInVertexStage; | |
| uint32_t maxStorageTexturesPerShaderStage = limits.v1.maxStorageTexturesPerShaderStage; | |
| for (SingleShaderStage stage : IterateStages(kAllStages)) { | |
| uint32_t sampledTextureCount = bindingCounts.perStage[stage].sampledTextureCount; | |
| DAWN_INVALID_IF( | |
| sampledTextureCount > maxSampledTexturesPerShaderStage, | |
| "The number of sampled textures (%u) in the %s stage exceeds the maximum " | |
| "per-stage limit (%u).%s", | |
| sampledTextureCount, stage, maxSampledTexturesPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxSampledTexturesPerShaderStage, | |
| sampledTextureCount)); | |
| uint32_t externalTextureCount = bindingCounts.perStage[stage].externalTextureCount; | |
| uint32_t sampledTextureAndExternalTextureCount = | |
| sampledTextureCount + (externalTextureCount * kSampledTexturesPerExternalTexture); | |
| DAWN_INVALID_IF( | |
| sampledTextureAndExternalTextureCount > maxSampledTexturesPerShaderStage, | |
| "The combination of sampled textures (%u) and external textures (%u × %u) in the %s " | |
| "stage exceeds the maximum per-stage limit (%u).%s", | |
| sampledTextureCount, externalTextureCount, kSampledTexturesPerExternalTexture, stage, | |
| maxSampledTexturesPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxSampledTexturesPerShaderStage, | |
| sampledTextureCount)); | |
| uint32_t samplerCount = bindingCounts.perStage[stage].samplerCount; | |
| // TODO(crbug.com/dawn/2463): Account for static samplers here. | |
| DAWN_INVALID_IF( | |
| samplerCount > maxSamplersPerShaderStage, | |
| "The number of samplers (%u) in the %s stage exceeds the maximum per-stage limit " | |
| "(%u).%s", | |
| samplerCount, stage, maxSamplersPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxSamplersPerShaderStage, | |
| samplerCount)); | |
| uint32_t samplerAndExternalTextureCount = | |
| samplerCount + (externalTextureCount * kSamplersPerExternalTexture); | |
| // TODO(crbug.com/dawn/2463): Account for static samplers here. | |
| DAWN_INVALID_IF( | |
| samplerAndExternalTextureCount > maxSamplersPerShaderStage, | |
| "The combination of samplers (%u) and external textures (%u × %u) in the %s stage " | |
| "exceeds the maximum per-stage limit (%u).%s", | |
| samplerCount, externalTextureCount, kSamplersPerExternalTexture, stage, | |
| maxSamplersPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxSamplersPerShaderStage, | |
| samplerAndExternalTextureCount)); | |
| uint32_t storageBufferCount = bindingCounts.perStage[stage].storageBufferCount; | |
| DAWN_INVALID_IF( | |
| storageBufferCount > maxStorageBuffersPerShaderStage, | |
| "The number of storage buffers (%u) in the %s stage exceeds the maximum per-stage " | |
| "limit (%u).%s", | |
| storageBufferCount, stage, maxStorageBuffersPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxStorageBuffersPerShaderStage, | |
| storageBufferCount)); | |
| uint32_t storageTextureCount = bindingCounts.perStage[stage].storageTextureCount; | |
| DAWN_INVALID_IF( | |
| storageTextureCount > maxStorageTexturesPerShaderStage, | |
| "The number of storage textures (%u) in the %s stage exceeds the maximum per-stage " | |
| "limit (%u).%s", | |
| storageTextureCount, stage, maxStorageTexturesPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxStorageTexturesPerShaderStage, | |
| storageTextureCount)); | |
| uint32_t uniformBufferCount = bindingCounts.perStage[stage].uniformBufferCount; | |
| DAWN_INVALID_IF( | |
| uniformBufferCount > maxUniformBuffersPerShaderStage, | |
| "The number of uniform buffers (%u) in the %s stage exceeds the maximum per-stage " | |
| "limit (%u).%s", | |
| uniformBufferCount, stage, maxUniformBuffersPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxUniformBuffersPerShaderStage, | |
| uniformBufferCount)); | |
| uint32_t uniformBuffersAndExternalTextureCount = | |
| uniformBufferCount + (externalTextureCount * kUniformsPerExternalTexture); | |
| DAWN_INVALID_IF( | |
| uniformBuffersAndExternalTextureCount > maxUniformBuffersPerShaderStage, | |
| "The combination of uniform buffers (%u) and external textures (%u × %u) in the %s " | |
| "stage exceeds the maximum per-stage limit (%u).%s", | |
| uniformBufferCount, externalTextureCount, kUniformsPerExternalTexture, stage, | |
| maxUniformBuffersPerShaderStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().v1, maxUniformBuffersPerShaderStage, | |
| uniformBuffersAndExternalTextureCount)); | |
| switch (stage) { | |
| case SingleShaderStage::Fragment: | |
| DAWN_INVALID_IF(storageBufferCount > maxStorageBuffersInFragmentStage, | |
| "number of storage buffers used in fragment stage (%u) exceeds " | |
| "maxStorageBuffersInFragmentStage (%u).%s", | |
| storageBufferCount, maxStorageBuffersInFragmentStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().compat, | |
| maxStorageBuffersInFragmentStage, | |
| storageBufferCount)); | |
| DAWN_INVALID_IF(storageTextureCount > maxStorageTexturesInFragmentStage, | |
| "number of storage textures used in fragment stage (%u) exceeds " | |
| "maxStorageTexturesInFragmentStage (%u).%s", | |
| storageTextureCount, maxStorageTexturesInFragmentStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().compat, | |
| maxStorageTexturesInFragmentStage, | |
| storageTextureCount)); | |
| break; | |
| case SingleShaderStage::Vertex: | |
| DAWN_INVALID_IF(storageBufferCount > maxStorageBuffersInVertexStage, | |
| "number of storage buffers used in vertex stage (%u) exceeds " | |
| "maxStorageBuffersInVertexStage (%u).%s", | |
| storageBufferCount, maxStorageBuffersInVertexStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().compat, | |
| maxStorageBuffersInVertexStage, | |
| storageBufferCount)); | |
| DAWN_INVALID_IF(storageTextureCount > maxStorageTexturesInVertexStage, | |
| "number of storage textures used in vertex stage (%u) exceeds " | |
| "maxStorageTexturesInVertexStage (%u).%s", | |
| storageTextureCount, maxStorageTexturesInVertexStage, | |
| DAWN_INCREASE_LIMIT_MESSAGE(adapter->GetLimits().compat, | |
| maxStorageTexturesInVertexStage, | |
| storageTextureCount)); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return {}; | |
| } | |
| // BufferBindingInfo | |
| // static | |
| BufferBindingInfo BufferBindingInfo::From(const BufferBindingLayout& layout) { | |
| BufferBindingLayout defaultedLayout = layout.WithTrivialFrontendDefaults(); | |
| return {{ | |
| .type = defaultedLayout.type, | |
| .minBindingSize = defaultedLayout.minBindingSize, | |
| .hasDynamicOffset = defaultedLayout.hasDynamicOffset, | |
| }}; | |
| } | |
| // TextureBindingInfo | |
| // static | |
| TextureBindingInfo TextureBindingInfo::From(const TextureBindingLayout& layout) { | |
| TextureBindingLayout defaultedLayout = layout.WithTrivialFrontendDefaults(); | |
| return {{ | |
| .sampleType = defaultedLayout.sampleType, | |
| .viewDimension = defaultedLayout.viewDimension, | |
| .multisampled = defaultedLayout.multisampled, | |
| }}; | |
| } | |
| // TexelBufferBindingInfo | |
| // static | |
| TexelBufferBindingInfo TexelBufferBindingInfo::From(const TexelBufferBindingLayout& layout) { | |
| TexelBufferBindingLayout defaultedLayout = layout.WithTrivialFrontendDefaults(); | |
| return {{ | |
| .format = defaultedLayout.format, | |
| .access = defaultedLayout.access, | |
| }}; | |
| } | |
| // StorageTextureBindingInfo | |
| // static | |
| StorageTextureBindingInfo StorageTextureBindingInfo::From( | |
| const StorageTextureBindingLayout& layout) { | |
| StorageTextureBindingLayout defaultedLayout = layout.WithTrivialFrontendDefaults(); | |
| return {{ | |
| .format = defaultedLayout.format, | |
| .viewDimension = defaultedLayout.viewDimension, | |
| .access = defaultedLayout.access, | |
| }}; | |
| } | |
| // SamplerBindingInfo | |
| // static | |
| SamplerBindingInfo SamplerBindingInfo::From(const SamplerBindingLayout& layout) { | |
| SamplerBindingLayout defaultedLayout = layout.WithTrivialFrontendDefaults(); | |
| return {{ | |
| .type = defaultedLayout.type, | |
| }}; | |
| } | |
| // SamplerBindingInfo | |
| // static | |
| StaticSamplerBindingInfo StaticSamplerBindingInfo::From(const StaticSamplerBindingLayout& layout) { | |
| return { | |
| .sampler = layout.sampler, | |
| .use = (layout.sampledTextureBinding == WGPU_LIMIT_U32_UNDEFINED) | |
| ? StaticSamplerUse::Freestanding | |
| : StaticSamplerUse::SingleTextureYCbCr, | |
| }; | |
| } | |
| } // namespace dawn::native |