Loading libs/renderengine/include/renderengine/DisplaySettings.h +16 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,22 @@ struct DisplaySettings { // Configures the rendering intent of the output display. This is used for tonemapping. aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC; // Tonemapping strategy to use for each layer. This is only used for tonemapping HDR source // content enum class TonemapStrategy { // Use a tonemapper defined by libtonemap. This may be OEM-defined as of Android 13, aka // undefined. // This is typically a global tonemapper, designed to match what is on screen. Libtonemap, // Use a local tonemapper. Because local tonemapping uses large intermediate allocations, // this // method is primarily recommended for infrequent rendering that does not need to exactly // match // pixels that are on-screen. Local, }; TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { Loading libs/renderengine/skia/SkiaRenderEngine.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -519,6 +519,11 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } if (parameters.requiresLinearEffect) { if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) { // TODO: Apply a local tonemap // fallthrough for now } auto effect = shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace, .outputDataspace = parameters.outputDataSpace, Loading services/surfaceflinger/ScreenCaptureOutput.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling, args.dimInGammaSpaceForEnhancedScreenshots); args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping); output->editState().isSecure = args.renderArea.isSecure(); output->editState().isProtected = args.isProtected; output->setCompositionEnabled(true); Loading Loading @@ -63,11 +63,13 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp ScreenCaptureOutput::ScreenCaptureOutput( const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots) bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping) : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling), mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {} mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots), mEnableLocalTonemapping(enableLocalTonemapping) {} void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { auto& outputState = editState(); Loading @@ -88,6 +90,11 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; } if (mEnableLocalTonemapping) { clientCompositionDisplay.tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local; } return clientCompositionDisplay; } Loading services/surfaceflinger/ScreenCaptureOutput.h +4 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ struct ScreenCaptureOutputArgs { bool treat170mAsSrgb; bool dimInGammaSpaceForEnhancedScreenshots; bool isProtected = false; bool enableLocalTonemapping = false; }; // ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer. Loading @@ -49,7 +50,8 @@ class ScreenCaptureOutput : public compositionengine::impl::Output { public: ScreenCaptureOutput(const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots); bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping); void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; Loading @@ -67,6 +69,7 @@ private: const compositionengine::Output::ColorProfile& mColorProfile; const bool mRegionSampling; const bool mDimInGammaSpaceForEnhancedScreenshots; const bool mEnableLocalTonemapping; }; std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs); Loading services/surfaceflinger/SurfaceFlinger.cpp +25 −13 Original line number Diff line number Diff line Loading @@ -8331,6 +8331,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( captureResults.capturedDataspace = requestedDataspace; const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() && !renderArea->getHintForSeamlessTransition(); { Mutex::Autolock lock(mStateLock); const DisplayDevice* display = nullptr; Loading Loading @@ -8364,18 +8367,21 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( displayBrightnessNits = sdrWhitePointNits; } else { displayBrightnessNits = state.displayBrightnessNits; // Only clamp the display brightness if this is not a seamless transition. Otherwise // for seamless transitions it's important to match the current display state as the // buffer will be shown under these same conditions, and we want to avoid any // flickers if (!enableLocalTonemapping) { // Only clamp the display brightness if this is not a seamless transition. // Otherwise for seamless transitions it's important to match the current // display state as the buffer will be shown under these same conditions, and we // want to avoid any flickers if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) { // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming // the SDR portion. 2.0 chosen by experimentation // Restrict the amount of HDR "headroom" in the screenshot to avoid // over-dimming the SDR portion. 2.0 chosen by experimentation constexpr float kMaxScreenshotHeadroom = 2.0f; displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, displayBrightnessNits); } } } // Screenshots leaving the device should be colorimetric if (requestedDataspace == ui::Dataspace::UNKNOWN && Loading Loading @@ -8405,7 +8411,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, layerFEs = copyLayerFEs(), layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent]() -> FenceResult { renderArea = std::move(renderArea), renderIntent, enableLocalTonemapping]() -> FenceResult { std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); compositionEngine->setRenderEngine(mRenderEngine.get()); Loading @@ -8414,7 +8421,11 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .renderIntent = renderIntent}; float targetBrightness = 1.0f; if (dataspace == ui::Dataspace::BT2020_HLG) { if (enableLocalTonemapping) { // Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr // sdr ratio via display brightness / sdrWhite nits. targetBrightness = sdrWhitePointNits / displayBrightnessNits; } else if (dataspace == ui::Dataspace::BT2020_HLG) { const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203; // With a low dimming ratio, don't fit the entire curve. Otherwise mixed content // will appear way too bright. Loading @@ -8440,7 +8451,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .treat170mAsSrgb = mTreat170mAsSrgb, .dimInGammaSpaceForEnhancedScreenshots = dimInGammaSpaceForEnhancedScreenshots, .isProtected = isProtected}); .isProtected = isProtected, .enableLocalTonemapping = enableLocalTonemapping}); const float colorSaturation = grayscale ? 0 : 1; compositionengine::CompositionRefreshArgs refreshArgs{ Loading Loading
libs/renderengine/include/renderengine/DisplaySettings.h +16 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,22 @@ struct DisplaySettings { // Configures the rendering intent of the output display. This is used for tonemapping. aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC; // Tonemapping strategy to use for each layer. This is only used for tonemapping HDR source // content enum class TonemapStrategy { // Use a tonemapper defined by libtonemap. This may be OEM-defined as of Android 13, aka // undefined. // This is typically a global tonemapper, designed to match what is on screen. Libtonemap, // Use a local tonemapper. Because local tonemapping uses large intermediate allocations, // this // method is primarily recommended for infrequent rendering that does not need to exactly // match // pixels that are on-screen. Local, }; TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { Loading
libs/renderengine/skia/SkiaRenderEngine.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -519,6 +519,11 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } if (parameters.requiresLinearEffect) { if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) { // TODO: Apply a local tonemap // fallthrough for now } auto effect = shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace, .outputDataspace = parameters.outputDataSpace, Loading
services/surfaceflinger/ScreenCaptureOutput.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling, args.dimInGammaSpaceForEnhancedScreenshots); args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping); output->editState().isSecure = args.renderArea.isSecure(); output->editState().isProtected = args.isProtected; output->setCompositionEnabled(true); Loading Loading @@ -63,11 +63,13 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp ScreenCaptureOutput::ScreenCaptureOutput( const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots) bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping) : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling), mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {} mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots), mEnableLocalTonemapping(enableLocalTonemapping) {} void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { auto& outputState = editState(); Loading @@ -88,6 +90,11 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; } if (mEnableLocalTonemapping) { clientCompositionDisplay.tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local; } return clientCompositionDisplay; } Loading
services/surfaceflinger/ScreenCaptureOutput.h +4 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ struct ScreenCaptureOutputArgs { bool treat170mAsSrgb; bool dimInGammaSpaceForEnhancedScreenshots; bool isProtected = false; bool enableLocalTonemapping = false; }; // ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer. Loading @@ -49,7 +50,8 @@ class ScreenCaptureOutput : public compositionengine::impl::Output { public: ScreenCaptureOutput(const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots); bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping); void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; Loading @@ -67,6 +69,7 @@ private: const compositionengine::Output::ColorProfile& mColorProfile; const bool mRegionSampling; const bool mDimInGammaSpaceForEnhancedScreenshots; const bool mEnableLocalTonemapping; }; std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs); Loading
services/surfaceflinger/SurfaceFlinger.cpp +25 −13 Original line number Diff line number Diff line Loading @@ -8331,6 +8331,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( captureResults.capturedDataspace = requestedDataspace; const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() && !renderArea->getHintForSeamlessTransition(); { Mutex::Autolock lock(mStateLock); const DisplayDevice* display = nullptr; Loading Loading @@ -8364,18 +8367,21 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( displayBrightnessNits = sdrWhitePointNits; } else { displayBrightnessNits = state.displayBrightnessNits; // Only clamp the display brightness if this is not a seamless transition. Otherwise // for seamless transitions it's important to match the current display state as the // buffer will be shown under these same conditions, and we want to avoid any // flickers if (!enableLocalTonemapping) { // Only clamp the display brightness if this is not a seamless transition. // Otherwise for seamless transitions it's important to match the current // display state as the buffer will be shown under these same conditions, and we // want to avoid any flickers if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) { // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming // the SDR portion. 2.0 chosen by experimentation // Restrict the amount of HDR "headroom" in the screenshot to avoid // over-dimming the SDR portion. 2.0 chosen by experimentation constexpr float kMaxScreenshotHeadroom = 2.0f; displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, displayBrightnessNits); } } } // Screenshots leaving the device should be colorimetric if (requestedDataspace == ui::Dataspace::UNKNOWN && Loading Loading @@ -8405,7 +8411,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, layerFEs = copyLayerFEs(), layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent]() -> FenceResult { renderArea = std::move(renderArea), renderIntent, enableLocalTonemapping]() -> FenceResult { std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); compositionEngine->setRenderEngine(mRenderEngine.get()); Loading @@ -8414,7 +8421,11 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .renderIntent = renderIntent}; float targetBrightness = 1.0f; if (dataspace == ui::Dataspace::BT2020_HLG) { if (enableLocalTonemapping) { // Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr // sdr ratio via display brightness / sdrWhite nits. targetBrightness = sdrWhitePointNits / displayBrightnessNits; } else if (dataspace == ui::Dataspace::BT2020_HLG) { const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203; // With a low dimming ratio, don't fit the entire curve. Otherwise mixed content // will appear way too bright. Loading @@ -8440,7 +8451,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .treat170mAsSrgb = mTreat170mAsSrgb, .dimInGammaSpaceForEnhancedScreenshots = dimInGammaSpaceForEnhancedScreenshots, .isProtected = isProtected}); .isProtected = isProtected, .enableLocalTonemapping = enableLocalTonemapping}); const float colorSaturation = grayscale ? 0 : 1; compositionengine::CompositionRefreshArgs refreshArgs{ Loading