From f11e5cb081ea5478d7ec62cb9a23e45ca0a5f9e0 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 23 Aug 2024 16:01:40 -0700 Subject: [PATCH] remove uses of the blackboard the framegraph blackboard tends to create a lot of problems hard to debug, so we are now more explicit. here we remove usages of the blackboard in RenderUtils.cpp. --- filament/src/PostProcessManager.cpp | 1 + filament/src/RendererUtils.cpp | 79 ++++++++++++++--------------- filament/src/RendererUtils.h | 26 +++++++++- filament/src/details/Renderer.cpp | 48 +++++++++++------- 4 files changed, 93 insertions(+), 61 deletions(-) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 18b6ab30dc7..f0ee30c682b 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -3193,6 +3193,7 @@ FrameGraphId PostProcessManager::resolve(FrameGraph& fg, assert_invariant(dst); assert_invariant(srcDesc.format == dstDesc.format); assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height); + assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1); driver.resolve( dst, dstSubDesc.level, dstSubDesc.layer, src, srcSubDesc.level, srcSubDesc.layer); diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index e1a18cc7f15..c25530f8811 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -50,8 +51,9 @@ namespace filament { using namespace backend; using namespace math; -FrameGraphId RendererUtils::colorPass( +RendererUtils::ColorPassOutput RendererUtils::colorPass( FrameGraph& fg, const char* name, FEngine& engine, FView const& view, + ColorPassInput const& colorPassInput, FrameGraphTexture::Descriptor const& colorBufferDesc, ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig, RenderPass::Executor passExecutor) noexcept { @@ -67,8 +69,6 @@ FrameGraphId RendererUtils::colorPass( FrameGraphId structure; }; - Blackboard& blackboard = fg.getBlackboard(); - auto& colorPass = fg.addPass(name, [&](FrameGraph::Builder& builder, ColorPassData& data) { @@ -76,21 +76,21 @@ FrameGraphId RendererUtils::colorPass( TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH; TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL; - data.shadows = blackboard.get("shadows"); - data.ssao = blackboard.get("ssao"); - data.color = blackboard.get("color"); - data.depth = blackboard.get("depth"); + data.color = colorPassInput.linearColor; + data.depth = colorPassInput.depth; + data.shadows = colorPassInput.shadows; + data.ssao = colorPassInput.ssao; // Screen-space reflection or refractions if (config.hasScreenSpaceReflectionsOrRefractions) { - data.ssr = blackboard.get("ssr"); + data.ssr = colorPassInput.ssr; if (data.ssr) { data.ssr = builder.sample(data.ssr); } } if (config.hasContactShadows) { - data.structure = blackboard.get("structure"); + data.structure = colorPassInput.structure; assert_invariant(data.structure); data.structure = builder.sample(data.structure); } @@ -196,7 +196,6 @@ FrameGraphId RendererUtils::colorPass( .samples = config.msaa, .layerCount = static_cast(colorBufferDesc.depth), .clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags}); - blackboard["depth"] = data.depth; }, [=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources, ColorPassData const& data, DriverApi& driver) { @@ -262,26 +261,21 @@ FrameGraphId RendererUtils::colorPass( } ); - // when color grading is done as a subpass, the output of the color-pass is the ldr buffer - auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color; - - // linear color buffer - blackboard["color"] = colorPass->color; - - return output; + return { + .linearColor = colorPass->color, + .tonemappedColor = colorPass->output, // can be null + .depth = colorPass->depth + }; } -std::pair, bool> RendererUtils::refractionPass( +std::optional RendererUtils::refractionPass( FrameGraph& fg, FEngine& engine, FView const& view, + ColorPassInput colorPassInput, ColorPassConfig config, PostProcessManager::ScreenSpaceRefConfig const& ssrConfig, PostProcessManager::ColorGradingConfig colorGradingConfig, RenderPass const& pass) noexcept { - auto& blackboard = fg.getBlackboard(); - - FrameGraphId output; - // find the first refractive object in channel 2 RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(), [](auto const& command) { @@ -296,34 +290,35 @@ std::pair, bool> RendererUtils::refractionPass( // if there wasn't any refractive object, just skip everything below. if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) { - PostProcessManager& ppm = engine.getPostProcessManager(); - - // clear the color/depth buffers, which will orphan (and cull) the color pass - blackboard.remove("color"); - blackboard.remove("depth"); - + assert_invariant(!colorPassInput.linearColor); + assert_invariant(!colorPassInput.depth); config.hasScreenSpaceReflectionsOrRefractions = true; - FrameGraphId const input = RendererUtils::colorPass(fg, - "Color Pass (opaque)", engine, view, { + PostProcessManager& ppm = engine.getPostProcessManager(); + auto const opaquePassOutput = RendererUtils::colorPass(fg, + "Color Pass (opaque)", engine, view, colorPassInput, { // When rendering the opaques, we need to conserve the sample buffer, // so create a config that specifies the sample count. .width = config.physicalViewport.width, .height = config.physicalViewport.height, .samples = config.msaa, .format = config.hdrFormat - }, config, { .asSubpass = false }, + }, + config, { .asSubpass = false, .customResolve = false }, pass.getExecutor(pass.begin(), refraction)); // generate the mipmap chain PostProcessManager::generateMipmapSSR(ppm, fg, - input, ssrConfig.refraction, true, ssrConfig); + opaquePassOutput.linearColor, + ssrConfig.refraction, + true, ssrConfig); // Now we're doing the refraction pass proper. - // This uses the same framebuffer (color and depth) used by the opaque pass. This happens - // automatically because these are set in the Blackboard (they were set by the opaque - // pass). For this reason, `desc` below is only used in colorPass() for the width and - // height. + // This uses the same framebuffer (color and depth) used by the opaque pass. + // For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for + // the width and height. + colorPassInput.linearColor = opaquePassOutput.linearColor; + colorPassInput.depth = opaquePassOutput.depth; // Since we're reusing the existing target we don't want to clear any of its buffer. // Important: if this target ended up being an imported target, then the clearFlags @@ -331,7 +326,8 @@ std::pair, bool> RendererUtils::refractionPass( // and we'd end up clearing the opaque pass. This scenario never happens because it is // prevented in Renderer.cpp's final blit. config.clearFlags = TargetBufferFlags::NONE; - output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, { + auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)", + engine, view, colorPassInput, { .width = config.physicalViewport.width, .height = config.physicalViewport.height }, config, colorGradingConfig, @@ -342,12 +338,13 @@ std::pair, bool> RendererUtils::refractionPass( // need to sample from 'output'. However, because we have MSAA, we know we're not // sampleable. And this is because in the SSR case, we had to use a renderbuffer to // conserve the multi-sample buffer. - output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 }); + transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer", + transparentPassOutput.linearColor, { .levels = 1 }); } - // this becomes the screen-space reflection history - blackboard["color"] = output; + return transparentPassOutput; } - return { output, hasScreenSpaceRefraction }; + + return std::nullopt; } UTILS_NOINLINE diff --git a/filament/src/RendererUtils.h b/filament/src/RendererUtils.h index 9b7e93cbc60..34ea7b0eda9 100644 --- a/filament/src/RendererUtils.h +++ b/filament/src/RendererUtils.h @@ -21,14 +21,19 @@ #include "RenderPass.h" #include "fg/FrameGraphId.h" +#include "fg/FrameGraphTexture.h" + +#include #include #include +#include #include #include +#include #include namespace filament { @@ -71,15 +76,32 @@ class RendererUtils { bool screenSpaceReflectionHistoryNotReady; }; - static FrameGraphId colorPass( + struct ColorPassInput { + FrameGraphId linearColor; + FrameGraphId tonemappedColor; + FrameGraphId depth; + FrameGraphId shadows; + FrameGraphId ssao; + FrameGraphId ssr; + FrameGraphId structure; + }; + struct ColorPassOutput { + FrameGraphId linearColor; + FrameGraphId tonemappedColor; + FrameGraphId depth; + }; + + static ColorPassOutput colorPass( FrameGraph& fg, const char* name, FEngine& engine, FView const& view, + ColorPassInput const& colorPassInput, FrameGraphTexture::Descriptor const& colorBufferDesc, ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig, RenderPass::Executor passExecutor) noexcept; - static std::pair, bool> refractionPass( + static std::optional refractionPass( FrameGraph& fg, FEngine& engine, FView const& view, + ColorPassInput colorPassInput, ColorPassConfig config, PostProcessManager::ScreenSpaceRefConfig const& ssrConfig, PostProcessManager::ColorGradingConfig colorGradingConfig, diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index ee9857b87f6..bb743fca2bd 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -955,7 +955,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { .scale = aoOptions.resolution, .picking = view.hasPicking() }); - blackboard["structure"] = structure; const auto picking = picking_; @@ -1003,7 +1002,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F, view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale); config.ssrLodOffset = ssrConfig.lodOffset; - blackboard["ssr"] = ssrConfig.ssr; // -------------------------------------------------------------------------------------------- // screen-space reflections pass @@ -1107,30 +1105,39 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { ); // the color pass itself + color-grading as subpass if needed - auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, + auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, { + .shadows = blackboard.get("shadows"), + .ssao = blackboard.get("ssao"), + .ssr = ssrConfig.ssr, + .structure = structure + }, colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor()); if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) { // This cancels the colorPass() call above if refraction is active. // The color pass + refraction + color-grading as subpass if needed - const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view, + auto const output = RendererUtils::refractionPass(fg, mEngine, view, { + .shadows = blackboard.get("shadows"), + .ssao = blackboard.get("ssao"), + .ssr = ssrConfig.ssr, + .structure = structure + }, config, ssrConfig, colorGradingConfigForColor, pass); - hasScreenSpaceRefraction = enabled; - if (enabled) { - colorPassOutput = output; + + hasScreenSpaceRefraction = output.has_value(); + if (hasScreenSpaceRefraction) { + colorPassOutput = output.value(); } } - // Here, colorPassOutput can be either tonemapped or not; it's tonemapped if color gradding - // is done as a subpass. - if (colorGradingConfig.customResolve) { + assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1); // TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used // by many other passes (Bloom, TAA, DoF, etc...). We could make this more // efficient by using ARM_shader_framebuffer_fetch. We use a load/store (i.e. // subpass) here because it's more convenient. - colorPassOutput = ppm.customResolveUncompressPass(fg, colorPassOutput); - blackboard["color"] = colorPassOutput; + colorPassOutput.linearColor = + ppm.customResolveUncompressPass(fg, colorPassOutput.linearColor); } // export the color buffer if screen-space reflections are enabled @@ -1148,8 +1155,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { builder.sideEffect(); // we can't use colorPassOutput here because it could be tonemapped - auto color = blackboard.get("color"); - data.history = builder.sample(color); // FIXME: an access must be declared for detach(), why? + data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why? }, [&view, projection](FrameGraphResources const& resources, auto const& data, backend::DriverApi&) { auto& history = view.getFrameHistory(); @@ -1159,7 +1165,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { }); } - FrameGraphId input = colorPassOutput; + // this is the output of the color pass / input to post processing, + // this is only used later for comparing it with the output after post-processing + FrameGraphId const postProcessInput = colorGradingConfig.asSubpass ? + colorPassOutput.tonemappedColor : + colorPassOutput.linearColor; + + // input can change below + FrameGraphId input = postProcessInput; fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) { // Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass. view.cleanupRenderPasses(); @@ -1170,8 +1183,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // if the depth is not used below or if the depth is not MS (e.g. it could have been // auto-resolved). // In practice, this is used on Vulkan and older Metal devices. - auto depth = blackboard.get("depth"); - depth = ppm.resolve(fg, "Resolved Depth Buffer", depth, { .levels = 1 }); + auto depth = ppm.resolve(fg, "Resolved Depth Buffer", colorPassOutput.depth, { .levels = 1 }); // Debug: CSM visualisation if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades && @@ -1287,7 +1299,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation. const bool outputIsSwapChain = - (input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle); + (input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle); if (mightNeedFinalBlit) { if (blendModeTranslucent || xvp != svp ||