diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index c4f14e4ce1..e1a18cc7f1 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -153,6 +153,8 @@ FrameGraphId RendererUtils::colorPass( } if (colorGradingConfig.asSubpass) { + assert_invariant(config.msaa <= 1); + assert_invariant(colorBufferDesc.samples <= 1); data.output = builder.createTexture("Tonemapped Buffer", { .width = colorBufferDesc.width, .height = colorBufferDesc.height, @@ -263,7 +265,9 @@ 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; - blackboard["color"] = output; + // linear color buffer + blackboard["color"] = colorPass->color; + return output; } @@ -275,7 +279,7 @@ std::pair, bool> RendererUtils::refractionPass( RenderPass const& pass) noexcept { auto& blackboard = fg.getBlackboard(); - auto input = blackboard.get("color"); + FrameGraphId output; // find the first refractive object in channel 2 @@ -295,13 +299,13 @@ std::pair, bool> RendererUtils::refractionPass( PostProcessManager& ppm = engine.getPostProcessManager(); // clear the color/depth buffers, which will orphan (and cull) the color pass - input.clear(); blackboard.remove("color"); blackboard.remove("depth"); config.hasScreenSpaceReflectionsOrRefractions = true; - input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, { + FrameGraphId const input = RendererUtils::colorPass(fg, + "Color Pass (opaque)", engine, view, { // When rendering the opaques, we need to conserve the sample buffer, // so create a config that specifies the sample count. .width = config.physicalViewport.width, @@ -330,7 +334,8 @@ std::pair, bool> RendererUtils::refractionPass( output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, { .width = config.physicalViewport.width, .height = config.physicalViewport.height }, - config, colorGradingConfig, pass.getExecutor(refraction, pass.end())); + config, colorGradingConfig, + pass.getExecutor(refraction, pass.end())); if (config.msaa > 1 && !colorGradingConfig.asSubpass) { // We need to do a resolve here because later passes (such as color grading or DoF) will @@ -339,8 +344,8 @@ std::pair, bool> RendererUtils::refractionPass( // conserve the multi-sample buffer. output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 }); } - } else { - output = input; + // this becomes the screen-space reflection history + blackboard["color"] = output; } return { output, hasScreenSpaceRefraction }; } diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 1b8ffdd359..ee9857b87f 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -655,16 +655,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // on qualcomm hardware -- we might need a backend dependent toggle at some point const PostProcessManager::ColorGradingConfig colorGradingConfig{ .asSubpass = - hasColorGrading && msaaSampleCount <= 1 && - !bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled && driver.isFrameBufferFetchSupported() && + hasColorGrading && + !bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled && !engine.debug.renderer.disable_subpasses, .customResolve = - msaaOptions.customResolve && msaaSampleCount > 1 && - hasColorGrading && driver.isFrameBufferFetchMultiSampleSupported() && + msaaOptions.customResolve && + hasColorGrading && !engine.debug.renderer.disable_subpasses, .translucent = needsAlphaChannel, .fxaa = hasFXAA, @@ -673,6 +673,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel) }; + // by construction (msaaSampleCount) both asSubpass and customResolve can't be true + assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2); + // whether we're scaled at all bool scaled = any(notEqual(scale, float2(1.0f))); @@ -1108,20 +1111,26 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { 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 + // 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, config, ssrConfig, colorGradingConfigForColor, pass); - colorPassOutput = output; hasScreenSpaceRefraction = enabled; + if (enabled) { + colorPassOutput = output; + } } + // Here, colorPassOutput can be either tonemapped or not; it's tonemapped if color gradding + // is done as a subpass. + if (colorGradingConfig.customResolve) { // 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; } // export the color buffer if screen-space reflections are enabled @@ -1137,7 +1146,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // The "output" of this pass is going to be used during the next frame as // an "import". builder.sideEffect(); - data.history = builder.sample(colorPassOutput); // FIXME: an access must be declared for detach(), why? + + // 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? }, [&view, projection](FrameGraphResources const& resources, auto const& data, backend::DriverApi&) { auto& history = view.getFrameHistory();