Add initial support for compute pipelines

This commit is contained in:
SirLynix
2022-12-24 11:54:55 +01:00
committed by Jérôme Leclercq
parent e4064997d8
commit 9578ba3ef5
57 changed files with 915 additions and 182 deletions

View File

@@ -54,11 +54,20 @@ namespace Nz
deviceInfo.name = physDevice.properties.deviceName;
deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy;
deviceInfo.features.computeShaders = true;
deviceInfo.features.depthClamping = physDevice.features.depthClamp;
deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid;
deviceInfo.features.storageBuffers = true;
deviceInfo.features.textureRead = true;
deviceInfo.features.textureReadWithoutFormat = physDevice.features.shaderStorageImageReadWithoutFormat;
deviceInfo.features.textureWrite = true;
deviceInfo.features.textureWriteWithoutFormat = physDevice.features.shaderStorageImageWriteWithoutFormat;
deviceInfo.features.unrestrictedTextureViews = true;
deviceInfo.limits.maxComputeSharedMemorySize = physDevice.properties.limits.maxComputeSharedMemorySize;
deviceInfo.limits.maxComputeWorkGroupCount = { physDevice.properties.limits.maxComputeWorkGroupCount[0], physDevice.properties.limits.maxComputeWorkGroupCount[1], physDevice.properties.limits.maxComputeWorkGroupCount[2] };
deviceInfo.limits.maxComputeWorkGroupSize = { physDevice.properties.limits.maxComputeWorkGroupSize[0], physDevice.properties.limits.maxComputeWorkGroupSize[1], physDevice.properties.limits.maxComputeWorkGroupSize[2] };
deviceInfo.limits.maxComputeWorkGroupInvocations = physDevice.properties.limits.maxComputeWorkGroupInvocations;
deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange;
deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange;
deviceInfo.limits.minStorageBufferOffsetAlignment = physDevice.properties.limits.minStorageBufferOffsetAlignment;

View File

@@ -5,6 +5,7 @@
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/VulkanRenderer/VulkanBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPass.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
@@ -72,6 +73,13 @@ namespace Nz
m_currentSubpassIndex = 0;
}
void VulkanCommandBufferBuilder::BindComputePipeline(const ComputePipeline& pipeline)
{
const VulkanComputePipeline& vkPipeline = static_cast<const VulkanComputePipeline&>(pipeline);
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vkPipeline.GetPipeline());
}
void VulkanCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset)
{
const VulkanBuffer& vkBuffer = static_cast<const VulkanBuffer&>(indexBuffer);
@@ -79,14 +87,14 @@ namespace Nz
m_commandBuffer.BindIndexBuffer(vkBuffer.GetBuffer(), offset, ToVulkan(indexType));
}
void VulkanCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline)
void VulkanCommandBufferBuilder::BindRenderPipeline(const RenderPipeline& pipeline)
{
if (!m_currentRenderPass)
throw std::runtime_error("BindPipeline must be called in a RenderPass");
const VulkanRenderPipeline& vkBinding = static_cast<const VulkanRenderPipeline&>(pipeline);
const VulkanRenderPipeline& vkPipeline = static_cast<const VulkanRenderPipeline&>(pipeline);
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkBinding.Get(*m_currentRenderPass, m_currentSubpassIndex));
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipeline.Get(*m_currentRenderPass, m_currentSubpassIndex));
}
void VulkanCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding)
@@ -207,6 +215,11 @@ namespace Nz
m_commandBuffer.CopyImage(vkFromTexture.GetImage(), ToVulkan(fromLayout), vkToTexture.GetImage(), ToVulkan(toLayout), region);
}
void VulkanCommandBufferBuilder::Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ)
{
m_commandBuffer.Dispatch(workgroupX, workgroupY, workgroupZ);
}
void VulkanCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)
{
m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance);

View File

@@ -0,0 +1,61 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Vulkan renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/VulkanRenderer/Utils.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
#include <Nazara/VulkanRenderer/VulkanShaderModule.hpp>
#include <cassert>
#include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz
{
VulkanComputePipeline::VulkanComputePipeline(VulkanDevice& device, ComputePipelineInfo pipelineInfo) :
m_pipelineInfo(std::move(pipelineInfo))
{
if (device.GetEnabledFeatures().computeShaders)
throw std::runtime_error("compute shaders are not enabled on the device");
VulkanShaderModule& vulkanModule = static_cast<VulkanShaderModule&>(*m_pipelineInfo.shaderModule);
const VulkanShaderModule::Stage* computeStage = nullptr;
for (const auto& stage : vulkanModule.GetStages())
{
if (stage.stage != nzsl::ShaderStageType::Compute)
continue;
if (computeStage)
throw std::runtime_error("multiple compute stages found in shader module");
computeStage = &stage;
}
if (!computeStage)
throw std::runtime_error("no compute stages found in shader module");
VkComputePipelineCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
createInfo.stage.module = vulkanModule.GetHandle();
createInfo.stage.pName = computeStage->name.data();
createInfo.stage.stage = ToVulkan(computeStage->stage);
VulkanRenderPipelineLayout& pipelineLayout = *static_cast<VulkanRenderPipelineLayout*>(m_pipelineInfo.pipelineLayout.get());
createInfo.layout = pipelineLayout.GetPipelineLayout();
if (!m_pipeline.CreateCompute(device, createInfo))
throw std::runtime_error("failed to create compute pipeline: " + TranslateVulkanError(m_pipeline.GetLastErrorCode()));
}
void VulkanComputePipeline::UpdateDebugName(std::string_view name)
{
m_pipeline.SetDebugName(name);
}
}
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Core/AntiWindows.hpp>
#endif

View File

@@ -4,6 +4,7 @@
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanComputePipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPass.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
@@ -37,6 +38,11 @@ namespace Nz
return std::make_shared<VulkanCommandPool>(*this, queueType);
}
std::shared_ptr<ComputePipeline> VulkanDevice::InstantiateComputePipeline(ComputePipelineInfo pipelineInfo)
{
return std::make_shared<VulkanComputePipeline>(*this, std::move(pipelineInfo));
}
std::shared_ptr<Framebuffer> VulkanDevice::InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr<RenderPass>& renderPass, const std::vector<std::shared_ptr<Texture>>& attachments)
{
return std::make_shared<VulkanTextureFramebuffer>(*this, width, height, renderPass, attachments);
@@ -111,6 +117,10 @@ namespace Nz
flags = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
break;
case TextureUsage::ShaderReadWrite:
flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
break;
case TextureUsage::TransferSource:
flags = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT;
break;

View File

@@ -27,9 +27,9 @@ namespace Nz
if constexpr (std::is_same_v<T, StorageBufferBinding> || std::is_same_v<T, UniformBufferBinding>)
bufferBindingCount++;
else if constexpr (std::is_same_v<T, TextureBinding>)
else if constexpr (std::is_same_v<T, SampledTextureBinding> || std::is_same_v<T, TextureBinding>)
imageBindingCount++;
else if constexpr (std::is_same_v<T, TextureBindings>)
else if constexpr (std::is_same_v<T, SampledTextureBindings>)
imageBindingCount += arg.arraySize;
else
static_assert(AlwaysFalse<T>(), "non-exhaustive visitor");
@@ -58,20 +58,7 @@ namespace Nz
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, StorageBufferBinding>)
{
VulkanBuffer* vkBuffer = SafeCast<VulkanBuffer*>(arg.buffer);
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
bufferInfo.offset = arg.offset;
bufferInfo.range = arg.range;
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeOp.pBufferInfo = &bufferInfo;
}
else if constexpr (std::is_same_v<T, TextureBinding>)
if constexpr (std::is_same_v<T, SampledTextureBinding>)
{
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.texture);
const VulkanTextureSampler* vkSampler = SafeCast<const VulkanTextureSampler*>(arg.sampler);
@@ -85,7 +72,7 @@ namespace Nz
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeOp.pImageInfo = &imageInfo;
}
else if constexpr (std::is_same_v<T, TextureBindings>)
else if constexpr (std::is_same_v<T, SampledTextureBindings>)
{
for (UInt32 i = 0; i < arg.arraySize; ++i)
{
@@ -102,6 +89,32 @@ namespace Nz
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeOp.pImageInfo = &imageBinding[imageBinding.size() - arg.arraySize];
}
else if constexpr (std::is_same_v<T, StorageBufferBinding>)
{
VulkanBuffer* vkBuffer = SafeCast<VulkanBuffer*>(arg.buffer);
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE;
bufferInfo.offset = arg.offset;
bufferInfo.range = arg.range;
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeOp.pBufferInfo = &bufferInfo;
}
else if constexpr (std::is_same_v<T, TextureBinding>)
{
const VulkanTexture* vkTexture = SafeCast<const VulkanTexture*>(arg.texture);
VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back();
imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
imageInfo.imageView = (vkTexture) ? vkTexture->GetImageView() : VK_NULL_HANDLE;
imageInfo.sampler = VK_NULL_HANDLE;
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeOp.pImageInfo = &imageInfo;
}
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
{
VulkanBuffer* vkBuffer = static_cast<VulkanBuffer*>(arg.buffer);