// Copyright 2016 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/gl/gpu_service_impl.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/allocator/partition_alloc_support.h"
#include "base/android/android_info.h"
#include "base/command_line.h"
#include "base/debug/asan_invalid_access.h"
#include "base/debug/crash_logging.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/memory_pressure_listener_registry.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/synchronization/waitable_event.h"
#include "base/system/sys_info.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/persistent_cache/pending_backend.h"
#include "components/startup_metric_utils/gpu/startup_metric_utils.h"
#include "components/version_info/version_info.h"
#include "components/viz/common/features.h"
#include "components/viz/common/resources/peak_gpu_memory_tracker_util.h"
#include "components/viz/service/gl/gpu_log_message_manager.h"
#include "components/vrp_flags/buildflags.h"
#include "gpu/command_buffer/service/dawn_caching_interface.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/config/gpu_util.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/common/gpu_peak_memory.h"
#include "gpu/ipc/common/memory_stats.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include "gpu/vulkan/buildflags.h"
#include "ipc/ipc_channel_proxy.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/gpu/buildflags.h"
#include "media/gpu/gpu_video_accelerator_util.h"
#include "media/gpu/gpu_video_encode_accelerator_factory.h"
#include "media/gpu/ipc/service/media_gpu_channel_manager.h"
#include "media/mojo/services/gpu_mojo_media_client.h"
#include "media/mojo/services/mojo_video_encode_accelerator_provider.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/webnn/webnn_context_provider_impl.h"
#include "skia/buildflags.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLInterface.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/base/ui_base_features.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/gpu_switching_manager.h"
#include "ui/gl/init/create_gr_gl_interface.h"
#include "ui/gl/init/gl_factory.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/viz/service/gl/throw_uncaught_exception.h"
#include "media/base/android/media_codec_util.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
#include "components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h"
#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
#include "gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gl/dcomp_surface_registry.h"
#include "ui/gl/direct_composition_support.h"
#endif

#if BUILDFLAG(IS_APPLE)
#include "ui/base/cocoa/quartz_util.h"
#endif

#if BUILDFLAG(SKIA_USE_DAWN)
#include "gpu/command_buffer/service/dawn_context_provider.h"
#include "gpu/command_buffer/service/gpu_persistent_cache.h"
#endif

#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
#include "base/test/clang_profiling.h"
#endif

#if BUILDFLAG(ENABLE_VULKAN)
#include "components/viz/common/gpu/vulkan_context_provider.h"
#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
#endif

#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#endif  // BUILDFLAG(IS_OZONE)

#if BUILDFLAG(ENABLE_VRP_FLAGS)
#include "components/vrp_flags/vrp_flags.h"       // nogncheck
#include "components/vrp_flags/vrp_flags_impl.h"  // nogncheck
#endif                                            // BUILDFLAG(ENABLE_VRP_FLAGS)

namespace viz {

namespace {

// The names emitted for GPU initialization trace events.
// This code may be removed after the following investigation:
// crbug.com/1350257
constexpr char kGpuInitializationEventCategory[] = "latency";
constexpr char kGpuInitializationEvent[] = "GpuInitialization";

bool IsAcceleratedJpegDecodeSupported() {
#if BUILDFLAG(IS_CHROMEOS)
  return chromeos_camera::GpuMjpegDecodeAcceleratorFactory::
      IsAcceleratedJpegDecodeSupported();
#else
  return false;
#endif  // BUILDFLAG(IS_CHROMEOS)
}

void RunGetPeakGpuMemoryUsageCallbackOnMainThread(
    GpuServiceImpl::GetPeakMemoryUsageCallback callback,
    uint64_t peak_memory,
    base::flat_map<gpu::GpuPeakMemoryAllocationSource, uint64_t>
        allocation_per_source) {
  std::move(callback).Run(peak_memory, std::move(allocation_per_source));
}

gpu::GpuPersistentCache::AsyncDiskWriteOpts
GetPersistentCacheAsyncDiskWriteOpts() {
  gpu::GpuPersistentCache::AsyncDiskWriteOpts async_opts;
  // The GpuPersistentCache uses a task runner for doing disk writes in the
  // background. These are low priority tasks but once the task starts running,
  // we do not want it to be interrupted because it holds locks on the database.
  // This behaviour is achieved with the BEST_EFFORT priority and
  // MUST_USE_FOREGROUND policy.
  async_opts.task_runner = base::ThreadPool::CreateSequencedTaskRunner(
      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
       base::ThreadPolicy::MUST_USE_FOREGROUND,
       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
  async_opts.max_pending_bytes_to_write = gpu::GetDefaultGpuDiskCacheSize();
  return async_opts;
}

gpu::GpuPersistentCache::MetadataOpts GetPersistentCacheMetadataOpts() {
  gpu::GpuPersistentCache::MetadataOpts metadata_options;

  metadata_options.enabled =
      base::FeatureList::IsEnabled(features::kGpuPersistentCacheMetadata);
  metadata_options.preload_count =
      features::kGpuPersistentCacheMetadataPreloadCount.Get();

  return metadata_options;
}

void InduceMemoryInvalidAccessHelper(mojom::MemoryInvalidAccessType action) {
  switch (action) {
    case mojom::MemoryInvalidAccessType::kHeapOverflow:
      base::debug::AsanHeapOverflow();
      break;
    case mojom::MemoryInvalidAccessType::kHeapUnderflow:
      base::debug::AsanHeapUnderflow();
      break;
    case mojom::MemoryInvalidAccessType::kUseAfterFree:
      base::debug::AsanHeapUseAfterFree();
      break;
    case mojom::MemoryInvalidAccessType::kMemberDereferenceAfterFree:
      base::debug::AsanHeapMemberDereferenceAfterFree();
      break;
  }
}

}  // namespace

GpuServiceImpl::GpuServiceImpl(
    const gpu::GpuPreferences& gpu_preferences,
    const gpu::GPUInfo& gpu_info,
    const gpu::GpuFeatureInfo& gpu_feature_info,
    const std::optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
    const std::optional<gpu::GpuFeatureInfo>& gpu_feature_info_for_hardware_gpu,
    const gfx::GpuExtraInfo& gpu_extra_info,
    InitParams init_params)
    : main_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
      io_runner_(std::move(init_params.io_runner)),
      watchdog_thread_(std::move(init_params.watchdog_thread)),
      gpu_preferences_(gpu_preferences),
      gpu_info_(gpu_info),
      gpu_feature_info_(gpu_feature_info),
      gpu_driver_bug_workarounds_(
          gpu_feature_info_.enabled_gpu_driver_bug_workarounds),
      gpu_info_for_hardware_gpu_(gpu_info_for_hardware_gpu),
      gpu_feature_info_for_hardware_gpu_(gpu_feature_info_for_hardware_gpu),
      gpu_extra_info_(gpu_extra_info),
#if BUILDFLAG(ENABLE_VULKAN)
      vulkan_implementation_(init_params.vulkan_implementation),
#endif
      persistent_caches_(
          /*max_in_memory_cache_size=*/gpu::GetDefaultGpuDiskCacheSize(),
          /*metadata_options=*/GetPersistentCacheMetadataOpts(),
          /*async_write_options=*/GetPersistentCacheAsyncDiskWriteOpts()),
      clear_shader_cache_(base::FeatureList::IsEnabled(
          features::kClearGrShaderDiskCacheOnInvalidPrefix)) {
  DCHECK(!io_runner_->BelongsToCurrentThread());

#if BUILDFLAG(ENABLE_VULKAN)
  if (vulkan_implementation_) {
    bool is_native_vulkan =
        gpu_preferences_.use_vulkan == gpu::VulkanImplementationName::kNative ||
        gpu_preferences_.use_vulkan ==
            gpu::VulkanImplementationName::kForcedNative;
    // With swiftshader the vendor_id is 0xffff. For some tests gpu_info is not
    // initialized, so the vendor_id is 0.
    bool is_native_gl =
        gpu_info_.gpu.vendor_id != 0xffff && gpu_info_.gpu.vendor_id != 0;

    const bool is_thread_safe = features::IsDrDcEnabled(gpu_feature_info);

    // If GL is using a real GPU, the gpu_info will be passed in and vulkan will
    // use the same GPU.
    vulkan_context_provider_ = VulkanInProcessContextProvider::Create(
        vulkan_implementation_, gpu_preferences_.vulkan_heap_memory_limit,
        gpu_preferences_.vulkan_sync_cpu_memory_limit, is_thread_safe,
        (is_native_vulkan && is_native_gl) ? &gpu_info_ : nullptr);
    if (!vulkan_context_provider_) {
      DLOG(ERROR) << "Failed to create Vulkan context provider.";
    }
  }
#endif

#if BUILDFLAG(USE_DAWN) || BUILDFLAG(SKIA_USE_DAWN)
  dawn_caching_interface_factory_ =
      std::make_unique<gpu::webgpu::DawnCachingInterfaceFactory>();
#endif

#if BUILDFLAG(SKIA_USE_DAWN)
  if (gpu_preferences_.gr_context_type == gpu::GrContextType::kGraphiteDawn) {
    dawn_context_provider_ = std::move(init_params.dawn_context_provider);

    if (dawn_context_provider_) {
      // GpuServiceImpl holds the instance of DawnContextProvider, so it
      // outlives the DawnContextProvider.
      if (features::SkiaGraphiteUsesPersistentCache()) {
        auto persistent_cache =
            persistent_caches_.GetCache(gpu::kGraphiteDawnGpuDiskCacheHandle);
        dawn_context_provider_->SetCachingInterface(
            std::move(persistent_cache));
      } else {
        auto cache_blob_callback = base::BindRepeating(
            [](GpuServiceImpl* self, const std::string& key,
               const std::string& blob) {
              self->StoreBlobToDisk(gpu::kGraphiteDawnGpuDiskCacheHandle, key,
                                    blob);
            },
            base::Unretained(this));
        auto dawn_caching_interface =
            dawn_caching_interface_factory_->CreateInstance(
                gpu::kGraphiteDawnGpuDiskCacheHandle,
                std::move(cache_blob_callback));
        dawn_context_provider_->SetCachingInterface(
            std::move(dawn_caching_interface));
      }
    }
  }
#endif  // BUILDFLAG(SKIA_USE_DAWN)

  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
}

GpuServiceImpl::GpuServiceImpl()
    : persistent_caches_(
          /*max_in_memory_cache_size=*/gpu::GetDefaultGpuDiskCacheSize(),
          /*metadata_options=*/GetPersistentCacheMetadataOpts(),
          /*async_write_options=*/GetPersistentCacheAsyncDiskWriteOpts()),
      clear_shader_cache_(base::FeatureList::IsEnabled(
          features::kClearGrShaderDiskCacheOnInvalidPrefix)) {}

GpuServiceImpl::~GpuServiceImpl() {
  if (main_runner_) {
    DCHECK(main_runner_->BelongsToCurrentThread());
  }

  // Ensure we don't try to exit when already in the process of exiting.
  is_exiting_.Set();

  bind_task_tracker_.TryCancelAll();

#if BUILDFLAG(IS_WIN)
  gl::DirectCompositionOverlayCapsMonitor::GetInstance()->RemoveObserver(this);
#endif

  // Destroy the receiver on the IO thread.
  {
    base::WaitableEvent wait;
    auto destroy_receiver_task = base::BindOnce(
        [](mojo::Receiver<mojom::GpuService>* receiver,
           base::WaitableEvent* wait) {
          receiver->reset();
          wait->Signal();
        },
        &receiver_, base::Unretained(&wait));
    if (io_runner_ &&
        io_runner_->PostTask(FROM_HERE, std::move(destroy_receiver_task))) {
      wait.Wait();
    }
  }

  if (watchdog_thread_)
    watchdog_thread_->OnGpuProcessTearDown();

  compositor_gpu_thread_.reset();
  media_gpu_channel_manager_.reset();
  gpu_channel_manager_.reset();

  // WebNN must be destroyed before the scheduler is destroyed.
  webnn_context_provider_.reset();

  // Scheduler must be destroyed before sync point manager is destroyed.
  owned_scheduler_.reset();
  owned_sync_point_manager_.reset();
  owned_shared_image_manager_.reset();

  // The image decode accelerator worker must outlive the GPU channel manager so
  // that it doesn't get any decode requests during/after destruction.
  DCHECK(!gpu_channel_manager_);

  // Signal this event before destroying the child process. That way all
  // background threads can cleanup. For example, in the renderer the
  // RenderThread instances will be able to notice shutdown before the render
  // process begins waiting for them to exit.
  if (owned_shutdown_event_)
    owned_shutdown_event_->Signal();
}

void GpuServiceImpl::UpdateGPUInfo() {
  DCHECK(main_runner_->BelongsToCurrentThread());
  DCHECK(!gpu_host_);

  gpu_info_.jpeg_decode_accelerator_supported =
      IsAcceleratedJpegDecodeSupported();

  // Record initialization only after collecting the GPU info because that can
  // take a significant amount of time.
  base::TimeTicks now = base::TimeTicks::Now();
  gpu_info_.initialization_time = now - start_time_;
  startup_metric_utils::GetGpu().RecordGpuInitialized(now);

  UMA_HISTOGRAM_CUSTOM_TIMES("GPU.GPUInitializationTime.V4",
                             gpu_info_.initialization_time,
                             base::Milliseconds(5), base::Seconds(90), 100);
  TRACE_EVENT_BEGIN(kGpuInitializationEventCategory, kGpuInitializationEvent,
                    perfetto::Track::FromPointer(this), start_time_);
  TRACE_EVENT_END(kGpuInitializationEventCategory,
                  /* kGpuInitializationEvent */
                  perfetto::Track::FromPointer(this), now);
}

void GpuServiceImpl::UpdateGPUInfoGL() {
  DCHECK(main_runner_->BelongsToCurrentThread());
  gpu::CollectGraphicsInfoGL(&gpu_info_, GetContextState()->display());
  gpu_host_->DidUpdateGPUInfo(gpu_info_);
}

#if BUILDFLAG(IS_ANDROID)
void GpuServiceImpl::InitializeWithHost(
    mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
    gpu::GpuProcessShmCount use_shader_cache_shm_count,
    scoped_refptr<gl::GLSurface> default_offscreen_surface,
    mojom::GpuServiceCreationParamsPtr creation_params,
    gpu::SyncPointManager* sync_point_manager,
    gpu::SharedImageManager* shared_image_manager,
    gpu::Scheduler* scheduler,
    base::WaitableEvent* shutdown_event,
    const gpu::SharedContextState::GrContextOptionsProvider*
        gr_context_options_provider) {
  if (!sync_point_manager) {
    sync_point_manager = CreateSyncPointManager();
  }

  if (!shared_image_manager) {
    shared_image_manager = CreateSharedImageManager();
  }

  if (!scheduler) {
    scheduler = CreateScheduler(sync_point_manager);
  }

  if (!shutdown_event) {
    shutdown_event = CreateShutdownEvent();
  }

  gr_context_options_provider_ = gr_context_options_provider;

  InitializeWithHostInternal(
      std::move(pending_gpu_host), std::move(use_shader_cache_shm_count),
      default_offscreen_surface, std::move(creation_params), sync_point_manager,
      shared_image_manager, scheduler, shutdown_event);
}
#else
void GpuServiceImpl::InitializeWithHost(
    mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
    gpu::GpuProcessShmCount use_shader_cache_shm_count,
    scoped_refptr<gl::GLSurface> default_offscreen_surface,
    mojom::GpuServiceCreationParamsPtr creation_params,
    base::WaitableEvent* shutdown_event) {
  gpu::SyncPointManager* sync_point_manager = CreateSyncPointManager();
#if BUILDFLAG(IS_OZONE)
  gpu::SharedImageManager* shared_image_manager =
      CreateSharedImageManager(creation_params->supports_overlays);
#else
  gpu::SharedImageManager* shared_image_manager = CreateSharedImageManager();
#endif
  gpu::Scheduler* scheduler = CreateScheduler(sync_point_manager);

  if (!shutdown_event) {
    shutdown_event = CreateShutdownEvent();
  }

  InitializeWithHostInternal(
      std::move(pending_gpu_host), std::move(use_shader_cache_shm_count),
      default_offscreen_surface, std::move(creation_params), sync_point_manager,
      shared_image_manager, scheduler, shutdown_event);

#if BUILDFLAG(IS_WIN)
  // shared_image_d3d must be initialized after we call
  // InitializeWithHostInternal as that is where the shared context state is
  // created.
  auto shared_context_state = GetContextState();
  if (shared_context_state) {
    gpu_info_.shared_image_d3d =
        gpu::D3DImageBackingFactory::IsD3DSharedImageSupported(
            shared_context_state->GetD3D11Device().Get(), gpu_preferences_);

    gpu_host_->DidUpdateGPUInfo(gpu_info_);
  }
#endif
}
#endif

void GpuServiceImpl::InitializeWithHostInternal(
    mojo::PendingRemote<mojom::GpuHost> pending_gpu_host,
    gpu::GpuProcessShmCount use_shader_cache_shm_count,
    scoped_refptr<gl::GLSurface> default_offscreen_surface,
    mojom::GpuServiceCreationParamsPtr creation_params,
    gpu::SyncPointManager* sync_point_manager,
    gpu::SharedImageManager* shared_image_manager,
    gpu::Scheduler* scheduler,
    base::WaitableEvent* shutdown_event) {
  DCHECK(main_runner_->BelongsToCurrentThread());

  scheduler_ = scheduler;
  shutdown_event_ = shutdown_event;

  use_shader_cache_shm_count_ =
      base::MakeRefCounted<gpu::RefCountedGpuProcessShmCount>(
          std::move(use_shader_cache_shm_count));

  mojo::Remote<mojom::GpuHost> gpu_host(std::move(pending_gpu_host));

#if BUILDFLAG(IS_LINUX)
  gpu_extra_info_.is_gmb_nv12_supported = IsGMBNV12Supported();
#endif

  gpu_host->DidInitialize(gpu_info_, gpu_feature_info_,
                          gpu_info_for_hardware_gpu_,
                          gpu_feature_info_for_hardware_gpu_, gpu_extra_info_);
  gpu_host_ = mojo::SharedRemote<mojom::GpuHost>(gpu_host.Unbind(), io_runner_);

  // Defer creation of the render thread. This is to prevent it from handling
  // IPC messages before the sandbox has been enabled and all other necessary
  // initialization has succeeded.
  gpu_channel_manager_ = std::make_unique<gpu::GpuChannelManager>(
      gpu_preferences_, this, watchdog_thread_.get(), main_runner_, io_runner_,
      scheduler_, sync_point_manager, shared_image_manager, gpu_feature_info_,
      &use_shader_cache_shm_count_->data, std::move(default_offscreen_surface),
      vulkan_context_provider(), dawn_context_provider(),
      dawn_caching_interface_factory(), gr_context_options_provider_,
      &persistent_caches_);

  media_gpu_channel_manager_ = std::make_unique<media::MediaGpuChannelManager>(
      gpu_channel_manager_.get());

  // Create and Initialize compositor gpu thread.
  if (features::IsDrDcEnabled(gpu_feature_info_)) {
    // Add a crash key for DrDC.
    static auto* drdc_crash_key = base::debug::AllocateCrashKeyString(
        "is-drdc-enabled", base::debug::CrashKeySize::Size32);
    base::debug::SetCrashKeyString(drdc_crash_key, "1");

    CompositorGpuThread::CreateParams params;
    params.gpu_channel_manager = gpu_channel_manager_.get();
    params.display =
        gpu_channel_manager_->default_offscreen_surface()
            ? gpu_channel_manager_->default_offscreen_surface()->GetGLDisplay()
            : nullptr;
    params.enable_watchdog = !!watchdog_thread_;

#if BUILDFLAG(ENABLE_VULKAN)
    params.vulkan_implementation = vulkan_implementation_;
    params.device_queue = vulkan_context_provider_
                              ? vulkan_context_provider_->GetDeviceQueue()
                              : nullptr;
#endif
#if BUILDFLAG(SKIA_USE_DAWN)
    // Initialize the thread-safe GraphiteSharedContext before starting the DrDC
    // thread to prevent a race on accessing it via SharedContextState.
    if (dawn_context_provider_ &&
        dawn_context_provider_->use_thread_safe_shared_context()) {
      dawn_context_provider_->InitializeThreadSafeGraphiteContext(
          gpu::GetDefaultGraphiteContextOptions(gpu_driver_bug_workarounds_),
          &use_shader_cache_shm_count_->data);
    }
    params.dawn_context_provider = dawn_context_provider_.get();
#endif

    compositor_gpu_thread_ = CompositorGpuThread::Create(params);
  }

  UMA_HISTOGRAM_BOOLEAN("GPU.DrDcEnabled", !!compositor_gpu_thread_);

#if BUILDFLAG(IS_WIN)
  // Add GpuServiceImpl to DirectCompositionOverlayCapsMonitor observer list for
  // overlay and DXGI info update. This should be added after |gpu_host_| is
  // initialized.
  gl::DirectCompositionOverlayCapsMonitor::GetInstance()->AddObserver(this);
#endif
}

void GpuServiceImpl::Bind(
    mojo::PendingReceiver<mojom::GpuService> pending_receiver) {
  if (main_runner_->BelongsToCurrentThread()) {
    bind_task_tracker_.PostTask(
        io_runner_.get(), FROM_HERE,
        base::BindOnce(&GpuServiceImpl::Bind, base::Unretained(this),
                       std::move(pending_receiver)));
    return;
  }
  DCHECK(!receiver_.is_bound());
  receiver_.Bind(std::move(pending_receiver));
}

void GpuServiceImpl::DisableGpuCompositing() {
  // Can be called from any thread.
  gpu_host_->DisableGpuCompositing();
}

scoped_refptr<gpu::SharedContextState> GpuServiceImpl::GetContextState() {
  DCHECK(main_runner_->BelongsToCurrentThread());
  gpu::ContextResult result;
  return gpu_channel_manager_->GetSharedContextState(&result);
}

void GpuServiceImpl::SetPriorityChangedCallback(
    PriorityChangedCallback callback) {
  DCHECK(main_runner_->BelongsToCurrentThread());
  priority_changed_callback_ = std::move(callback);
}

#if BUILDFLAG(IS_CHROMEOS)
void GpuServiceImpl::CreateJpegDecodeAccelerator(
    mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
        jda_receiver) {
  DCHECK(io_runner_->BelongsToCurrentThread());
  chromeos_camera::MojoMjpegDecodeAcceleratorService::Create(
      std::move(jda_receiver),
      base::BindRepeating(
          &GpuServiceImpl::SetMjpegDecodeAcceleratorBeginFrameCB,
          base::Unretained(this)));
}

void GpuServiceImpl::CreateJpegEncodeAccelerator(
    mojo::PendingReceiver<chromeos_camera::mojom::JpegEncodeAccelerator>
        jea_receiver) {
  DCHECK(io_runner_->BelongsToCurrentThread());
  chromeos_camera::MojoJpegEncodeAcceleratorService::Create(
      std::move(jea_receiver));
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
void GpuServiceImpl::RegisterDCOMPSurfaceHandle(
    mojo::PlatformHandle surface_handle,
    RegisterDCOMPSurfaceHandleCallback callback) {
  base::UnguessableToken token =
      gl::DCOMPSurfaceRegistry::GetInstance()->RegisterDCOMPSurfaceHandle(
          surface_handle.TakeHandle());
  std::move(callback).Run(token);
}

void GpuServiceImpl::UnregisterDCOMPSurfaceHandle(
    const base::UnguessableToken& token) {
  gl::DCOMPSurfaceRegistry::GetInstance()->UnregisterDCOMPSurfaceHandle(token);
}
#endif  // BUILDFLAG(IS_WIN)

void GpuServiceImpl::CreateVideoEncodeAcceleratorProvider(
    mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
        vea_provider_receiver) {
  DCHECK(io_runner_->BelongsToCurrentThread());

  // Offload VEA providers to a dedicated runner. Things like loading profiles
  // and creating encoder might take quite some time, and they might block
  // processing of other mojo calls if executed on the current runner.
  scoped_refptr<base::SequencedTaskRunner> runner;
#if BUILDFLAG(IS_FUCHSIA)
  // TODO(crbug.com/40850116): Fuchsia does not support FIDL communication from
  // ThreadPool's worker threads.
  if (!vea_thread_) {
    base::Thread::Options thread_options(base::MessagePumpType::IO, /*size=*/0);
    vea_thread_ =
        std::make_unique<base::Thread>("GpuVideoEncodeAcceleratorThread");
    CHECK(vea_thread_->StartWithOptions(std::move(thread_options)));
  }
  runner = vea_thread_->task_runner();
#elif BUILDFLAG(IS_WIN)
  // Windows hardware encoder requires a COM STA thread.
  runner = base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()});
#else
  // MayBlock() because MF VEA can take long time running GetSupportedProfiles()
  if (base::FeatureList::IsEnabled(
          media::kUseSequencedTaskRunnerForMojoVEAProvider)) {
    runner = base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
  } else {
    runner = base::ThreadPool::CreateSingleThreadTaskRunner({base::MayBlock()});
  }
#endif
  media::MojoVideoEncodeAcceleratorProvider::Create(
      std::move(vea_provider_receiver),
      base::BindRepeating(&media::GpuVideoEncodeAcceleratorFactory::CreateVEA),
      gpu_preferences_, gpu_channel_manager_->gpu_driver_bug_workarounds(),
      gpu_info_.active_gpu(), std::move(runner),
      media_gpu_channel_manager_->AsWeakPtr(), main_runner_);
}

void GpuServiceImpl::BindWebNNContextProvider(
    mojo::PendingReceiver<webnn::mojom::WebNNContextProvider> pending_receiver,
    int client_id,
    uint64_t client_tracing_id,
    bool is_incognito) {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::BindWebNNContextProvider,
                                  weak_ptr_, std::move(pending_receiver),
                                  client_id, client_tracing_id, is_incognito));
    return;
  }

  CreateWebNNContextProviderIfNeeded();

  webnn_context_provider_->BindWebNNContextProvider(
      std::move(pending_receiver),
      {is_incognito, client_id, client_tracing_id});
}

void GpuServiceImpl::CreateWebNNContextProviderIfNeeded() {
  if (webnn_context_provider_) {
    return;
  }

  webnn_context_provider_ = webnn::WebNNContextProviderImpl::Create(
      gpu_feature_info_, gpu_info_, shared_image_manager(),
      gpu_channel_manager_->peak_memory_monitor(),
      base::BindOnce(&GpuServiceImpl::LoseAllContexts, weak_ptr_),
      main_runner(), GetGpuScheduler(), gpu_host_);
}

void GpuServiceImpl::BindWebNNServiceIntrospection(
    mojo::PendingReceiver<webnn::mojom::WebNNServiceIntrospection>
        pending_receiver) {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::BindWebNNServiceIntrospection,
                       weak_ptr_, std::move(pending_receiver)));
    return;
  }

  CreateWebNNContextProviderIfNeeded();

  webnn_context_provider_->BindWebNNServiceIntrospection(
      std::move(pending_receiver));
}

void GpuServiceImpl::GetVideoMemoryUsageStats(
    GetVideoMemoryUsageStatsCallback callback) {
  if (io_runner_->BelongsToCurrentThread()) {
    auto wrap_callback = base::BindPostTask(io_runner_, std::move(callback));
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::GetVideoMemoryUsageStats,
                                  weak_ptr_, std::move(wrap_callback)));
    return;
  }
  gpu::VideoMemoryUsageStats video_memory_usage_stats;
  gpu_channel_manager_->GetVideoMemoryUsageStats(&video_memory_usage_stats);

  if (compositor_gpu_thread()) {
    // when DrDC is enabled, add SKIA and SHARED_CONTEXT_STATE memory from
    // CompositorGpuThread.
    compositor_gpu_thread()->AddVideoMemoryUsageStatsOnCompositorGpu(
        std::move(callback), video_memory_usage_stats);
  } else {
    std::move(callback).Run(video_memory_usage_stats);
  }
}

void GpuServiceImpl::StartPeakMemoryMonitor(uint32_t sequence_num) {
  main_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&GpuServiceImpl::StartPeakMemoryMonitorOnMainThread,
                     weak_ptr_, sequence_num));
}

void GpuServiceImpl::GetPeakMemoryUsage(uint32_t sequence_num,
                                        GetPeakMemoryUsageCallback callback) {
  main_runner_->PostTask(
      FROM_HERE, base::BindOnce(&GpuServiceImpl::GetPeakMemoryUsageOnMainThread,
                                weak_ptr_, sequence_num, std::move(callback)));
}

#if BUILDFLAG(IS_WIN)
void GpuServiceImpl::RequestDXGIInfo(RequestDXGIInfoCallback callback) {
  DCHECK(io_runner_->BelongsToCurrentThread());
  main_runner_->PostTask(
      FROM_HERE, base::BindOnce(&GpuServiceImpl::RequestDXGIInfoOnMainThread,
                                weak_ptr_, std::move(callback)));
}

void GpuServiceImpl::RequestDXGIInfoOnMainThread(
    RequestDXGIInfoCallback callback) {
  DCHECK(main_runner_->BelongsToCurrentThread());
  dxgi_info_ = gl::GetDirectCompositionHDRMonitorDXGIInfo();
  io_runner_->PostTask(FROM_HERE,
                       base::BindOnce(std::move(callback), dxgi_info_.Clone()));
}
#endif

void GpuServiceImpl::LoseAllContexts() {
  if (IsExiting())
    return;
  gpu_channel_manager_->LoseAllContexts();
  if (compositor_gpu_thread_)
    compositor_gpu_thread_->LoseContext();
}

void GpuServiceImpl::DidCreateContextSuccessfully() {
  gpu_host_->DidCreateContextSuccessfully();
}

void GpuServiceImpl::DidCreateOffscreenContext(const GURL& active_url) {
  gpu_host_->DidCreateOffscreenContext(active_url);
}

void GpuServiceImpl::DidDestroyChannel(int client_id) {
  DCHECK(main_runner_->BelongsToCurrentThread());
  media_gpu_channel_manager_->RemoveChannel(client_id);
  gpu_host_->DidDestroyChannel(client_id);
}

void GpuServiceImpl::DidDestroyAllChannels() {
  DCHECK(main_runner_->BelongsToCurrentThread());
  gpu_host_->DidDestroyAllChannels();
}

void GpuServiceImpl::DidDestroyOffscreenContext(const GURL& active_url) {
  DCHECK(main_runner_->BelongsToCurrentThread());
  gpu_host_->DidDestroyOffscreenContext(active_url);
}

void GpuServiceImpl::DidLoseContext(gpu::error::ContextLostReason reason,
                                    const GURL& active_url) {
  gpu_host_->DidLoseContext(reason, active_url);
}

std::string GpuServiceImpl::GetShaderPrefixKey() {
  base::AutoLock lock(shader_prefix_key_lock_);
  if (shader_prefix_key_.empty()) {
    const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info_.active_gpu();
    std::string product =
        std::string(version_info::GetProductNameAndVersionForUserAgent());

    shader_prefix_key_ =
        product + "-" + gpu_info_.gl_vendor + "-" + gpu_info_.gl_renderer +
        "-" + active_gpu.driver_version + "-" + active_gpu.driver_vendor + "-" +
        base::SysInfo::ProcessCPUArchitecture();

#if BUILDFLAG(IS_ANDROID)
    std::string build_fp = base::android::android_info::android_build_fp();
    shader_prefix_key_ += "-" + build_fp;
#endif
  }

  return shader_prefix_key_;
}

void GpuServiceImpl::StoreBlobToDisk(const gpu::GpuDiskCacheHandle& handle,
                                     const std::string& key,
                                     const std::string& shader) {
  std::string prefix_key = key;
  if (GetHandleType(handle) == gpu::GpuDiskCacheType::kGlShaders) {
    std::string prefix = GetShaderPrefixKey();
    prefix_key = prefix + ":" + key;
  }
  gpu_host_->StoreBlobToDisk(handle, prefix_key, shader);
}

void GpuServiceImpl::LoadedBlob(const gpu::GpuDiskCacheHandle& handle,
                                const std::string& key,
                                const std::string& data) {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::LoadedBlob, weak_ptr_,
                                  handle, key, data));
    return;
  }

  std::string no_prefix_key = key;
  if (GetHandleType(handle) == gpu::GpuDiskCacheType::kGlShaders) {
    std::string prefix = GetShaderPrefixKey();
    bool prefix_ok = !key.compare(0, prefix.length(), prefix);
    UMA_HISTOGRAM_BOOLEAN("GPU.ShaderLoadPrefixOK", prefix_ok);
    if (prefix_ok) {
      // Remove the prefix from the key before load.
      no_prefix_key = key.substr(prefix.length() + 1);
    } else {
      // If the prefix is not ok, its likely that all the other entries in the
      // cache will have prefix that does not matches. Clear the whole disk
      // cache in that case to remove all stale entries and make room for newer
      // entries.
      if (clear_shader_cache_) {
        gpu_host_->ClearGrShaderDiskCache();
      }
      return;
    }
  }
  gpu_channel_manager_->PopulateCache(handle, no_prefix_key, data);
}

void GpuServiceImpl::GetIsolationKey(
    int client_id,
    const blink::WebGPUExecutionContextToken& token,
    GetIsolationKeyCallback cb) {
  gpu_host_->GetIsolationKey(client_id, token, std::move(cb));
}

void GpuServiceImpl::MaybeExitOnContextLost(
    gpu::error::ContextLostReason context_lost_reason) {
  DCHECK(main_runner_->BelongsToCurrentThread());

  if (in_host_process()) {
    // We can't restart the GPU process when running in the host process;
    // instead, just hope for recovery from the context loss.
    return;
  }

  RestartGpuProcessForContextLoss("Context was lost.");
}

bool GpuServiceImpl::IsExiting() const {
  return is_exiting_.IsSet();
}

void GpuServiceImpl::EstablishGpuChannel(
    int32_t client_id,
    uint64_t client_tracing_id,
    bool is_gpu_host,
    bool enable_extra_handles_validation,
    mojo::ScopedMessagePipeHandle channel_handle,
    EstablishGpuChannelCallback callback) {
  if (io_runner_->BelongsToCurrentThread()) {
    if (IsExiting()) {
      // We are already exiting so there is no point in responding. Close the
      // receiver so we can safely drop the callback.
      receiver_.reset();
      return;
    }

    if (gpu::IsReservedClientId(client_id)) {
      std::move(callback).Run(/*success=*/false, gpu::GPUInfo(),
                              gpu::GpuFeatureInfo(),
                              gpu::SharedImageCapabilities());
      return;
    }
    EstablishGpuChannelCallback wrap_callback =
        base::BindPostTask(io_runner_, std::move(callback));
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::EstablishGpuChannel, weak_ptr_,
                       client_id, client_tracing_id, is_gpu_host,
                       enable_extra_handles_validation,
                       std::move(channel_handle), std::move(wrap_callback)));
    return;
  }

  auto channel_token = base::UnguessableToken::Create();
  gpu::GpuChannel* gpu_channel = gpu_channel_manager_->EstablishChannel(
      channel_token, client_id, client_tracing_id, is_gpu_host,
      enable_extra_handles_validation, gpu_extra_info_, gpu_info_,
      gpu_feature_info_);

  if (!gpu_channel) {
    std::move(callback).Run(/*success=*/false, gpu::GPUInfo(),
                            gpu::GpuFeatureInfo(),
                            gpu::SharedImageCapabilities());
    return;
  }

  if (features::IsLegacyIpcDisabled()) {
    gpu_channel->Start(std::move(channel_handle));
  } else {
    gpu_channel->Init(channel_handle.release());
  }

  media_gpu_channel_manager_->AddChannel(client_id, channel_token);

  std::move(callback).Run(
      /*success=*/true, gpu_info_, gpu_feature_info_,
      gpu_channel->shared_image_stub()->factory()->MakeCapabilities());
}

void GpuServiceImpl::SetChannelClientPid(int32_t client_id,
                                         base::ProcessId client_pid) {
  if (io_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(FROM_HERE,
                           base::BindOnce(&GpuServiceImpl::SetChannelClientPid,
                                          weak_ptr_, client_id, client_pid));
    return;
  }

  // Note that the GpuService client must be trusted by definition, so DCHECKing
  // this condition is reasonable.
  DCHECK_NE(client_pid, base::kNullProcessId);
  gpu_channel_manager_->SetChannelClientPid(client_id, client_pid);
}

void GpuServiceImpl::SetChannelPersistentCachePendingBackend(
    int32_t client_id,
    const gpu::GpuDiskCacheHandle& handle,
    persistent_cache::PendingBackend pending_backend) {
  TRACE_EVENT2("gpu", "GpuServiceImpl::SetChannelPersistentCachePendingBackend",
               "client_id", client_id, "handle_type", GetHandleType(handle));

  // TODO(399642827): Support persistent cache for non-reserved clients (WebGPU,
  // WebGL)
  CHECK(gpu::IsReservedGpuDiskCacheHandle(handle));
  // The persistent cache does not use a separate cache for the display
  // compositor.
  CHECK_NE(client_id, gpu::kDisplayCompositorClientId);

  scoped_refptr<gpu::GpuPersistentCache> cache =
      persistent_caches_.GetCache(handle);
  CHECK(cache);
  cache->InitializeCache(std::move(pending_backend),
                         use_shader_cache_shm_count_);
}

void GpuServiceImpl::SetChannelDiskCacheHandle(
    int32_t client_id,
    const gpu::GpuDiskCacheHandle& handle) {
  if (io_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::SetChannelDiskCacheHandle,
                                  weak_ptr_, client_id, handle));
    return;
  }
  gpu_channel_manager_->SetChannelDiskCacheHandle(client_id, handle);
}

void GpuServiceImpl::OnDiskCacheHandleDestoyed(
    const gpu::GpuDiskCacheHandle& handle) {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::OnDiskCacheHandleDestoyed,
                                  weak_ptr_, handle));
    return;
  }
  gpu_channel_manager_->OnDiskCacheHandleDestoyed(handle);
}

void GpuServiceImpl::CloseChannel(int32_t client_id) {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::CloseChannel, weak_ptr_, client_id));
    return;
  }
  gpu_channel_manager_->RemoveChannel(client_id);
}

void GpuServiceImpl::SetWakeUpGpuClosure(base::RepeatingClosure closure) {
  wake_up_closure_ = std::move(closure);
}

void GpuServiceImpl::WakeUpGpu() {
  if (wake_up_closure_)
    wake_up_closure_.Run();
  if (main_runner_->BelongsToCurrentThread()) {
    WakeUpGpuOnMainThread();
    return;
  }
  main_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&GpuServiceImpl::WakeUpGpuOnMainThread, weak_ptr_));
}

void GpuServiceImpl::WakeUpGpuOnMainThread() {
  if (gpu_feature_info_.IsWorkaroundEnabled(gpu::WAKE_UP_GPU_BEFORE_DRAWING)) {
#if BUILDFLAG(IS_ANDROID)
    gpu_channel_manager_->WakeUpGpu();
#else
    NOTREACHED() << "WakeUpGpu() not supported on this platform.";
#endif
  }
}

void GpuServiceImpl::GpuSwitched() {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::GpuSwitched, weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: GPU has switched";

  if (watchdog_thread_)
    watchdog_thread_->ReportProgress();

  if (!in_host_process()) {
    ui::GpuSwitchingManager::GetInstance()->NotifyGpuSwitched();
  }
  GpuServiceImpl::UpdateGPUInfoGL();
}

void GpuServiceImpl::DisplayAdded() {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::DisplayAdded, weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: A monitor is plugged in";

  if (!in_host_process())
    ui::GpuSwitchingManager::GetInstance()->NotifyDisplayAdded();
}

void GpuServiceImpl::DisplayRemoved() {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE, base::BindOnce(&GpuServiceImpl::DisplayRemoved, weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: A monitor is unplugged ";

  if (!in_host_process())
    ui::GpuSwitchingManager::GetInstance()->NotifyDisplayRemoved();
}

void GpuServiceImpl::DisplayMetricsChanged() {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::DisplayMetricsChanged, weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: Display Metrics changed";

  if (!in_host_process())
    ui::GpuSwitchingManager::GetInstance()->NotifyDisplayMetricsChanged();
}

void GpuServiceImpl::DestroyAllChannels() {
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::DestroyAllChannels, weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: Removing all contexts";
  gpu_channel_manager_->DestroyAllChannels();
}

void GpuServiceImpl::OnBackgroundCleanup() {
  OnBackgroundCleanupGpuMainThread();
  OnBackgroundCleanupCompositorGpuThread();
}

void GpuServiceImpl::OnBackgroundCleanupGpuMainThread() {
  // Currently only called on Android.
#if BUILDFLAG(IS_ANDROID)
  if (!main_runner_->BelongsToCurrentThread()) {
    main_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&GpuServiceImpl::OnBackgroundCleanupGpuMainThread,
                       weak_ptr_));
    return;
  }
  DVLOG(1) << "GPU: Performing background cleanup";
  gpu_channel_manager_->OnBackgroundCleanup();
#else
  NOTREACHED();
#endif
}

void GpuServiceImpl::OnBackgroundCleanupCompositorGpuThread() {
  // Currently only called on Android.
#if BUILDFLAG(IS_ANDROID)
  if (compositor_gpu_thread_)
    compositor_gpu_thread_->OnBackgroundCleanup();
#else
  NOTREACHED();
#endif
}

void GpuServiceImpl::OnBackgrounded() {
  DCHECK(io_runner_->BelongsToCurrentThread());
  if (watchdog_thread_)
    watchdog_thread_->OnBackgrounded();
  if (compositor_gpu_thread_)
    compositor_gpu_thread_->OnBackgrounded();

  main_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&GpuServiceImpl::OnBackgroundedOnMainThread, weak_ptr_));
}

void GpuServiceImpl::OnBackgroundedOnMainThread() {
  gpu_channel_manager_->OnApplicationBackgrounded();

  if (priority_changed_callback_) {
    priority_changed_callback_.Run(base::Process::Priority::kBestEffort);
    if (gpu_preferences_.enable_gpu_benchmarking_extension) {
      ++gpu_info_.visibility_callback_call_count;
      UpdateGPUInfoGL();
    }
  }

  base::allocator::PartitionAllocSupport::Get()->OnBackgrounded();
}

void GpuServiceImpl::OnForegrounded() {
  DCHECK(io_runner_->BelongsToCurrentThread());
  if (watchdog_thread_)
    watchdog_thread_->OnForegrounded();
  if (compositor_gpu_thread_)
    compositor_gpu_thread_->OnForegrounded();

  main_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&GpuServiceImpl::OnForegroundedOnMainThread, weak_ptr_));
}

void GpuServiceImpl::OnForegroundedOnMainThread() {
  if (priority_changed_callback_) {
    priority_changed_callback_.Run(base::Process::Priority::kUserBlocking);
    if (gpu_preferences_.enable_gpu_benchmarking_extension) {
      ++gpu_info_.visibility_callback_call_count;
      UpdateGPUInfoGL();
    }
  }
  gpu_channel_manager_->OnApplicationForegounded();
  base::allocator::PartitionAllocSupport::Get()->OnForegrounded();
}

#if BUILDFLAG(IS_APPLE)
void GpuServiceImpl::BeginCATransaction() {
  DCHECK(io_runner_->BelongsToCurrentThread());
  main_runner_->PostTask(FROM_HERE, base::BindOnce(&ui::BeginCATransaction));
}

void GpuServiceImpl::CommitCATransaction(CommitCATransactionCallback callback) {
  DCHECK(io_runner_->BelongsToCurrentThread());
  main_runner_->PostTaskAndReply(
      FROM_HERE, base::BindOnce(&ui::CommitCATransaction),
      base::BindPostTask(io_runner_, std::move(callback)));
}
#endif

#if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
void GpuServiceImpl::WriteClangProfilingProfile(
    WriteClangProfilingProfileCallback callback) {
  base::WriteClangProfilingProfile();
  std::move(callback).Run();
}
#endif

void GpuServiceImpl::Crash() {
  DCHECK(io_runner_->BelongsToCurrentThread());
  gl::Crash();
}

void GpuServiceImpl::Hang() {
  DCHECK(io_runner_->BelongsToCurrentThread());
  main_runner_->PostTask(FROM_HERE, base::BindOnce(&gl::Hang));
}

void GpuServiceImpl::ThrowJavaException() {
  DCHECK(io_runner_->BelongsToCurrentThread());
#if BUILDFLAG(IS_ANDROID)
  ThrowUncaughtException();
#else
  NOTREACHED() << "Java exception not supported on this platform.";
#endif
}

void GpuServiceImpl::InduceMemoryInvalidAccess(
    mojom::MemoryInvalidAccessType action) {
  DCHECK(io_runner_->BelongsToCurrentThread());
  main_runner_->PostTask(
      FROM_HERE, base::BindOnce(&InduceMemoryInvalidAccessHelper, action));
}

#if BUILDFLAG(ENABLE_VRP_FLAGS)
void GpuServiceImpl::GetVrpFlags(GetVrpFlagsCallback callback) {
  mojo::PendingRemote<vrp_flags::mojom::VrpFlags> remote;
  if (!vrp_flags::IsEnabled()) {
    std::move(callback).Run(std::move(remote));
    return;
  }

  vrp_flags::VrpFlagsImpl::GetInstance()->Bind(
      remote.InitWithNewPipeAndPassReceiver());
  std::move(callback).Run(std::move(remote));
}
#endif

void GpuServiceImpl::StartPeakMemoryMonitorOnMainThread(uint32_t sequence_num) {
  gpu_channel_manager_->StartPeakMemoryMonitor(sequence_num);
}

void GpuServiceImpl::GetPeakMemoryUsageOnMainThread(
    uint32_t sequence_num,
    GetPeakMemoryUsageCallback callback) {
  uint64_t peak_memory = 0u;
  auto allocation_per_source =
      gpu_channel_manager_->GetPeakMemoryUsage(sequence_num, &peak_memory);

  auto seq_loc = GetPeakMemoryUsageRequestLocation(sequence_num);
  if (seq_loc == SequenceLocation::kGpuProcess) {
    RunGetPeakGpuMemoryUsageCallbackOnMainThread(
        std::move(callback), peak_memory, std::move(allocation_per_source));
    return;
  }
  io_runner_->PostTask(FROM_HERE,
                       base::BindOnce(std::move(callback), peak_memory,
                                      std::move(allocation_per_source)));
}

gpu::Scheduler* GpuServiceImpl::GetGpuScheduler() {
  return scheduler_;
}

bool GpuServiceImpl::OnBeginFrameDerivedImpl(const BeginFrameArgs& args) {
  io_runner_->PostTask(FROM_HERE,
                       base::BindOnce(&GpuServiceImpl::OnBeginFrameOnIO,
                                      base::Unretained(this), args));
  return true;
}

#if BUILDFLAG(IS_LINUX)
bool GpuServiceImpl::IsGMBNV12Supported() {
  CHECK(main_runner_->BelongsToCurrentThread());

  // Determine whether it's possible to create an NV12 NativePixmap with
  // GPU_READ_CPU_READ_WRITE usage (the relevant usage for the clients of this
  // method).
  auto format = MultiPlaneFormat::kNV12;
  auto buffer_usage = gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;

  if (!gpu::SharedImageFactory::IsNativeBufferSupported(format, buffer_usage,
                                                        gpu_extra_info_)) {
    return false;
  }

  auto size = gfx::Size(2, 2);
  scoped_refptr<gfx::NativePixmap> pixmap =
      ui::OzonePlatform::GetInstance()
          ->GetSurfaceFactoryOzone()
          ->CreateNativePixmap(gpu::kNullSurfaceHandle,
                               vulkan_context_provider()
                                   ? vulkan_context_provider()->GetDeviceQueue()
                                   : nullptr,
                               size, format, buffer_usage, size);
  if (!pixmap.get() || pixmap->ExportHandle().planes.empty()) {
    return false;
  }

  return true;
}
#endif

void GpuServiceImpl::OnBeginFrameSourcePausedChanged(bool paused) {}

void GpuServiceImpl::OnBeginFrameOnIO(const BeginFrameArgs& args) {
  DCHECK(io_runner_->BelongsToCurrentThread());

  if (mjpeg_decode_accelerator_begin_frame_cb_) {
    mjpeg_decode_accelerator_begin_frame_cb_->Run();
  }
}

void GpuServiceImpl::SetRequestBeginFrameForGpuServiceCB(
    RequestBeginFrameForGpuServiceCB cb) {
  request_begin_frame_for_gpu_service_cb_ = std::move(cb);
}

void GpuServiceImpl::SetMjpegDecodeAcceleratorBeginFrameCB(
    std::optional<base::RepeatingClosure> cb) {
  DCHECK(io_runner_->BelongsToCurrentThread());

  main_runner_->PostTask(FROM_HERE,
                         base::BindOnce(request_begin_frame_for_gpu_service_cb_,
                                        cb ? true : false));
  mjpeg_decode_accelerator_begin_frame_cb_ = std::move(cb);
}

#if BUILDFLAG(IS_WIN)
// Update Overlay and DXGI Info
void GpuServiceImpl::OnOverlayCapsChanged() {
  gpu::OverlayInfo old_overlay_info = gpu_info_.overlay_info;
  gpu::CollectHardwareOverlayInfo(&gpu_info_.overlay_info);

  // Update overlay info in the GPU process and send the updated data back to
  // the GPU host in the Browser process through mojom if the info has changed.
  if (old_overlay_info != gpu_info_.overlay_info)
    gpu_host_->DidUpdateOverlayInfo(gpu_info_.overlay_info);

  // Update DXGI adapter info in the GPU process through the GPU host mojom.
  auto old_dxgi_info = std::move(dxgi_info_);
  dxgi_info_ = gl::GetDirectCompositionHDRMonitorDXGIInfo();
  if (!mojo::Equals(dxgi_info_, old_dxgi_info)) {
    gpu_host_->DidUpdateDXGIInfo(dxgi_info_.Clone());
  }
}
#endif

gpu::SyncPointManager* GpuServiceImpl::CreateSyncPointManager() {
  owned_sync_point_manager_ = std::make_unique<gpu::SyncPointManager>();
  return owned_sync_point_manager_.get();
}

gpu::SharedImageManager* GpuServiceImpl::CreateSharedImageManager(
    bool supports_overlays) {
  // When using real buffers for testing overlay configurations, we need
  // access to SharedImageManager on the viz thread to obtain the buffer
  // corresponding to a mailbox.
  const bool display_context_on_another_thread =
      features::IsDrDcEnabled(gpu_feature_info_);

  // |display_context_on_another_thread|, features::IsUsingRawDraw(),
  // kAlwaysUseRealBufferTestingOnOzone, and kSharedBitmapToSharedImage
  // requires |thread_safe_manager| to be true.
  bool thread_safe_manager = true;
  owned_shared_image_manager_ = std::make_unique<gpu::SharedImageManager>(
      thread_safe_manager, display_context_on_another_thread,
      vulkan_context_provider(), io_runner_);
#if BUILDFLAG(IS_OZONE)
  owned_shared_image_manager_->SetSupportsOverlays(supports_overlays);
#endif
  return owned_shared_image_manager_.get();
}

gpu::Scheduler* GpuServiceImpl::CreateScheduler(
    gpu::SyncPointManager* sync_point_manager) {
  owned_scheduler_ = std::make_unique<gpu::Scheduler>(sync_point_manager);
  return owned_scheduler_.get();
}

base::WaitableEvent* GpuServiceImpl::CreateShutdownEvent() {
  owned_shutdown_event_ = std::make_unique<base::WaitableEvent>(
      base::WaitableEvent::ResetPolicy::MANUAL,
      base::WaitableEvent::InitialState::NOT_SIGNALED);
  return owned_shutdown_event_.get();
}

void GpuServiceImpl::GetDawnInfo(bool collect_metrics,
                                 GetDawnInfoCallback callback) {
  DCHECK(io_runner_->BelongsToCurrentThread());

  main_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&GpuServiceImpl::GetDawnInfoOnMain, base::Unretained(this),
                     collect_metrics, std::move(callback)));
}

void GpuServiceImpl::GetDawnInfoOnMain(bool collect_metrics,
                                       GetDawnInfoCallback callback) {
  DCHECK(main_runner_->BelongsToCurrentThread());

  std::vector<std::string> dawn_info_list;
  // Pause the watchdog around Dawn info collection since it is known to be
  // slow loading GPU drivers.
  if (watchdog_thread_) {
    watchdog_thread_->PauseWatchdog();
  }

  // Don't collect metrics if gpu watchdog is still running. Otherwise fast
  // timings will be recorded, and very-slow timings will crash and not
  // record, skewing the results.
  {
    SCOPED_UMA_HISTOGRAM_TIMER("GPU.Dawn.InfoCollectionTimeMS");
    gpu::CollectDawnInfo(gpu_preferences_, collect_metrics, &dawn_info_list);
  }

  if (watchdog_thread_) {
    watchdog_thread_->ResumeWatchdog();
  }

  io_runner_->PostTask(FROM_HERE,
                       base::BindOnce(std::move(callback), dawn_info_list));
}

#if BUILDFLAG(IS_ANDROID)
void GpuServiceImpl::SetHostProcessId(base::ProcessId pid) {
  host_process_id_ = pid;
}
#endif

GpuServiceImpl::InitParams::InitParams() = default;
GpuServiceImpl::InitParams::InitParams(InitParams&& other) = default;
GpuServiceImpl::InitParams& GpuServiceImpl::InitParams::operator=(
    InitParams&& other) = default;
GpuServiceImpl::InitParams::~InitParams() = default;

}  // namespace viz
