Renderer/RenderPass: Implement RenderPass correctly

This commit is contained in:
Jérôme Leclercq
2021-02-15 18:14:47 +01:00
parent 3d84479d0e
commit d058a127e1
11 changed files with 543 additions and 133 deletions

View File

@@ -17,7 +17,7 @@ namespace Nz
class NAZARA_OPENGLRENDERER_API OpenGLRenderPass final : public RenderPass
{
public:
OpenGLRenderPass() = default;
using RenderPass::RenderPass;
OpenGLRenderPass(const OpenGLRenderPass&) = delete;
OpenGLRenderPass(OpenGLRenderPass&&) noexcept = default;
~OpenGLRenderPass() = default;

View File

@@ -11,6 +11,96 @@
namespace Nz
{
enum class AttachmentLoadOp
{
Clear,
Discard,
Load
};
enum class AttachmentStoreOp
{
Discard,
Store
};
enum class MemoryAccess
{
ColorRead,
ColorWrite,
DepthStencilRead,
DepthStencilWrite,
IndexBufferRead,
IndirectCommandRead,
HostRead,
HostWrite,
MemoryRead,
MemoryWrite,
ShaderRead,
ShaderWrite,
TransferRead,
TransferWrite,
UniformBufferRead,
VertexBufferRead,
Max = VertexBufferRead
};
template<>
struct EnumAsFlags<MemoryAccess>
{
static constexpr MemoryAccess max = MemoryAccess::Max;
};
using MemoryAccessFlags = Flags<MemoryAccess>;
enum class PipelineStage
{
TopOfPipe,
ColorOutput,
DrawIndirect,
FragmentShader,
FragmentTestsEarly,
FragmentTestsLate,
GeometryShader,
TessellationControlShader,
TessellationEvaluationShader,
Transfer,
TransformFeedback,
VertexInput,
VertexShader,
BottomOfPipe,
Max = BottomOfPipe
};
template<>
struct EnumAsFlags<PipelineStage>
{
static constexpr PipelineStage max = PipelineStage::Max;
};
using PipelineStageFlags = Flags<PipelineStage>;
enum class QueueType
{
Compute,
Graphics,
Transfer,
Max = Transfer
};
template<>
struct EnumAsFlags<QueueType>
{
static constexpr QueueType max = QueueType::Max;
};
using QueueTypeFlags = Flags<QueueType>;
enum class RenderAPI
{
Direct3D, ///< Microsoft Render API, only works on MS platforms
@@ -53,22 +143,17 @@ namespace Nz
SpirV
};
enum class QueueType
enum class TextureLayout
{
Compute,
Graphics,
Transfer,
Max = Transfer
ColorInput,
ColorOutput,
DepthStencilInput,
DepthStencilOutput,
Present,
TransferSource,
TransferDestination,
Undefined
};
template<>
struct EnumAsFlags<QueueType>
{
static constexpr QueueType max = QueueType::Max;
};
using QueueTypeFlags = Flags<QueueType>;
}
#endif // NAZARA_ENUMS_RENDERER_HPP

View File

@@ -11,6 +11,9 @@
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <limits>
#include <optional>
#include <vector>
namespace Nz
{
@@ -18,20 +21,64 @@ namespace Nz
{
public:
struct Attachment;
struct SubpassDependency;
struct SubpassDescription;
RenderPass() = default;
inline RenderPass(std::vector<Attachment> attachments, std::vector<SubpassDescription> subpassDescriptions, std::vector<SubpassDependency> subpassDependencies);
RenderPass(const RenderPass&) = delete;
RenderPass(RenderPass&&) noexcept = default;
virtual ~RenderPass();
inline const Attachment& GetAttachment(std::size_t attachmentIndex) const;
inline std::size_t GetAttachmentCount() const;
inline const std::vector<Attachment>& GetAttachments() const;
inline const std::vector<SubpassDescription>& GetSubpassDescriptions() const;
inline const std::vector<SubpassDependency>& GetsubpassDependencies() const;
RenderPass& operator=(const RenderPass&) = delete;
RenderPass& operator=(RenderPass&&) noexcept = default;
struct Attachment
{
PixelFormat format;
// TODO
AttachmentLoadOp loadOp = AttachmentLoadOp::Load;
AttachmentLoadOp stencilLoadOp = AttachmentLoadOp::Load;
AttachmentStoreOp storeOp = AttachmentStoreOp::Store;
AttachmentStoreOp stencilStoreOp = AttachmentStoreOp::Store;
TextureLayout initialLayout = TextureLayout::Undefined;
TextureLayout finalLayout = TextureLayout::Present;
};
struct AttachmentReference
{
std::size_t attachmentIndex;
TextureLayout attachmentLayout = TextureLayout::ColorInput;
};
struct SubpassDependency
{
std::size_t fromSubpassIndex;
PipelineStageFlags fromStages;
MemoryAccessFlags fromAccessFlags;
std::size_t toSubpassIndex;
PipelineStageFlags toStages;
MemoryAccessFlags toAccessFlags;
bool tilable = false;
};
struct SubpassDescription
{
std::vector<AttachmentReference> colorAttachment;
std::vector<AttachmentReference> inputAttachments;
std::optional<AttachmentReference> depthStencilAttachment;
};
static constexpr std::size_t ExternalSubpassIndex = std::numeric_limits<std::size_t>::max();
protected:
std::vector<Attachment> m_attachments;
std::vector<SubpassDependency> m_subpassDependencies;
std::vector<SubpassDescription> m_subpassDescriptions;
};
}

View File

@@ -3,10 +3,43 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Renderer/RenderPass.hpp>
#include <cassert>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
inline RenderPass::RenderPass(std::vector<Attachment> attachments, std::vector<SubpassDescription> subpassDescriptions, std::vector<SubpassDependency> subpassDependencies) :
m_attachments(std::move(attachments)),
m_subpassDescriptions(std::move(subpassDescriptions)),
m_subpassDependencies(std::move(subpassDependencies))
{
}
inline auto Nz::RenderPass::GetAttachment(std::size_t attachmentIndex) const -> const Attachment&
{
assert(attachmentIndex < m_attachments.size());
return m_attachments[attachmentIndex];
}
inline std::size_t RenderPass::GetAttachmentCount() const
{
return m_attachments.size();
}
inline auto RenderPass::GetAttachments() const -> const std::vector<Attachment>&
{
return m_attachments;
}
inline auto RenderPass::GetSubpassDescriptions() const -> const std::vector<SubpassDescription>&
{
return m_subpassDescriptions;
}
inline auto RenderPass::GetsubpassDependencies() const -> const std::vector<SubpassDependency>&
{
return m_subpassDependencies;
}
}
#include <Nazara/Renderer/DebugOff.hpp>

View File

@@ -11,14 +11,24 @@
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Loader.hpp>
#include <optional>
#include <string>
namespace Nz
{
inline std::optional<PixelFormat> FromVulkan(VkFormat format);
inline VkAttachmentLoadOp ToVulkan(AttachmentLoadOp loadOp);
inline VkAttachmentStoreOp ToVulkan(AttachmentStoreOp storeOp);
inline VkBufferUsageFlags ToVulkan(BufferType bufferType);
inline VkFormat ToVulkan(ComponentType componentType);
inline VkCullModeFlagBits ToVulkan(FaceSide faceSide);
inline VkPolygonMode ToVulkan(FaceFilling faceFilling);
inline VkAccessFlagBits ToVulkan(MemoryAccess memoryAccess);
inline VkAccessFlags ToVulkan(MemoryAccessFlags memoryAccessFlags);
inline VkPipelineStageFlagBits ToVulkan(PipelineStage pipelineStage);
inline VkPipelineStageFlags ToVulkan(PipelineStageFlags pipelineStages);
inline VkFormat ToVulkan(PixelFormat pixelFormat);
inline VkPrimitiveTopology ToVulkan(PrimitiveMode primitiveMode);
inline VkCompareOp ToVulkan(RendererComparison comparison);
inline VkFilter ToVulkan(SamplerFilter samplerFilter);
@@ -28,6 +38,7 @@ namespace Nz
inline VkShaderStageFlagBits ToVulkan(ShaderStageType stageType);
inline VkShaderStageFlags ToVulkan(ShaderStageTypeFlags stageType);
inline VkStencilOp ToVulkan(StencilOperation stencilOp);
inline VkImageLayout ToVulkan(TextureLayout textureLayout);
inline VkVertexInputRate ToVulkan(VertexInputRate inputRate);
NAZARA_VULKANRENDERER_API std::string TranslateVulkanError(VkResult code);

View File

@@ -10,6 +10,45 @@
namespace Nz
{
std::optional<PixelFormat> FromVulkan(VkFormat format)
{
switch (format)
{
case VK_FORMAT_B8G8R8A8_UNORM: return PixelFormat::PixelFormat_BGRA8;
case VK_FORMAT_D24_UNORM_S8_UINT: return PixelFormat::PixelFormat_Depth24Stencil8;
case VK_FORMAT_D32_SFLOAT: return PixelFormat::PixelFormat_Depth32;
case VK_FORMAT_R8G8B8A8_UNORM: return PixelFormat::PixelFormat_RGBA8;
default: break;
}
return std::nullopt;
}
VkAttachmentLoadOp ToVulkan(AttachmentLoadOp loadOp)
{
switch (loadOp)
{
case AttachmentLoadOp::Clear: return VK_ATTACHMENT_LOAD_OP_CLEAR;
case AttachmentLoadOp::Discard: return VK_ATTACHMENT_LOAD_OP_DONT_CARE;
case AttachmentLoadOp::Load: return VK_ATTACHMENT_LOAD_OP_LOAD;
}
NazaraError("Unhandled AttachmentLoadOp 0x" + NumberToString(UnderlyingCast(loadOp), 16));
return {};
}
VkAttachmentStoreOp ToVulkan(AttachmentStoreOp storeOp)
{
switch (storeOp)
{
case AttachmentStoreOp::Discard: return VK_ATTACHMENT_STORE_OP_DONT_CARE;
case AttachmentStoreOp::Store: return VK_ATTACHMENT_STORE_OP_STORE;
}
NazaraError("Unhandled AttachmentStoreOp 0x" + NumberToString(UnderlyingCast(storeOp), 16));
return {};
}
inline VkBufferUsageFlags ToVulkan(BufferType bufferType)
{
switch (bufferType)
@@ -73,6 +112,97 @@ namespace Nz
NazaraError("Unhandled FaceFilling 0x" + NumberToString(faceFilling, 16));
return VK_POLYGON_MODE_FILL;
}
inline VkAccessFlagBits ToVulkan(MemoryAccess memoryAccess)
{
switch (memoryAccess)
{
case MemoryAccess::ColorRead: return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
case MemoryAccess::ColorWrite: return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case MemoryAccess::DepthStencilRead: return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
case MemoryAccess::DepthStencilWrite: return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case MemoryAccess::IndexBufferRead: return VK_ACCESS_INDEX_READ_BIT;
case MemoryAccess::IndirectCommandRead: return VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
case MemoryAccess::HostRead: return VK_ACCESS_HOST_READ_BIT;
case MemoryAccess::HostWrite: return VK_ACCESS_HOST_WRITE_BIT;
case MemoryAccess::MemoryRead: return VK_ACCESS_MEMORY_READ_BIT;
case MemoryAccess::MemoryWrite: return VK_ACCESS_MEMORY_WRITE_BIT;
case MemoryAccess::ShaderRead: return VK_ACCESS_SHADER_READ_BIT;
case MemoryAccess::ShaderWrite: return VK_ACCESS_SHADER_WRITE_BIT;
case MemoryAccess::TransferRead: return VK_ACCESS_TRANSFER_READ_BIT;
case MemoryAccess::TransferWrite: return VK_ACCESS_TRANSFER_WRITE_BIT;
case MemoryAccess::UniformBufferRead: return VK_ACCESS_UNIFORM_READ_BIT;
case MemoryAccess::VertexBufferRead: return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
}
NazaraError("Unhandled MemoryAccess 0x" + NumberToString(UnderlyingCast(memoryAccess), 16));
return {};
}
inline VkAccessFlags ToVulkan(MemoryAccessFlags memoryAccessFlags)
{
VkShaderStageFlags accessBits = 0;
for (int i = 0; i <= UnderlyingCast(MemoryAccess::Max); ++i)
{
MemoryAccess memoryAccess = static_cast<MemoryAccess>(i);
if (memoryAccessFlags.Test(memoryAccess))
accessBits |= ToVulkan(memoryAccess);
}
return accessBits;
}
VkPipelineStageFlagBits ToVulkan(PipelineStage pipelineStage)
{
switch (pipelineStage)
{
case PipelineStage::TopOfPipe: return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
case PipelineStage::ColorOutput: return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
case PipelineStage::DrawIndirect: return VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
case PipelineStage::FragmentShader: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case PipelineStage::FragmentTestsEarly: return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
case PipelineStage::FragmentTestsLate: return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case PipelineStage::GeometryShader: return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
case PipelineStage::TessellationControlShader: return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
case PipelineStage::TessellationEvaluationShader: return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
case PipelineStage::Transfer: return VK_PIPELINE_STAGE_TRANSFER_BIT;
case PipelineStage::TransformFeedback: return VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT;
case PipelineStage::VertexInput: return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
case PipelineStage::VertexShader: return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
case PipelineStage::BottomOfPipe: return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
NazaraError("Unhandled PipelineStage 0x" + NumberToString(UnderlyingCast(pipelineStage), 16));
return {};
}
VkPipelineStageFlags ToVulkan(PipelineStageFlags pipelineStages)
{
VkShaderStageFlags pipelineStageBits = 0;
for (int i = 0; i <= UnderlyingCast(PipelineStage::Max); ++i)
{
PipelineStage pipelineStage = static_cast<PipelineStage>(i);
if (pipelineStages.Test(pipelineStage))
pipelineStageBits |= ToVulkan(pipelineStage);
}
return pipelineStageBits;
}
VkFormat ToVulkan(PixelFormat pixelFormat)
{
switch (pixelFormat)
{
case PixelFormat::PixelFormat_BGRA8: return VK_FORMAT_B8G8R8A8_UNORM;
case PixelFormat::PixelFormat_Depth24Stencil8: return VK_FORMAT_D24_UNORM_S8_UINT;
case PixelFormat::PixelFormat_Depth32: return VK_FORMAT_D32_SFLOAT;
case PixelFormat::PixelFormat_RGBA8: return VK_FORMAT_R8G8B8A8_UNORM;
default: break;
}
NazaraError("Unhandled PixelFormat 0x" + NumberToString(pixelFormat, 16));
return {};
}
inline VkPrimitiveTopology ToVulkan(PrimitiveMode primitiveMode)
{
@@ -172,14 +302,12 @@ namespace Nz
inline VkShaderStageFlags ToVulkan(ShaderStageTypeFlags stageType)
{
VkShaderStageFlags shaderStageBits = 0;
if (stageType.Test(ShaderStageType::Fragment))
shaderStageBits |= VK_SHADER_STAGE_FRAGMENT_BIT;
if (stageType.Test(ShaderStageType::Vertex))
shaderStageBits |= VK_SHADER_STAGE_VERTEX_BIT;
static_assert(UnderlyingCast(ShaderStageType::Max) + 1 == 2);
for (int i = 0; i <= UnderlyingCast(ShaderStageType::Max); ++i)
{
ShaderStageType shaderStage = static_cast<ShaderStageType>(i);
if (stageType.Test(shaderStage))
shaderStageBits |= ToVulkan(shaderStage);
}
return shaderStageBits;
}
@@ -198,8 +326,26 @@ namespace Nz
case StencilOperation_Zero: return VK_STENCIL_OP_ZERO;
}
NazaraError("Unhandled RendererComparison 0x" + NumberToString(stencilOp, 16));
return VK_STENCIL_OP_KEEP;
NazaraError("Unhandled StencilOperation 0x" + NumberToString(stencilOp, 16));
return {};
}
VkImageLayout ToVulkan(TextureLayout textureLayout)
{
switch (textureLayout)
{
case TextureLayout::ColorInput: return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
case TextureLayout::ColorOutput: return VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
case TextureLayout::DepthStencilInput: return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL;
case TextureLayout::DepthStencilOutput: return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
case TextureLayout::Present: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
case TextureLayout::TransferSource: return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
case TextureLayout::TransferDestination: return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
case TextureLayout::Undefined: return VK_IMAGE_LAYOUT_UNDEFINED;
}
NazaraError("Unhandled TextureLayout 0x" + NumberToString(UnderlyingCast(textureLayout), 16));
return {};
}
inline VkVertexInputRate ToVulkan(VertexInputRate inputRate)

View File

@@ -18,12 +18,11 @@ namespace Nz
class NAZARA_VULKANRENDERER_API VulkanRenderPass final : public RenderPass
{
public:
inline VulkanRenderPass(Vk::RenderPass renderPass, std::initializer_list<PixelFormat> formats); //< FIXME
VulkanRenderPass(Vk::Device& device, std::vector<Attachment> attachments, std::vector<SubpassDescription> subpassDescriptions, std::vector<SubpassDependency> subpassDependencies);
VulkanRenderPass(const VulkanRenderPass&) = delete;
VulkanRenderPass(VulkanRenderPass&&) noexcept = default;
~VulkanRenderPass() = default;
inline PixelFormat GetAttachmentFormat(std::size_t attachmentIndex) const;
inline Vk::RenderPass& GetRenderPass();
inline const Vk::RenderPass& GetRenderPass() const;
@@ -31,7 +30,6 @@ namespace Nz
VulkanRenderPass& operator=(VulkanRenderPass&&) noexcept = default;
private:
std::vector<PixelFormat> m_formats;
Vk::RenderPass m_renderPass;
};
}

View File

@@ -7,17 +7,6 @@
namespace Nz
{
inline VulkanRenderPass::VulkanRenderPass(Vk::RenderPass renderPass, std::initializer_list<PixelFormat> formats) :
m_formats(std::begin(formats), std::end(formats)),
m_renderPass(std::move(renderPass))
{
}
inline PixelFormat VulkanRenderPass::GetAttachmentFormat(std::size_t attachmentIndex) const
{
return m_formats[attachmentIndex];
}
inline Vk::RenderPass& VulkanRenderPass::GetRenderPass()
{
return m_renderPass;