Skip to content

Commit d1c93b1

Browse files
committed
WIP: using Unreal Engine's way to do RenderGraph
1 parent f3920d9 commit d1c93b1

5 files changed

Lines changed: 407 additions & 167 deletions

File tree

Core/Source/Lux/Renderer/RenderGraph.cpp

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "RenderGraph.h"
33

44
#include <algorithm>
5+
#include <format>
56

67
namespace Lux {
78

@@ -17,12 +18,24 @@ namespace Lux {
1718
return static_cast<ResourceHandle>(m_Textures.size() - 1);
1819
}
1920

20-
uint32_t RenderGraph::AddPass(const PassDesc& desc)
21+
uint32_t RenderGraph::AddPass(PassDesc desc)
2122
{
22-
m_Passes.push_back(desc);
23+
NormalizeResourceList(desc.Reads);
24+
NormalizeResourceList(desc.Writes);
25+
26+
if (desc.Name.empty())
27+
desc.Name = std::format("Pass {}", m_Passes.size());
28+
29+
m_Passes.push_back(std::move(desc));
2330
return static_cast<uint32_t>(m_Passes.size() - 1);
2431
}
2532

33+
uint32_t RenderGraph::AddPass(PassDesc desc, ExecuteCallback execute)
34+
{
35+
desc.Execute = std::move(execute);
36+
return AddPass(std::move(desc));
37+
}
38+
2639
bool RenderGraph::AreAliasCompatible(const TextureDesc& lhs, const TextureDesc& rhs)
2740
{
2841
return lhs.Transient && rhs.Transient
@@ -36,7 +49,14 @@ namespace Lux {
3649
&& lhs.Layers == rhs.Layers;
3750
}
3851

39-
std::vector<RenderGraph::ResourceLifetime> RenderGraph::BuildAliasPlan() const
52+
void RenderGraph::NormalizeResourceList(std::vector<ResourceHandle>& resources)
53+
{
54+
resources.erase(std::remove(resources.begin(), resources.end(), InvalidResource), resources.end());
55+
std::sort(resources.begin(), resources.end());
56+
resources.erase(std::unique(resources.begin(), resources.end()), resources.end());
57+
}
58+
59+
std::vector<RenderGraph::ResourceLifetime> RenderGraph::BuildResourceLifetimes(std::vector<std::string>* diagnostics) const
4060
{
4161
std::vector<ResourceLifetime> lifetimes(m_Textures.size());
4262
for (ResourceHandle resource = 0; resource < m_Textures.size(); resource++)
@@ -48,7 +68,11 @@ namespace Lux {
4868
auto touchResource = [&](ResourceHandle resource)
4969
{
5070
if (resource >= lifetimes.size())
71+
{
72+
if (diagnostics)
73+
diagnostics->push_back(std::format("Pass '{}' references invalid resource {}", pass.Name, resource));
5174
return;
75+
}
5276

5377
ResourceLifetime& lifetime = lifetimes[resource];
5478
lifetime.FirstPass = std::min(lifetime.FirstPass, passIndex);
@@ -61,6 +85,94 @@ namespace Lux {
6185
touchResource(resource);
6286
}
6387

88+
return lifetimes;
89+
}
90+
91+
RenderGraph::CompileResult RenderGraph::Compile() const
92+
{
93+
CompileResult result;
94+
result.Lifetimes = BuildResourceLifetimes(&result.Diagnostics);
95+
result.Valid = result.Diagnostics.empty();
96+
97+
std::vector<bool> neededResources(m_Textures.size(), false);
98+
for (ResourceHandle resource = 0; resource < m_Textures.size(); resource++)
99+
{
100+
const TextureDesc& texture = m_Textures[resource];
101+
if (!texture.Transient)
102+
neededResources[resource] = true;
103+
}
104+
105+
std::vector<bool> passNeeded(m_Passes.size(), false);
106+
for (uint32_t passIndex = static_cast<uint32_t>(m_Passes.size()); passIndex > 0; passIndex--)
107+
{
108+
const uint32_t index = passIndex - 1;
109+
const PassDesc& pass = m_Passes[index];
110+
111+
const bool pinned = HasFlag(pass.Flags, PassFlags::SideEffect) || HasFlag(pass.Flags, PassFlags::NeverCull);
112+
bool writesNeededOutput = false;
113+
for (ResourceHandle resource : pass.Writes)
114+
{
115+
if (resource < neededResources.size() && neededResources[resource])
116+
{
117+
writesNeededOutput = true;
118+
break;
119+
}
120+
}
121+
122+
const bool keepPass = pinned || writesNeededOutput;
123+
passNeeded[index] = keepPass;
124+
if (!keepPass)
125+
continue;
126+
127+
for (ResourceHandle resource : pass.Writes)
128+
{
129+
if (resource < neededResources.size())
130+
neededResources[resource] = false;
131+
}
132+
133+
for (ResourceHandle resource : pass.Reads)
134+
{
135+
if (resource < neededResources.size())
136+
neededResources[resource] = true;
137+
}
138+
}
139+
140+
result.ExecutionOrder.reserve(m_Passes.size());
141+
for (uint32_t passIndex = 0; passIndex < m_Passes.size(); passIndex++)
142+
{
143+
if (passNeeded[passIndex])
144+
result.ExecutionOrder.push_back(passIndex);
145+
else
146+
result.CulledPasses.push_back(passIndex);
147+
}
148+
149+
return result;
150+
}
151+
152+
RenderGraph::CompileResult RenderGraph::Execute() const
153+
{
154+
CompileResult result = Compile();
155+
Execute(result);
156+
return result;
157+
}
158+
159+
void RenderGraph::Execute(const CompileResult& compileResult) const
160+
{
161+
for (uint32_t passIndex : compileResult.ExecutionOrder)
162+
{
163+
if (passIndex >= m_Passes.size())
164+
continue;
165+
166+
const PassDesc& pass = m_Passes[passIndex];
167+
if (pass.Execute)
168+
pass.Execute();
169+
}
170+
}
171+
172+
std::vector<RenderGraph::ResourceLifetime> RenderGraph::BuildAliasPlan() const
173+
{
174+
std::vector<ResourceLifetime> lifetimes = BuildResourceLifetimes();
175+
64176
std::vector<ResourceHandle> lifetimeOrder;
65177
lifetimeOrder.reserve(lifetimes.size());
66178
for (ResourceHandle resource = 0; resource < lifetimes.size(); resource++)

Core/Source/Lux/Renderer/RenderGraph.h

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Lux/Renderer/Image.h"
44

55
#include <cstdint>
6+
#include <functional>
67
#include <string>
78
#include <vector>
89

@@ -12,8 +13,19 @@ namespace Lux {
1213
{
1314
public:
1415
using ResourceHandle = uint32_t;
16+
using ExecuteCallback = std::function<void()>;
1517
static constexpr ResourceHandle InvalidResource = UINT32_MAX;
1618

19+
enum class PassFlags : uint32_t
20+
{
21+
None = 0,
22+
Graphics = BIT(0),
23+
Compute = BIT(1),
24+
Transfer = BIT(2),
25+
SideEffect = BIT(3),
26+
NeverCull = BIT(4)
27+
};
28+
1729
struct TextureDesc
1830
{
1931
std::string Name;
@@ -34,6 +46,8 @@ namespace Lux {
3446
std::string Name;
3547
std::vector<ResourceHandle> Reads;
3648
std::vector<ResourceHandle> Writes;
49+
PassFlags Flags = PassFlags::None;
50+
ExecuteCallback Execute;
3751
};
3852

3953
struct ResourceLifetime
@@ -44,16 +58,41 @@ namespace Lux {
4458
uint32_t AliasIndex = UINT32_MAX;
4559
};
4660

61+
struct CompileResult
62+
{
63+
std::vector<ResourceLifetime> Lifetimes;
64+
std::vector<uint32_t> ExecutionOrder;
65+
std::vector<uint32_t> CulledPasses;
66+
std::vector<std::string> Diagnostics;
67+
bool Valid = true;
68+
};
69+
4770
void Reset();
4871
ResourceHandle AddTransientTexture(const TextureDesc& desc);
49-
uint32_t AddPass(const PassDesc& desc);
72+
uint32_t AddPass(PassDesc desc);
73+
uint32_t AddPass(PassDesc desc, ExecuteCallback execute);
74+
CompileResult Compile() const;
75+
CompileResult Execute() const;
76+
void Execute(const CompileResult& compileResult) const;
5077
std::vector<ResourceLifetime> BuildAliasPlan() const;
5178

5279
const std::vector<TextureDesc>& GetTextures() const { return m_Textures; }
5380
const std::vector<PassDesc>& GetPasses() const { return m_Passes; }
5481

82+
static constexpr PassFlags CombineFlags(PassFlags lhs, PassFlags rhs)
83+
{
84+
return static_cast<PassFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
85+
}
86+
87+
static constexpr bool HasFlag(PassFlags flags, PassFlags flag)
88+
{
89+
return (static_cast<uint32_t>(flags) & static_cast<uint32_t>(flag)) != 0;
90+
}
91+
5592
private:
5693
static bool AreAliasCompatible(const TextureDesc& lhs, const TextureDesc& rhs);
94+
static void NormalizeResourceList(std::vector<ResourceHandle>& resources);
95+
std::vector<ResourceLifetime> BuildResourceLifetimes(std::vector<std::string>* diagnostics = nullptr) const;
5796

5897
std::vector<TextureDesc> m_Textures;
5998
std::vector<PassDesc> m_Passes;

0 commit comments

Comments
 (0)