summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/gui/LayerState.cpp4
-rw-r--r--libs/gui/aidl/android/gui/ISurfaceComposer.aidl4
-rw-r--r--libs/gui/include/gui/DisplayCaptureArgs.h11
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.cpp6
-rw-r--r--libs/shaders/shaders.cpp58
-rw-r--r--services/surfaceflinger/DisplayRenderArea.cpp11
-rw-r--r--services/surfaceflinger/DisplayRenderArea.h4
-rw-r--r--services/surfaceflinger/LayerRenderArea.cpp5
-rw-r--r--services/surfaceflinger/LayerRenderArea.h3
-rw-r--r--services/surfaceflinger/RegionSamplingThread.cpp4
-rw-r--r--services/surfaceflinger/RenderArea.h12
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.cpp2
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.h2
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp118
-rw-r--r--services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp3
-rw-r--r--services/surfaceflinger/tests/unittests/CompositionTest.cpp2
16 files changed, 173 insertions, 76 deletions
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index fee91a40c2..2322b70d1c 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -897,11 +897,11 @@ status_t CaptureArgs::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
SAFE_PARCEL(output->writeBool, allowProtected);
SAFE_PARCEL(output->writeBool, grayscale);
-
SAFE_PARCEL(output->writeInt32, excludeHandles.size());
for (auto& excludeHandle : excludeHandles) {
SAFE_PARCEL(output->writeStrongBinder, excludeHandle);
}
+ SAFE_PARCEL(output->writeBool, hintForSeamlessTransition);
return NO_ERROR;
}
@@ -918,7 +918,6 @@ status_t CaptureArgs::readFromParcel(const Parcel* input) {
dataspace = static_cast<ui::Dataspace>(value);
SAFE_PARCEL(input->readBool, &allowProtected);
SAFE_PARCEL(input->readBool, &grayscale);
-
int32_t numExcludeHandles = 0;
SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
excludeHandles.reserve(numExcludeHandles);
@@ -927,6 +926,7 @@ status_t CaptureArgs::readFromParcel(const Parcel* input) {
SAFE_PARCEL(input->readStrongBinder, &binder);
excludeHandles.emplace(binder);
}
+ SAFE_PARCEL(input->readBool, &hintForSeamlessTransition);
return NO_ERROR;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index aa58e2e580..ec3266ca83 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -230,6 +230,10 @@ interface ISurfaceComposer {
*/
void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
+ /**
+ * Capture the specified screen. This requires the READ_FRAME_BUFFER
+ * permission.
+ */
void captureDisplayById(long displayId, IScreenCaptureListener listener);
/**
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index 5c794aea37..2676e0a338 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -41,7 +41,7 @@ struct CaptureArgs : public Parcelable {
bool captureSecureLayers{false};
int32_t uid{UNSET_UID};
// Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
- // result will be in the display's colorspace.
+ // result will be in a colorspace appropriate for capturing the display contents
// The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
// different from SRGB (byte per color), and failed when checking colors in tests.
// NOTE: In normal cases, we want the screen to be captured in display's colorspace.
@@ -59,6 +59,15 @@ struct CaptureArgs : public Parcelable {
std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
+ // Hint that the caller will use the screenshot animation as part of a transition animation.
+ // The canonical example would be screen rotation - in such a case any color shift in the
+ // screenshot is a detractor so composition in the display's colorspace is required.
+ // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
+ // such as file encoding or for blending HDR content into an ap's UI, where the display's
+ // exact colorspace is not an appropriate intermediate result.
+ // Note that if the caller is requesting a specific dataspace, this hint does nothing.
+ bool hintForSeamlessTransition = false;
+
virtual status_t writeToParcel(Parcel* output) const;
virtual status_t readFromParcel(const Parcel* input);
};
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 8256dd8e69..2a51493cea 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -517,16 +517,18 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
} else {
runtimeEffect = effectIter->second;
}
+
mat4 colorTransform = parameters.layer.colorTransform;
colorTransform *=
mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
parameters.layerDimmingRatio, 1.f));
+
const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
- return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
- parameters.display.maxLuminance,
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect,
+ std::move(colorTransform), parameters.display.maxLuminance,
parameters.display.currentLuminanceNits,
parameters.layer.source.buffer.maxLuminanceNits,
hardwareBuffer, parameters.display.renderIntent);
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index a3c403e551..19518eaecc 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -191,31 +191,35 @@ void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace
)");
break;
default:
+ // Input is SDR so map to its white point luminance
switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
+ // Max HLG output is nominally 1000 nits, but BT. 2100-2 allows
+ // for gamma correcting the HLG OOTF for displays with a different
+ // dynamic range. Scale to 1000 nits to apply an inverse OOTF against
+ // a reference display correctly.
+ // TODO: Use knowledge of the dimming ratio here to prevent
+ // unintended gamma shaft.
case HAL_DATASPACE_TRANSFER_HLG:
- // SDR -> HDR tonemap
shader.append(R"(
float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_inputMaxLuminance;
+ return xyz * 1000.0;
}
)");
break;
default:
- // Input and output are both SDR, so no tone-mapping is expected so
- // no-op the luminance normalization.
shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_displayMaxLuminance;
- }
- )");
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_displayMaxLuminance;
+ }
+ )");
break;
}
}
}
// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace,
std::string& shader) {
switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
@@ -226,11 +230,28 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
)");
break;
case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0;
- }
- )");
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0;
+ }
+ )");
+ break;
+ default:
+ // Transcoding to HLG requires applying the inverse OOTF
+ // with the expectation that the OOTF is then applied during
+ // tonemapping downstream.
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ // BT. 2100-2 operates on normalized luminances,
+ // so renormalize to the input
+ float ootfGain = pow(xyz.y / 1000.0, -0.2 / 1.2) / 1000.0;
+ return xyz * ootfGain;
+ }
+ )");
+ break;
+ }
break;
default:
shader.append(R"(
@@ -250,7 +271,7 @@ void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
.c_str());
generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(inputDataspace, outputDataspace, shader);
shader.append(R"(
float3 OOTF(float3 linearRGB, float3 xyz) {
@@ -501,9 +522,8 @@ std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
// If the input luminance is unknown, use display luminance (aka,
- // no-op any luminance changes)
- // This will be the case for eg screenshots in addition to
- // uncalibrated displays
+ // no-op any luminance changes).
+ // This is expected to only be meaningful for PQ content
.contentMaxLuminance =
maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
.currentDisplayLuminance = currentDisplayLuminanceNits > 0
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index 8f39e26e0f..e55cd3ea42 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -35,21 +35,24 @@ std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> di
const Rect& sourceCrop, ui::Size reqSize,
ui::Dataspace reqDataSpace,
bool useIdentityTransform,
+ bool hintForSeamlessTransition,
bool allowSecureLayers) {
if (auto display = displayWeak.promote()) {
// Using new to access a private constructor.
return std::unique_ptr<DisplayRenderArea>(
new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
- useIdentityTransform, allowSecureLayers));
+ useIdentityTransform, hintForSeamlessTransition,
+ allowSecureLayers));
}
return nullptr;
}
DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace reqDataSpace,
- bool useIdentityTransform, bool allowSecureLayers)
- : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, allowSecureLayers,
- applyDeviceOrientation(useIdentityTransform, *display)),
+ bool useIdentityTransform, bool hintForSeamlessTransition,
+ bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
+ allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
mDisplay(std::move(display)),
mSourceCrop(sourceCrop) {}
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index ce5410a90d..9a4981c881 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -30,6 +30,7 @@ public:
static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace,
bool useIdentityTransform,
+ bool hintForSeamlessTransition,
bool allowSecureLayers = true);
const ui::Transform& getTransform() const override;
@@ -39,7 +40,8 @@ public:
private:
DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+ ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition,
+ bool allowSecureLayers = true);
const sp<const DisplayDevice> mDisplay;
const Rect mSourceCrop;
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 1b8ff28a76..f6b5afc6a8 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -40,8 +40,9 @@ void reparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
bool allowSecureLayers, const ui::Transform& layerTransform,
- const Rect& layerBufferSize)
- : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, allowSecureLayers),
+ const Rect& layerBufferSize, bool hintForSeamlessTransition)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, hintForSeamlessTransition,
+ allowSecureLayers),
mLayer(std::move(layer)),
mLayerTransform(layerTransform),
mLayerBufferSize(layerBufferSize),
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 9bb13b3d6a..aa609eea38 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -34,7 +34,8 @@ class LayerRenderArea : public RenderArea {
public:
LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
- const ui::Transform& layerTransform, const Rect& layerBufferSize);
+ const ui::Transform& layerTransform, const Rect& layerBufferSize,
+ bool hintForSeamlessTransition);
const ui::Transform& getTransform() const override;
bool isSecure() const override;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 531d277ffc..8f658d5a09 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -277,10 +277,12 @@ void RegionSamplingThread::captureSample() {
const Rect sampledBounds = sampleRegion.bounds();
constexpr bool kUseIdentityTransform = false;
+ constexpr bool kHintForSeamlessTransition = false;
SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
- ui::Dataspace::V0_SRGB, kUseIdentityTransform);
+ ui::Dataspace::V0_SRGB, kUseIdentityTransform,
+ kHintForSeamlessTransition);
});
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 910fce043c..71b85bd3b2 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -25,12 +25,14 @@ public:
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- bool allowSecureLayers = false, RotationFlags rotation = ui::Transform::ROT_0)
+ bool hintForSeamlessTransition, bool allowSecureLayers = false,
+ RotationFlags rotation = ui::Transform::ROT_0)
: mAllowSecureLayers(allowSecureLayers),
mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation) {}
+ mRotationFlags(rotation),
+ mHintForSeamlessTransition(hintForSeamlessTransition) {}
static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
std::function<void(const LayerVector::Visitor&)> traverseLayers) {
@@ -90,6 +92,10 @@ public:
// capture operation.
virtual sp<Layer> getParentLayer() const { return nullptr; }
+ // Returns whether the render result may be used for system animations that
+ // must preserve the exact colors of the display.
+ bool getHintForSeamlessTransition() const { return mHintForSeamlessTransition; }
+
protected:
const bool mAllowSecureLayers;
@@ -98,7 +104,7 @@ private:
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
- const Rect mLayerStackSpaceRect;
+ const bool mHintForSeamlessTransition;
};
} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index a1d5cd7379..b70b53d05a 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -36,6 +36,7 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp
output->setLayerFilter({args.layerStack});
output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
+ output->editState().clientTargetBrightness = args.targetBrightness;
output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
compositionengine::DisplayColorProfileCreationArgsBuilder()
@@ -75,7 +76,6 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp
auto clientCompositionDisplay =
compositionengine::impl::Output::generateClientCompositionDisplaySettings();
clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
- clientCompositionDisplay.targetLuminanceNits = -1;
return clientCompositionDisplay;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 4e5a0ccfd2..3c307b0733 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -33,6 +33,8 @@ struct ScreenCaptureOutputArgs {
std::shared_ptr<renderengine::ExternalTexture> buffer;
float sdrWhitePointNits;
float displayBrightnessNits;
+ // Counterintuitively, when targetBrightness > 1.0 then dim the scene.
+ float targetBrightness;
bool regionSampling;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c88bff5ed8..d406afff1f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6915,6 +6915,32 @@ status_t SurfaceFlinger::setSchedAttr(bool enabled) {
return NO_ERROR;
}
+namespace {
+
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display,
+ bool capturingHdrLayers, bool hintForSeamlessTransition) {
+ if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) {
+ return requestedDataspace;
+ }
+
+ const auto& state = display->getCompositionDisplay()->getState();
+
+ const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
+
+ if (capturingHdrLayers && !hintForSeamlessTransition) {
+ // For now since we only support 8-bit screenshots, just use HLG and
+ // assume that 1.0 >= display max luminance. This isn't quite as future
+ // proof as PQ is, but is good enough.
+ // Consider using PQ once we support 16-bit screenshots and we're able
+ // to consistently supply metadata to image encoders.
+ return ui::Dataspace::BT2020_HLG;
+ }
+
+ return dataspaceForColorMode;
+}
+
+} // namespace
+
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -6930,7 +6956,6 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
@@ -6952,17 +6977,12 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
return NAME_NOT_FOUND;
}
}
-
- // Allow the caller to specify a dataspace regardless of the display's color mode, e.g. if
- // it wants sRGB regardless of the display's wide color mode.
- dataspace = args.dataspace == ui::Dataspace::UNKNOWN
- ? ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode)
- : args.dataspace;
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
- args.useIdentityTransform, args.captureSecureLayers);
+ return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize,
+ ui::Dataspace::UNKNOWN, args.useIdentityTransform,
+ args.hintForSeamlessTransition, args.captureSecureLayers);
});
GetLayerSnapshotsFunction getLayerSnapshots;
@@ -6988,7 +7008,6 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
ui::Size size;
- ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
@@ -7000,12 +7019,12 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
displayWeak = display;
layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
- dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN,
false /* useIdentityTransform */,
+ false /* hintForSeamlessTransition */,
false /* captureSecureLayers */);
});
@@ -7047,7 +7066,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
sp<Layer> parent;
Rect crop(args.sourceCrop);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace;
+ ui::Dataspace dataspace = args.dataspace;
// Call this before holding mStateLock to avoid any deadlocking.
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -7094,12 +7113,6 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
return NAME_NOT_FOUND;
}
}
-
- // The dataspace is depended on the color mode of display, that could use non-native mode
- // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
- // and failed if display is not in native mode. This provide a way to force using native
- // colors when capture.
- dataspace = args.dataspace;
} // mStateLock
// really small crop or frameScale
@@ -7128,7 +7141,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
childrenOnly, args.captureSecureLayers,
- layerTransform, layerBufferSize);
+ layerTransform, layerBufferSize,
+ args.hintForSeamlessTransition);
});
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
@@ -7314,29 +7328,48 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
return ftl::yield<FenceResult>(base::unexpected(PERMISSION_DENIED)).share();
}
- captureResults.buffer = buffer->getBuffer();
- auto dataspace = renderArea->getReqDataSpace();
+ auto capturedBuffer = buffer;
+
+ auto requestedDataspace = renderArea->getReqDataSpace();
auto parent = renderArea->getParentLayer();
auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
- if (dataspace == ui::Dataspace::UNKNOWN && parent) {
+ captureResults.capturedDataspace = requestedDataspace;
+
+ {
Mutex::Autolock lock(mStateLock);
- auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
- return display.getLayerStack() == layerStack;
- });
- if (!display) {
- // If the layer is not on a display, use the dataspace for the default display.
- display = getDefaultDisplayDeviceLocked();
+ const DisplayDevice* display = nullptr;
+ if (parent) {
+ display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ }).get();
+ }
+
+ if (display == nullptr) {
+ display = renderArea->getDisplayDevice().get();
+ }
+
+ if (display == nullptr) {
+ display = getDefaultDisplayDeviceLocked().get();
}
- dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
- renderIntent = display->getCompositionDisplay()->getState().renderIntent;
- sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits;
- displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits;
+ if (display != nullptr) {
+ const auto& state = display->getCompositionDisplay()->getState();
+ captureResults.capturedDataspace =
+ pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
+ renderArea->getHintForSeamlessTransition());
+ sdrWhitePointNits = state.sdrWhitePointNits;
+ displayBrightnessNits = state.displayBrightnessNits;
+
+ if (requestedDataspace == ui::Dataspace::UNKNOWN) {
+ renderIntent = state.renderIntent;
+ }
+ }
}
- captureResults.capturedDataspace = dataspace;
+
+ captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
if (!layers.empty()) {
@@ -7353,9 +7386,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
return layerFEs;
};
- auto present = [this, buffer = std::move(buffer), dataspace, sdrWhitePointNits,
- displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(), layerStack,
- regionSampling, renderArea = std::move(renderArea),
+ auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
+ sdrWhitePointNits, displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(),
+ layerStack, regionSampling, renderArea = std::move(renderArea),
renderIntent]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
@@ -7364,6 +7397,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
.renderIntent = renderIntent};
+ float targetBrightness = 1.0f;
+ 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.
+ if (maxBrightnessNits < 1000.f) {
+ targetBrightness = 1000.f / maxBrightnessNits;
+ }
+ }
+
std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
.colorProfile = colorProfile,
@@ -7372,6 +7415,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
.buffer = std::move(buffer),
.sdrWhitePointNits = sdrWhitePointNits,
.displayBrightnessNits = displayBrightnessNits,
+ .targetBrightness = targetBrightness,
.regionSampling = regionSampling});
const float colorSaturation = grayscale ? 0 : 1;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index c3dcb85686..921cae4e41 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -177,7 +177,8 @@ void LayerFuzzer::invokeBufferStateLayer() {
{mFdp.ConsumeIntegral<int32_t>(),
mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/,
mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(),
- mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect());
+ mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect(),
+ mFdp.ConsumeBool());
layerArea.render([]() {} /*drawLayers*/);
if (!ownsHandle) {
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 156007b862..6ca21bd458 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -200,7 +200,7 @@ void CompositionTest::captureScreenComposition() {
constexpr bool regionSampling = false;
auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
- ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
+ ui::Dataspace::V0_SRGB, true, true);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),