Switch to FrameGraph
Filament's main rendering loop now uses the new FrameGraph API.
This commit is contained in:
committed by
Mathias Agopian
parent
c5fc7a01e8
commit
bb72e796bf
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "PostProcessManager.h"
|
||||
#include "RenderTargetPool.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
@@ -30,14 +29,13 @@
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace math;
|
||||
using namespace driver;
|
||||
using namespace details;
|
||||
using namespace filament::details;
|
||||
|
||||
void PostProcessManager::init(FEngine& engine) noexcept {
|
||||
mEngine = &engine;
|
||||
|
||||
mCommands.reserve(8);
|
||||
|
||||
mPostProcessUb = UniformBuffer(engine.getPerPostProcessUib());
|
||||
|
||||
// create sampler for post-process FBO
|
||||
@@ -70,10 +68,11 @@ void PostProcessManager::setSource(uint32_t viewportWidth, uint32_t viewportHeig
|
||||
auto duration = engine.getEngineTime();
|
||||
float fraction = (duration.count() % 1000000000) / 1000000000.0f;
|
||||
|
||||
float2 uvScale = float2{ viewportWidth, viewportHeight } / float2{ textureWidth, textureHeight };
|
||||
|
||||
UniformBuffer& ub = mPostProcessUb;
|
||||
ub.setUniform(offsetof(PostProcessingUib, time), fraction);
|
||||
ub.setUniform(offsetof(PostProcessingUib, uvScale),
|
||||
filament::math::float2{ viewportWidth, viewportHeight } / filament::math::float2{ textureWidth, textureHeight });
|
||||
ub.setUniform(offsetof(PostProcessingUib, uvScale), uvScale);
|
||||
|
||||
// The shader may need to know the offset between the top of the texture and the top
|
||||
// of the rectangle that it actually needs to sample from.
|
||||
@@ -84,144 +83,8 @@ void PostProcessManager::setSource(uint32_t viewportWidth, uint32_t viewportHeig
|
||||
driver.updateUniformBuffer(mPostProcessUbh, ub.toBufferDescriptor(driver));
|
||||
}
|
||||
|
||||
void PostProcessManager::blit(driver::TextureFormat format) noexcept {
|
||||
mCommands.push_back({{}, format});
|
||||
}
|
||||
|
||||
void PostProcessManager::pass(driver::TextureFormat format, Handle<HwProgram> program) noexcept {
|
||||
mCommands.push_back({program, format});
|
||||
}
|
||||
|
||||
void PostProcessManager::finish(driver::TargetBufferFlags discarded,
|
||||
Handle<HwRenderTarget> viewRenderTarget,
|
||||
filament::Viewport const& vp,
|
||||
RenderTargetPool::Target const* previous,
|
||||
filament::Viewport const& svp) {
|
||||
|
||||
assert(viewRenderTarget);
|
||||
assert(previous);
|
||||
|
||||
FEngine& engine = *mEngine;
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
RenderTargetPool& rtp = engine.getRenderTargetPool();
|
||||
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
|
||||
std::vector<Command>& commands = mCommands;
|
||||
|
||||
if (UTILS_UNLIKELY(commands.empty())) {
|
||||
rtp.put(previous);
|
||||
return;
|
||||
}
|
||||
|
||||
Driver::PipelineState pipeline;
|
||||
|
||||
pipeline.rasterState.culling = Driver::RasterState::CullingMode::NONE;
|
||||
pipeline.rasterState.colorWrite = true;
|
||||
pipeline.rasterState.depthFunc = Driver::RasterState::DepthFunc::A;
|
||||
|
||||
RenderPassParams params = {};
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::DEPTH_AND_STENCIL;
|
||||
params.flags.dependencies = RenderPassFlags::DEPENDENCY_BY_REGION;
|
||||
params.viewport.left = 0;
|
||||
params.viewport.bottom = 0;
|
||||
params.viewport.width = svp.width;
|
||||
params.viewport.height = svp.height;
|
||||
|
||||
for (size_t i = 0, c = commands.size() - 1; i < c; i++) {
|
||||
// if the next command is a blit, it we don't need a texture
|
||||
uint8_t flags = !commands[i + 1].program ? RenderTargetPool::Target::NO_TEXTURE : uint8_t();
|
||||
|
||||
// create a render target for this pass
|
||||
RenderTargetPool::Target const* target = rtp.get(
|
||||
TargetBufferFlags::COLOR, svp.width, svp.height, 1, commands[i].format, flags);
|
||||
|
||||
assert(target);
|
||||
|
||||
if (commands[i].program) {
|
||||
// set the source for this pass (i.e. previous target)
|
||||
setSource(params.viewport.width, params.viewport.height, previous->texture, previous->w, previous->h);
|
||||
|
||||
// draw a full screen triangle
|
||||
pipeline.program = commands[i].program;
|
||||
driver.beginRenderPass(target->target, params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive);
|
||||
driver.endRenderPass();
|
||||
} else {
|
||||
driver.blit(TargetBufferFlags::COLOR,
|
||||
target->target, { 0, 0, svp.width, svp.height },
|
||||
previous->target, { 0, 0, svp.width, svp.height });
|
||||
}
|
||||
// return the previous target to the pool
|
||||
rtp.put(previous);
|
||||
previous = target;
|
||||
}
|
||||
|
||||
assert(!commands.empty());
|
||||
assert(previous);
|
||||
|
||||
// The last command is special, it always draw to the viewRenderTarget and uses
|
||||
// the non scaled viewport.
|
||||
if (commands.back().program) {
|
||||
params.flags.discardStart = discarded;
|
||||
params.flags.discardEnd = TargetBufferFlags::DEPTH_AND_STENCIL;
|
||||
params.viewport.left = vp.left;
|
||||
params.viewport.bottom = vp.bottom;
|
||||
params.viewport.width = vp.width;
|
||||
params.viewport.height = vp.height;
|
||||
|
||||
setSource(params.viewport.width, params.viewport.height, previous->texture, previous->w, previous->h);
|
||||
pipeline.program = commands.back().program;
|
||||
driver.beginRenderPass(viewRenderTarget, params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive);
|
||||
driver.endRenderPass();
|
||||
|
||||
} else {
|
||||
driver.blit(TargetBufferFlags::COLOR,
|
||||
viewRenderTarget, { vp.left, vp.bottom, vp.width, vp.height },
|
||||
previous->target, { 0, 0, svp.width, svp.height });
|
||||
}
|
||||
|
||||
rtp.put(previous);
|
||||
|
||||
// clear our command buffer
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FrameGraphResource PostProcessManager::msaa(FrameGraph& fg,
|
||||
FrameGraphResource input, driver::TextureFormat outFormat) noexcept {
|
||||
|
||||
struct PostProcessMSAA {
|
||||
FrameGraphResource input;
|
||||
FrameGraphResource output;
|
||||
};
|
||||
|
||||
auto& ppMSAA = fg.addPass<PostProcessMSAA>("msaa",
|
||||
[&](FrameGraph::Builder& builder, PostProcessMSAA& data) {
|
||||
auto const* inputDesc = fg.getDescriptor(input);
|
||||
data.input = builder.useRenderTarget(input).textures[0];
|
||||
|
||||
FrameGraphResource::Descriptor outputDesc{
|
||||
.width = inputDesc->width,
|
||||
.height = inputDesc->height,
|
||||
.format = outFormat
|
||||
};
|
||||
data.output = builder.createTexture("msaa output", outputDesc);
|
||||
data.output = builder.useRenderTarget(data.output).textures[0];
|
||||
},
|
||||
[=](FrameGraphPassResources const& resources,
|
||||
PostProcessMSAA const& data, DriverApi& driver) {
|
||||
auto in = resources.getRenderTarget(data.input);
|
||||
auto out = resources.getRenderTarget(data.output);
|
||||
driver.blit(TargetBufferFlags::COLOR,
|
||||
out.target, out.params.viewport, in.target, in.params.viewport);
|
||||
});
|
||||
|
||||
return ppMSAA.getData().output;
|
||||
}
|
||||
|
||||
FrameGraphResource PostProcessManager::toneMapping(FrameGraph& fg,
|
||||
FrameGraphResource input, driver::TextureFormat outFormat, bool translucent) noexcept {
|
||||
|
||||
@@ -257,10 +120,14 @@ FrameGraphResource PostProcessManager::toneMapping(FrameGraph& fg,
|
||||
pipeline.rasterState.depthFunc = Driver::RasterState::DepthFunc::A;
|
||||
pipeline.program = toneMappingProgram;
|
||||
|
||||
auto const& targetDesc = resources.getDescriptor(data.output);
|
||||
auto const& textureDesc = resources.getDescriptor(data.input);
|
||||
auto const& texture = resources.getTexture(data.input);
|
||||
setSource(targetDesc.width, targetDesc.height, texture, textureDesc.width, textureDesc.height);
|
||||
// TODO: the first parameters below are the *actual viewport* size
|
||||
// (as opposed to the size of the source texture). Currently we don't allow
|
||||
// the texture to be resized, so they match. We'll need something more
|
||||
// sofisticated in the future.
|
||||
setSource(textureDesc.width, textureDesc.height,
|
||||
texture, textureDesc.width, textureDesc.height);
|
||||
|
||||
auto const& target = resources.getRenderTarget(data.output);
|
||||
driver.beginRenderPass(target.target, target.params);
|
||||
@@ -308,10 +175,14 @@ FrameGraphResource PostProcessManager::fxaa(FrameGraph& fg,
|
||||
pipeline.rasterState.depthFunc = Driver::RasterState::DepthFunc::A;
|
||||
pipeline.program = antiAliasingProgram;
|
||||
|
||||
auto const& targetDesc = resources.getDescriptor(data.output);
|
||||
auto const& textureDesc = resources.getDescriptor(data.input);
|
||||
auto const& texture = resources.getTexture(data.input);
|
||||
setSource(targetDesc.width, targetDesc.height, texture, textureDesc.width, textureDesc.height);
|
||||
// TODO: the first parameters below are the *actual viewport* size
|
||||
// (as opposed to the size of the source texture). Currently we don't allow
|
||||
// the texture to be resized, so they match. We'll need something more
|
||||
// sofisticated in the future.
|
||||
setSource(textureDesc.width, textureDesc.height,
|
||||
texture, textureDesc.width, textureDesc.height);
|
||||
|
||||
auto const& target = resources.getRenderTarget(data.output);
|
||||
driver.beginRenderPass(target.target, target.params);
|
||||
@@ -323,8 +194,7 @@ FrameGraphResource PostProcessManager::fxaa(FrameGraph& fg,
|
||||
}
|
||||
|
||||
FrameGraphResource PostProcessManager::dynamicScaling(FrameGraph& fg,
|
||||
FrameGraphResource input, driver::TextureFormat outFormat,
|
||||
filament::Viewport const& outViewport) noexcept {
|
||||
FrameGraphResource input, driver::TextureFormat outFormat) noexcept {
|
||||
|
||||
struct PostProcessScaling {
|
||||
FrameGraphResource input;
|
||||
|
||||
@@ -17,21 +17,14 @@
|
||||
#ifndef TNT_FILAMENT_POSTPROCESS_MANAGER_H
|
||||
#define TNT_FILAMENT_POSTPROCESS_MANAGER_H
|
||||
|
||||
#include "RenderTargetPool.h"
|
||||
|
||||
#include "UniformBuffer.h"
|
||||
|
||||
#include "driver/DriverApiForward.h"
|
||||
|
||||
#include "fg/FrameGraphResource.h"
|
||||
|
||||
#include "driver/DriverApiForward.h"
|
||||
#include "driver/Handle.h"
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <filament/driver/DriverEnums.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace filament {
|
||||
|
||||
namespace details {
|
||||
@@ -43,29 +36,9 @@ class PostProcessManager {
|
||||
public:
|
||||
void init(details::FEngine& engine) noexcept;
|
||||
void terminate(driver::DriverApi& driver) noexcept;
|
||||
void setSource(uint32_t viewportWidth, uint32_t viewportHeight, Handle <HwTexture> texture,
|
||||
void setSource(uint32_t viewportWidth, uint32_t viewportHeight, Handle<HwTexture> texture,
|
||||
uint32_t textureWidth, uint32_t textureHeight) const noexcept;
|
||||
|
||||
// start() is a scam, it does nothing
|
||||
void start() noexcept { }
|
||||
|
||||
// a fullscreen pass, using the given format as target and writing into the specified program
|
||||
void pass(driver::TextureFormat format, Handle<HwProgram> program) noexcept;
|
||||
|
||||
// a blit pass, using the given format as target
|
||||
void blit(driver::TextureFormat format = driver::TextureFormat::RGBA8) noexcept;
|
||||
|
||||
void finish(driver::TargetBufferFlags discarded,
|
||||
Handle<HwRenderTarget> viewRenderTarget,
|
||||
Viewport const& vp,
|
||||
RenderTargetPool::Target const* linearTarget,
|
||||
Viewport const& svp);
|
||||
|
||||
|
||||
FrameGraphResource msaa(
|
||||
FrameGraph& fg, FrameGraphResource input,
|
||||
driver::TextureFormat outFormat) noexcept;
|
||||
|
||||
FrameGraphResource toneMapping(
|
||||
FrameGraph& fg, FrameGraphResource input, driver::TextureFormat outFormat,
|
||||
bool translucent) noexcept;
|
||||
@@ -75,20 +48,11 @@ public:
|
||||
bool translucent) noexcept;
|
||||
|
||||
FrameGraphResource dynamicScaling(
|
||||
FrameGraph& fg, FrameGraphResource input,
|
||||
driver::TextureFormat outFormat, Viewport const& outViewport) noexcept;
|
||||
|
||||
FrameGraph& fg, FrameGraphResource input, driver::TextureFormat outFormat) noexcept;
|
||||
|
||||
private:
|
||||
details::FEngine* mEngine = nullptr;
|
||||
|
||||
struct Command {
|
||||
Handle<HwProgram> program = {};
|
||||
driver::TextureFormat format;
|
||||
};
|
||||
|
||||
std::vector<Command> mCommands;
|
||||
|
||||
// we need only one of these
|
||||
mutable UniformBuffer mPostProcessUb;
|
||||
Handle<HwSamplerBuffer> mPostProcessSbh;
|
||||
|
||||
+67
-83
@@ -157,7 +157,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
PostProcessManager& ppm = engine.getPostProcessManager();
|
||||
RenderTargetPool& rtp = engine.getRenderTargetPool();
|
||||
|
||||
// DEBUG: driver commands must all happen from the same thread. Enforce that on debug builds.
|
||||
engine.getDriverApi().debugThreading();
|
||||
@@ -205,103 +204,88 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* Frame graph
|
||||
*/
|
||||
|
||||
FrameGraph fg;
|
||||
|
||||
const TextureFormat hdrFormat = getHdrFormat(view);
|
||||
const uint8_t useMSAA = view.getSampleCount();
|
||||
|
||||
// FIXME: we use "hasPostProcess" as a proxy for deciding if we need a depth-buffer or not
|
||||
// historically this has been true, but it's definitely wrong.
|
||||
// This hack is needed because viewRenderTarget(output) doesn't have a depth-buffer,
|
||||
// so when skipping post-process (which draws directly into it), we can't rely on it.
|
||||
const bool colorPassNeedsDepthBuffer = hasPostProcess;
|
||||
|
||||
const Handle<HwRenderTarget> viewRenderTarget = getRenderTarget();
|
||||
FrameGraphResource output = fg.importResource("viewRenderTarget",
|
||||
{ .viewport = vp }, viewRenderTarget, vp.width, vp.height);
|
||||
|
||||
/*
|
||||
* Depth + Color passes
|
||||
*/
|
||||
|
||||
const uint8_t useMSAA = view.getSampleCount();
|
||||
const TextureFormat hdrFormat = getHdrFormat(view);
|
||||
const TextureFormat ldrFormat = getLdrFormat();
|
||||
RenderTargetPool::Target const* colorTarget = nullptr;
|
||||
struct ColorPassData {
|
||||
FrameGraphResource color;
|
||||
FrameGraphResource depth;
|
||||
};
|
||||
|
||||
if (UTILS_LIKELY(hasPostProcess)) {
|
||||
// allocate the target we need for rendering the scene
|
||||
colorTarget = rtp.get(TargetBufferFlags::COLOR_AND_DEPTH,
|
||||
svp.width, svp.height, useMSAA, hdrFormat);
|
||||
svp.left = svp.bottom = 0;
|
||||
}
|
||||
auto& colorPass = fg.addPass<ColorPassData>("Color pass",
|
||||
[&](FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
|
||||
// FIXME: viewRenderTarget doesn't have a depth-buffer, so when skipping post-process, don't rely on it
|
||||
const Handle<HwRenderTarget> viewRenderTarget = getRenderTarget();
|
||||
ColorPass::renderColorPass(engine, js, jobFroxelize,
|
||||
colorTarget ? colorTarget->target : viewRenderTarget, view, svp, commands);
|
||||
data.color = builder.createTexture("color buffer",
|
||||
{ .width = svp.width, .height = svp.height, .format = hdrFormat });
|
||||
|
||||
if (colorPassNeedsDepthBuffer) {
|
||||
data.depth = builder.createTexture("depth buffer",
|
||||
{ .width = svp.width, .height = svp.height });
|
||||
}
|
||||
|
||||
FrameGraphRenderTarget::Descriptor desc{
|
||||
.samples = useMSAA,
|
||||
.attachments.color = data.color,
|
||||
.attachments.depth = data.depth
|
||||
};
|
||||
data.color = builder.useRenderTarget("colorRenderTarget", desc).textures[0];
|
||||
},
|
||||
[=, &engine, &js, &view, &commands]
|
||||
(FrameGraphPassResources const& resources,
|
||||
ColorPassData const& data, DriverApi& driver) {
|
||||
auto out = resources.getRenderTarget(data.color);
|
||||
ColorPass::renderColorPass(engine, js, jobFroxelize, out.target, view,
|
||||
static_cast<filament::Viewport const&>(out.params.viewport),
|
||||
commands);
|
||||
});
|
||||
|
||||
FrameGraphResource input = colorPass.getData().color;
|
||||
|
||||
/*
|
||||
* Post Processing...
|
||||
*/
|
||||
|
||||
if (hasPostProcess) {
|
||||
const TextureFormat ldrFormat = getLdrFormat();
|
||||
const bool translucent = mSwapChain->isTransparent();
|
||||
|
||||
#define USE_FRAME_GRAPH false
|
||||
|
||||
if (UTILS_LIKELY(hasPostProcess)) {
|
||||
driver.pushGroupMarker("Post Processing");
|
||||
|
||||
assert(colorTarget);
|
||||
|
||||
if (USE_FRAME_GRAPH) {
|
||||
FrameGraph fg;
|
||||
|
||||
const bool translucent = mSwapChain->isTransparent();
|
||||
|
||||
FrameGraphResource::Descriptor colorDesc{
|
||||
.width = colorTarget->w,
|
||||
.height= colorTarget->h,
|
||||
.format= colorTarget->format,
|
||||
};
|
||||
FrameGraphResource input = fg.importResource("colorTarget",
|
||||
colorDesc, colorTarget->texture);
|
||||
|
||||
FrameGraphResource output = fg.importResource("viewRenderTarget", { .viewport = vp },
|
||||
viewRenderTarget, vp.width, vp.height);
|
||||
|
||||
if (useMSAA > 1) {
|
||||
input = ppm.msaa(fg, input, hdrFormat);
|
||||
}
|
||||
input = ppm.toneMapping(fg, input, ldrFormat, translucent);
|
||||
if (useFXAA) {
|
||||
input = ppm.fxaa(fg, input, ldrFormat, translucent);
|
||||
}
|
||||
if (scaled) {
|
||||
input = ppm.dynamicScaling(fg, input, ldrFormat, vp);
|
||||
}
|
||||
|
||||
fg.moveResource(output, input);
|
||||
fg.present(output);
|
||||
|
||||
fg.compile();
|
||||
//fg.export_graphviz(slog.d);
|
||||
fg.execute(driver);
|
||||
|
||||
rtp.put(colorTarget);
|
||||
|
||||
} else {
|
||||
|
||||
ppm.start();
|
||||
|
||||
const bool translucent = mSwapChain->isTransparent();
|
||||
Handle<HwProgram> toneMappingProgram = engine.getPostProcessProgram(
|
||||
translucent ? PostProcessStage::TONE_MAPPING_TRANSLUCENT
|
||||
: PostProcessStage::TONE_MAPPING_OPAQUE);
|
||||
ppm.pass(useFXAA ? TextureFormat::RGBA8 : ldrFormat, toneMappingProgram);
|
||||
|
||||
if (useFXAA) {
|
||||
Handle<HwProgram> antiAliasingProgram = engine.getPostProcessProgram(
|
||||
translucent ? PostProcessStage::ANTI_ALIASING_TRANSLUCENT
|
||||
: PostProcessStage::ANTI_ALIASING_OPAQUE);
|
||||
ppm.pass(ldrFormat, antiAliasingProgram);
|
||||
}
|
||||
|
||||
if (scaled) {
|
||||
// because it's the last command, the TextureFormat is not relevant
|
||||
ppm.blit();
|
||||
}
|
||||
ppm.finish(view.getDiscardedTargetBuffers(), viewRenderTarget, vp, colorTarget, svp);
|
||||
|
||||
input = ppm.toneMapping(fg, input, ldrFormat, translucent);
|
||||
if (useFXAA) {
|
||||
input = ppm.fxaa(fg, input, ldrFormat, translucent);
|
||||
}
|
||||
if (scaled) {
|
||||
input = ppm.dynamicScaling(fg, input, ldrFormat);
|
||||
}
|
||||
|
||||
driver.popGroupMarker();
|
||||
}
|
||||
|
||||
fg.present(input);
|
||||
|
||||
fg.moveResource(output, input);
|
||||
|
||||
fg.compile();
|
||||
//fg.export_graphviz(slog.d);
|
||||
fg.execute(driver);
|
||||
|
||||
// for debugging
|
||||
recordHighWatermark(commands);
|
||||
}
|
||||
|
||||
@@ -43,13 +43,6 @@ struct Alias { //4
|
||||
FrameGraphResource from, to;
|
||||
};
|
||||
|
||||
struct TargetFlags {
|
||||
uint8_t clear = 0; // this is provided by the user -- it overides discardStart
|
||||
uint8_t discardStart = 0; // calculated in compile()
|
||||
uint8_t discardEnd = 0; // calculated in compile()
|
||||
uint8_t dependencies = 0; // currently ignored
|
||||
};
|
||||
|
||||
struct VirtualResource {
|
||||
VirtualResource() noexcept = default;
|
||||
VirtualResource(VirtualResource const&) = default;
|
||||
@@ -110,6 +103,10 @@ struct ResourceNode { // 24
|
||||
PassNode* writer = nullptr; // writer to this node
|
||||
uint32_t readerCount = 0; // # of passes reading from this resource
|
||||
|
||||
// updated by builder
|
||||
static constexpr uint16_t UNINITIALIZED = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t renderTargetIndex = UNINITIALIZED; // used to retrieve the RT infos
|
||||
|
||||
// constants
|
||||
const uint8_t version; // version of the resource when the node was created
|
||||
};
|
||||
@@ -202,10 +199,10 @@ struct RenderTarget { // 32
|
||||
|
||||
// set by builder
|
||||
FrameGraphRenderTarget::Descriptor desc;
|
||||
TargetFlags userTargetFlags{};
|
||||
RenderPassFlags userTargetFlags{};
|
||||
|
||||
// set in compile
|
||||
TargetFlags targetFlags{};
|
||||
RenderPassFlags targetFlags{};
|
||||
RenderTargetResource* cache = nullptr;
|
||||
|
||||
void resolve(FrameGraph& fg) noexcept {
|
||||
@@ -258,9 +255,9 @@ struct RenderTarget { // 32
|
||||
colorFormat = pResource->desc.format;
|
||||
}
|
||||
|
||||
// if the resource doesn't need an actual texture, we are free to
|
||||
// increase its size.
|
||||
relaxed = relaxed && (pResource->desc.relaxed || !pResource->needsTexture);
|
||||
// TODO: if the resource doesn't need an actual texture, we may be able to
|
||||
// increase its size.
|
||||
relaxed = relaxed && pResource->desc.relaxed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,13 +394,6 @@ struct PassNode { // 200
|
||||
return r;
|
||||
}
|
||||
|
||||
FrameGraphResource useRenderTarget(FrameGraph& fg, FrameGraphResource handle) {
|
||||
// using a resource as a RT implies reading (i.e. adds a reference to that resource) from it
|
||||
handle = read(fg, handle, true);
|
||||
// using a resource as a RT implies writing (i.e. adds a reference to the pass) into it
|
||||
return write(fg, handle);
|
||||
}
|
||||
|
||||
// constants
|
||||
const char* const name; // our name
|
||||
const uint32_t id; // a unique id (only for debugging)
|
||||
@@ -506,13 +496,18 @@ FrameGraph::Builder::Attachments FrameGraph::Builder::useRenderTarget(const char
|
||||
for (FrameGraphResource const& attachment : desc.attachments.textures) {
|
||||
const size_t index = &attachment - desc.attachments.textures.data();
|
||||
if (attachment.isValid()) {
|
||||
// using a resource as a RT implies reading (i.e. adds a reference to that resource) from it
|
||||
rt.textures[index] = mPass.read(fg, attachment, true);
|
||||
// using a resource as a RT implies writing (i.e. adds a reference to the pass) into it
|
||||
rt.textures[index] = mPass.write(fg, rt.textures[index]);
|
||||
|
||||
ResourceNode& node = fg.getResource(attachment);
|
||||
ResourceNode& node = fg.getResource(rt.textures[index]);
|
||||
uint8_t usage = node.resource->usage;
|
||||
usage |= usages[index];
|
||||
node.resource->usage = TextureUsage(usage);
|
||||
|
||||
rt.textures[index] = mPass.useRenderTarget(fg, attachment);
|
||||
// renderTargetIndex is used to retrieve the Descriptor
|
||||
node.renderTargetIndex = renderTarget.index;
|
||||
}
|
||||
}
|
||||
return rt;
|
||||
@@ -520,13 +515,14 @@ FrameGraph::Builder::Attachments FrameGraph::Builder::useRenderTarget(const char
|
||||
|
||||
FrameGraph::Builder::Attachments FrameGraph::Builder::useRenderTarget(
|
||||
FrameGraphResource texture, TargetBufferFlags clearFlags) noexcept {
|
||||
uint8_t samples = 1;
|
||||
if (isAttachment(texture)) {
|
||||
samples = getRenderTargetDescriptor(texture).samples;
|
||||
}
|
||||
ResourceNode& resourceNode = mFrameGraph.getResource(texture);
|
||||
Resource* pResource = resourceNode.resource;
|
||||
assert(pResource);
|
||||
FrameGraphRenderTarget::Descriptor desc {
|
||||
.attachments.color = texture,
|
||||
.samples = 1
|
||||
};
|
||||
FrameGraphRenderTarget::Descriptor desc { .attachments.color = texture, .samples = samples };
|
||||
return useRenderTarget(pResource->name, desc, clearFlags);
|
||||
}
|
||||
|
||||
@@ -547,6 +543,21 @@ FrameGraph::Builder& FrameGraph::Builder::sideEffect() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool FrameGraph::Builder::isAttachment(FrameGraphResource resource) const noexcept {
|
||||
ResourceNode& node = mFrameGraph.getResource(resource);
|
||||
return node.renderTargetIndex != ResourceNode::UNINITIALIZED;
|
||||
}
|
||||
|
||||
FrameGraphRenderTarget::Descriptor const&
|
||||
FrameGraph::Builder::getRenderTargetDescriptor(FrameGraphResource attachment) const {
|
||||
FrameGraph& fg = mFrameGraph;
|
||||
ResourceNode& node = fg.getResource(attachment);
|
||||
ASSERT_POSTCONDITION(node.renderTargetIndex != ResourceNode::UNINITIALIZED,
|
||||
"Resource \"%s\" isn't a render target attachment", node.resource->name);
|
||||
assert(node.renderTargetIndex < fg.mRenderTargets.size());
|
||||
return fg.mRenderTargets[node.renderTargetIndex].desc;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FrameGraphPassResources::FrameGraphPassResources(FrameGraph& fg, fg::PassNode const& pass) noexcept
|
||||
@@ -726,6 +737,10 @@ bool FrameGraph::equals(FrameGraphRenderTarget::Descriptor const& lhs,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!rhs.isValid()) {
|
||||
// it's okay if the cached RT has more attachments than we require
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) && lhs.samples == rhs.samples;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,13 @@ public:
|
||||
// Calling write() on an imported resource automatically adds a side-effect.
|
||||
Builder& sideEffect() noexcept;
|
||||
|
||||
// returns whether this resource is an attachment to some rendertarget
|
||||
bool isAttachment(FrameGraphResource resource) const noexcept;
|
||||
|
||||
// returns the descriptor of the render target this attachment belongs to
|
||||
FrameGraphRenderTarget::Descriptor const& getRenderTargetDescriptor(
|
||||
FrameGraphResource attachment) const;
|
||||
|
||||
private:
|
||||
// this is private for now because we only have textures, and this is for regular buffers
|
||||
FrameGraphResource write(FrameGraphResource const& output);
|
||||
|
||||
Reference in New Issue
Block a user