// Copyright 2024 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/service/frame_sinks/shared_image_interface_provider.h"

#include <memory>
#include <utility>

#include "base/functional/callback.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/command_buffer/service/scheduler_sequence.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_interface_in_process.h"

namespace viz {

SharedImageInterfaceProvider::SharedImageInterfaceProvider(
    GpuServiceImpl* gpu_service)
    : gpu_service_(gpu_service) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
}

SharedImageInterfaceProvider::~SharedImageInterfaceProvider() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);

  base::AutoLock hold(context_lock_);
  if (shared_context_state_) {
    shared_context_state_->RemoveContextLostObserver(this);
    shared_context_state_.reset();
  }
}

gpu::SharedImageInterface*
SharedImageInterfaceProvider::GetSharedImageInterface() {
  if (NeedsNewSharedImageInterface()) {
    CreateSharedImageInterface();
  }
  return shared_image_interface_.get();
}

bool SharedImageInterfaceProvider::NeedsNewSharedImageInterface() {
  base::AutoLock hold(context_lock_);
  return !shared_image_interface_ || context_lost_;
}

void SharedImageInterfaceProvider::CreateSharedImageInterface() {
  // This function should only be called on the compositor thread.
  CHECK(!gpu_service_->main_runner()->BelongsToCurrentThread());

  auto scheduler_sequence = std::make_unique<gpu::SchedulerSequence>(
      gpu_service_->GetGpuScheduler(), gpu_service_->main_runner(),
      /*target_thread_is_always_available=*/true);
  gpu::SingleTaskSequence* sequence_ptr = scheduler_sequence.get();

  base::ScopedAllowBaseSyncPrimitives allow_wait;
  base::WaitableEvent event;
  sequence_ptr->ScheduleTask(
      base::BindOnce(
          &SharedImageInterfaceProvider::CreateSharedImageInterfaceOnGpu,
          base::Unretained(this), std::move(scheduler_sequence), &event),
      /*sync_token_fences=*/{}, gpu::SyncToken());
  event.Wait();
}

void SharedImageInterfaceProvider::CreateSharedImageInterfaceOnGpu(
    std::unique_ptr<gpu::SingleTaskSequence> scheduler_sequence,
    base::WaitableEvent* event) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
  base::AutoLock hold(context_lock_);
  shared_context_state_ =
      gl::GetGLImplementation() != gl::kGLImplementationDisabled && gpu_service_
          ? gpu_service_->GetContextState()
          : nullptr;
  context_lost_ = false;

  shared_image_interface_ = gpu::SharedImageInterfaceInProcess::Create(
      std::move(scheduler_sequence), gpu_service_->gpu_preferences(),
      gpu_service_->gpu_driver_bug_workarounds(),
      gpu_service_->gpu_feature_info(), shared_context_state_.get(),
      gpu_service_->shared_image_manager(),
      /*is_for_display_compositor=*/false, gpu_service_->main_runner());

  if (shared_context_state_) {
    shared_context_state_->AddContextLostObserver(this);
  }

  event->Signal();
}

void SharedImageInterfaceProvider::OnContextLost() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
  base::AutoLock hold(context_lock_);
  shared_context_state_->RemoveContextLostObserver(this);
  shared_context_state_ = nullptr;
  context_lost_ = true;
}

}  // namespace viz
