Graphics/FrameGraph: Add support for cubemap and slice rendering

This commit is contained in:
SirLynix 2022-12-02 23:00:44 +01:00 committed by Jérôme Leclercq
parent 4ae3f51174
commit 5a57976b4b
6 changed files with 229 additions and 61 deletions

View File

@ -9,6 +9,7 @@
#include <Nazara/Prerequisites.hpp> #include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/FrameGraphStructs.hpp>
#include <Nazara/Graphics/FramePass.hpp> #include <Nazara/Graphics/FramePass.hpp>
#include <Nazara/Math/Rect.hpp> #include <Nazara/Math/Rect.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp> #include <Nazara/Renderer/CommandBufferBuilder.hpp>
@ -81,15 +82,10 @@ namespace Nz
bool forceCommandBufferRegeneration = true; bool forceCommandBufferRegeneration = true;
}; };
struct TextureData struct TextureData : FrameGraphTextureData
{ {
std::string name; std::string name;
std::shared_ptr<Texture> texture; std::shared_ptr<Texture> texture;
FramePassAttachmentSize size;
PixelFormat format;
TextureUsageFlags usage;
unsigned int width;
unsigned int height;
}; };
std::shared_ptr<CommandPool> m_commandPool; std::shared_ptr<CommandPool> m_commandPool;

View File

@ -10,6 +10,7 @@
#include <Nazara/Prerequisites.hpp> #include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/BakedFrameGraph.hpp> #include <Nazara/Graphics/BakedFrameGraph.hpp>
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
#include <Nazara/Graphics/FrameGraphStructs.hpp>
#include <Nazara/Graphics/FramePass.hpp> #include <Nazara/Graphics/FramePass.hpp>
#include <Nazara/Graphics/FramePassAttachment.hpp> #include <Nazara/Graphics/FramePassAttachment.hpp>
#include <Nazara/Renderer/Enums.hpp> #include <Nazara/Renderer/Enums.hpp>
@ -32,6 +33,8 @@ namespace Nz
~FrameGraph() = default; ~FrameGraph() = default;
inline std::size_t AddAttachment(FramePassAttachment attachment); inline std::size_t AddAttachment(FramePassAttachment attachment);
inline std::size_t AddAttachmentCube(FramePassAttachment attachment);
inline std::size_t AddAttachmentCubeFace(std::size_t attachmentId, CubemapFace face);
inline std::size_t AddAttachmentProxy(std::string name, std::size_t attachmentId); inline std::size_t AddAttachmentProxy(std::string name, std::size_t attachmentId);
inline void AddBackbufferOutput(std::size_t backbufferOutput); inline void AddBackbufferOutput(std::size_t backbufferOutput);
inline FramePass& AddPass(std::string name); inline FramePass& AddPass(std::string name);
@ -52,6 +55,16 @@ namespace Nz
using PassIdToPhysicalPassIndex = std::unordered_map<std::size_t /*passId*/, std::size_t /*physicalPassId*/>; using PassIdToPhysicalPassIndex = std::unordered_map<std::size_t /*passId*/, std::size_t /*physicalPassId*/>;
using TextureBarrier = BakedFrameGraph::TextureBarrier; using TextureBarrier = BakedFrameGraph::TextureBarrier;
struct AttachmentCube : FramePassAttachment
{
};
struct AttachmentLayer
{
std::size_t attachmentId;
std::size_t layerIndex;
};
struct AttachmentProxy struct AttachmentProxy
{ {
std::size_t attachmentId; std::size_t attachmentId;
@ -84,22 +97,13 @@ namespace Nz
std::vector<Subpass> passes; std::vector<Subpass> passes;
}; };
struct TextureData
{
std::string name;
PixelFormat format;
FramePassAttachmentSize size;
TextureUsageFlags usage;
unsigned int width;
unsigned int height;
};
struct WorkData struct WorkData
{ {
std::vector<std::shared_ptr<RenderPass>> renderPasses; std::vector<std::shared_ptr<RenderPass>> renderPasses;
std::vector<PhysicalPassData> physicalPasses; std::vector<PhysicalPassData> physicalPasses;
std::vector<TextureData> textures; std::vector<FrameGraphTextureData> textures;
std::vector<std::size_t> texturePool; std::vector<std::size_t> texture2DPool;
std::vector<std::size_t> textureCubePool;
AttachmentIdToPassId attachmentLastUse; AttachmentIdToPassId attachmentLastUse;
AttachmentIdToPassMap attachmentReadList; AttachmentIdToPassMap attachmentReadList;
AttachmentIdToPassMap attachmentWriteList; AttachmentIdToPassMap attachmentWriteList;
@ -123,9 +127,11 @@ namespace Nz
void ReorderPasses(); void ReorderPasses();
void TraverseGraph(std::size_t passIndex); void TraverseGraph(std::size_t passIndex);
using AttachmentType = std::variant<FramePassAttachment, AttachmentProxy, AttachmentCube, AttachmentLayer>;
std::vector<std::size_t> m_backbufferOutputs; std::vector<std::size_t> m_backbufferOutputs;
std::vector<FramePass> m_framePasses; std::vector<FramePass> m_framePasses;
std::vector<std::variant<FramePassAttachment, AttachmentProxy>> m_attachments; std::vector<AttachmentType> m_attachments;
WorkData m_pending; WorkData m_pending;
}; };
} }

View File

@ -16,10 +16,32 @@ namespace Nz
return id; return id;
} }
inline std::size_t FrameGraph::AddAttachmentCube(FramePassAttachment attachment)
{
std::size_t id = m_attachments.size();
m_attachments.emplace_back(AttachmentCube{ std::move(attachment) });
return id;
}
inline std::size_t FrameGraph::AddAttachmentCubeFace(std::size_t attachmentId, CubemapFace face)
{
attachmentId = ResolveAttachmentIndex(attachmentId);
assert(std::holds_alternative<AttachmentCube>(m_attachments[attachmentId]));
std::size_t id = m_attachments.size();
m_attachments.emplace_back(AttachmentLayer{
attachmentId,
SafeCast<std::size_t>(face)
});
return id;
}
inline std::size_t FrameGraph::AddAttachmentProxy(std::string name, std::size_t attachmentId) inline std::size_t FrameGraph::AddAttachmentProxy(std::string name, std::size_t attachmentId)
{ {
assert(attachmentId < m_attachments.size()); assert(attachmentId < m_attachments.size());
assert(std::holds_alternative<FramePassAttachment>(m_attachments[attachmentId]));
std::size_t id = m_attachments.size(); std::size_t id = m_attachments.size();
m_attachments.emplace_back(AttachmentProxy { m_attachments.emplace_back(AttachmentProxy {

View File

@ -0,0 +1,38 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_GRAPHICS_FRAMEGRAPHSTRUCTS_HPP
#define NAZARA_GRAPHICS_FRAMEGRAPHSTRUCTS_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Graphics/FramePassAttachment.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <optional>
#include <string>
namespace Nz
{
struct FrameGraphTextureData
{
struct ViewData
{
std::size_t parentTextureId;
std::size_t arrayLayer;
};
std::optional<ViewData> viewData;
std::string name;
ImageType type;
PixelFormat format;
FramePassAttachmentSize size;
TextureUsageFlags usage;
unsigned int width;
unsigned int height;
};
}
#endif // NAZARA_GRAPHICS_FRAMEGRAPHSTRUCTS_HPP

View File

@ -149,11 +149,29 @@ namespace Nz
for (auto& textureData : m_textures) for (auto& textureData : m_textures)
{ {
if (textureData.viewData)
{
TextureData& parentTexture = m_textures[textureData.viewData->parentTextureId];
// This is a view on another texture
TextureViewInfo textureViewParams;
textureViewParams.viewType = textureData.type;
textureViewParams.reinterpretFormat = textureData.format;
textureViewParams.baseArrayLayer = textureData.viewData->arrayLayer;
textureData.texture = parentTexture.texture->CreateView(textureViewParams);
}
else
{
TextureInfo textureCreationParams; TextureInfo textureCreationParams;
textureCreationParams.type = ImageType::E2D; textureCreationParams.type = textureData.type;
textureCreationParams.usageFlags = textureData.usage; textureCreationParams.usageFlags = textureData.usage;
textureCreationParams.pixelFormat = textureData.format; textureCreationParams.pixelFormat = textureData.format;
if (textureCreationParams.type == ImageType::Cubemap)
textureCreationParams.layerCount = 6;
textureCreationParams.width = 1; textureCreationParams.width = 1;
textureCreationParams.height = 1; textureCreationParams.height = 1;
switch (textureData.size) switch (textureData.size)
@ -173,6 +191,7 @@ namespace Nz
if (!textureData.name.empty()) if (!textureData.name.empty())
textureData.texture->UpdateDebugName(textureData.name); textureData.texture->UpdateDebugName(textureData.name);
} }
}
std::vector<std::shared_ptr<Texture>> textures; std::vector<std::shared_ptr<Texture>> textures;
for (auto& passData : m_passes) for (auto& passData : m_passes)

View File

@ -39,7 +39,8 @@ namespace Nz
m_pending.physicalPasses.clear(); m_pending.physicalPasses.clear();
m_pending.renderPasses.clear(); m_pending.renderPasses.clear();
m_pending.textures.clear(); m_pending.textures.clear();
m_pending.texturePool.clear(); m_pending.texture2DPool.clear();
m_pending.textureCubePool.clear();
BuildReadWriteList(); BuildReadWriteList();
@ -113,12 +114,7 @@ namespace Nz
for (auto& texture : m_pending.textures) for (auto& texture : m_pending.textures)
{ {
auto& bakedTexture = bakedTextures.emplace_back(); auto& bakedTexture = bakedTextures.emplace_back();
bakedTexture.name = std::move(texture.name); static_cast<FrameGraphTextureData&>(bakedTexture) = std::move(texture);
bakedTexture.format = texture.format;
bakedTexture.height = texture.height;
bakedTexture.size = texture.size;
bakedTexture.usage = texture.usage;
bakedTexture.width = texture.width;
} }
return BakedFrameGraph(std::move(bakedPasses), std::move(bakedTextures), std::move(m_pending.attachmentToTextures), std::move(m_pending.passIdToPhysicalPassIndex)); return BakedFrameGraph(std::move(bakedPasses), std::move(bakedTextures), std::move(m_pending.attachmentToTextures), std::move(m_pending.passIdToPhysicalPassIndex));
@ -195,7 +191,7 @@ namespace Nz
{ {
std::size_t textureId = RegisterTexture(input.attachmentId); std::size_t textureId = RegisterTexture(input.attachmentId);
TextureData& attachmentData = m_pending.textures[textureId]; FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::ShaderSampling; attachmentData.usage |= TextureUsage::ShaderSampling;
} }
@ -203,7 +199,7 @@ namespace Nz
{ {
std::size_t textureId = RegisterTexture(output.attachmentId); std::size_t textureId = RegisterTexture(output.attachmentId);
TextureData& attachmentData = m_pending.textures[textureId]; FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::ColorAttachment; attachmentData.usage |= TextureUsage::ColorAttachment;
} }
@ -211,7 +207,7 @@ namespace Nz
{ {
std::size_t textureId = RegisterTexture(depthStencilInput); std::size_t textureId = RegisterTexture(depthStencilInput);
TextureData& attachmentData = m_pending.textures[textureId]; FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::DepthStencilAttachment; attachmentData.usage |= TextureUsage::DepthStencilAttachment;
if (std::size_t depthStencilOutput = framePass.GetDepthStencilOutput(); depthStencilOutput != FramePass::InvalidAttachmentId) if (std::size_t depthStencilOutput = framePass.GetDepthStencilOutput(); depthStencilOutput != FramePass::InvalidAttachmentId)
@ -240,7 +236,7 @@ namespace Nz
{ {
std::size_t textureId = RegisterTexture(depthStencilOutput); std::size_t textureId = RegisterTexture(depthStencilOutput);
TextureData& attachmentData = m_pending.textures[textureId]; FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
attachmentData.usage |= TextureUsage::DepthStencilAttachment; attachmentData.usage |= TextureUsage::DepthStencilAttachment;
} }
@ -252,11 +248,22 @@ namespace Nz
// If this pass is the last one where this attachment is used, push the texture to the reuse pool // If this pass is the last one where this attachment is used, push the texture to the reuse pool
if (it != m_pending.attachmentLastUse.end() && passIndex == it->second) if (it != m_pending.attachmentLastUse.end() && passIndex == it->second)
{
const auto& attachmentData = m_attachments[attachmentId];
if (std::holds_alternative<FramePassAttachment>(attachmentData))
{ {
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId); std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId);
assert(std::find(m_pending.texturePool.begin(), m_pending.texturePool.end(), textureId) == m_pending.texturePool.end()); assert(std::find(m_pending.texture2DPool.begin(), m_pending.texture2DPool.end(), textureId) == m_pending.texture2DPool.end());
m_pending.texturePool.push_back(textureId); m_pending.texture2DPool.push_back(textureId);
}
else if (std::holds_alternative<AttachmentCube>(attachmentData))
{
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId);
assert(std::find(m_pending.textureCubePool.begin(), m_pending.textureCubePool.end(), textureId) == m_pending.textureCubePool.end());
m_pending.textureCubePool.push_back(textureId);
}
} }
}); });
} }
@ -270,6 +277,16 @@ namespace Nz
auto& backbufferTexture = m_pending.textures[it->second]; auto& backbufferTexture = m_pending.textures[it->second];
backbufferTexture.usage |= TextureUsage::ShaderSampling; backbufferTexture.usage |= TextureUsage::ShaderSampling;
} }
// Apply texture view usage to their parents
for (auto& textureData : m_pending.textures)
{
if (textureData.viewData)
{
auto& parentTextureData = m_pending.textures[textureData.viewData->parentTextureId];
parentTextureData.usage |= textureData.usage;
}
}
} }
void FrameGraph::BuildBarriers() void FrameGraph::BuildBarriers()
@ -946,18 +963,20 @@ namespace Nz
const FramePassAttachment& attachmentData = arg; const FramePassAttachment& attachmentData = arg;
// Fetch from reuse pool if possible // Fetch from reuse pool if possible
for (auto it = m_pending.texturePool.begin(); it != m_pending.texturePool.end(); ++it) for (auto it = m_pending.texture2DPool.begin(); it != m_pending.texture2DPool.end(); ++it)
{ {
std::size_t textureId = *it; std::size_t textureId = *it;
TextureData& data = m_pending.textures[textureId]; FrameGraphTextureData& data = m_pending.textures[textureId];
assert(data.type == ImageType::E2D);
if (data.format != attachmentData.format || if (data.format != attachmentData.format ||
data.width != attachmentData.width || data.width != attachmentData.width ||
data.height != attachmentData.height || data.height != attachmentData.height ||
data.size != attachmentData.size) data.size != attachmentData.size)
continue; continue;
m_pending.texturePool.erase(it); m_pending.texture2DPool.erase(it);
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
if (!attachmentData.name.empty() && data.name != attachmentData.name) if (!attachmentData.name.empty() && data.name != attachmentData.name)
@ -969,7 +988,8 @@ namespace Nz
std::size_t textureId = m_pending.textures.size(); std::size_t textureId = m_pending.textures.size();
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId); m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
TextureData& data = m_pending.textures.emplace_back(); FrameGraphTextureData& data = m_pending.textures.emplace_back();
data.type = ImageType::E2D;
data.name = attachmentData.name; data.name = attachmentData.name;
data.format = attachmentData.format; data.format = attachmentData.format;
data.width = attachmentData.width; data.width = attachmentData.width;
@ -978,6 +998,72 @@ namespace Nz
return textureId; return textureId;
} }
else if constexpr (std::is_same_v<T, AttachmentCube>)
{
const AttachmentCube& attachmentData = arg;
// Fetch from reuse pool if possible
for (auto it = m_pending.textureCubePool.begin(); it != m_pending.textureCubePool.end(); ++it)
{
std::size_t textureId = *it;
FrameGraphTextureData& data = m_pending.textures[textureId];
assert(data.type == ImageType::Cubemap);
if (data.format != attachmentData.format ||
data.width != attachmentData.width ||
data.height != attachmentData.height ||
data.size != attachmentData.size)
continue;
m_pending.textureCubePool.erase(it);
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
if (!attachmentData.name.empty() && data.name != attachmentData.name)
data.name += " / " + attachmentData.name;
return textureId;
}
std::size_t textureId = m_pending.textures.size();
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
FrameGraphTextureData& data = m_pending.textures.emplace_back();
data.type = ImageType::Cubemap;
data.name = attachmentData.name;
data.format = attachmentData.format;
data.width = attachmentData.width;
data.height = attachmentData.height;
data.size = attachmentData.size;
return textureId;
}
else if constexpr (std::is_same_v<T, AttachmentLayer>)
{
const AttachmentLayer& texLayer = arg;
// TODO: Reuse texture views from pool?
std::size_t parentTextureId = RegisterTexture(texLayer.attachmentId);
std::size_t textureId = m_pending.textures.size();
m_pending.attachmentToTextures.emplace(attachmentIndex, textureId);
FrameGraphTextureData& data = m_pending.textures.emplace_back();
const FrameGraphTextureData& parentTexture = m_pending.textures[parentTextureId];
data.type = ImageType::E2D;
data.format = parentTexture.format;
data.width = parentTexture.width;
data.height = parentTexture.height;
data.size = parentTexture.size;
data.viewData = {
parentTextureId,
texLayer.layerIndex
};
return textureId;
}
else if constexpr (std::is_same_v<T, AttachmentProxy>) else if constexpr (std::is_same_v<T, AttachmentProxy>)
{ {
const AttachmentProxy& proxy = arg; const AttachmentProxy& proxy = arg;
@ -1022,8 +1108,9 @@ namespace Nz
std::size_t FrameGraph::ResolveAttachmentIndex(std::size_t attachmentIndex) const std::size_t FrameGraph::ResolveAttachmentIndex(std::size_t attachmentIndex) const
{ {
assert(attachmentIndex < m_attachments.size()); assert(attachmentIndex < m_attachments.size());
if (const AttachmentProxy* proxy = std::get_if<AttachmentProxy>(&m_attachments[attachmentIndex]))
return proxy->attachmentId; while (const AttachmentProxy* proxy = std::get_if<AttachmentProxy>(&m_attachments[attachmentIndex]))
attachmentIndex = proxy->attachmentId;
return attachmentIndex; return attachmentIndex;
} }