Files
Vulkan-Samples/framework/core/buffer.h
T
2025-09-04 10:54:47 +08:00

274 lines
10 KiB
C++

/* Copyright (c) 2019-2025, Arm Limited and Contributors
* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#pragma once
#include "builder_base.h"
#include "common/vk_common.h"
#include "core/allocated.h"
namespace vkb
{
namespace core
{
template <vkb::BindingType bindingType>
class Buffer;
template <vkb::BindingType bindingType>
using BufferPtr = std::unique_ptr<Buffer<bindingType>>;
template <vkb::BindingType bindingType>
class Device;
template <vkb::BindingType bindingType>
struct BufferBuilder
: public vkb::allocated::BuilderBase<bindingType,
BufferBuilder<bindingType>,
typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateInfo, VkBufferCreateInfo>::type>
{
public:
using BufferCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateFlags, VkBufferCreateFlags>::type;
using BufferCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateInfo, VkBufferCreateInfo>::type;
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
using SharingModeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SharingMode, VkSharingMode>::type;
private:
using ParentType = vkb::allocated::BuilderBase<bindingType, BufferBuilder<bindingType>, BufferCreateInfoType>;
public:
BufferBuilder(DeviceSizeType size);
Buffer<bindingType> build(vkb::core::Device<bindingType> &device) const;
BufferPtr<bindingType> build_unique(vkb::core::Device<bindingType> &device) const;
BufferBuilder &with_flags(BufferCreateFlagsType flags);
BufferBuilder &with_usage(BufferUsageFlagsType usage);
};
using BufferBuilderC = BufferBuilder<vkb::BindingType::C>;
using BufferBuilderCpp = BufferBuilder<vkb::BindingType::Cpp>;
template <>
inline BufferBuilder<vkb::BindingType::Cpp>::BufferBuilder(vk::DeviceSize size) :
ParentType(BufferCreateInfoType{.size = size})
{
}
template <>
inline BufferBuilder<vkb::BindingType::C>::BufferBuilder(VkDeviceSize size) :
ParentType(VkBufferCreateInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0, size})
{}
template <vkb::BindingType bindingType>
inline Buffer<bindingType> BufferBuilder<bindingType>::build(vkb::core::Device<bindingType> &device) const
{
return Buffer<bindingType>{device, *this};
}
template <vkb::BindingType bindingType>
inline BufferPtr<bindingType> BufferBuilder<bindingType>::build_unique(vkb::core::Device<bindingType> &device) const
{
return std::make_unique<Buffer<bindingType>>(device, *this);
}
template <vkb::BindingType bindingType>
inline BufferBuilder<bindingType> &BufferBuilder<bindingType>::with_flags(BufferCreateFlagsType flags)
{
this->create_info.flags = flags;
return *this;
}
template <vkb::BindingType bindingType>
inline BufferBuilder<bindingType> &BufferBuilder<bindingType>::with_usage(BufferUsageFlagsType usage)
{
this->get_create_info().usage = usage;
return *this;
}
/*=========================================================*/
template <vkb::BindingType bindingType>
class Buffer
: public vkb::allocated::Allocated<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Buffer, VkBuffer>::type>
{
public:
using BufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Buffer, VkBuffer>::type;
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
private:
using ParentType = vkb::allocated::Allocated<bindingType, BufferType>;
public:
static Buffer<bindingType> create_staging_buffer(vkb::core::Device<bindingType> &device, DeviceSizeType size, const void *data);
template <typename T>
static Buffer create_staging_buffer(vkb::core::Device<bindingType> &device, std::vector<T> const &data);
template <typename T>
static Buffer create_staging_buffer(vkb::core::Device<bindingType> &device, const T &data);
Buffer() = delete;
Buffer(const Buffer &) = delete;
Buffer(Buffer &&other) = default;
Buffer &operator=(const Buffer &) = delete;
Buffer &operator=(Buffer &&) = default;
/**
* @brief Creates a buffer using VMA
* @param device A valid Vulkan device
* @param size The size in bytes of the buffer
* @param buffer_usage The usage flags for the VkBuffer
* @param memory_usage The memory usage of the buffer
* @param flags The allocation create flags
* @param queue_family_indices optional queue family indices
*/
// [[deprecated("Use the BufferBuilder ctor instead")]]
Buffer(vkb::core::Device<bindingType> &device,
DeviceSizeType size,
BufferUsageFlagsType buffer_usage,
VmaMemoryUsage memory_usage,
VmaAllocationCreateFlags flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
const std::vector<uint32_t> &queue_family_indices = {});
Buffer(vkb::core::Device<bindingType> &device, BufferBuilder<bindingType> const &builder);
~Buffer();
/**
* @return Return the buffer's device address (note: requires that the buffer has been created with the VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT usage fla)
*/
uint64_t get_device_address() const;
/**
* @return The size of the buffer
*/
DeviceSizeType get_size() const;
private:
static Buffer<vkb::BindingType::Cpp> create_staging_buffer_impl(vkb::core::DeviceCpp &device, vk::DeviceSize size, const void *data);
private:
vk::DeviceSize size = 0;
};
using BufferC = Buffer<vkb::BindingType::C>;
using BufferCpp = Buffer<vkb::BindingType::Cpp>;
template <vkb::BindingType bindingType>
template <typename T>
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, const T &data)
{
return create_staging_buffer(device, sizeof(T), &data);
}
template <vkb::BindingType bindingType>
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, DeviceSizeType size, const void *data)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return create_staging_buffer_impl(device, size, data);
}
else
{
BufferCpp buffer = create_staging_buffer_impl(reinterpret_cast<vkb::core::DeviceCpp &>(device), static_cast<vk::DeviceSize>(size), data);
return std::move(*reinterpret_cast<BufferC *>(&buffer));
}
}
template <vkb::BindingType bindingType>
inline BufferCpp Buffer<bindingType>::create_staging_buffer_impl(vkb::core::DeviceCpp &device, vk::DeviceSize size, const void *data)
{
BufferBuilderCpp builder(size);
builder.with_vma_flags(VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT)
.with_usage(vk::BufferUsageFlagBits::eTransferSrc);
BufferCpp result(device, builder);
if (data != nullptr)
{
result.update(data, size);
}
return result;
}
template <vkb::BindingType bindingType>
template <typename T>
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, std::vector<T> const &data)
{
return create_staging_buffer(device, data.size() * sizeof(T), data.data());
}
template <vkb::BindingType bindingType>
inline Buffer<bindingType>::Buffer(vkb::core::Device<bindingType> &device,
DeviceSizeType size,
BufferUsageFlagsType buffer_usage,
VmaMemoryUsage memory_usage,
VmaAllocationCreateFlags flags,
const std::vector<uint32_t> &queue_family_indices) :
Buffer(device,
BufferBuilder<bindingType>(size)
.with_usage(buffer_usage)
.with_vma_usage(memory_usage)
.with_vma_flags(flags)
.with_queue_families(queue_family_indices)
.with_implicit_sharing_mode())
{}
template <vkb::BindingType bindingType>
inline Buffer<bindingType>::Buffer(vkb::core::Device<bindingType> &device, const BufferBuilder<bindingType> &builder) :
ParentType(builder.get_allocation_create_info(), nullptr, &device), size(builder.get_create_info().size)
{
this->set_handle(this->create_buffer(builder.get_create_info()));
if (!builder.get_debug_name().empty())
{
this->set_debug_name(builder.get_debug_name());
}
}
template <vkb::BindingType bindingType>
inline Buffer<bindingType>::~Buffer()
{
this->destroy_buffer(this->get_handle());
}
template <vkb::BindingType bindingType>
inline uint64_t Buffer<bindingType>::get_device_address() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return this->get_device().get_handle().getBufferAddressKHR({.buffer = this->get_handle()});
}
else
{
return static_cast<vk::Device>(this->get_device().get_handle()).getBufferAddressKHR({.buffer = static_cast<vk::Buffer>(this->get_handle())});
}
}
template <vkb::BindingType bindingType>
inline typename Buffer<bindingType>::DeviceSizeType Buffer<bindingType>::get_size() const
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return size;
}
else
{
return static_cast<VkDeviceSize>(size);
}
}
} // namespace core
} // namespace vkb