Verified Commit 4e27f4b0 authored by Jamie Nicol's avatar Jamie Nicol Committed by ma1
Browse files

Bug 2049810 - Allocate HardwareBuffer for screen pixels request in parent...

Bug 2049810 - Allocate HardwareBuffer for screen pixels request in parent process. r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D308519
parent d13ffd02
Loading
Loading
Loading
Loading
+4 −7
Original line number Diff line number Diff line
@@ -3,9 +3,7 @@
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
using mozilla::layers::CompositorScrollUpdate from "mozilla/layers/CompositorScrollUpdate.h";
using mozilla::void_t from "mozilla/ipc/IPCCore.h";

include "mozilla/GfxMessageUtils.h";
include "mozilla/layers/LayersMessageUtils.h";
@@ -34,15 +32,14 @@ parent:
  async MaxToolbarHeight(int32_t aHeight);
  async FixedBottomOffset(int32_t aOffset);
  async DefaultClearColor(uint32_t aColor);
  async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect, IntSize aDestSize);
  async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect,
                            FileDescriptor aHardwareBuffer);
  async EnableLayerUpdateNotifications(bool aEnable);
child:
  async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
  async NotifyCompositorScrollUpdate(CompositorScrollUpdate aUpdate);
  // Returns when the child side has finished using the HardwareBuffer,
  // indicating that the parent side can now release it.
  async ScreenPixels(uint64_t aRequestId, FileDescriptor? aHardwareBuffer, FileDescriptor? aAcquireFence)
    returns (void_t ok);
  async ScreenPixels(uint64_t aRequestId, bool aSuccess,
                     FileDescriptor? aAcquireFence);
};

} // layers
+39 −33
Original line number Diff line number Diff line
@@ -148,19 +148,36 @@ UiCompositorControllerChild::RequestScreenPixels(gfx::IntRect aSourceRect,

  // We only support one request at a time. If an old request is still
  // outstanding when a new request is made, just reject the old request.
  if (mScreenPixelsPromise) {
    mScreenPixelsPromise.extract().second->Reject(NS_ERROR_ABORT, __func__);
  if (mScreenPixelsRequest) {
    mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_ABORT, __func__);
  }

  RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer =
      layers::AndroidHardwareBuffer::Create(aDestSize,
                                            gfx::SurfaceFormat::R8G8B8A8);
  if (!hardwareBuffer) {
    return ScreenPixelsPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY,
                                                __func__);
  }

  UniqueFileHandle bufferFd = hardwareBuffer->SerializeToFileDescriptor();
  if (!bufferFd) {
    return ScreenPixelsPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
  }

  static uint64_t nextRequestId = 0;
  const uint64_t requestId = nextRequestId++;
  auto promise = MakeRefPtr<ScreenPixelsPromise::Private>(__func__);
  // Using synchronous dispatch ensures we are done using the hardware buffer
  // prior to RecvScreenPixels calling aResolver which in turn will cause the
  // hardware buffer on the parent side to be released.
  promise->UseSynchronousTaskDispatch(__func__);
  mScreenPixelsPromise.emplace(requestId, promise);
  (void)SendRequestScreenPixels(requestId, aSourceRect, aDestSize);
  mScreenPixelsRequest.emplace(ScreenPixelsRequest{
      .mRequestId = requestId,
      .mHardwareBuffer = hardwareBuffer,
      .mPromise = promise,
  });
  if (!SendRequestScreenPixels(requestId, aSourceRect,
                               ipc::FileDescriptor(std::move(bufferFd)))) {
    mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_NOT_AVAILABLE,
                                                    __func__);
  }
  return promise;
}
#endif
@@ -213,8 +230,8 @@ void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) {
  mParent = nullptr;

#ifdef MOZ_WIDGET_ANDROID
  if (mScreenPixelsPromise) {
    mScreenPixelsPromise->second->Reject(NS_ERROR_ABORT, __func__);
  if (mScreenPixelsRequest) {
    mScreenPixelsRequest->mPromise->Reject(NS_ERROR_ABORT, __func__);
  }
#endif
  if (mProcessToken) {
@@ -258,39 +275,28 @@ UiCompositorControllerChild::RecvNotifyCompositorScrollUpdate(
}

mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels(
    uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
    Maybe<ipc::FileDescriptor>&& aAcquireFence,
    ScreenPixelsResolver&& aResolver) {
    uint64_t aRequestId, bool aSuccess,
    Maybe<ipc::FileDescriptor>&& aAcquireFence) {
#if defined(MOZ_WIDGET_ANDROID)
  if (!mScreenPixelsPromise || mScreenPixelsPromise->first != aRequestId) {
  if (!mScreenPixelsRequest || mScreenPixelsRequest->mRequestId != aRequestId) {
    // Response is for an outdated request whose promise will have already been
    // rejected. Just ignore it.
    return IPC_OK();
  }

  RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer;
  if (aHardwareBuffer) {
    hardwareBuffer =
        layers::AndroidHardwareBuffer::DeserializeFromFileDescriptor(
            aHardwareBuffer->TakePlatformHandle());
  auto request = mScreenPixelsRequest.extract();
  if (!aSuccess) {
    request.mPromise->Reject(NS_ERROR_FAILURE, __func__);
    return IPC_OK();
  }
  if (hardwareBuffer && aAcquireFence) {
    hardwareBuffer->SetAcquireFence(aAcquireFence->TakePlatformHandle());

  if (aAcquireFence) {
    request.mHardwareBuffer->SetAcquireFence(
        aAcquireFence->TakePlatformHandle());
  }
  // Note this is resolved synchronously, ensuring we have finished using the
  // hardware buffer as soon as this call returns (and importantly before the
  // aResolver call below).
  mScreenPixelsPromise.extract().second->Resolve(std::move(hardwareBuffer),
                                                 __func__);
  request.mPromise->Resolve(std::move(request.mHardwareBuffer), __func__);
#endif  // defined(MOZ_WIDGET_ANDROID)

  // Notify the parent side that it can drop its reference to the hardware
  // buffer. In theory this could be done as soon as we have called
  // DeserializeFromFileDescriptor(). However, on certain Exynos devices we have
  // seen that releasing the original hardware buffer frees the underlying
  // resource even if a reference obtained via (de)serialization remains alive.
  // See bug 2017901.
  aResolver(void_t{});
  return IPC_OK();
}

+8 −5
Original line number Diff line number Diff line
@@ -84,9 +84,8 @@ class UiCompositorControllerChild final
  mozilla::ipc::IPCResult RecvNotifyCompositorScrollUpdate(
      const CompositorScrollUpdate& aUpdate);
  mozilla::ipc::IPCResult RecvScreenPixels(
      uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
      Maybe<ipc::FileDescriptor>&& aAcquireFence,
      ScreenPixelsResolver&& aResolver);
      uint64_t aRequestId, bool aSuccess,
      Maybe<ipc::FileDescriptor>&& aAcquireFence);

 private:
  explicit UiCompositorControllerChild(const uint64_t& aProcessToken,
@@ -118,8 +117,12 @@ class UiCompositorControllerChild final
  // RecvScreenPixels() altogether. Unfortunately, however, we cannot chain to a
  // promise returned from an IPDL function on the Android UI thread, as the
  // thread does not support direct task dispatch.
  Maybe<std::pair<uint64_t, RefPtr<ScreenPixelsPromise::Private>>>
      mScreenPixelsPromise;
  struct ScreenPixelsRequest {
    uint64_t mRequestId;
    RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
    RefPtr<ScreenPixelsPromise::Private> mPromise;
  };
  Maybe<ScreenPixelsRequest> mScreenPixelsRequest;
#endif

  // Should only be set when compositor is in process.
+21 −21
Original line number Diff line number Diff line
@@ -139,39 +139,39 @@ mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
}

mozilla::ipc::IPCResult UiCompositorControllerParent::RecvRequestScreenPixels(
    uint64_t aRequestId, gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
    uint64_t aRequestId, gfx::IntRect aSourceRect,
    ipc::FileDescriptor&& aHardwareBuffer) {
#if defined(MOZ_WIDGET_ANDROID)
  RefPtr<AndroidHardwareBuffer> hardwareBuffer =
      AndroidHardwareBuffer::DeserializeFromFileDescriptor(
          aHardwareBuffer.TakePlatformHandle());
  if (!hardwareBuffer) {
    (void)SendScreenPixels(aRequestId, false, Nothing());
    return IPC_OK();
  }

  LayerTreeState* state =
      CompositorBridgeParent::GetLayerTreeState(mRootLayerTreeId);

  if (state && state->mWrBridge) {
    state->mWrBridge->RequestScreenPixels(aSourceRect, aDestSize)
    state->mWrBridge->RequestScreenPixels(aSourceRect, hardwareBuffer)
        ->Then(
            GetCurrentSerialEventTarget(), __func__,
            [target = RefPtr{this},
             aRequestId](RefPtr<AndroidHardwareBuffer> aHardwareBuffer) {
              UniqueFileHandle bufferFd =
                  aHardwareBuffer->SerializeToFileDescriptor();
            [target = RefPtr{this}, aRequestId,
             hardwareBuffer = std::move(hardwareBuffer)](Ok) {
              UniqueFileHandle fenceFd =
                  aHardwareBuffer->GetAndResetAcquireFence();
              target
                  ->SendScreenPixels(
                      aRequestId,
                      aHardwareBuffer
                          ? Some(ipc::FileDescriptor(std::move(bufferFd)))
                          : Nothing(),
                  hardwareBuffer->GetAndResetAcquireFence();
              (void)target->SendScreenPixels(
                  aRequestId, true,
                  fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd)))
                              : Nothing())
                  // Ensure the hardware buffer remains alive until child side
                  // has finished using it.
                  ->Then(GetCurrentSerialEventTarget(), __func__,
                         [aHardwareBuffer](
                             ScreenPixelsPromise::ResolveOrRejectValue&&) {});
                          : Nothing());
            },
            [target = RefPtr{this}, aRequestId](nsresult aError) {
              (void)target->SendScreenPixels(aRequestId, Nothing(), Nothing());
              (void)target->SendScreenPixels(aRequestId, false, Nothing());
            });
    state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER);
  } else {
    (void)SendScreenPixels(aRequestId, false, Nothing());
  }
#endif  // defined(MOZ_WIDGET_ANDROID)

+3 −3
Original line number Diff line number Diff line
@@ -41,9 +41,9 @@ class UiCompositorControllerParent final
  mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight);
  mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset);
  mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor);
  mozilla::ipc::IPCResult RecvRequestScreenPixels(uint64_t aRequestId,
                                                  gfx::IntRect aSourceRect,
                                                  gfx::IntSize aDestSize);
  mozilla::ipc::IPCResult RecvRequestScreenPixels(
      uint64_t aRequestId, gfx::IntRect aSourceRect,
      ipc::FileDescriptor&& aHardwareBuffer);
  mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(
      const bool& aEnable);
  void ActorDestroy(ActorDestroyReason aWhy) override;
Loading