Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9892aac6 authored by Alec Mouri's avatar Alec Mouri
Browse files

Correctly pass screenshot fences to transaction callbacks

In some instances, a screenshot may be captured before a layer has a
release callback registered. This can happen when a new buffer has
not yet been transacted before a screenshot is captured. This causes the
screenshot fence to be dropped and the possibility of tearing when
capturing a screenshot while continuously rendering.

To resolve this, buffer screenshot fences into a list of future fences
when there is no callback registered, and merge those fences when
dispatching the release callback.

Bug: 302703346
Test: SurfaceViewTests#testMovingWhiteSurfaceView 100 times
Change-Id: I91aec3cdb0973092d48cd77e59dd3999e9d9e847
parent 05e3113c
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -73,8 +73,18 @@ class BaseFuture<Self, T, std::future> {
    return std::get<Impl>(self()).get();
  }

  template <class Rep, class Period>
  std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
    if (std::holds_alternative<T>(self())) {
      return std::future_status::ready;
    }

    return std::get<Impl>(self()).wait_for(timeout_duration);
  }

 private:
  auto& self() { return static_cast<Self&>(*this).future_; }
  const auto& self() const { return static_cast<const Self&>(*this).future_; }
};

template <typename Self, typename T>
@@ -90,6 +100,15 @@ class BaseFuture<Self, T, std::shared_future> {
    return std::get<Impl>(self()).get();
  }

  template <class Rep, class Period>
  std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout_duration) const {
    if (std::holds_alternative<T>(self())) {
      return std::future_status::ready;
    }

    return std::get<Impl>(self()).wait_for(timeout_duration);
  }

 private:
  const auto& self() const { return static_cast<const Self&>(*this).future_; }
};
+1 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ class Future final : public details::BaseFuture<Future<T, FutureImpl>, T, Future
  // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the
  // following are defined for either FutureImpl:
  using Base::get;
  using Base::wait_for;

  // Attaches a continuation to the future. The continuation is a function that maps T to either R
  // or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by
+38 −0
Original line number Diff line number Diff line
@@ -102,4 +102,42 @@ TEST(Future, Chain) {
  decrement_thread.join();
}

TEST(Future, WaitFor) {
  using namespace std::chrono_literals;
  {
    auto future = ftl::yield(42);
    // Check that we can wait_for multiple times without invalidating the future
    EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
    EXPECT_EQ(future.wait_for(1s), std::future_status::ready);
    EXPECT_EQ(future.get(), 42);
  }

  {
    std::condition_variable cv;
    std::mutex m;
    bool ready = false;

    std::packaged_task<int32_t()> get_int([&] {
      std::unique_lock lk(m);
      cv.wait(lk, [&] { return ready; });
      return 24;
    });

    auto get_future = ftl::Future(get_int.get_future());
    std::thread get_thread(std::move(get_int));

    EXPECT_EQ(get_future.wait_for(0s), std::future_status::timeout);
    {
      std::unique_lock lk(m);
      ready = true;
    }
    cv.notify_one();

    EXPECT_EQ(get_future.wait_for(1s), std::future_status::ready);
    EXPECT_EQ(get_future.get(), 24);

    get_thread.join();
  }
}

}  // namespace android::test
+43 −1
Original line number Diff line number Diff line
@@ -78,11 +78,13 @@
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
#include "Utils/FenceUtils.h"

#define DEBUG_RESIZE 0
#define EARLY_RELEASE_ENABLED false

namespace android {
using namespace std::chrono_literals;
namespace {
constexpr int kDumpTableRowLength = 159;

@@ -2911,7 +2913,8 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l
}

void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
                             ui::LayerStack layerStack) {
                             ui::LayerStack layerStack,
                             std::function<FenceResult(FenceResult)>&& continuation) {
    // If we are displayed on multiple displays in a single composition cycle then we would
    // need to do careful tracking to enable the use of the mLastClientCompositionFence.
    //  For example we can only use it if all the displays are client comp, and we need
@@ -2946,11 +2949,41 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
            break;
        }
    }

    if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
        futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
    }

    if (ch != nullptr) {
        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
        ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
        ch->name = mName;
    } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
        // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
        // asynchronously, then make sure we don't drop the fence.
        mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult),
                                                      std::move(continuation));
        std::vector<FenceAndContinuation> mergedFences;
        sp<Fence> prevFence = nullptr;
        // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
        // grow unbounded.
        for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
            auto result = futureAndContinution.future.wait_for(0s);
            if (result != std::future_status::ready) {
                mergedFences.emplace_back(futureAndContinution);
                continue;
            }

            mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE),
                       prevFence);
        }
        if (prevFence != nullptr) {
            mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
        }

        mAdditionalPreviousReleaseFences.swap(mergedFences);
    }

    if (mBufferInfo.mBuffer) {
        mPreviouslyPresentedLayerStacks.push_back(layerStack);
    }
@@ -3440,6 +3473,15 @@ bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle
            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
            handle->frameNumber = mDrawingState.frameNumber;
            handle->previousFrameNumber = mDrawingState.previousFrameNumber;
            if (FlagManager::getInstance().screenshot_fence_preservation() &&
                mPreviousReleaseBufferEndpoint == handle->listener) {
                // Add fences from previous screenshots now so that they can be dispatched to the
                // client.
                for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
                    handle->previousReleaseFences.emplace_back(futureAndContinution.chain());
                }
                mAdditionalPreviousReleaseFences.clear();
            }

            // Store so latched time and release fence can be set
            mDrawingState.callbackHandles.push_back(handle);
+15 −1
Original line number Diff line number Diff line
@@ -555,7 +555,8 @@ public:
    const compositionengine::LayerFECompositionState* getCompositionState() const;
    bool fenceHasSignaled() const;
    void onPreComposition(nsecs_t refreshStartTime);
    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
                          std::function<FenceResult(FenceResult)>&& continuation = nullptr);

    void setWasClientComposed(const sp<Fence>& fence) {
        mLastClientCompositionFence = fence;
@@ -932,6 +933,19 @@ public:
    // the release fences from the correct displays when we release the last buffer
    // from the layer.
    std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
    struct FenceAndContinuation {
        ftl::SharedFuture<FenceResult> future;
        std::function<FenceResult(FenceResult)> continuation;

        ftl::SharedFuture<FenceResult> chain() const {
            if (continuation) {
                return ftl::Future(future).then(continuation).share();
            } else {
                return future;
            }
        }
    };
    std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences;
    // Exposed so SurfaceFlinger can assert that it's held
    const sp<SurfaceFlinger> mFlinger;

Loading