refactor: implement new, more basic barrier system

This commit is contained in:
PancakeTAS 2025-09-01 18:04:11 +02:00
parent c3cccb4967
commit 5d9660c1eb
No known key found for this signature in database
7 changed files with 103 additions and 104 deletions

View file

@ -19,51 +19,6 @@
namespace LSFG::Utils {
///
/// Insert memory barriers for images in a command buffer.
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
class BarrierBuilder {
public:
/// Create a barrier builder.
BarrierBuilder(const Core::CommandBuffer& buffer)
: commandBuffer(&buffer) {
this->barriers.reserve(16); // this is performance critical
}
// Add a resource to the barrier builder.
BarrierBuilder& addR2W(Core::Image& image);
BarrierBuilder& addW2R(Core::Image& image);
// Add an optional resource to the barrier builder.
BarrierBuilder& addR2W(std::optional<Core::Image>& image) {
if (image.has_value()) this->addR2W(*image); return *this; }
BarrierBuilder& addW2R(std::optional<Core::Image>& image) {
if (image.has_value()) this->addW2R(*image); return *this; }
/// Add a list of resources to the barrier builder.
BarrierBuilder& addR2W(std::vector<Core::Image>& images) {
for (auto& image : images) this->addR2W(image); return *this; }
BarrierBuilder& addW2R(std::vector<Core::Image>& images) {
for (auto& image : images) this->addW2R(image); return *this; }
/// Add an array of resources to the barrier builder.
template<std::size_t N>
BarrierBuilder& addR2W(std::array<Core::Image, N>& images) {
for (auto& image : images) this->addR2W(image); return *this; }
template<std::size_t N>
BarrierBuilder& addW2R(std::array<Core::Image, N>& images) {
for (auto& image : images) this->addW2R(image); return *this; }
/// Finish building the barrier
void build() const;
private:
const Core::CommandBuffer* commandBuffer;
std::vector<VkImageMemoryBarrier2> barriers;
};
///
/// Upload a DDS file to a Vulkan image.
///

View file

@ -21,57 +21,6 @@
using namespace LSFG;
using namespace LSFG::Utils;
BarrierBuilder& BarrierBuilder::addR2W(Core::Image& image) {
this->barriers.emplace_back(VkImageMemoryBarrier2 {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.oldLayout = image.getLayout(),
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.image = image.handle(),
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
});
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
return *this;
}
BarrierBuilder& BarrierBuilder::addW2R(Core::Image& image) {
this->barriers.emplace_back(VkImageMemoryBarrier2 {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
.oldLayout = image.getLayout(),
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.image = image.handle(),
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
});
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
return *this;
}
void BarrierBuilder::build() const {
const VkDependencyInfo dependencyInfo = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = static_cast<uint32_t>(this->barriers.size()),
.pImageMemoryBarriers = this->barriers.data()
};
vkCmdPipelineBarrier2(this->commandBuffer->handle(), &dependencyInfo);
}
void Utils::uploadImage(const Core::Device& device, const Core::CommandPool& commandPool,
Core::Image& image, const std::string& path) {
// read image bytecode

View file

@ -50,7 +50,6 @@ namespace VK::Core {
/// Get the size of the buffer.
[[nodiscard]] auto getSize() const { return this->size; }
private:
std::shared_ptr<VkBuffer> buffer;
std::shared_ptr<VkDeviceMemory> memory;

View file

@ -1,7 +1,7 @@
#pragma once
#include "vk/core/commandpool.hpp"
#include "vk/core/descriptorset.hpp"
#include "vk/core/commandpool.hpp"
#include "vk/core/semaphore.hpp"
#include "vk/core/pipeline.hpp"
#include "vk/core/device.hpp"
@ -76,8 +76,27 @@ namespace VK::Core {
///
void bindDescriptorSet(const Pipeline& pipeline, const DescriptorSet& set) const;
// TODO: Method for inserting a pipeline barrier.
// TODO: Rework abstraction for barriers.
///
/// Insert memory barriers transitioning images into the general layout.
///
/// @param images Images to transition to general layout
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
void insertBarrier(const std::vector<VkImage>& images) const;
///
/// Insert memory barriers for images in the command buffer.
///
/// @param readableImages Images that will be transitioned from rw to read-only
/// @param writableImages Images that will be transitioned from read-only to rw
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
void insertBarrier(
const std::vector<VkImage>& readableImages,
const std::vector<VkImage>& writableImages) const;
// TODO: Method for copying a buffer to an image
// TODO: Method for clearing an image to a color

View file

@ -33,7 +33,7 @@ namespace VK::Core {
Image(const Device& device, VkExtent2D extent,
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM,
VkImageUsageFlags usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT);
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT); // TODO: get rid
///
/// Create the image with shared backing memory.

View file

@ -2,6 +2,7 @@
#include <vulkan/vulkan_core.h>
#include "vk/core/commandbuffer.hpp"
#include "vk/core/descriptorset.hpp"
#include "vk/core/commandpool.hpp"
#include "vk/core/semaphore.hpp"
#include "vk/core/pipeline.hpp"
@ -72,6 +73,79 @@ void CommandBuffer::bindDescriptorSet(const Pipeline& pipeline, const Descriptor
0, 1, &descriptorSetHandle, 0, nullptr);
}
void CommandBuffer::insertBarrier(
const std::vector<VkImage>& images) const {
if (*this->state != CommandBufferState::Recording)
throw std::logic_error("Command buffer is not in Recording state");
std::vector<VkImageMemoryBarrier2> barriers(images.size());
for (size_t i = 0; i < images.size(); i++) {
barriers[i] = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.image = images[i],
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
}
const VkDependencyInfo dependencyInfo = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = static_cast<uint32_t>(barriers.size()),
.pImageMemoryBarriers = barriers.data()
};
vkCmdPipelineBarrier2(*this->commandBuffer, &dependencyInfo);
}
void CommandBuffer::insertBarrier(
const std::vector<VkImage>& readableImages,
const std::vector<VkImage>& writableImages) const {
if (*this->state != CommandBufferState::Recording)
throw std::logic_error("Command buffer is not in Recording state");
// create barriers
const VkImageMemoryBarrier2 dummyBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const size_t totalImages =
readableImages.size() + writableImages.size();
std::vector<VkImageMemoryBarrier2> barriers(totalImages);
for (const auto& image : readableImages) {
VkImageMemoryBarrier2& barrier = barriers.emplace_back(dummyBarrier);
barrier.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
barrier.image = image;
}
for (const auto& image : writableImages) {
VkImageMemoryBarrier2& barrier = barriers.emplace_back(dummyBarrier);
barrier.srcAccessMask = VK_ACCESS_2_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT;
barrier.image = image;
}
// insert barriers
const VkDependencyInfo dependencyInfo = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = static_cast<uint32_t>(barriers.size()),
.pImageMemoryBarriers = barriers.data()
};
vkCmdPipelineBarrier2(*this->commandBuffer, &dependencyInfo);
}
void CommandBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z) const {
if (*this->state != CommandBufferState::Recording)
throw std::logic_error("Command buffer is not in Recording state");

View file

@ -1,13 +1,16 @@
#include <cstddef>
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "vk/core/descriptorset.hpp"
#include "vk/core/device.hpp"
#include "vk/core/descriptorpool.hpp"
#include "vk/core/descriptorset.hpp"
#include "vk/core/shadermodule.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
using namespace VK::Core;