chromium/components/viz/common/overlay_state/win/overlay_state_service.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/viz/common/overlay_state/win/overlay_state_service.h"

#include <memory>
#include <utility>

#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/trace_event/base_tracing.h"
#include "base/trace_event/trace_event.h"

namespace viz {

OverlayStateService::MailboxState::MailboxState() = default;
OverlayStateService::MailboxState::~MailboxState() = default;
OverlayStateService::OverlayStateService() = default;
OverlayStateService::~OverlayStateService() = default;

OverlayStateService* OverlayStateService::GetInstance() {
  // TODO(wicarr, crbug.com/1316009): Ideally the OverlayStateService should be
  // a singleton. Instead the GpuServiceImpl should be responsible for creating
  // the OverlayStateService and injecting it into dependent GpuChannel(s) and
  // the DCLayerOverlayProcessor. Further the OverlayStateService should live
  // in gpu to avoid gpu needing to take a dependency on viz.
  static base::NoDestructor<OverlayStateService> service_wrapper;
  return service_wrapper.get();
}

void OverlayStateService::Initialize(
    scoped_refptr<base::SequencedTaskRunner> task_runner) {
  // GpuServiceImpl is expected to initialize the OverlayStateService and
  // OverlayStateService will operate on the GpuMain sequenced task runner.
  // RegisterObserver is expected to be called by GpuChannel and should operate
  // on the same GpuMain sequence as GpuServiceImpl.
  // SetPromotionHint is called by DCLayerOverlayProcessor which operates on a
  // separate task sequence, VizCompositorThread, so calls are posted to
  // 'task_runner_' to allow mojo'ing back out to bound PromotionHintObserver
  // clients on the proper sequence.
  DCHECK(!initialized_);
  task_runner_ = std::move(task_runner);
  initialized_ = true;
}

bool OverlayStateService::IsInitialized() {
  return initialized_;
}

void OverlayStateService::RegisterObserver(
    mojo::PendingRemote<gpu::mojom::OverlayStateObserver>
        overlay_state_observer,
    const gpu::Mailbox& mailbox) {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
  TRACE_EVENT1("gpu", "OverlayStateService::RegisterObserver", "mailbox",
               mailbox.ToDebugString());

  auto [mailbox_iterator, insert] =
      mailboxes_.try_emplace(mailbox, std::make_unique<MailboxState>());
  MailboxState* mailbox_state = mailbox_iterator->second.get();
  DCHECK(mailbox_state != nullptr);

  if (insert) {
    mailbox_state->observer_set_.set_disconnect_handler(
        base::BindRepeating(&OverlayStateService::OnBoundObserverDisconnect,
                            base::Unretained(this), mailbox));
  }

  // Add observer to the RemoteSet
  mojo::RemoteSetElementId id =
      mailbox_state->observer_set_.Add(std::move(overlay_state_observer));

  // It's possible that the overlay processor has already set promotion hint
  // information for this mailbox. If this is the case then we send a Hint
  // Changed event to the observer to inform them of the current promotion
  // state.
  OverlayStateAggregator::PromotionState promotion_state =
      mailbox_state->aggregator_.GetPromotionState();

  // If promotion state is unset there is no further work to do. If it is set
  // then inform the new observer of the current state.
  if (promotion_state != OverlayStateAggregator::PromotionState::kUnset) {
    bool promoted =
        promotion_state == OverlayStateAggregator::PromotionState::kPromoted;
    mailbox_state->observer_set_.Get(id)->OnStateChanged(promoted);
  }
}

void OverlayStateService::OnBoundObserverDisconnect(const gpu::Mailbox& mailbox,
                                                    mojo::RemoteSetElementId) {
  TRACE_EVENT1("gpu", "OverlayStateService::OnBoundObserverDisconnect",
               "mailbox", mailbox.ToDebugString());
  auto mailbox_iterator = mailboxes_.find(mailbox);
  if (mailbox_iterator != mailboxes_.end() &&
      mailbox_iterator->second->observer_set_.empty()) {
    // When the last observer has been disconnected, stop  tracking mailbox.
    mailboxes_.erase(mailbox_iterator);
  }
}

void OverlayStateService::OnStateChanged(
    const gpu::Mailbox& mailbox,
    OverlayStateAggregator::PromotionState promotion_state) {
  // If promotion state is unset there is no further work to do.
  if (promotion_state == OverlayStateAggregator::PromotionState::kUnset)
    return;

  if (!task_runner_->RunsTasksInCurrentSequence()) {
    // Use of base::Unretained is safe as OverlayStateService is a singleton
    // service bound to the lifetime of the GPU process.
    task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&OverlayStateService::OnStateChangedOnTaskRunnerSequence,
                       base::Unretained(this), mailbox, promotion_state));
  } else {
    OnStateChangedOnTaskRunnerSequence(mailbox, promotion_state);
  }
}

void OverlayStateService::OnStateChangedOnTaskRunnerSequence(
    const gpu::Mailbox& mailbox,
    OverlayStateAggregator::PromotionState promotion_state) {
  // Notify all observers of the new hint state.
  bool promoted =
      promotion_state == OverlayStateAggregator::PromotionState::kPromoted;

  TRACE_EVENT2("gpu", "OverlayStateService::OnStateChangedOnTaskRunnerSequence",
               "mailbox", mailbox.ToDebugString(), "promoted", promoted);
  auto mailbox_iterator = mailboxes_.find(mailbox);
  DCHECK(mailbox_iterator != mailboxes_.end());
  for (auto& observer : mailbox_iterator->second->observer_set_) {
    observer->OnStateChanged(promoted);
  }
}

void OverlayStateService::SetPromotionHint(const gpu::Mailbox& mailbox,
                                           bool promoted) {
  DCHECK(!task_runner_->RunsTasksInCurrentSequence());
  // Use of base::Unretained is safe as OverlayStateService is a singleton
  // service bound to the lifetime of the GPU process.
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&OverlayStateService::SetPromotionHintOnTaskRunnerSequence,
                     base::Unretained(this), mailbox, promoted));
}

void OverlayStateService::SetPromotionHintOnTaskRunnerSequence(
    const gpu::Mailbox& mailbox,
    bool promoted) {
  TRACE_EVENT2("gpu",
               "OverlayStateService::SetPromotionHintOnTaskRunnerSequence",
               "mailbox", mailbox.ToDebugString(), "promoted", promoted);
  // The OverlayStateService is made aware of mailboxes of interest through two
  // channels:
  // 1.) The registration of an observer for a mailbox.
  // 2.) The setting of a promotion hint for a mailbox (the overlay processor
  //  will only send hints for mailboxes which are tagged as
  //  'wants_promotion_hint' on their TransferResource).
  //
  // If this is the first promotion hint associated with a mailbox which has
  // not had an observer registered yet, then we will not have an existing
  // entry in 'mailboxes_' tracking it. Since there is no guarantee when we'll
  // receive another promotion hint for the mailbox we'll store the result so
  // any future observer can be informed of the current promotion state.
  auto [mailbox_iterator, insert] =
      mailboxes_.try_emplace(mailbox, std::make_unique<MailboxState>());
  MailboxState* mailbox_state = mailbox_iterator->second.get();
  DCHECK(mailbox_state != nullptr);
  bool state_change = mailbox_state->aggregator_.SetPromotionHint(promoted);

  if (!insert & state_change) {
    // Notifying observers requires an IPC so we only send an update when
    // the underlying state changes.
    OnStateChanged(mailbox, mailbox_state->aggregator_.GetPromotionState());
  }
}

void OverlayStateService::MailboxDestroyed(const gpu::Mailbox& mailbox) {
  DCHECK(!task_runner_->RunsTasksInCurrentSequence());
  // Use of base::Unretained is safe as OverlayStateService is a singleton
  // service bound to the lifetime of the GPU process.
  task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&OverlayStateService::MailboxDestroyedOnTaskRunnerSequence,
                     base::Unretained(this), mailbox));
}

void OverlayStateService::MailboxDestroyedOnTaskRunnerSequence(
    const gpu::Mailbox& mailbox) {
  TRACE_EVENT1("gpu",
               "OverlayStateService::MailboxDestroyedOnTaskRunnerSequence",
               "mailbox", mailbox.ToDebugString());
  mailboxes_.erase(mailbox);
}

}  // namespace viz