From bb72e796bf08dff7645eb6cb735505d4038e7691 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 12 Feb 2019 17:46:28 -0800 Subject: [PATCH] Switch to FrameGraph Filament's main rendering loop now uses the new FrameGraph API. --- filament/src/PostProcessManager.cpp | 166 +++------------------------- filament/src/PostProcessManager.h | 44 +------- filament/src/Renderer.cpp | 150 +++++++++++-------------- filament/src/fg/FrameGraph.cpp | 65 ++++++----- filament/src/fg/FrameGraph.h | 7 ++ 5 files changed, 136 insertions(+), 296 deletions(-) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 4947562187..52c68dab5b 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -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 program) noexcept { - mCommands.push_back({program, format}); -} - -void PostProcessManager::finish(driver::TargetBufferFlags discarded, - Handle 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 const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive(); - std::vector& 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("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; diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 8799d5d1f9..b102b397e3 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -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 - #include -#include - 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 texture, + void setSource(uint32_t viewportWidth, uint32_t viewportHeight, Handle 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 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 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 program = {}; - driver::TextureFormat format; - }; - - std::vector mCommands; - // we need only one of these mutable UniformBuffer mPostProcessUb; Handle mPostProcessSbh; diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 229639199f..d76edd8053 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -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 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("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 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(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 toneMappingProgram = engine.getPostProcessProgram( - translucent ? PostProcessStage::TONE_MAPPING_TRANSLUCENT - : PostProcessStage::TONE_MAPPING_OPAQUE); - ppm.pass(useFXAA ? TextureFormat::RGBA8 : ldrFormat, toneMappingProgram); - - if (useFXAA) { - Handle 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); } diff --git a/filament/src/fg/FrameGraph.cpp b/filament/src/fg/FrameGraph.cpp index afee9fe011..f43d4f5d29 100644 --- a/filament/src/fg/FrameGraph.cpp +++ b/filament/src/fg/FrameGraph.cpp @@ -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::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; } diff --git a/filament/src/fg/FrameGraph.h b/filament/src/fg/FrameGraph.h index 2c74f4ce69..5f5f1fd212 100644 --- a/filament/src/fg/FrameGraph.h +++ b/filament/src/fg/FrameGraph.h @@ -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);