// Copyright 2013 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/test/test_in_process_context_provider.h"

#include <stdint.h>

#include <memory>
#include <utility>

#include "base/task/single_thread_task_runner.h"
#include "base/types/optional_util.h"
#include "components/viz/common/gpu/context_cache_controller.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
#include "gpu/command_buffer/common/context_creation_attribs.h"
#include "gpu/config/skia_limits.h"
#include "gpu/ipc/gl_in_process_context.h"
#include "gpu/ipc/raster_in_process_context.h"

namespace viz {

TestInProcessContextProvider::TestInProcessContextProvider(
    TestContextType type,
    bool support_locking,
    gpu::raster::GrShaderCache* gr_shader_cache,
    gpu::GpuProcessShmCount* use_shader_cache_shm_count)
    : type_(type), use_shader_cache_shm_count_(use_shader_cache_shm_count) {
  CHECK(main_thread_checker_.CalledOnValidThread());
  context_thread_checker_.DetachFromThread();

  if (support_locking) {
    context_lock_.emplace();
  }
}

TestInProcessContextProvider::~TestInProcessContextProvider() {
  CHECK(main_thread_checker_.CalledOnValidThread() ||
        context_thread_checker_.CalledOnValidThread());
}

void TestInProcessContextProvider::AddRef() const {
  base::RefCountedThreadSafe<TestInProcessContextProvider>::AddRef();
}

void TestInProcessContextProvider::Release() const {
  base::RefCountedThreadSafe<TestInProcessContextProvider>::Release();
}

gpu::ContextResult TestInProcessContextProvider::BindToCurrentSequence() {
  CHECK(context_thread_checker_.CalledOnValidThread());

  if (is_bound_) {
    return gpu::ContextResult::kSuccess;
  }

  auto* holder = TestGpuServiceHolder::GetInstance();

  if (type_ == TestContextType::kGLES2) {
    gles2_context_ = std::make_unique<gpu::GLInProcessContext>();
    auto result = gles2_context_->Initialize(
        TestGpuServiceHolder::GetInstance()->task_executor());
    CHECK_EQ(result, gpu::ContextResult::kSuccess);

    caps_ = gles2_context_->GetCapabilities();
  } else {
    raster_context_ = std::make_unique<gpu::RasterInProcessContext>();
    auto result = raster_context_->Initialize(
        holder->task_executor(), holder->gpu_service()->gr_shader_cache(),
        use_shader_cache_shm_count_);
    CHECK_EQ(result, gpu::ContextResult::kSuccess);

    caps_ = raster_context_->GetCapabilities();
  }

  cache_controller_ = std::make_unique<ContextCacheController>(
      ContextSupport(), base::SingleThreadTaskRunner::GetCurrentDefault());
  cache_controller_->SetLock(GetLock());

  is_bound_ = true;
  return gpu::ContextResult::kSuccess;
}

gpu::gles2::GLES2Interface* TestInProcessContextProvider::ContextGL() {
  CheckValidThreadOrLockAcquired();
  CHECK(gles2_context_);
  return gles2_context_->GetImplementation();
}

gpu::raster::RasterInterface* TestInProcessContextProvider::RasterInterface() {
  CheckValidThreadOrLockAcquired();
  CHECK(raster_context_);
  return raster_context_->GetImplementation();
}

gpu::ContextSupport* TestInProcessContextProvider::ContextSupport() {
  return gles2_context_ ? gles2_context_->GetImplementation()
                        : raster_context_->GetContextSupport();
}

gpu::SharedImageInterface*
TestInProcessContextProvider::SharedImageInterface() {
  return gles2_context_ ? gles2_context_->GetSharedImageInterface()
                        : raster_context_->GetSharedImageInterface();
}

ContextCacheController* TestInProcessContextProvider::CacheController() {
  CheckValidThreadOrLockAcquired();
  return cache_controller_.get();
}

base::Lock* TestInProcessContextProvider::GetLock() {
  return base::OptionalToPtr(context_lock_);
}

const gpu::Capabilities& TestInProcessContextProvider::ContextCapabilities()
    const {
  CheckValidThreadOrLockAcquired();
  return caps_;
}

const gpu::GpuFeatureInfo& TestInProcessContextProvider::GetGpuFeatureInfo()
    const {
  CheckValidThreadOrLockAcquired();
  return gles2_context_ ? gles2_context_->GetGpuFeatureInfo()
                        : raster_context_->GetGpuFeatureInfo();
}

void TestInProcessContextProvider::AddObserver(ContextLostObserver* obs) {
  observers_.AddObserver(obs);
}

void TestInProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {
  observers_.RemoveObserver(obs);
}

bool TestInProcessContextProvider::IsLost() {
  if (gles2_context_) {
    return gles2_context_->GetImplementation()->GetGraphicsResetStatusKHR() !=
           GL_NO_ERROR;
  } else {
    return raster_context_->GetImplementation()->GetGraphicsResetStatusKHR() !=
           GL_NO_ERROR;
  }
}

void TestInProcessContextProvider::SendOnContextLost() {
  for (auto& observer : observers_) {
    observer.OnContextLost();
  }
}

void TestInProcessContextProvider::ExecuteOnGpuThread(base::OnceClosure task) {
  CHECK(raster_context_);
  raster_context_->GetCommandBufferForTest()
      ->service_for_testing()
      ->ScheduleOutOfOrderTask(std::move(task));
}

void TestInProcessContextProvider::CheckValidThreadOrLockAcquired() const {
#if DCHECK_IS_ON()
  if (context_lock_) {
    context_lock_->AssertAcquired();
  } else {
    DCHECK(context_thread_checker_.CalledOnValidThread());
  }
#endif
}

GpuServiceImpl* TestInProcessContextProvider::GpuService() {
  return TestGpuServiceHolder::GetInstance()->gpu_service();
}

}  // namespace viz
