22#include " RenderGraph.h"
33
44#include < algorithm>
5+ #include < format>
56
67namespace 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++)
0 commit comments