Integrate render target handling in frame graphs (#411)
* Graphics: Integrate RenderTarget in FrameGraph - This handles the blit to texture/swapchain in the FrameGraph and fixes RenderTextureBlit - Dummy attachments were added to the FrameGraph class to handle link without texture (used to setup a dependency between two passes with no texture) - FramePass now supports custom access/layout/usage for inputs * Graphics/RenderTarget: Allow to set any RenderTarget as output
This commit is contained in:
parent
f57fc3c1d5
commit
32d227628c
|
|
@ -28,6 +28,7 @@
|
|||
#include <NazaraUtils/MemoryPool.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
|
@ -80,12 +81,14 @@ namespace Nz
|
|||
ForwardFramePipeline& operator=(ForwardFramePipeline&&) = delete;
|
||||
|
||||
private:
|
||||
struct ViewerData;
|
||||
|
||||
BakedFrameGraph BuildFrameGraph();
|
||||
|
||||
void RegisterMaterialInstance(MaterialInstance* materialPass);
|
||||
void UnregisterMaterialInstance(MaterialInstance* material);
|
||||
|
||||
struct ViewerData;
|
||||
static std::size_t BuildMergePass(FrameGraph& frameGraph, std::span<ViewerData*> targetViewers);
|
||||
|
||||
struct LightData
|
||||
{
|
||||
|
|
@ -119,7 +122,6 @@ namespace Nz
|
|||
|
||||
struct RenderTargetData
|
||||
{
|
||||
std::size_t finalAttachment;
|
||||
std::vector<const ViewerData*> viewers;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <Nazara/Graphics/FramePassAttachment.hpp>
|
||||
#include <Nazara/Renderer/Enums.hpp>
|
||||
#include <Nazara/Renderer/RenderPass.hpp>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
|
@ -26,6 +27,8 @@ namespace Nz
|
|||
{
|
||||
class NAZARA_GRAPHICS_API FrameGraph
|
||||
{
|
||||
friend class BakedFrameGraph;
|
||||
|
||||
public:
|
||||
FrameGraph() = default;
|
||||
FrameGraph(const FrameGraph&) = delete;
|
||||
|
|
@ -38,6 +41,7 @@ namespace Nz
|
|||
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 AddDummyAttachment();
|
||||
inline FramePass& AddPass(std::string name);
|
||||
inline void AddOutput(std::size_t attachmentIndex);
|
||||
|
||||
|
|
@ -88,6 +92,10 @@ namespace Nz
|
|||
TextureLayout layout;
|
||||
};
|
||||
|
||||
struct DummyAttachment
|
||||
{
|
||||
};
|
||||
|
||||
struct PassBarriers
|
||||
{
|
||||
std::vector<Barrier> invalidationBarriers;
|
||||
|
|
@ -138,7 +146,10 @@ namespace Nz
|
|||
void ReorderPasses();
|
||||
void TraverseGraph(std::size_t passIndex);
|
||||
|
||||
using AttachmentType = std::variant<FramePassAttachment, AttachmentProxy, AttachmentArray, AttachmentCube, AttachmentLayer>;
|
||||
using AttachmentType = std::variant<FramePassAttachment, AttachmentProxy, AttachmentArray, AttachmentCube, AttachmentLayer, DummyAttachment>;
|
||||
|
||||
static constexpr std::size_t InvalidAttachmentIndex = std::numeric_limits<std::size_t>::max();
|
||||
static constexpr std::size_t InvalidTextureIndex = std::numeric_limits<std::size_t>::max();
|
||||
|
||||
std::vector<std::size_t> m_graphOutputs;
|
||||
std::vector<FramePass> m_framePasses;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ namespace Nz
|
|||
return id;
|
||||
}
|
||||
|
||||
inline std::size_t FrameGraph::AddDummyAttachment()
|
||||
{
|
||||
std::size_t id = m_attachments.size();
|
||||
m_attachments.emplace_back(DummyAttachment{});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline FramePass& FrameGraph::AddPass(std::string name)
|
||||
{
|
||||
std::size_t id = m_framePasses.size();
|
||||
|
|
|
|||
|
|
@ -73,7 +73,9 @@ namespace Nz
|
|||
inline void SetDepthStencilInput(std::size_t attachmentId);
|
||||
inline void SetDepthStencilOutput(std::size_t attachmentId);
|
||||
inline void SetExecutionCallback(ExecutionCallback callback);
|
||||
inline void SetInputLayout(std::size_t inputIndex, TextureLayout layout);
|
||||
inline void SetInputAccess(std::size_t inputIndex, TextureLayout layout, PipelineStageFlags stageFlags, MemoryAccessFlags accessFlags);
|
||||
inline void SetInputAssumedLayout(std::size_t inputIndex, TextureLayout layout);
|
||||
inline void SetInputUsage(std::size_t inputIndex, TextureUsageFlags usageFlags);
|
||||
inline void SetReadInput(std::size_t inputIndex, bool doesRead);
|
||||
|
||||
FramePass& operator=(const FramePass&) = delete;
|
||||
|
|
@ -90,7 +92,11 @@ namespace Nz
|
|||
struct Input
|
||||
{
|
||||
std::optional<TextureLayout> assumedLayout;
|
||||
std::optional<TextureUsageFlags> textureUsageFlags;
|
||||
std::size_t attachmentId;
|
||||
MemoryAccessFlags accessFlags = MemoryAccess::ShaderRead;
|
||||
PipelineStageFlags stageFlags = PipelineStage::FragmentShader;
|
||||
TextureLayout layout = TextureLayout::ColorInput;
|
||||
bool doesRead = true;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -127,12 +127,26 @@ namespace Nz
|
|||
m_executionCallback = std::move(callback);
|
||||
}
|
||||
|
||||
inline void FramePass::SetInputLayout(std::size_t inputIndex, TextureLayout layout)
|
||||
inline void FramePass::SetInputAccess(std::size_t inputIndex, TextureLayout layout, PipelineStageFlags stageFlags, MemoryAccessFlags accessFlags)
|
||||
{
|
||||
assert(inputIndex < m_inputs.size());
|
||||
m_inputs[inputIndex].accessFlags = accessFlags;
|
||||
m_inputs[inputIndex].layout = layout;
|
||||
m_inputs[inputIndex].stageFlags = stageFlags;
|
||||
}
|
||||
|
||||
inline void FramePass::SetInputAssumedLayout(std::size_t inputIndex, TextureLayout layout)
|
||||
{
|
||||
assert(inputIndex < m_inputs.size());
|
||||
m_inputs[inputIndex].assumedLayout = layout;
|
||||
}
|
||||
|
||||
inline void FramePass::SetInputUsage(std::size_t inputIndex, TextureUsageFlags usageFlags)
|
||||
{
|
||||
assert(inputIndex < m_inputs.size());
|
||||
m_inputs[inputIndex].textureUsageFlags = usageFlags;
|
||||
}
|
||||
|
||||
inline void FramePass::SetReadInput(std::size_t inputIndex, bool doesRead)
|
||||
{
|
||||
assert(inputIndex < m_inputs.size());
|
||||
|
|
|
|||
|
|
@ -19,22 +19,33 @@ namespace Nz
|
|||
class Framebuffer;
|
||||
class FrameGraph;
|
||||
class RenderPass;
|
||||
class Texture;
|
||||
class RenderResources;
|
||||
class Texture;
|
||||
|
||||
class NAZARA_GRAPHICS_API RenderTarget
|
||||
{
|
||||
public:
|
||||
RenderTarget() = default;
|
||||
inline RenderTarget(Int32 renderOrder = 0);
|
||||
virtual ~RenderTarget();
|
||||
|
||||
virtual void OnBuildGraph(FrameGraph& frameGraph, std::size_t attachmentIndex) const = 0;
|
||||
virtual void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const = 0;
|
||||
|
||||
inline Int32 GetRenderOrder() const;
|
||||
virtual const Vector2ui& GetSize() const = 0;
|
||||
|
||||
inline bool IsFrameGraphOutput() const;
|
||||
|
||||
virtual std::size_t OnBuildGraph(FrameGraph& frameGraph, std::size_t attachmentIndex) const = 0;
|
||||
|
||||
inline void SetFrameGraphOutput(bool output = true);
|
||||
|
||||
inline void UpdateRenderOrder(Int32 renderOrder);
|
||||
|
||||
NazaraSignal(OnRenderTargetRelease, const RenderTarget* /*renderTarget*/);
|
||||
NazaraSignal(OnRenderTargetRenderOrderChange, const RenderTarget* /*renderTarget*/, Int32 /*newOrder*/);
|
||||
NazaraSignal(OnRenderTargetSizeChange, const RenderTarget* /*renderTarget*/, const Vector2ui& /*newSize*/);
|
||||
|
||||
private:
|
||||
Int32 m_renderOrder;
|
||||
bool m_frameGraphOutput;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,32 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
inline RenderTarget::RenderTarget(Int32 renderOrder) :
|
||||
m_renderOrder(renderOrder),
|
||||
m_frameGraphOutput(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline Int32 RenderTarget::GetRenderOrder() const
|
||||
{
|
||||
return m_renderOrder;
|
||||
}
|
||||
|
||||
inline bool RenderTarget::IsFrameGraphOutput() const
|
||||
{
|
||||
return m_frameGraphOutput;
|
||||
}
|
||||
|
||||
inline void RenderTarget::SetFrameGraphOutput(bool output)
|
||||
{
|
||||
m_frameGraphOutput = output;
|
||||
}
|
||||
|
||||
inline void RenderTarget::UpdateRenderOrder(Int32 renderOrder)
|
||||
{
|
||||
OnRenderTargetRenderOrderChange(this, renderOrder);
|
||||
m_renderOrder = renderOrder;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ namespace Nz
|
|||
RenderTexture(RenderTexture&&) = delete;
|
||||
~RenderTexture() = default;
|
||||
|
||||
void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const override;
|
||||
std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
|
||||
const Vector2ui& GetSize() const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ namespace Nz
|
|||
RenderTextureBlit(RenderTextureBlit&&) = delete;
|
||||
~RenderTextureBlit() = default;
|
||||
|
||||
void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
void OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const override;
|
||||
std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
|
||||
const Vector2ui& GetSize() const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,14 +25,15 @@ namespace Nz
|
|||
RenderWindow(RenderWindow&&) = delete;
|
||||
~RenderWindow() = default;
|
||||
|
||||
void OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
void OnRenderEnd(RenderResources& renderResources, const BakedFrameGraph& frameGraph, std::size_t attachmentId) const override;
|
||||
std::size_t OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const override;
|
||||
|
||||
const Vector2ui& GetSize() const override;
|
||||
|
||||
RenderWindow& operator=(const RenderWindow&) = delete;
|
||||
RenderWindow& operator=(RenderWindow&&) = delete;
|
||||
|
||||
static constexpr Int32 DefaultRenderOrder = 1000;
|
||||
|
||||
private:
|
||||
void SetSwapchain(Swapchain* swapchain);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
namespace Nz
|
||||
{
|
||||
inline RenderWindow::RenderWindow(Swapchain& swapchain) :
|
||||
RenderTarget(DefaultRenderOrder),
|
||||
m_swapchain(&swapchain),
|
||||
m_windowSwapchain(nullptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/BakedFrameGraph.hpp>
|
||||
#include <Nazara/Graphics/FrameGraph.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Renderer/RenderFrame.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
|
|
@ -60,7 +60,8 @@ namespace Nz
|
|||
builder.TextureBarrier(textureTransition.srcStageMask, textureTransition.dstStageMask, textureTransition.srcAccessMask, textureTransition.dstAccessMask, textureTransition.oldLayout, textureTransition.newLayout, *texture);
|
||||
}
|
||||
|
||||
builder.BeginRenderPass(*passData.framebuffer, *passData.renderPass, passData.renderRect, passData.outputClearValues.data(), passData.outputClearValues.size());
|
||||
if (passData.framebuffer)
|
||||
builder.BeginRenderPass(*passData.framebuffer, *passData.renderPass, passData.renderRect, passData.outputClearValues.data(), passData.outputClearValues.size());
|
||||
|
||||
if (!passData.name.empty())
|
||||
builder.BeginDebugRegion(passData.name, Color::Green());
|
||||
|
|
@ -85,7 +86,8 @@ namespace Nz
|
|||
if (!passData.name.empty())
|
||||
builder.EndDebugRegion();
|
||||
|
||||
builder.EndRenderPass();
|
||||
if (passData.framebuffer)
|
||||
builder.EndRenderPass();
|
||||
});
|
||||
|
||||
passData.forceCommandBufferRegeneration = false;
|
||||
|
|
@ -102,7 +104,7 @@ namespace Nz
|
|||
const std::shared_ptr<Texture>& BakedFrameGraph::GetAttachmentTexture(std::size_t attachmentIndex) const
|
||||
{
|
||||
auto it = m_attachmentToTextureMapping.find(attachmentIndex);
|
||||
if (it == m_attachmentToTextureMapping.end())
|
||||
if (it == m_attachmentToTextureMapping.end() || it->second == FrameGraph::InvalidTextureIndex)
|
||||
{
|
||||
static std::shared_ptr<Texture> dummy;
|
||||
return dummy;
|
||||
|
|
@ -116,7 +118,7 @@ namespace Nz
|
|||
const std::shared_ptr<RenderPass>& BakedFrameGraph::GetRenderPass(std::size_t passIndex) const
|
||||
{
|
||||
auto it = m_attachmentToTextureMapping.find(passIndex);
|
||||
if (it == m_attachmentToTextureMapping.end())
|
||||
if (it == m_attachmentToTextureMapping.end() || it->second == FrameGraph::InvalidTextureIndex)
|
||||
{
|
||||
static std::shared_ptr<RenderPass> dummy;
|
||||
return dummy;
|
||||
|
|
@ -224,24 +226,29 @@ namespace Nz
|
|||
{
|
||||
textures.clear();
|
||||
|
||||
unsigned int framebufferWidth = std::numeric_limits<unsigned int>::max();
|
||||
unsigned int framebufferHeight = std::numeric_limits<unsigned int>::max();
|
||||
for (std::size_t textureId : passData.outputTextureIndices)
|
||||
if (!passData.outputTextureIndices.empty())
|
||||
{
|
||||
auto& textureData = m_textures[textureId];
|
||||
textures.push_back(textureData.texture);
|
||||
unsigned int framebufferWidth = std::numeric_limits<unsigned int>::max();
|
||||
unsigned int framebufferHeight = std::numeric_limits<unsigned int>::max();
|
||||
for (std::size_t textureId : passData.outputTextureIndices)
|
||||
{
|
||||
auto& textureData = m_textures[textureId];
|
||||
textures.push_back(textureData.texture);
|
||||
|
||||
auto [width, height] = ComputeTextureSize(textureData);
|
||||
auto [width, height] = ComputeTextureSize(textureData);
|
||||
|
||||
framebufferWidth = std::min(framebufferWidth, width);
|
||||
framebufferHeight = std::min(framebufferHeight, height);
|
||||
framebufferWidth = std::min(framebufferWidth, width);
|
||||
framebufferHeight = std::min(framebufferHeight, height);
|
||||
}
|
||||
|
||||
passData.renderRect = Recti(0, 0, int(framebufferWidth), int(framebufferHeight));
|
||||
|
||||
passData.framebuffer = renderDevice->InstantiateFramebuffer(framebufferWidth, framebufferHeight, passData.renderPass, textures);
|
||||
if (!passData.name.empty())
|
||||
passData.framebuffer->UpdateDebugName(passData.name);
|
||||
}
|
||||
|
||||
passData.renderRect = Recti(0, 0, int(framebufferWidth), int(framebufferHeight));
|
||||
|
||||
passData.framebuffer = renderDevice->InstantiateFramebuffer(framebufferWidth, framebufferHeight, passData.renderPass, textures);
|
||||
if (!passData.name.empty())
|
||||
passData.framebuffer->UpdateDebugName(passData.name);
|
||||
else
|
||||
passData.renderRect = Recti(0, 0, -1, -1);
|
||||
|
||||
passData.forceCommandBufferRegeneration = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ namespace Nz
|
|||
PerViewerData& viewerData = *Retrieve(m_viewerData, viewer);
|
||||
|
||||
std::size_t arrayInputIndex = pass.AddInput(viewerData.textureArrayAttachmentIndex);
|
||||
pass.SetInputLayout(arrayInputIndex, TextureLayout::ColorInput);
|
||||
pass.SetInputAssumedLayout(arrayInputIndex, TextureLayout::ColorInput);
|
||||
|
||||
for (CascadeData& cascade : viewerData.cascades)
|
||||
pass.AddInput(cascade.attachmentIndex);
|
||||
|
|
|
|||
|
|
@ -383,21 +383,26 @@ namespace Nz
|
|||
}
|
||||
|
||||
frameGraphInvalidated |= m_bakedFrameGraph.Resize(renderResources, viewerSizes);
|
||||
if (frameGraphInvalidated)
|
||||
{
|
||||
for (ViewerData& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.blitShaderBinding)
|
||||
renderResources.PushForRelease(std::move(viewerData.blitShaderBinding));
|
||||
}
|
||||
}
|
||||
|
||||
// Find active lights (i.e. visible in any frustum)
|
||||
m_activeLights.Clear();
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
for (ViewerData* viewerData : m_orderedViewers)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 renderMask = viewerData.viewer->GetRenderMask();
|
||||
UInt32 renderMask = viewerData->viewer->GetRenderMask();
|
||||
|
||||
// Extract frustum from viewproj matrix
|
||||
const Matrix4f& viewProjMatrix = viewerData.viewer->GetViewerInstance().GetViewProjMatrix();
|
||||
viewerData.frame.frustum = Frustumf::Extract(viewProjMatrix);
|
||||
const Matrix4f& viewProjMatrix = viewerData->viewer->GetViewerInstance().GetViewProjMatrix();
|
||||
viewerData->frame.frustum = Frustumf::Extract(viewProjMatrix);
|
||||
|
||||
viewerData.frame.visibleLights.Clear();
|
||||
viewerData->frame.visibleLights.Clear();
|
||||
for (auto it = m_lightPool.begin(); it != m_lightPool.end(); ++it)
|
||||
{
|
||||
const LightData& lightData = *it;
|
||||
|
|
@ -407,7 +412,7 @@ namespace Nz
|
|||
continue;
|
||||
|
||||
m_activeLights.UnboundedSet(lightIndex);
|
||||
viewerData.frame.visibleLights.UnboundedSet(lightIndex);
|
||||
viewerData->frame.visibleLights.UnboundedSet(lightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -422,61 +427,34 @@ namespace Nz
|
|||
}
|
||||
|
||||
// Viewer handling (second pass)
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
for (ViewerData* viewerData : m_orderedViewers)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
UInt32 renderMask = viewerData.viewer->GetRenderMask();
|
||||
UInt32 renderMask = viewerData->viewer->GetRenderMask();
|
||||
|
||||
// Per-viewer shadow map handling
|
||||
for (std::size_t lightIndex : viewerData.frame.visibleLights.IterBits())
|
||||
for (std::size_t lightIndex : viewerData->frame.visibleLights.IterBits())
|
||||
{
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(lightIndex);
|
||||
if (lightData->shadowData && lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
|
||||
lightData->shadowData->PrepareRendering(renderResources, viewerData.viewer);
|
||||
lightData->shadowData->PrepareRendering(renderResources, viewerData->viewer);
|
||||
}
|
||||
|
||||
// Frustum culling
|
||||
std::size_t visibilityHash = 5;
|
||||
const auto& visibleRenderables = FrustumCull(viewerData.frame.frustum, renderMask, visibilityHash);
|
||||
const auto& visibleRenderables = FrustumCull(viewerData->frame.frustum, renderMask, visibilityHash);
|
||||
|
||||
FramePipelinePass::FrameData passData = {
|
||||
&viewerData.frame.visibleLights,
|
||||
viewerData.frame.frustum,
|
||||
&viewerData->frame.visibleLights,
|
||||
viewerData->frame.frustum,
|
||||
renderResources,
|
||||
visibleRenderables,
|
||||
visibilityHash
|
||||
};
|
||||
|
||||
for (auto& passPtr : viewerData.passes)
|
||||
for (auto& passPtr : viewerData->passes)
|
||||
passPtr->Prepare(passData);
|
||||
}
|
||||
|
||||
if (frameGraphInvalidated)
|
||||
{
|
||||
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
if (viewerData.blitShaderBinding)
|
||||
renderResources.PushForRelease(std::move(viewerData.blitShaderBinding));
|
||||
|
||||
viewerData.blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0);
|
||||
viewerData.blitShaderBinding->Update({
|
||||
{
|
||||
0,
|
||||
ShaderBinding::SampledTextureBinding {
|
||||
m_bakedFrameGraph.GetAttachmentTexture(viewerData.finalColorAttachment).get(),
|
||||
sampler.get()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update UBOs and materials
|
||||
renderResources.Execute([&](CommandBufferBuilder& builder)
|
||||
{
|
||||
|
|
@ -498,10 +476,6 @@ namespace Nz
|
|||
m_bakedFrameGraph.Execute(renderResources);
|
||||
m_rebuildFrameGraph = false;
|
||||
|
||||
// Final blit (TODO: Make part of frame graph?)
|
||||
for (auto&& [renderTargetPtr, renderTargetData] : m_renderTargets)
|
||||
renderTargetPtr->OnRenderEnd(renderResources, m_bakedFrameGraph, renderTargetData.finalAttachment);
|
||||
|
||||
// reset at the end instead of the beginning so debug draw can be used before calling this method
|
||||
DebugDrawer& debugDrawer = GetDebugDrawer();
|
||||
debugDrawer.Reset(renderResources);
|
||||
|
|
@ -648,6 +622,7 @@ namespace Nz
|
|||
{
|
||||
FrameGraph frameGraph;
|
||||
|
||||
// Register viewer-independent passes
|
||||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
{
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
|
|
@ -655,149 +630,177 @@ namespace Nz
|
|||
lightData->shadowData->RegisterToFrameGraph(frameGraph, nullptr);
|
||||
}
|
||||
|
||||
using ViewerPair = std::pair<const RenderTarget*, ViewerData*>;
|
||||
|
||||
StackArray<ViewerPair> viewers = NazaraStackArray(ViewerPair, m_viewerPool.size());
|
||||
auto viewerIt = viewers.begin();
|
||||
|
||||
// Group every viewer by their render order and RenderTarget
|
||||
m_orderedViewers.clear();
|
||||
for (auto& viewerData : m_viewerPool)
|
||||
{
|
||||
if (viewerData.pendingDestruction)
|
||||
continue;
|
||||
|
||||
const RenderTarget& renderTarget = viewerData.viewer->GetRenderTarget();
|
||||
*viewerIt++ = std::make_pair(&renderTarget, &viewerData);
|
||||
m_orderedViewers.push_back(&viewerData);
|
||||
}
|
||||
|
||||
std::sort(viewers.begin(), viewers.end(), [](const ViewerPair& lhs, const ViewerPair& rhs)
|
||||
if (m_orderedViewers.empty())
|
||||
return frameGraph.Bake();
|
||||
|
||||
std::sort(m_orderedViewers.begin(), m_orderedViewers.end(), [](ViewerData* lhs, ViewerData* rhs)
|
||||
{
|
||||
return lhs.second->renderOrder < rhs.second->renderOrder;
|
||||
// Order by RenderTarget render order and then by viewer render order
|
||||
Int32 leftTargetRenderOrder1 = lhs->viewer->GetRenderTarget().GetRenderOrder();
|
||||
Int32 rightTargetRenderOrder1 = rhs->viewer->GetRenderTarget().GetRenderOrder();
|
||||
|
||||
if (leftTargetRenderOrder1 == rightTargetRenderOrder1)
|
||||
return leftTargetRenderOrder1 < rightTargetRenderOrder1;
|
||||
else
|
||||
return lhs->renderOrder < rhs->renderOrder;
|
||||
});
|
||||
|
||||
StackVector<std::size_t> dependenciesColorAttachments = NazaraStackVector(std::size_t, viewers.size());
|
||||
StackVector<std::size_t> dependenciesColorAttachments = NazaraStackVector(std::size_t, m_orderedViewers.size());
|
||||
std::size_t dependenciesColorAttachmentCount = 0;
|
||||
Int32 lastRenderOrder = m_orderedViewers.front()->viewer->GetRenderTarget().GetRenderOrder();
|
||||
|
||||
m_orderedViewers.clear();
|
||||
m_renderTargets.clear();
|
||||
unsigned int viewerIndex = 0;
|
||||
for (auto it = viewers.begin(), prevIt = it; it != viewers.end(); ++it)
|
||||
std::size_t viewerIndex = 0;
|
||||
auto HandleRenderTarget = [&](const RenderTarget& renderTarget, std::span<ViewerData*> viewers)
|
||||
{
|
||||
auto&& [renderTarget, viewerData] = *it;
|
||||
|
||||
UInt32 renderMask = viewerData->viewer->GetRenderMask();
|
||||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
if (renderTarget.GetRenderOrder() > lastRenderOrder)
|
||||
{
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
|
||||
lightData->shadowData->RegisterToFrameGraph(frameGraph, viewerData->viewer);
|
||||
dependenciesColorAttachmentCount = dependenciesColorAttachments.size();
|
||||
lastRenderOrder = renderTarget.GetRenderOrder();
|
||||
}
|
||||
|
||||
// Keep track of previous dependencies attachments (from viewers having a smaller render order)
|
||||
Int32 renderOrder = viewerData->renderOrder;
|
||||
for (auto it2 = prevIt; prevIt != it; ++prevIt)
|
||||
{
|
||||
ViewerData* prevViewerData = prevIt->second;
|
||||
Int32 prevRenderOrder = prevViewerData->renderOrder;
|
||||
if (prevRenderOrder >= renderOrder)
|
||||
break;
|
||||
assert(!viewers.empty());
|
||||
|
||||
dependenciesColorAttachments.push_back(prevViewerData->finalColorAttachment);
|
||||
prevIt = it2;
|
||||
}
|
||||
|
||||
auto framePassCallback = [&, viewerData = viewerData](std::size_t /*passIndex*/, FramePass& framePass, FramePipelinePassFlags flags)
|
||||
for (ViewerData* viewerData : viewers)
|
||||
{
|
||||
// Inject previous final attachments as inputs for all passes, to force framegraph to order viewers passes relative to each other
|
||||
// TODO: Allow the user to define which pass of viewer A uses viewer B rendering
|
||||
for (std::size_t finalAttachment : dependenciesColorAttachments)
|
||||
UInt32 renderMask = viewerData->viewer->GetRenderMask();
|
||||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
{
|
||||
std::size_t inputIndex = framePass.AddInput(finalAttachment);
|
||||
|
||||
// Disable ReadInput to prevent the framegraph from transitionning the texture layout (for now it's handled externally)
|
||||
// (however if we manage to get rid of the texture blit from RenderTexture by making the framegraph use the external texture directly, this would be necessary)
|
||||
framePass.SetReadInput(inputIndex, false);
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
if (lightData->shadowData->IsPerViewer() && (renderMask & lightData->renderMask) != 0)
|
||||
lightData->shadowData->RegisterToFrameGraph(frameGraph, viewerData->viewer);
|
||||
}
|
||||
|
||||
if (flags.Test(FramePipelinePassFlag::LightShadowing))
|
||||
auto framePassCallback = [&, viewerData = viewerData](std::size_t /*passIndex*/, FramePass& framePass, FramePipelinePassFlags flags)
|
||||
{
|
||||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
// Inject previous final attachments as inputs for all passes, to force framegraph to order viewers passes relative to each other
|
||||
// TODO: Allow the user to define which pass of viewer A uses viewer B rendering
|
||||
for (std::size_t i = 0; i < dependenciesColorAttachmentCount; ++i)
|
||||
framePass.AddInput(dependenciesColorAttachments[i]);
|
||||
|
||||
if (flags.Test(FramePipelinePassFlag::LightShadowing))
|
||||
{
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
if ((renderMask & lightData->renderMask) != 0)
|
||||
lightData->shadowData->RegisterPassInputs(framePass, (lightData->shadowData->IsPerViewer()) ? viewerData->viewer : nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
viewerData->finalColorAttachment = viewerData->viewer->RegisterPasses(viewerData->passes, frameGraph, viewerIndex++, framePassCallback);
|
||||
|
||||
// Group viewers by render targets
|
||||
auto& renderTargetData = m_renderTargets[renderTarget];
|
||||
renderTargetData.viewers.push_back(viewerData);
|
||||
m_orderedViewers.push_back(viewerData);
|
||||
}
|
||||
|
||||
for (auto&& [renderTarget, renderTargetData] : m_renderTargets)
|
||||
{
|
||||
const auto& targetViewers = renderTargetData.viewers;
|
||||
|
||||
if (targetViewers.size() > 1)
|
||||
{
|
||||
// Multiple viewers on the same targets, merge them
|
||||
FramePass& mergePass = frameGraph.AddPass("Merge pass");
|
||||
|
||||
renderTargetData.finalAttachment = frameGraph.AddAttachment({
|
||||
"Viewer output",
|
||||
PixelFormat::RGBA8
|
||||
});
|
||||
|
||||
for (const ViewerData* viewerData : targetViewers)
|
||||
mergePass.AddInput(viewerData->finalColorAttachment);
|
||||
|
||||
mergePass.AddOutput(renderTargetData.finalAttachment);
|
||||
mergePass.SetClearColor(0, Color::Black());
|
||||
|
||||
mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const FramePassEnvironment& /*env*/)
|
||||
{
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(false));
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (const ViewerData* viewerData : targetViewers)
|
||||
{
|
||||
Recti renderRect = viewerData->viewer->GetViewport();
|
||||
|
||||
builder.SetScissor(renderRect);
|
||||
builder.SetViewport(renderRect);
|
||||
|
||||
const ShaderBindingPtr& blitShaderBinding = viewerData->blitShaderBinding;
|
||||
|
||||
builder.BindRenderShaderBinding(0, *blitShaderBinding);
|
||||
builder.Draw(3);
|
||||
|
||||
if (first)
|
||||
for (std::size_t i : m_shadowCastingLights.IterBits())
|
||||
{
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(true));
|
||||
first = false;
|
||||
LightData* lightData = m_lightPool.RetrieveFromIndex(i);
|
||||
if ((renderMask & lightData->renderMask) != 0)
|
||||
lightData->shadowData->RegisterPassInputs(framePass, (lightData->shadowData->IsPerViewer()) ? viewerData->viewer : nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderTarget->OnBuildGraph(frameGraph, renderTargetData.finalAttachment);
|
||||
viewerData->finalColorAttachment = viewerData->viewer->RegisterPasses(viewerData->passes, frameGraph, viewerIndex++, framePassCallback);
|
||||
}
|
||||
else if (targetViewers.size() == 1)
|
||||
|
||||
std::size_t finalAttachment;
|
||||
if (viewers.size() > 1)
|
||||
{
|
||||
// Multiple viewers on the same targets, merge them
|
||||
finalAttachment = renderTarget.OnBuildGraph(frameGraph, BuildMergePass(frameGraph, viewers));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single viewer on that target
|
||||
const auto& viewer = *targetViewers.front();
|
||||
const auto& viewer = *viewers.front();
|
||||
finalAttachment = renderTarget.OnBuildGraph(frameGraph, viewer.finalColorAttachment);
|
||||
}
|
||||
|
||||
renderTargetData.finalAttachment = viewer.finalColorAttachment;
|
||||
renderTarget->OnBuildGraph(frameGraph, renderTargetData.finalAttachment);
|
||||
if (!renderTarget.IsFrameGraphOutput())
|
||||
{
|
||||
// Keep track of previous dependencies attachments
|
||||
dependenciesColorAttachments.push_back(finalAttachment);
|
||||
}
|
||||
else
|
||||
frameGraph.AddOutput(finalAttachment);
|
||||
};
|
||||
|
||||
const RenderTarget* currentTarget = &m_orderedViewers.front()->viewer->GetRenderTarget();
|
||||
std::size_t currentTargetIndex = 0;
|
||||
for (std::size_t i = 1; i < m_orderedViewers.size(); ++i)
|
||||
{
|
||||
const RenderTarget* target = &m_orderedViewers[i]->viewer->GetRenderTarget();
|
||||
if (currentTarget != target)
|
||||
{
|
||||
HandleRenderTarget(*currentTarget, std::span(&m_orderedViewers[currentTargetIndex], &m_orderedViewers[i]));
|
||||
currentTarget = target;
|
||||
currentTargetIndex = i;
|
||||
}
|
||||
}
|
||||
HandleRenderTarget(*currentTarget, std::span(m_orderedViewers.data() + currentTargetIndex, m_orderedViewers.data() + m_orderedViewers.size()));
|
||||
|
||||
return frameGraph.Bake();
|
||||
}
|
||||
|
||||
std::size_t ForwardFramePipeline::BuildMergePass(FrameGraph& frameGraph, std::span<ViewerData*> targetViewers)
|
||||
{
|
||||
FramePass& mergePass = frameGraph.AddPass("Merge pass");
|
||||
|
||||
std::size_t mergedAttachment = frameGraph.AddAttachment({
|
||||
"Merged output",
|
||||
PixelFormat::RGBA8
|
||||
});
|
||||
|
||||
for (const ViewerData* viewerData : targetViewers)
|
||||
mergePass.AddInput(viewerData->finalColorAttachment);
|
||||
|
||||
mergePass.AddOutput(mergedAttachment);
|
||||
mergePass.SetClearColor(0, Color::Black());
|
||||
|
||||
mergePass.SetCommandCallback([&targetViewers](CommandBufferBuilder& builder, const FramePassEnvironment& env)
|
||||
{
|
||||
Graphics* graphics = Graphics::Instance();
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(false));
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (ViewerData* viewerData : targetViewers)
|
||||
{
|
||||
Recti renderRect = viewerData->viewer->GetViewport();
|
||||
|
||||
builder.SetScissor(renderRect);
|
||||
builder.SetViewport(renderRect);
|
||||
|
||||
if (!viewerData->blitShaderBinding)
|
||||
{
|
||||
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
|
||||
|
||||
viewerData->blitShaderBinding = graphics->GetBlitPipelineLayout()->AllocateShaderBinding(0);
|
||||
viewerData->blitShaderBinding->Update({
|
||||
{
|
||||
0,
|
||||
ShaderBinding::SampledTextureBinding {
|
||||
env.frameGraph.GetAttachmentTexture(viewerData->finalColorAttachment).get(),
|
||||
sampler.get()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const ShaderBindingPtr& blitShaderBinding = viewerData->blitShaderBinding;
|
||||
|
||||
builder.BindRenderShaderBinding(0, *blitShaderBinding);
|
||||
builder.Draw(3);
|
||||
|
||||
if (first)
|
||||
{
|
||||
builder.BindRenderPipeline(*graphics->GetBlitPipeline(true));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return mergedAttachment;
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::RegisterMaterialInstance(MaterialInstance* materialInstance)
|
||||
{
|
||||
auto it = m_materialInstances.find(materialInstance);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Nz
|
|||
BakedFrameGraph FrameGraph::Bake()
|
||||
{
|
||||
if (m_graphOutputs.empty())
|
||||
throw std::runtime_error("no backbuffer output has been set");
|
||||
throw std::runtime_error("no graph output has been set");
|
||||
|
||||
m_pending.attachmentReadList.clear();
|
||||
m_pending.attachmentToTextures.clear();
|
||||
|
|
@ -89,7 +89,8 @@ namespace Nz
|
|||
for (std::size_t i = 0; i < colorOutputs.size(); ++i)
|
||||
{
|
||||
const auto& output = colorOutputs[i];
|
||||
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, output.attachmentId));
|
||||
if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, output.attachmentId); textureIndex != InvalidTextureIndex)
|
||||
bakedPass.outputTextureIndices.push_back(textureIndex);
|
||||
|
||||
if (output.clearColor)
|
||||
{
|
||||
|
|
@ -111,9 +112,15 @@ namespace Nz
|
|||
|
||||
std::size_t attachmentId;
|
||||
if (attachmentId = framePass.GetDepthStencilOutput(); attachmentId != FramePass::InvalidAttachmentId)
|
||||
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId));
|
||||
{
|
||||
if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, attachmentId); textureIndex != InvalidTextureIndex)
|
||||
bakedPass.outputTextureIndices.push_back(textureIndex);
|
||||
}
|
||||
else if (attachmentId = framePass.GetDepthStencilInput(); attachmentId != FramePass::InvalidAttachmentId)
|
||||
bakedPass.outputTextureIndices.push_back(Retrieve(m_pending.attachmentToTextures, attachmentId)); //< FIXME?
|
||||
{
|
||||
if (std::size_t textureIndex = Retrieve(m_pending.attachmentToTextures, attachmentId); textureIndex != InvalidTextureIndex)
|
||||
bakedPass.outputTextureIndices.push_back(textureIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,25 +206,31 @@ namespace Nz
|
|||
for (const auto& input : framePass.GetInputs())
|
||||
{
|
||||
std::size_t textureId = RegisterTexture(input.attachmentId);
|
||||
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= TextureUsage::ShaderSampling;
|
||||
if (textureId != InvalidTextureIndex)
|
||||
{
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= input.textureUsageFlags.value_or(TextureUsage::ShaderSampling);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& output : framePass.GetOutputs())
|
||||
{
|
||||
std::size_t textureId = RegisterTexture(output.attachmentId);
|
||||
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= TextureUsage::ColorAttachment;
|
||||
if (textureId != InvalidTextureIndex)
|
||||
{
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= TextureUsage::ColorAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
if (std::size_t depthStencilInput = framePass.GetDepthStencilInput(); depthStencilInput != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
std::size_t textureId = RegisterTexture(depthStencilInput);
|
||||
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= TextureUsage::DepthStencilAttachment;
|
||||
if (textureId != InvalidTextureIndex)
|
||||
{
|
||||
FrameGraphTextureData& attachmentData = m_pending.textures[textureId];
|
||||
attachmentData.usage |= TextureUsage::DepthStencilAttachment;
|
||||
}
|
||||
|
||||
if (std::size_t depthStencilOutput = framePass.GetDepthStencilOutput(); depthStencilOutput != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
|
|
@ -296,8 +309,11 @@ namespace Nz
|
|||
auto it = m_pending.attachmentToTextures.find(output);
|
||||
assert(it != m_pending.attachmentToTextures.end());
|
||||
|
||||
auto& finalTexture = m_pending.textures[it->second];
|
||||
finalTexture.usage |= TextureUsage::ShaderSampling | TextureUsage::TransferSource;
|
||||
if (std::size_t textureIndex = it->second; textureIndex != InvalidTextureIndex)
|
||||
{
|
||||
auto& finalTexture = m_pending.textures[textureIndex];
|
||||
finalTexture.usage |= TextureUsage::ShaderSampling | TextureUsage::TransferSource;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply texture view usage to their parents
|
||||
|
|
@ -316,13 +332,15 @@ namespace Nz
|
|||
assert(m_pending.barrierList.empty());
|
||||
m_pending.barrierList.reserve(m_pending.passList.size());
|
||||
|
||||
auto GetBarrier = [&](std::vector<Barrier>& barriers, std::size_t attachmentId) -> Barrier&
|
||||
auto GetBarrier = [&](std::vector<Barrier>& barriers, std::size_t attachmentId) -> Barrier*
|
||||
{
|
||||
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, ResolveAttachmentIndex(attachmentId));
|
||||
if (textureId == InvalidTextureIndex)
|
||||
return nullptr;
|
||||
|
||||
auto it = std::find_if(barriers.begin(), barriers.end(), [&](const Barrier& barrier) { return barrier.textureId == textureId; });
|
||||
if (it != barriers.end())
|
||||
return *it;
|
||||
return &*it;
|
||||
else
|
||||
{
|
||||
// Insert a new barrier
|
||||
|
|
@ -330,7 +348,7 @@ namespace Nz
|
|||
barrier.textureId = textureId;
|
||||
barrier.layout = TextureLayout::Undefined;
|
||||
|
||||
return barrier;
|
||||
return &barrier;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -340,29 +358,35 @@ namespace Nz
|
|||
|
||||
auto& barriers = m_pending.barrierList.emplace_back();
|
||||
|
||||
auto GetInvalidationBarrier = [&](std::size_t attachmentId) -> Barrier& { return GetBarrier(barriers.invalidationBarriers, attachmentId); };
|
||||
auto GetFlushBarrier = [&](std::size_t attachmentId) -> Barrier& { return GetBarrier(barriers.flushBarriers, attachmentId); };
|
||||
auto GetInvalidationBarrier = [&](std::size_t attachmentId) -> Barrier* { return GetBarrier(barriers.invalidationBarriers, attachmentId); };
|
||||
auto GetFlushBarrier = [&](std::size_t attachmentId) -> Barrier* { return GetBarrier(barriers.flushBarriers, attachmentId); };
|
||||
|
||||
for (const auto& input : framePass.GetInputs())
|
||||
{
|
||||
auto& barrier = GetInvalidationBarrier(input.attachmentId);
|
||||
if (barrier.layout != TextureLayout::Undefined)
|
||||
Barrier* barrier = GetInvalidationBarrier(input.attachmentId);
|
||||
if (!barrier)
|
||||
continue;
|
||||
|
||||
if (barrier->layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
|
||||
barrier.access |= MemoryAccess::ShaderRead;
|
||||
barrier.stages |= PipelineStage::FragmentShader;
|
||||
barrier.layout = TextureLayout::ColorInput;
|
||||
barrier->access |= input.accessFlags;
|
||||
barrier->stages |= input.stageFlags;
|
||||
barrier->layout = input.layout;
|
||||
}
|
||||
|
||||
for (const auto& output : framePass.GetOutputs())
|
||||
{
|
||||
auto& barrier = GetFlushBarrier(output.attachmentId);
|
||||
if (barrier.layout != TextureLayout::Undefined)
|
||||
Barrier* barrier = GetFlushBarrier(output.attachmentId);
|
||||
if (!barrier)
|
||||
continue;
|
||||
|
||||
if (barrier->layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
|
||||
barrier.access |= MemoryAccess::ColorWrite;
|
||||
barrier.stages |= PipelineStage::ColorOutput;
|
||||
barrier.layout = TextureLayout::ColorOutput;
|
||||
barrier->access |= MemoryAccess::ColorWrite;
|
||||
barrier->stages |= PipelineStage::ColorOutput;
|
||||
barrier->layout = TextureLayout::ColorOutput;
|
||||
}
|
||||
|
||||
std::size_t dsInputAttachment = framePass.GetDepthStencilInput();
|
||||
|
|
@ -371,40 +395,48 @@ namespace Nz
|
|||
if (dsInputAttachment != FramePass::InvalidAttachmentId && dsOutputAttachement != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
// DS input/output
|
||||
auto& invalidationBarrier = GetInvalidationBarrier(dsInputAttachment);
|
||||
if (invalidationBarrier.layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
if (Barrier* invalidationBarrier = GetInvalidationBarrier(dsInputAttachment))
|
||||
{
|
||||
if (invalidationBarrier->layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
|
||||
invalidationBarrier.layout = TextureLayout::DepthStencilReadWrite;
|
||||
invalidationBarrier.access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite;
|
||||
invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate;
|
||||
invalidationBarrier->layout = TextureLayout::DepthStencilReadWrite;
|
||||
invalidationBarrier->access = MemoryAccess::DepthStencilRead | MemoryAccess::DepthStencilWrite;
|
||||
invalidationBarrier->stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate;
|
||||
}
|
||||
|
||||
auto& flushBarrier = GetFlushBarrier(dsOutputAttachement);
|
||||
flushBarrier.layout = TextureLayout::DepthStencilReadWrite;
|
||||
flushBarrier.access = MemoryAccess::DepthStencilWrite;
|
||||
flushBarrier.stages = PipelineStage::FragmentTestsLate;
|
||||
if (Barrier* flushBarrier = GetFlushBarrier(dsOutputAttachement))
|
||||
{
|
||||
flushBarrier->layout = TextureLayout::DepthStencilReadWrite;
|
||||
flushBarrier->access = MemoryAccess::DepthStencilWrite;
|
||||
flushBarrier->stages = PipelineStage::FragmentTestsLate;
|
||||
}
|
||||
}
|
||||
else if (dsInputAttachment != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
// DS input-only
|
||||
auto& invalidationBarrier = GetInvalidationBarrier(dsInputAttachment);
|
||||
if (invalidationBarrier.layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
if (Barrier* invalidationBarrier = GetInvalidationBarrier(dsInputAttachment))
|
||||
{
|
||||
if (invalidationBarrier->layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
|
||||
invalidationBarrier.layout = TextureLayout::DepthStencilReadWrite;
|
||||
invalidationBarrier.access = MemoryAccess::DepthStencilRead;
|
||||
invalidationBarrier.stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate;
|
||||
invalidationBarrier->layout = TextureLayout::DepthStencilReadWrite;
|
||||
invalidationBarrier->access = MemoryAccess::DepthStencilRead;
|
||||
invalidationBarrier->stages = PipelineStage::FragmentTestsEarly | PipelineStage::FragmentTestsLate;
|
||||
}
|
||||
}
|
||||
else if (dsOutputAttachement != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
// DS output-only
|
||||
auto& flushBarrier = GetFlushBarrier(dsOutputAttachement);
|
||||
if (flushBarrier.layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
if (Barrier* flushBarrier = GetFlushBarrier(dsOutputAttachement))
|
||||
{
|
||||
if (flushBarrier->layout != TextureLayout::Undefined)
|
||||
throw std::runtime_error("layout mismatch");
|
||||
|
||||
flushBarrier.layout = TextureLayout::DepthStencilReadWrite;
|
||||
flushBarrier.access = MemoryAccess::DepthStencilWrite;
|
||||
flushBarrier.stages = PipelineStage::FragmentTestsLate;
|
||||
flushBarrier->layout = TextureLayout::DepthStencilReadWrite;
|
||||
flushBarrier->access = MemoryAccess::DepthStencilWrite;
|
||||
flushBarrier->stages = PipelineStage::FragmentTestsLate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -729,12 +761,14 @@ namespace Nz
|
|||
auto RegisterColorInputRead = [&](const FramePass::Input& input)
|
||||
{
|
||||
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, input.attachmentId);
|
||||
if (textureId == InvalidTextureIndex)
|
||||
return;
|
||||
|
||||
TextureLayout& textureLayout = textureLayouts[textureId];
|
||||
if (!input.assumedLayout)
|
||||
{
|
||||
assert(textureLayouts[textureId] != TextureLayout::Undefined);
|
||||
textureLayout = TextureLayout::ColorInput;
|
||||
textureLayout = input.layout;
|
||||
}
|
||||
else
|
||||
textureLayout = *input.assumedLayout;
|
||||
|
|
@ -743,6 +777,8 @@ namespace Nz
|
|||
auto RegisterColorOutput = [&](const FramePass::Output& output, bool shouldLoad)
|
||||
{
|
||||
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, output.attachmentId);
|
||||
if (textureId == InvalidTextureIndex)
|
||||
return InvalidAttachmentIndex;
|
||||
|
||||
TextureLayout initialLayout = textureLayouts[textureId];
|
||||
textureLayouts[textureId] = TextureLayout::ColorOutput;
|
||||
|
|
@ -770,19 +806,21 @@ namespace Nz
|
|||
return attachmentIndex;
|
||||
};
|
||||
|
||||
auto RegisterDepthStencil = [&](std::size_t attachmentId, TextureLayout textureLayout, bool* first) -> RenderPass::Attachment&
|
||||
auto RegisterDepthStencil = [&](std::size_t attachmentId, TextureLayout textureLayout, bool* first) -> RenderPass::Attachment*
|
||||
{
|
||||
if (depthStencilAttachmentIndex)
|
||||
{
|
||||
assert(depthStencilAttachmentId == attachmentId);
|
||||
*first = false;
|
||||
|
||||
return renderPassAttachments[depthStencilAttachmentIndex.value()];
|
||||
return &renderPassAttachments[depthStencilAttachmentIndex.value()];
|
||||
}
|
||||
|
||||
*first = true;
|
||||
|
||||
std::size_t textureId = Retrieve(m_pending.attachmentToTextures, attachmentId);
|
||||
if (textureId == InvalidTextureIndex)
|
||||
return nullptr;
|
||||
|
||||
TextureLayout initialLayout = textureLayouts[textureId];
|
||||
textureLayouts[textureId] = textureLayout;
|
||||
|
|
@ -796,7 +834,7 @@ namespace Nz
|
|||
depthStencilAttachment.format = m_pending.textures[textureId].format;
|
||||
depthStencilAttachment.initialLayout = initialLayout;
|
||||
|
||||
return depthStencilAttachment;
|
||||
return &depthStencilAttachment;
|
||||
};
|
||||
|
||||
std::size_t physicalPassIndex = 0;
|
||||
|
|
@ -834,11 +872,13 @@ namespace Nz
|
|||
shouldLoad = true;
|
||||
|
||||
std::size_t attachmentIndex = RegisterColorOutput(output, shouldLoad);
|
||||
|
||||
colorAttachments.push_back({
|
||||
attachmentIndex,
|
||||
TextureLayout::ColorOutput
|
||||
});
|
||||
if (attachmentIndex != InvalidAttachmentIndex)
|
||||
{
|
||||
colorAttachments.push_back({
|
||||
attachmentIndex,
|
||||
TextureLayout::ColorOutput
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -855,73 +895,79 @@ namespace Nz
|
|||
{
|
||||
// DS input/output
|
||||
bool first;
|
||||
auto& dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadWrite, &first);
|
||||
|
||||
if (first)
|
||||
RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadWrite, &first);
|
||||
if (dsAttachment)
|
||||
{
|
||||
dsAttachment.loadOp = AttachmentLoadOp::Load;
|
||||
dsAttachment.storeOp = AttachmentStoreOp::Store;
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
dsAttachment->loadOp = AttachmentLoadOp::Load;
|
||||
dsAttachment->storeOp = AttachmentStoreOp::Store;
|
||||
}
|
||||
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadWrite
|
||||
};
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadWrite
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (dsInputAttachment != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
// DS input-only
|
||||
bool first;
|
||||
auto& dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadOnly, &first);
|
||||
|
||||
if (first)
|
||||
RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsInputAttachment, TextureLayout::DepthStencilReadOnly, &first);
|
||||
if (dsAttachment)
|
||||
{
|
||||
bool canDiscard = true;
|
||||
|
||||
// Check if a future pass reads from the DS buffer or if we can discard it after this pass
|
||||
if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end())
|
||||
if (first)
|
||||
{
|
||||
for (std::size_t passIndex : readIt->second)
|
||||
{
|
||||
auto it = m_pending.passIdToPhysicalPassIndex.find(passIndex);
|
||||
if (it == m_pending.passIdToPhysicalPassIndex.end())
|
||||
continue; //< pass may have been discarded
|
||||
bool canDiscard = true;
|
||||
|
||||
std::size_t readPhysicalPassIndex = it->second;
|
||||
if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass?
|
||||
// Check if a future pass reads from the DS buffer or if we can discard it after this pass
|
||||
if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end())
|
||||
{
|
||||
for (std::size_t passIndex : readIt->second)
|
||||
{
|
||||
// Yes, store it
|
||||
canDiscard = false;
|
||||
break;
|
||||
auto it = m_pending.passIdToPhysicalPassIndex.find(passIndex);
|
||||
if (it == m_pending.passIdToPhysicalPassIndex.end())
|
||||
continue; //< pass may have been discarded
|
||||
|
||||
std::size_t readPhysicalPassIndex = it->second;
|
||||
if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass?
|
||||
{
|
||||
// Yes, store it
|
||||
canDiscard = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dsAttachment->storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store;
|
||||
}
|
||||
|
||||
dsAttachment.storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store;
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadOnly
|
||||
};
|
||||
}
|
||||
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadOnly
|
||||
};
|
||||
}
|
||||
else if (dsOutputAttachement != FramePass::InvalidAttachmentId)
|
||||
{
|
||||
// DS output-only
|
||||
bool first;
|
||||
auto& dsAttachment = RegisterDepthStencil(dsOutputAttachement, TextureLayout::DepthStencilReadWrite, &first);
|
||||
|
||||
if (first)
|
||||
RenderPass::Attachment* dsAttachment = RegisterDepthStencil(dsOutputAttachement, TextureLayout::DepthStencilReadWrite, &first);
|
||||
if (dsAttachment)
|
||||
{
|
||||
dsAttachment.initialLayout = TextureLayout::Undefined; //< Don't care about initial layout
|
||||
dsAttachment.loadOp = (framePass.GetDepthStencilClear()) ? AttachmentLoadOp::Clear : AttachmentLoadOp::Discard;
|
||||
dsAttachment.storeOp = AttachmentStoreOp::Store;
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
dsAttachment->initialLayout = TextureLayout::Undefined; //< Don't care about initial layout
|
||||
dsAttachment->loadOp = (framePass.GetDepthStencilClear()) ? AttachmentLoadOp::Clear : AttachmentLoadOp::Discard;
|
||||
dsAttachment->storeOp = AttachmentStoreOp::Store;
|
||||
}
|
||||
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadWrite
|
||||
};
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
depthStencilAttachmentIndex.value(),
|
||||
TextureLayout::DepthStencilReadWrite
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
subpassesDesc.push_back({
|
||||
|
|
@ -943,7 +989,10 @@ namespace Nz
|
|||
|
||||
BuildPhysicalPassDependencies(colorAttachmentCount, depthStencilAttachmentIndex.has_value(), renderPassAttachments, subpassesDesc, subpassesDeps);
|
||||
|
||||
m_pending.renderPasses.push_back(renderPassCache.Get(renderPassAttachments, subpassesDesc, subpassesDeps));
|
||||
if (!renderPassAttachments.empty())
|
||||
m_pending.renderPasses.push_back(renderPassCache.Get(renderPassAttachments, subpassesDesc, subpassesDeps));
|
||||
else
|
||||
m_pending.renderPasses.push_back(nullptr);
|
||||
|
||||
physicalPassIndex++;
|
||||
}
|
||||
|
|
@ -1223,6 +1272,11 @@ namespace Nz
|
|||
|
||||
return textureId;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, DummyAttachment>)
|
||||
{
|
||||
m_pending.attachmentToTextures.emplace(attachmentIndex, InvalidTextureIndex);
|
||||
return InvalidTextureIndex;
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
}, m_attachments[attachmentIndex]);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Nz
|
|||
assert(viewer == nullptr);
|
||||
|
||||
std::size_t cubeInputIndex = pass.AddInput(m_cubeAttachmentIndex);
|
||||
pass.SetInputLayout(cubeInputIndex, TextureLayout::ColorInput);
|
||||
pass.SetInputAssumedLayout(cubeInputIndex, TextureLayout::ColorInput);
|
||||
|
||||
for (DirectionData& direction : m_directions)
|
||||
pass.AddInput(direction.attachmentIndex);
|
||||
|
|
|
|||
|
|
@ -15,13 +15,10 @@ namespace Nz
|
|||
{
|
||||
}
|
||||
|
||||
void RenderTexture::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
std::size_t RenderTexture::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
{
|
||||
graph.BindExternalTexture(attachmentIndex, m_targetTexture);
|
||||
}
|
||||
|
||||
void RenderTexture::OnRenderEnd(RenderResources& /*renderFrame*/, const BakedFrameGraph& /*frameGraph*/, std::size_t /*finalAttachment*/) const
|
||||
{
|
||||
return attachmentIndex;
|
||||
}
|
||||
|
||||
const Vector2ui& RenderTexture::GetSize() const
|
||||
|
|
|
|||
|
|
@ -12,33 +12,33 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
void RenderTextureBlit::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
std::size_t RenderTextureBlit::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
{
|
||||
graph.AddOutput(attachmentIndex);
|
||||
}
|
||||
std::size_t linkAttachment = graph.AddDummyAttachment();
|
||||
|
||||
void RenderTextureBlit::OnRenderEnd(RenderResources& resources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const
|
||||
{
|
||||
const std::shared_ptr<Texture>& sourceTexture = frameGraph.GetAttachmentTexture(finalAttachment);
|
||||
FramePass& blitPass = graph.AddPass("Blit to texture");
|
||||
blitPass.AddInput(attachmentIndex);
|
||||
blitPass.SetInputAccess(0, TextureLayout::TransferSource, PipelineStage::Transfer, MemoryAccess::MemoryRead);
|
||||
blitPass.SetInputUsage(0, TextureUsage::TransferSource);
|
||||
|
||||
Vector2ui sourceTextureSize = Vector2ui(sourceTexture->GetSize());
|
||||
Vector2ui targetTextureSize = Vector2ui(m_targetTexture->GetSize());
|
||||
blitPass.AddOutput(linkAttachment);
|
||||
|
||||
resources.Execute([&](CommandBufferBuilder& builder)
|
||||
blitPass.SetCommandCallback([this, attachmentIndex](CommandBufferBuilder& builder, const FramePassEnvironment& env)
|
||||
{
|
||||
builder.BeginDebugRegion("Blit to texture", Color::Blue());
|
||||
{
|
||||
builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::Transfer, MemoryAccess::ColorWrite, MemoryAccess::TransferRead, TextureLayout::ColorOutput, TextureLayout::TransferSource, *sourceTexture);
|
||||
builder.TextureBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, {}, MemoryAccess::TransferWrite, TextureLayout::Undefined, TextureLayout::TransferDestination, *m_targetTexture);
|
||||
const std::shared_ptr<Texture>& sourceTexture = env.frameGraph.GetAttachmentTexture(attachmentIndex);
|
||||
|
||||
Boxui fromBox(0, 0, 0, sourceTextureSize.x, sourceTextureSize.y, 1);
|
||||
Boxui toBox(0, 0, 0, targetTextureSize.x, targetTextureSize.y, 1);
|
||||
Vector2ui sourceTextureSize = Vector2ui(sourceTexture->GetSize());
|
||||
Vector2ui targetTextureSize = Vector2ui(m_targetTexture->GetSize());
|
||||
|
||||
builder.BlitTexture(*sourceTexture, fromBox, TextureLayout::TransferSource, *m_targetTexture, toBox, TextureLayout::TransferDestination, m_samplerFilter);
|
||||
builder.TextureBarrier(PipelineStage::Transfer, m_targetPipelineStage, MemoryAccess::TransferWrite, m_targetMemoryFlags, TextureLayout::TransferDestination, m_targetLayout, *m_targetTexture);
|
||||
}
|
||||
builder.EndDebugRegion();
|
||||
}, QueueType::Graphics);
|
||||
Boxui fromBox(0, 0, 0, sourceTextureSize.x, sourceTextureSize.y, 1);
|
||||
Boxui toBox(0, 0, 0, targetTextureSize.x, targetTextureSize.y, 1);
|
||||
|
||||
builder.TextureBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, {}, MemoryAccess::TransferWrite, TextureLayout::Undefined, TextureLayout::TransferDestination, *m_targetTexture);
|
||||
builder.BlitTexture(*sourceTexture, fromBox, TextureLayout::TransferSource, *m_targetTexture, toBox, TextureLayout::TransferDestination, m_samplerFilter);
|
||||
builder.TextureBarrier(PipelineStage::Transfer, m_targetPipelineStage, MemoryAccess::TransferWrite, m_targetMemoryFlags, TextureLayout::TransferDestination, m_targetLayout, *m_targetTexture);
|
||||
});
|
||||
|
||||
return linkAttachment;
|
||||
}
|
||||
|
||||
const Vector2ui& RenderTextureBlit::GetSize() const
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
namespace Nz
|
||||
{
|
||||
RenderWindow::RenderWindow(WindowSwapchain& swapchain) :
|
||||
RenderTarget(DefaultRenderOrder),
|
||||
m_swapchain(nullptr),
|
||||
m_windowSwapchain(&swapchain)
|
||||
{
|
||||
|
|
@ -28,30 +29,40 @@ namespace Nz
|
|||
SetSwapchain(nullptr);
|
||||
});
|
||||
|
||||
SetFrameGraphOutput(true);
|
||||
SetSwapchain(m_windowSwapchain->GetSwapchain());
|
||||
}
|
||||
|
||||
void RenderWindow::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
std::size_t RenderWindow::OnBuildGraph(FrameGraph& graph, std::size_t attachmentIndex) const
|
||||
{
|
||||
graph.AddOutput(attachmentIndex);
|
||||
}
|
||||
// TODO: Replace the blit to swapchain by a graph.BindExternalSwapchain?
|
||||
std::size_t linkAttachment = graph.AddDummyAttachment();
|
||||
|
||||
FramePass& blitPass = graph.AddPass("Blit to swapchain");
|
||||
blitPass.AddInput(attachmentIndex);
|
||||
blitPass.SetInputAccess(0, TextureLayout::TransferSource, PipelineStage::Transfer, MemoryAccess::MemoryRead);
|
||||
blitPass.SetInputUsage(0, TextureUsage::TransferSource);
|
||||
|
||||
void RenderWindow::OnRenderEnd(RenderResources& renderResources, const BakedFrameGraph& frameGraph, std::size_t finalAttachment) const
|
||||
{
|
||||
const std::shared_ptr<Texture>& texture = frameGraph.GetAttachmentTexture(finalAttachment);
|
||||
blitPass.AddOutput(linkAttachment);
|
||||
|
||||
Vector2ui textureSize = Vector2ui(texture->GetSize());
|
||||
Boxui blitRegion(0, 0, 0, textureSize.x, textureSize.y, 1);
|
||||
|
||||
renderResources.Execute([&](CommandBufferBuilder& builder)
|
||||
// Force regeneration of RenderWindow execution callback (since image index changes every frame)
|
||||
// TODO: Maybe handle this in a better way (temporary command buffer? multiple commands buffers selected by frame index?)
|
||||
blitPass.SetExecutionCallback([]
|
||||
{
|
||||
builder.BeginDebugRegion("Blit to swapchain", Color::Blue());
|
||||
{
|
||||
builder.TextureBarrier(PipelineStage::ColorOutput, PipelineStage::Transfer, MemoryAccess::ColorWrite, MemoryAccess::TransferRead, TextureLayout::ColorOutput, TextureLayout::TransferSource, *texture);
|
||||
builder.BlitTextureToSwapchain(*texture, blitRegion, TextureLayout::TransferSource, *m_swapchain, renderResources.GetImageIndex());
|
||||
}
|
||||
builder.EndDebugRegion();
|
||||
}, QueueType::Graphics);
|
||||
return FramePassExecution::UpdateAndExecute;
|
||||
});
|
||||
|
||||
blitPass.SetCommandCallback([this, attachmentIndex](CommandBufferBuilder& builder, const FramePassEnvironment& env)
|
||||
{
|
||||
const std::shared_ptr<Texture>& sourceTexture = env.frameGraph.GetAttachmentTexture(attachmentIndex);
|
||||
|
||||
Vector2ui textureSize = Vector2ui(sourceTexture->GetSize());
|
||||
Boxui blitRegion(0, 0, 0, textureSize.x, textureSize.y, 1);
|
||||
|
||||
builder.BlitTextureToSwapchain(*sourceTexture, blitRegion, TextureLayout::TransferSource, *m_swapchain, env.renderResources.GetImageIndex());
|
||||
});
|
||||
|
||||
return linkAttachment;
|
||||
}
|
||||
|
||||
const Vector2ui& RenderWindow::GetSize() const
|
||||
|
|
|
|||
Loading…
Reference in New Issue