Loading services/surfaceflinger/RegionSamplingThread.cpp +8 −27 Original line number Diff line number Diff line Loading @@ -214,7 +214,7 @@ void RegionSamplingThread::binderDied(const wp<IBinder>& who) { } float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& sample_area) { const Rect& sample_area) { if (!sample_area.isValid() || (sample_area.getWidth() > width) || (sample_area.getHeight() > height)) { ALOGE("invalid sampling region requested"); Loading Loading @@ -243,7 +243,7 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st std::vector<float> RegionSamplingThread::sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) { const std::vector<RegionSamplingThread::Descriptor>& descriptors) { void* data_raw = nullptr; buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw); std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw), Loading @@ -256,7 +256,7 @@ std::vector<float> RegionSamplingThread::sampleBuffer( std::vector<float> lumas(descriptors.size()); std::transform(descriptors.begin(), descriptors.end(), lumas.begin(), [&](auto const& descriptor) { return sampleArea(data.get(), width, height, stride, orientation, return sampleArea(data.get(), width, height, stride, descriptor.area - leftTop); }); return lumas; Loading @@ -270,23 +270,6 @@ void RegionSamplingThread::captureSample() { return; } wp<const DisplayDevice> displayWeak; ui::LayerStack layerStack; ui::Transform::RotationFlags orientation; ui::Size displaySize; Rect layerStackSpaceRect; { // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread const sp<const DisplayDevice> display = mFlinger.getFrontInternalDisplay(); displayWeak = display; layerStack = display->getLayerStack(); orientation = ui::Transform::toRotationFlags(display->getOrientation()); displaySize = display->getSize(); layerStackSpaceRect = display->getLayerStackSpaceRect(); } std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; for (const auto& [listener, descriptor] : mDescriptors) { Loading Loading @@ -360,15 +343,13 @@ void RegionSamplingThread::captureSample() { } SurfaceFlinger::ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, screenshotArgs{.captureTypeVariant = std::monostate{}, .displayIdVariant = std::nullopt, .snapshotRequest = SurfaceFlinger::SnapshotRequestArgs{.uid = gui::Uid::INVALID, .layerStack = layerStack, .snapshotFilterFn = filterFn}, .sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds, .sourceCrop = sampledBounds, .size = sampledBounds.getSize(), .dataspace = ui::Dataspace::V0_SRGB, .disableBlur = true, Loading @@ -378,7 +359,7 @@ void RegionSamplingThread::captureSample() { .debugName = "RegionSampling"}; std::vector<std::pair<Layer*, sp<LayerFE>>> layers; mFlinger.getSnapshotsFromMainThread(screenshotArgs); mFlinger.setScreenshotSnapshotsAndDisplayState(screenshotArgs); FenceResult fenceResult = mFlinger.captureScreenshot(screenshotArgs, buffer, nullptr).get(); if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); Loading @@ -392,8 +373,8 @@ void RegionSamplingThread::captureSample() { } ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(), activeDescriptors, orientation); std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(), activeDescriptors); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); Loading services/surfaceflinger/RegionSamplingThread.h +2 −2 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ struct SamplingOffsetCallback; using gui::IRegionSamplingListener; float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& area); const Rect& area); class RegionSamplingThread : public IBinder::DeathRecipient { public: Loading Loading @@ -96,7 +96,7 @@ private: std::vector<float> sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); const std::vector<RegionSamplingThread::Descriptor>& descriptors); void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline); void binderDied(const wp<IBinder>& who) override; Loading services/surfaceflinger/SurfaceFlinger.cpp +115 −131 Original line number Diff line number Diff line Loading @@ -7440,33 +7440,6 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } wp<const DisplayDevice> displayWeak; ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::LayerStack layerStack; ui::Size reqSize(args.width, args.height); Rect layerStackSpaceRect; bool displayIsSecure; { Mutex::Autolock lock(mStateLock); sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken); if (!display) { ALOGD("Unable to find display device for captureDisplay"); invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); return; } displayWeak = display; displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); displayIsSecure = display->isSecure(); layerStackSpaceRect = display->getLayerStackSpaceRect(); // set the requested width/height to the logical display layer stack rect size by default if (args.width == 0 || args.height == 0) { reqSize = layerStackSpaceRect.getSize(); } } auto excludeLayerIds = getExcludeLayerIds(captureArgs.excludeHandles); if (!excludeLayerIds) { ALOGD("Invalid layer handle passed as excludeLayer to captureDisplay"); Loading @@ -7474,88 +7447,41 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, .displayIdVariant = displayIdVariantOpt, ScreenshotArgs screenshotArgs{.captureTypeVariant = args.displayToken, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid{static_cast<uid_t>( captureArgs.uid)}, .layerStack = layerStack, .excludeLayerIds = excludeLayerIds.value()}, .sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop), .size = reqSize, .size = ui::Size(args.width, args.height), .dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace), .disableBlur = false, .isGrayscale = captureArgs.grayscale, .isSecure = captureArgs.captureSecureLayers && displayIsSecure, .isSecure = captureArgs.captureSecureLayers, .includeProtected = captureArgs.allowProtected, .seamlessTransition = captureArgs.hintForSeamlessTransition, .debugName = "ScreenCapture"}; if (screenshotArgs.sourceCrop.isEmpty()) { screenshotArgs.sourceCrop = layerStackSpaceRect; } captureScreenCommon(screenshotArgs, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::Size size; Rect layerStackSpaceRect; bool displayIsSecure; { Mutex::Autolock lock(mStateLock); const auto display = getDisplayDeviceLocked(displayId); if (!display) { ALOGD("Unable to find display device for captureDisplay"); invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); return; } displayWeak = display; displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); layerStackSpaceRect = display->getLayerStackSpaceRect(); size = display->getLayerStackSpaceRect().getSize(); displayIsSecure = display->isSecure(); } size.width *= args.frameScaleX; size.height *= args.frameScaleY; // We could query a real value for this but it'll be a long, long time until we support // displays that need upwards of 1GB per buffer so... constexpr auto kMaxTextureSize = 16384; if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize || size.height >= kMaxTextureSize) { ALOGD("captureDisplay resolved to invalid size %d x %d", size.width, size.height); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } if (captureListener == nullptr) { ALOGE("capture screen must provide a capture listener callback"); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, .displayIdVariant = displayIdVariantOpt, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid::INVALID, .layerStack = layerStack}, .sourceCrop = layerStackSpaceRect, .size = size, ScreenshotArgs screenshotArgs{.captureTypeVariant = displayId, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid::INVALID}, .size = ui::Size(args.frameScaleX, args.frameScaleY), .dataspace = static_cast<ui::Dataspace>(args.dataspace), .disableBlur = false, .isGrayscale = false, .isSecure = args.captureSecureLayers && displayIsSecure, .isSecure = args.captureSecureLayers, .includeProtected = false, .seamlessTransition = args.hintForSeamlessTransition, .debugName = "ScreenCapture"}; Loading Loading @@ -7705,13 +7631,14 @@ bool SurfaceFlinger::layersHasProtectedLayer( // Getting layer snapshots and accessing display state should take place on // main thread. Accessing display requires mStateLock, and contention for // this lock is reduced when grabbed from the main thread, thus also reducing // risk of deadlocks. Returns false if no display is found. bool SurfaceFlinger::getSnapshotsFromMainThread(ScreenshotArgs& args) { // risk of deadlocks. Returns an error status if no display is found. status_t SurfaceFlinger::setScreenshotSnapshotsAndDisplayState(ScreenshotArgs& args) { return mScheduler ->schedule([=, this, &args]() REQUIRES(kMainThreadContext) { SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Screenshot"); mPowerAdvisor->setScreenshotWorkload(); SFTRACE_NAME("getSnapshotsFromMainThread"); SFTRACE_NAME("setScreenshotSnapshotsAndDisplayState"); status_t status = setScreenshotDisplayState(args); args.layers = getLayerSnapshotsForScreenshots(args.snapshotRequest); // Non-threaded RenderEngine eventually returns to the main thread a 2nd time // to complete the screenshot. Release fences should only be added during the 2nd Loading @@ -7723,7 +7650,7 @@ bool SurfaceFlinger::getSnapshotsFromMainThread(ScreenshotArgs& args) { ui::UNASSIGNED_LAYER_STACK); } } return getDisplayStateOnMainThread(args); return status; }) .get(); } Loading @@ -7732,6 +7659,11 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r const sp<IScreenCaptureListener>& captureListener) { SFTRACE_CALL(); status_t status = setScreenshotSnapshotsAndDisplayState(args); if (status != OK) { invokeScreenCaptureError(status, captureListener); } if (exceedsMaxRenderTargetSize(args.size.getWidth(), args.size.getHeight())) { ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 ") that exceeds render target size limit.", Loading @@ -7740,12 +7672,6 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r return; } bool hasDisplayState = getSnapshotsFromMainThread(args); if (!hasDisplayState) { ALOGD("Display state not found"); invokeScreenCaptureError(NO_MEMORY, captureListener); } const bool hasHdrLayer = std::any_of(args.layers.cbegin(), args.layers.cend(), [this](const auto& layer) { return isHdrLayer(*(layer.second->mSnapshot.get())); Loading Loading @@ -7822,17 +7748,16 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r futureFence.get(); } // Returns true if display is found and args was populated with display state // data. Otherwise, returns false. bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { sp<const DisplayDevice> display = nullptr; { // Returns OK if display is found and args was populated with display state // data. Otherwise, returns an error status. status_t SurfaceFlinger::setScreenshotDisplayState(ScreenshotArgs& args) { Mutex::Autolock lock(mStateLock); sp<const DisplayDevice> display = nullptr; // Screenshot initiated through captureLayers if (auto* layerSequence = std::get_if<int32_t>(&args.captureTypeVariant)) { // LayerSnapshotBuilder should only be accessed from the main thread. const frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(*layerSequence); const frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(*layerSequence); if (!snapshot) { ALOGW("Couldn't find layer snapshot for %d", *layerSequence); } else { Loading @@ -7849,10 +7774,69 @@ bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { args.debugName.append(", ").append(snapshot->debugName); } // Screenshot initiated through captureDisplay } else if (auto* displayWeak = std::get_if<wp<const DisplayDevice>>(&args.captureTypeVariant)) { display = displayWeak->promote(); // Screenshot initiated through captureDisplay by ID } else if (auto* displayId = std::get_if<DisplayId>(&args.captureTypeVariant)) { display = getDisplayDeviceLocked(*displayId); if (!display) { ALOGD("Unable to find display device for captureDisplay by ID"); return NAME_NOT_FOUND; } Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.displayIdVariant = display->getDisplayIdVariant(); args.isSecure &= display->isSecure(); args.snapshotRequest.layerStack = display->getLayerStack(); args.sourceCrop = layerStackSpaceRect; args.size.width *= layerStackSpaceRect.getWidth(); args.size.height *= layerStackSpaceRect.getHeight(); // We could query a real value for this but it'll be a long, long time until we support // displays that need upwards of 1GB per buffer so... constexpr auto kMaxTextureSize = 16384; if (args.size.width <= 0 || args.size.height <= 0 || args.size.width >= kMaxTextureSize || args.size.height >= kMaxTextureSize) { ALOGD("captureDisplay resolved to invalid size %d x %d", args.size.width, args.size.height); return BAD_VALUE; } // Screenshot initiated through captureDisplay by displayToken } else if (auto* displayToken = std::get_if<sp<IBinder>>(&args.captureTypeVariant)) { display = getDisplayDeviceLocked(*displayToken); if (!display) { ALOGD("Unable to find display device for captureDisplay by displayToken"); return NAME_NOT_FOUND; } Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.displayIdVariant = display->getDisplayIdVariant(); args.isSecure &= display->isSecure(); args.snapshotRequest.layerStack = display->getLayerStack(); if (args.sourceCrop.isEmpty()) { args.sourceCrop = layerStackSpaceRect; } // Set the requested width/height to the logical display layer stack rect size by // default if (args.size.width == 0 || args.size.height == 0) { args.size = layerStackSpaceRect.getSize(); } // Screenshot initiated for region sampling } else if (std::holds_alternative<std::monostate>(args.captureTypeVariant)) { display = getFrontInternalDisplayLocked(); if (!display) { ALOGD("Unable to find display device for region sampling"); return NAME_NOT_FOUND; } args.snapshotRequest.layerStack = display->getLayerStack(); if (args.sourceCrop.isEmpty()) { Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.sourceCrop = layerStackSpaceRect; args.size = layerStackSpaceRect.getSize(); } } if (display == nullptr) { Loading @@ -7867,10 +7851,10 @@ bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { args.colorMode = state.colorMode; args.debugName.append(", ").append(display->getDisplayName()); args.debugName.append(" (").append(to_string(display->getId())).append(")"); return true; } return OK; } return false; ALOGD("Display state not found"); return NO_MEMORY; } ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( Loading services/surfaceflinger/SurfaceFlinger.h +7 −5 Original line number Diff line number Diff line Loading @@ -903,9 +903,11 @@ private: */ struct ScreenshotArgs { // Contains the sequence ID of the parent layer if the screenshot is // initiated though captureLayers(), or the display that the render // result will be on if initiated through captureDisplay() std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant; // initiated though captureLayers(), or the displayToken or displayID // that the render result will be on if initiated through captureDisplay(). // The monostate type is used to denote that the screenshot is initiated // for region sampling. std::variant<std::monostate, int32_t, sp<IBinder>, DisplayId> captureTypeVariant; // Display ID of the display the result will be on ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt}; Loading Loading @@ -962,12 +964,12 @@ private: std::string debugName; }; bool getSnapshotsFromMainThread(ScreenshotArgs& args); status_t setScreenshotSnapshotsAndDisplayState(ScreenshotArgs& args); void captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat, const sp<IScreenCaptureListener>&); bool getDisplayStateOnMainThread(ScreenshotArgs& args) REQUIRES(kMainThreadContext); status_t setScreenshotDisplayState(ScreenshotArgs& args) REQUIRES(kMainThreadContext); ftl::SharedFuture<FenceResult> captureScreenshot( ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer, Loading services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +9 −10 Original line number Diff line number Diff line Loading @@ -39,20 +39,19 @@ public: static int constexpr kWidth = 98; static int constexpr kStride = 100; static int constexpr kHeight = 29; static int constexpr kOrientation = ui::Transform::ROT_0; std::array<uint32_t, kHeight * kStride> buffer; Rect const whole_area{0, 0, kWidth, kHeight}; }; TEST_F(RegionSamplingTest, calculate_mean_white) { std::fill(buffer.begin(), buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(1.0f)); } TEST_F(RegionSamplingTest, calculate_mean_black) { std::fill(buffer.begin(), buffer.end(), kBlack); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(0.0f)); } Loading @@ -63,7 +62,7 @@ TEST_F(RegionSamplingTest, calculate_mean_partial_region) { whole_area.top + halfway_down}; std::fill(buffer.begin(), buffer.begin() + half, 0); std::fill(buffer.begin() + half, buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, partial_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, partial_region), testing::FloatEq(0.0f)); } Loading @@ -74,14 +73,14 @@ TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { return pixel; }); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatNear(0.16f, 0.01f)); } TEST_F(RegionSamplingTest, bimodal_tiebreaker) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; }); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(0.5f)); } Loading @@ -90,19 +89,19 @@ TEST_F(RegionSamplingTest, bounds_checking) { [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect invalid_region{0, 0, 4, kHeight + 1}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 0, -4, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{3, 0, 2, 0}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 3, 0, 2}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); } Loading Loading
services/surfaceflinger/RegionSamplingThread.cpp +8 −27 Original line number Diff line number Diff line Loading @@ -214,7 +214,7 @@ void RegionSamplingThread::binderDied(const wp<IBinder>& who) { } float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& sample_area) { const Rect& sample_area) { if (!sample_area.isValid() || (sample_area.getWidth() > width) || (sample_area.getHeight() > height)) { ALOGE("invalid sampling region requested"); Loading Loading @@ -243,7 +243,7 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st std::vector<float> RegionSamplingThread::sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) { const std::vector<RegionSamplingThread::Descriptor>& descriptors) { void* data_raw = nullptr; buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw); std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw), Loading @@ -256,7 +256,7 @@ std::vector<float> RegionSamplingThread::sampleBuffer( std::vector<float> lumas(descriptors.size()); std::transform(descriptors.begin(), descriptors.end(), lumas.begin(), [&](auto const& descriptor) { return sampleArea(data.get(), width, height, stride, orientation, return sampleArea(data.get(), width, height, stride, descriptor.area - leftTop); }); return lumas; Loading @@ -270,23 +270,6 @@ void RegionSamplingThread::captureSample() { return; } wp<const DisplayDevice> displayWeak; ui::LayerStack layerStack; ui::Transform::RotationFlags orientation; ui::Size displaySize; Rect layerStackSpaceRect; { // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread const sp<const DisplayDevice> display = mFlinger.getFrontInternalDisplay(); displayWeak = display; layerStack = display->getLayerStack(); orientation = ui::Transform::toRotationFlags(display->getOrientation()); displaySize = display->getSize(); layerStackSpaceRect = display->getLayerStackSpaceRect(); } std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; for (const auto& [listener, descriptor] : mDescriptors) { Loading Loading @@ -360,15 +343,13 @@ void RegionSamplingThread::captureSample() { } SurfaceFlinger::ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, screenshotArgs{.captureTypeVariant = std::monostate{}, .displayIdVariant = std::nullopt, .snapshotRequest = SurfaceFlinger::SnapshotRequestArgs{.uid = gui::Uid::INVALID, .layerStack = layerStack, .snapshotFilterFn = filterFn}, .sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds, .sourceCrop = sampledBounds, .size = sampledBounds.getSize(), .dataspace = ui::Dataspace::V0_SRGB, .disableBlur = true, Loading @@ -378,7 +359,7 @@ void RegionSamplingThread::captureSample() { .debugName = "RegionSampling"}; std::vector<std::pair<Layer*, sp<LayerFE>>> layers; mFlinger.getSnapshotsFromMainThread(screenshotArgs); mFlinger.setScreenshotSnapshotsAndDisplayState(screenshotArgs); FenceResult fenceResult = mFlinger.captureScreenshot(screenshotArgs, buffer, nullptr).get(); if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); Loading @@ -392,8 +373,8 @@ void RegionSamplingThread::captureSample() { } ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(), activeDescriptors, orientation); std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(), activeDescriptors); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); Loading
services/surfaceflinger/RegionSamplingThread.h +2 −2 Original line number Diff line number Diff line Loading @@ -44,7 +44,7 @@ struct SamplingOffsetCallback; using gui::IRegionSamplingListener; float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& area); const Rect& area); class RegionSamplingThread : public IBinder::DeathRecipient { public: Loading Loading @@ -96,7 +96,7 @@ private: std::vector<float> sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); const std::vector<RegionSamplingThread::Descriptor>& descriptors); void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline); void binderDied(const wp<IBinder>& who) override; Loading
services/surfaceflinger/SurfaceFlinger.cpp +115 −131 Original line number Diff line number Diff line Loading @@ -7440,33 +7440,6 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } wp<const DisplayDevice> displayWeak; ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::LayerStack layerStack; ui::Size reqSize(args.width, args.height); Rect layerStackSpaceRect; bool displayIsSecure; { Mutex::Autolock lock(mStateLock); sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken); if (!display) { ALOGD("Unable to find display device for captureDisplay"); invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); return; } displayWeak = display; displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); displayIsSecure = display->isSecure(); layerStackSpaceRect = display->getLayerStackSpaceRect(); // set the requested width/height to the logical display layer stack rect size by default if (args.width == 0 || args.height == 0) { reqSize = layerStackSpaceRect.getSize(); } } auto excludeLayerIds = getExcludeLayerIds(captureArgs.excludeHandles); if (!excludeLayerIds) { ALOGD("Invalid layer handle passed as excludeLayer to captureDisplay"); Loading @@ -7474,88 +7447,41 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, return; } ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, .displayIdVariant = displayIdVariantOpt, ScreenshotArgs screenshotArgs{.captureTypeVariant = args.displayToken, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid{static_cast<uid_t>( captureArgs.uid)}, .layerStack = layerStack, .excludeLayerIds = excludeLayerIds.value()}, .sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop), .size = reqSize, .size = ui::Size(args.width, args.height), .dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace), .disableBlur = false, .isGrayscale = captureArgs.grayscale, .isSecure = captureArgs.captureSecureLayers && displayIsSecure, .isSecure = captureArgs.captureSecureLayers, .includeProtected = captureArgs.allowProtected, .seamlessTransition = captureArgs.hintForSeamlessTransition, .debugName = "ScreenCapture"}; if (screenshotArgs.sourceCrop.isEmpty()) { screenshotArgs.sourceCrop = layerStackSpaceRect; } captureScreenCommon(screenshotArgs, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; ftl::Optional<DisplayIdVariant> displayIdVariantOpt; ui::Size size; Rect layerStackSpaceRect; bool displayIsSecure; { Mutex::Autolock lock(mStateLock); const auto display = getDisplayDeviceLocked(displayId); if (!display) { ALOGD("Unable to find display device for captureDisplay"); invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); return; } displayWeak = display; displayIdVariantOpt = display->getDisplayIdVariant(); layerStack = display->getLayerStack(); layerStackSpaceRect = display->getLayerStackSpaceRect(); size = display->getLayerStackSpaceRect().getSize(); displayIsSecure = display->isSecure(); } size.width *= args.frameScaleX; size.height *= args.frameScaleY; // We could query a real value for this but it'll be a long, long time until we support // displays that need upwards of 1GB per buffer so... constexpr auto kMaxTextureSize = 16384; if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize || size.height >= kMaxTextureSize) { ALOGD("captureDisplay resolved to invalid size %d x %d", size.width, size.height); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } if (captureListener == nullptr) { ALOGE("capture screen must provide a capture listener callback"); invokeScreenCaptureError(BAD_VALUE, captureListener); return; } ScreenshotArgs screenshotArgs{.captureTypeVariant = displayWeak, .displayIdVariant = displayIdVariantOpt, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid::INVALID, .layerStack = layerStack}, .sourceCrop = layerStackSpaceRect, .size = size, ScreenshotArgs screenshotArgs{.captureTypeVariant = displayId, .snapshotRequest = SnapshotRequestArgs{.uid = gui::Uid::INVALID}, .size = ui::Size(args.frameScaleX, args.frameScaleY), .dataspace = static_cast<ui::Dataspace>(args.dataspace), .disableBlur = false, .isGrayscale = false, .isSecure = args.captureSecureLayers && displayIsSecure, .isSecure = args.captureSecureLayers, .includeProtected = false, .seamlessTransition = args.hintForSeamlessTransition, .debugName = "ScreenCapture"}; Loading Loading @@ -7705,13 +7631,14 @@ bool SurfaceFlinger::layersHasProtectedLayer( // Getting layer snapshots and accessing display state should take place on // main thread. Accessing display requires mStateLock, and contention for // this lock is reduced when grabbed from the main thread, thus also reducing // risk of deadlocks. Returns false if no display is found. bool SurfaceFlinger::getSnapshotsFromMainThread(ScreenshotArgs& args) { // risk of deadlocks. Returns an error status if no display is found. status_t SurfaceFlinger::setScreenshotSnapshotsAndDisplayState(ScreenshotArgs& args) { return mScheduler ->schedule([=, this, &args]() REQUIRES(kMainThreadContext) { SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Screenshot"); mPowerAdvisor->setScreenshotWorkload(); SFTRACE_NAME("getSnapshotsFromMainThread"); SFTRACE_NAME("setScreenshotSnapshotsAndDisplayState"); status_t status = setScreenshotDisplayState(args); args.layers = getLayerSnapshotsForScreenshots(args.snapshotRequest); // Non-threaded RenderEngine eventually returns to the main thread a 2nd time // to complete the screenshot. Release fences should only be added during the 2nd Loading @@ -7723,7 +7650,7 @@ bool SurfaceFlinger::getSnapshotsFromMainThread(ScreenshotArgs& args) { ui::UNASSIGNED_LAYER_STACK); } } return getDisplayStateOnMainThread(args); return status; }) .get(); } Loading @@ -7732,6 +7659,11 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r const sp<IScreenCaptureListener>& captureListener) { SFTRACE_CALL(); status_t status = setScreenshotSnapshotsAndDisplayState(args); if (status != OK) { invokeScreenCaptureError(status, captureListener); } if (exceedsMaxRenderTargetSize(args.size.getWidth(), args.size.getHeight())) { ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 ") that exceeds render target size limit.", Loading @@ -7740,12 +7672,6 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r return; } bool hasDisplayState = getSnapshotsFromMainThread(args); if (!hasDisplayState) { ALOGD("Display state not found"); invokeScreenCaptureError(NO_MEMORY, captureListener); } const bool hasHdrLayer = std::any_of(args.layers.cbegin(), args.layers.cend(), [this](const auto& layer) { return isHdrLayer(*(layer.second->mSnapshot.get())); Loading Loading @@ -7822,17 +7748,16 @@ void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat r futureFence.get(); } // Returns true if display is found and args was populated with display state // data. Otherwise, returns false. bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { sp<const DisplayDevice> display = nullptr; { // Returns OK if display is found and args was populated with display state // data. Otherwise, returns an error status. status_t SurfaceFlinger::setScreenshotDisplayState(ScreenshotArgs& args) { Mutex::Autolock lock(mStateLock); sp<const DisplayDevice> display = nullptr; // Screenshot initiated through captureLayers if (auto* layerSequence = std::get_if<int32_t>(&args.captureTypeVariant)) { // LayerSnapshotBuilder should only be accessed from the main thread. const frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(*layerSequence); const frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(*layerSequence); if (!snapshot) { ALOGW("Couldn't find layer snapshot for %d", *layerSequence); } else { Loading @@ -7849,10 +7774,69 @@ bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { args.debugName.append(", ").append(snapshot->debugName); } // Screenshot initiated through captureDisplay } else if (auto* displayWeak = std::get_if<wp<const DisplayDevice>>(&args.captureTypeVariant)) { display = displayWeak->promote(); // Screenshot initiated through captureDisplay by ID } else if (auto* displayId = std::get_if<DisplayId>(&args.captureTypeVariant)) { display = getDisplayDeviceLocked(*displayId); if (!display) { ALOGD("Unable to find display device for captureDisplay by ID"); return NAME_NOT_FOUND; } Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.displayIdVariant = display->getDisplayIdVariant(); args.isSecure &= display->isSecure(); args.snapshotRequest.layerStack = display->getLayerStack(); args.sourceCrop = layerStackSpaceRect; args.size.width *= layerStackSpaceRect.getWidth(); args.size.height *= layerStackSpaceRect.getHeight(); // We could query a real value for this but it'll be a long, long time until we support // displays that need upwards of 1GB per buffer so... constexpr auto kMaxTextureSize = 16384; if (args.size.width <= 0 || args.size.height <= 0 || args.size.width >= kMaxTextureSize || args.size.height >= kMaxTextureSize) { ALOGD("captureDisplay resolved to invalid size %d x %d", args.size.width, args.size.height); return BAD_VALUE; } // Screenshot initiated through captureDisplay by displayToken } else if (auto* displayToken = std::get_if<sp<IBinder>>(&args.captureTypeVariant)) { display = getDisplayDeviceLocked(*displayToken); if (!display) { ALOGD("Unable to find display device for captureDisplay by displayToken"); return NAME_NOT_FOUND; } Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.displayIdVariant = display->getDisplayIdVariant(); args.isSecure &= display->isSecure(); args.snapshotRequest.layerStack = display->getLayerStack(); if (args.sourceCrop.isEmpty()) { args.sourceCrop = layerStackSpaceRect; } // Set the requested width/height to the logical display layer stack rect size by // default if (args.size.width == 0 || args.size.height == 0) { args.size = layerStackSpaceRect.getSize(); } // Screenshot initiated for region sampling } else if (std::holds_alternative<std::monostate>(args.captureTypeVariant)) { display = getFrontInternalDisplayLocked(); if (!display) { ALOGD("Unable to find display device for region sampling"); return NAME_NOT_FOUND; } args.snapshotRequest.layerStack = display->getLayerStack(); if (args.sourceCrop.isEmpty()) { Rect layerStackSpaceRect = display->getLayerStackSpaceRect(); args.sourceCrop = layerStackSpaceRect; args.size = layerStackSpaceRect.getSize(); } } if (display == nullptr) { Loading @@ -7867,10 +7851,10 @@ bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) { args.colorMode = state.colorMode; args.debugName.append(", ").append(display->getDisplayName()); args.debugName.append(" (").append(to_string(display->getId())).append(")"); return true; } return OK; } return false; ALOGD("Display state not found"); return NO_MEMORY; } ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( Loading
services/surfaceflinger/SurfaceFlinger.h +7 −5 Original line number Diff line number Diff line Loading @@ -903,9 +903,11 @@ private: */ struct ScreenshotArgs { // Contains the sequence ID of the parent layer if the screenshot is // initiated though captureLayers(), or the display that the render // result will be on if initiated through captureDisplay() std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant; // initiated though captureLayers(), or the displayToken or displayID // that the render result will be on if initiated through captureDisplay(). // The monostate type is used to denote that the screenshot is initiated // for region sampling. std::variant<std::monostate, int32_t, sp<IBinder>, DisplayId> captureTypeVariant; // Display ID of the display the result will be on ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt}; Loading Loading @@ -962,12 +964,12 @@ private: std::string debugName; }; bool getSnapshotsFromMainThread(ScreenshotArgs& args); status_t setScreenshotSnapshotsAndDisplayState(ScreenshotArgs& args); void captureScreenCommon(ScreenshotArgs& args, ui::PixelFormat, const sp<IScreenCaptureListener>&); bool getDisplayStateOnMainThread(ScreenshotArgs& args) REQUIRES(kMainThreadContext); status_t setScreenshotDisplayState(ScreenshotArgs& args) REQUIRES(kMainThreadContext); ftl::SharedFuture<FenceResult> captureScreenshot( ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer, Loading
services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +9 −10 Original line number Diff line number Diff line Loading @@ -39,20 +39,19 @@ public: static int constexpr kWidth = 98; static int constexpr kStride = 100; static int constexpr kHeight = 29; static int constexpr kOrientation = ui::Transform::ROT_0; std::array<uint32_t, kHeight * kStride> buffer; Rect const whole_area{0, 0, kWidth, kHeight}; }; TEST_F(RegionSamplingTest, calculate_mean_white) { std::fill(buffer.begin(), buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(1.0f)); } TEST_F(RegionSamplingTest, calculate_mean_black) { std::fill(buffer.begin(), buffer.end(), kBlack); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(0.0f)); } Loading @@ -63,7 +62,7 @@ TEST_F(RegionSamplingTest, calculate_mean_partial_region) { whole_area.top + halfway_down}; std::fill(buffer.begin(), buffer.begin() + half, 0); std::fill(buffer.begin() + half, buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, partial_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, partial_region), testing::FloatEq(0.0f)); } Loading @@ -74,14 +73,14 @@ TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { return pixel; }); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatNear(0.16f, 0.01f)); } TEST_F(RegionSamplingTest, bimodal_tiebreaker) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; }); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, whole_area), testing::FloatEq(0.5f)); } Loading @@ -90,19 +89,19 @@ TEST_F(RegionSamplingTest, bounds_checking) { [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect invalid_region{0, 0, 4, kHeight + 1}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 0, -4, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{3, 0, 2, 0}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 3, 0, 2}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, invalid_region), testing::Eq(0.0)); } Loading