blob: a87a91fc9811f5fc081a68d08af9dd7797fb7eda [file] [log] [blame]
// Copyright 2018 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/vulkan/BindGroupLayoutVk.h"
#include <map>
#include <utility>
#include "dawn/common/BitSetIterator.h"
#include "dawn/common/ityp_vector.h"
#include "dawn/native/CacheKey.h"
#include "dawn/native/vulkan/DescriptorSetAllocator.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/FencedDeleter.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
namespace dawn::native::vulkan {
namespace {
VkShaderStageFlags VulkanShaderStageFlags(wgpu::ShaderStage stages) {
VkShaderStageFlags flags = 0;
if (stages & wgpu::ShaderStage::Vertex) {
flags |= VK_SHADER_STAGE_VERTEX_BIT;
}
if (stages & wgpu::ShaderStage::Fragment) {
flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages & wgpu::ShaderStage::Compute) {
flags |= VK_SHADER_STAGE_COMPUTE_BIT;
}
return flags;
}
} // anonymous namespace
VkDescriptorType VulkanDescriptorType(const BindingInfo& bindingInfo) {
switch (bindingInfo.bindingType) {
case BindingInfoType::Buffer:
switch (bindingInfo.buffer.type) {
case wgpu::BufferBindingType::Uniform:
if (bindingInfo.buffer.hasDynamicOffset) {
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case wgpu::BufferBindingType::Storage:
case kInternalStorageBufferBinding:
case wgpu::BufferBindingType::ReadOnlyStorage:
if (bindingInfo.buffer.hasDynamicOffset) {
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
}
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
case wgpu::BufferBindingType::Undefined:
UNREACHABLE();
}
case BindingInfoType::Sampler:
return VK_DESCRIPTOR_TYPE_SAMPLER;
case BindingInfoType::Texture:
case BindingInfoType::ExternalTexture:
return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
case BindingInfoType::StorageTexture:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
}
UNREACHABLE();
}
// static
ResultOrError<Ref<BindGroupLayout>> BindGroupLayout::Create(
Device* device,
const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken) {
Ref<BindGroupLayout> bgl =
AcquireRef(new BindGroupLayout(device, descriptor, pipelineCompatibilityToken));
DAWN_TRY(bgl->Initialize());
return bgl;
}
MaybeError BindGroupLayout::Initialize() {
// Compute the bindings that will be chained in the DescriptorSetLayout create info. We add
// one entry per binding set. This might be optimized by computing continuous ranges of
// bindings of the same type.
ityp::vector<BindingIndex, VkDescriptorSetLayoutBinding> bindings;
bindings.reserve(GetBindingCount());
for (const auto& [_, bindingIndex] : GetBindingMap()) {
const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex);
VkDescriptorSetLayoutBinding vkBinding;
vkBinding.binding = static_cast<uint32_t>(bindingIndex);
vkBinding.descriptorType = VulkanDescriptorType(bindingInfo);
vkBinding.descriptorCount = 1;
vkBinding.stageFlags = VulkanShaderStageFlags(bindingInfo.visibility);
vkBinding.pImmutableSamplers = nullptr;
bindings.emplace_back(vkBinding);
}
VkDescriptorSetLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.bindingCount = static_cast<uint32_t>(bindings.size());
createInfo.pBindings = bindings.data();
// Record cache key information now since the createInfo is not stored.
mCacheKey.Record(createInfo);
Device* device = ToBackend(GetDevice());
DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout(device->GetVkDevice(), &createInfo,
nullptr, &*mHandle),
"CreateDescriptorSetLayout"));
// Compute the size of descriptor pools used for this layout.
std::map<VkDescriptorType, uint32_t> descriptorCountPerType;
for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) {
VkDescriptorType vulkanType = VulkanDescriptorType(GetBindingInfo(bindingIndex));
// map::operator[] will return 0 if the key doesn't exist.
descriptorCountPerType[vulkanType]++;
}
// TODO(enga): Consider deduping allocators for layouts with the same descriptor type
// counts.
mDescriptorSetAllocator =
DescriptorSetAllocator::Create(this, std::move(descriptorCountPerType));
SetLabelImpl();
return {};
}
BindGroupLayout::BindGroupLayout(DeviceBase* device,
const BindGroupLayoutDescriptor* descriptor,
PipelineCompatibilityToken pipelineCompatibilityToken)
: BindGroupLayoutBase(device, descriptor, pipelineCompatibilityToken),
mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {}
BindGroupLayout::~BindGroupLayout() = default;
void BindGroupLayout::DestroyImpl() {
BindGroupLayoutBase::DestroyImpl();
Device* device = ToBackend(GetDevice());
// DescriptorSetLayout aren't used by execution on the GPU and can be deleted at any time,
// so we can destroy mHandle immediately instead of using the FencedDeleter.
// (Swiftshader implements this wrong b/154522740).
// In practice, the GPU is done with all descriptor sets because bind group deallocation
// refs the bind group layout so that once the bind group is finished being used, we can
// recycle its descriptor set.
if (mHandle != VK_NULL_HANDLE) {
device->fn.DestroyDescriptorSetLayout(device->GetVkDevice(), mHandle, nullptr);
mHandle = VK_NULL_HANDLE;
}
mDescriptorSetAllocator = nullptr;
}
VkDescriptorSetLayout BindGroupLayout::GetHandle() const {
return mHandle;
}
ResultOrError<Ref<BindGroup>> BindGroupLayout::AllocateBindGroup(
Device* device,
const BindGroupDescriptor* descriptor) {
DescriptorSetAllocation descriptorSetAllocation;
DAWN_TRY_ASSIGN(descriptorSetAllocation, mDescriptorSetAllocator->Allocate());
return AcquireRef(mBindGroupAllocator.Allocate(device, descriptor, descriptorSetAllocation));
}
void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup,
DescriptorSetAllocation* descriptorSetAllocation) {
mDescriptorSetAllocator->Deallocate(descriptorSetAllocation);
mBindGroupAllocator.Deallocate(bindGroup);
}
void BindGroupLayout::SetLabelImpl() {
SetDebugName(ToBackend(GetDevice()), mHandle, "Dawn_BindGroupLayout", GetLabel());
}
} // namespace dawn::native::vulkan