Since no new shader can be created during runtime, the full set is known ahead at compile-time. Each shader must reference a "pass" in which it will used to render.
To avoid frame-spikes during runtime, I'd like to pre-create all pipeline objects during startup. And to create a pipieline, the number of outputs and the format of each output attachment must be known - either to create a VkRenderPass or to specify the outputs for the dynamic rendering feature.
However, I'd also like to use the frame graph concept (speech by Yuriy O'Donnell) which in turn builds a graph of render passes with input-output specification and dependencies between them. Some passes are conditionally created (e.g. debug passes), some passes might be dropped from the graph (after "compiling" it).
Additionally, I need to support the "write on top" feature, so instead of specifying a new output during the building of the render pass, I can simply say that the output of this pass will use an output from a previous pass - this is useful for adding alpha-blended rendering, for example.
How can I match the two separate section of the code? In other words, how can I define all render passes during initialization but also use a dynamic approach of building the frame graph each frame without repeating myself?
This is what I'd like to avoid (pseudo-code):
struct Pass1Def
{
output1 = ImageFormat::RGBA8;
output2 = ImageFormat::RGBA8;
// ...
outputs = // outputs in order (corresponds to location in shader)
};
void init()
{
for_each_shaders shader {
passDef = findPassDef(shader);
createPipeline(shader, passDef);
}
}
void render()
{
auto previousResource = someCondition ? passA.outputResource1 : passB.outputResource2;
graph.addPass(..., [&](PassBuilder& builder, Pass1Data& data) {
// error-prone: order of function calls matter (corresponds to location in shader)
// error-prone: use the same format defined in Pass1Def
data.outputResource1 = builder.create(... ImageFormat::RGBA8);
// error-prone: the format depends on the outputResource of a previous pass
// however the format must be (and was) specified in Pass1Def
data.outputResource2 = builder.write(previousResource);
});
}