bugfixes and pipeline barriers

This commit is contained in:
PancakeTAS 2025-06-29 22:34:36 +02:00
parent dc11a64c0a
commit f724229ae4
No known key found for this signature in database
7 changed files with 146 additions and 51 deletions

View file

@ -38,6 +38,8 @@ namespace Vulkan::Core {
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
/// Get the format of the image.
[[nodiscard]] VkFormat getFormat() const { return this->format; }
/// Get the aspect flags of the image.
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
/// Trivially copyable, moveable and destructible
Image(const Image&) noexcept = default;
@ -52,6 +54,7 @@ namespace Vulkan::Core {
VkExtent2D extent;
VkFormat format;
VkImageAspectFlags aspectFlags;
};
}

View file

@ -0,0 +1,40 @@
#ifndef BARRIERS_HPP
#define BARRIERS_HPP
#include "core/commandbuffer.hpp"
#include "core/image.hpp"
#include <vector>
#include <vulkan/vulkan_core.h>
namespace Barriers {
///
/// Insert memory barriers for images in a command buffer.
///
/// @param buffer Command buffer to insert barriers into
/// @param r2wImages Images that are being read and will be written to
/// @param w2rImages Images that are being written to and will be read from
/// @param srcLayout Optional source layout for the images, defaults to VK_IMAGE_LAYOUT_GENERAL
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
void insertBarrier(
const Vulkan::Core::CommandBuffer& buffer,
const std::vector<Vulkan::Core::Image>& r2wImages,
const std::vector<Vulkan::Core::Image>& w2rImages,
std::optional<VkImageLayout> srcLayout = std::nullopt);
///
/// Insert a global memory barrier in a command buffer.
///
/// @param buffer Command buffer to insert the barrier into
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
void insertGlobalBarrier(const Vulkan::Core::CommandBuffer& buffer);
};
#endif // BARRIERS_HPP

View file

@ -15,6 +15,7 @@ DescriptorPool::DescriptorPool(const Device& device) {
}};
const VkDescriptorPoolCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.maxSets = 16384,
.poolSizeCount = static_cast<uint32_t>(pools.size()),
.pPoolSizes = pools.data()

View file

@ -7,7 +7,7 @@ using namespace Vulkan::Core;
Image::Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags)
: extent(extent), format(format) {
: extent(extent), format(format), aspectFlags(aspectFlags) {
// create image
const VkImageCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,

View file

@ -3,6 +3,7 @@
#include <optional>
#include <vector>
#include <vulkan/vulkan_core.h>
using namespace Vulkan;
@ -49,10 +50,15 @@ Device::Device(const Instance& instance) {
// create logical device
const float queuePriority{1.0F}; // highest priority
const VkPhysicalDeviceVulkan12Features features{
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.synchronization2 = VK_TRUE
};
const VkPhysicalDeviceVulkan12Features features12{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = &features13,
.timelineSemaphore = VK_TRUE,
.vulkanMemoryModel = VK_TRUE,
.vulkanMemoryModel = VK_TRUE
};
const VkDeviceQueueCreateInfo computeQueueDesc{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
@ -62,7 +68,7 @@ Device::Device(const Instance& instance) {
};
const VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &features,
.pNext = &features12,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &computeQueueDesc
};

View file

@ -10,6 +10,7 @@
#include "core/shadermodule.hpp"
#include "device.hpp"
#include "instance.hpp"
#include "utils/memorybarriers.hpp"
#include <algorithm>
#include <array>
@ -40,27 +41,22 @@ const static DataBuffer data{
};
int main() {
// initialize Vulkan
// initialize application
const Instance instance;
const Device device(instance);
const Core::DescriptorPool descriptorPool(device);
const Core::CommandPool commandPool(device);
// load shader
const Core::ShaderModule computeShader(device, "shaders/downsample.spv",
{ { 1, VK_DESCRIPTOR_TYPE_SAMPLER},
{ 1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE},
{ 7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE},
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER} });
// prepare render pass
const Core::CommandPool commandPool(device);
const Core::DescriptorPool descriptorPool(device);
const Core::Pipeline computePipeline(device, computeShader);
const Core::DescriptorSet descriptorSet(device, descriptorPool, computeShader);
// create shader inputs
const Core::Sampler sampler(device, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
const std::vector<Core::Image> images(1, Core::Image(
const std::vector<Core::Image> inputImages(1, Core::Image(
device, { 2560, 1411 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
@ -72,51 +68,22 @@ int main() {
const Core::Buffer buffer(device, dataVec.size(), dataVec,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
// create shader outputs
const std::vector<Core::Image> outputImages = {
Core::Image(
device, { 1280, 705 }, VK_FORMAT_R8G8B8A8_UNORM,
std::vector<Core::Image> outputImages;
outputImages.reserve(7);
for (size_t i = 0; i < 7; ++i)
outputImages.emplace_back(device,
VkExtent2D { .width = 2560U >> i, .height = 1411U >> i },
VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 640, 352 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 320, 176 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 160, 88 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 80, 44 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 40, 22 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
),
Core::Image(
device, { 20, 11 }, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT
)
};
VK_IMAGE_ASPECT_COLOR_BIT);
// load descriptor set
const Core::DescriptorSet descriptorSet(device, descriptorPool, computeShader);
descriptorSet.update(
device,
{
{{ VK_DESCRIPTOR_TYPE_SAMPLER, sampler }},
{{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, images[0] }},
{{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, inputImages[0] }},
{
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, outputImages[0] },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, outputImages[1] },
@ -137,6 +104,13 @@ int main() {
commandBuffer.begin();
// render
Barriers::insertBarrier(
commandBuffer,
inputImages,
outputImages,
VK_IMAGE_LAYOUT_UNDEFINED
);
computePipeline.bind(commandBuffer);
descriptorSet.bind(commandBuffer, computePipeline);
commandBuffer.dispatch(40, 23, 1);

View file

@ -0,0 +1,71 @@
#include "utils/memorybarriers.hpp"
#include <optional>
using namespace Barriers;
void Barriers::insertBarrier(
const Vulkan::Core::CommandBuffer& buffer,
const std::vector<Vulkan::Core::Image>& r2wImages,
const std::vector<Vulkan::Core::Image>& w2rImages,
std::optional<VkImageLayout> srcLayout) {
std::vector<VkImageMemoryBarrier2> barriers(r2wImages.size() + w2rImages.size());
size_t index = 0;
for (const auto& image : r2wImages) {
barriers[index++] = {
.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 = srcLayout.value_or(VK_IMAGE_LAYOUT_GENERAL),
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.image = image.handle(),
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
};
}
for (const auto& image : w2rImages) {
barriers[index++] = {
.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 = srcLayout.value_or(VK_IMAGE_LAYOUT_GENERAL),
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.image = image.handle(),
.subresourceRange = {
.aspectMask = image.getAspectFlags(),
.levelCount = 1,
.layerCount = 1
}
};
}
const VkDependencyInfo dependencyInfo = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = static_cast<uint32_t>(barriers.size()),
.pImageMemoryBarriers = barriers.data()
};
vkCmdPipelineBarrier2(buffer.handle(), &dependencyInfo);
}
void Barriers::insertGlobalBarrier(const Vulkan::Core::CommandBuffer& buffer) {
const VkMemoryBarrier2 barrier = {
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT
};
const VkDependencyInfo dependencyInfo = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.memoryBarrierCount = 1,
.pMemoryBarriers = &barrier
};
vkCmdPipelineBarrier2(buffer.handle(), &dependencyInfo);
}