// 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 "content/browser/gpu/gpu_data_manager_impl_private.h"

#include "base/notreached.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include <aclapi.h>
#include <sddl.h>
#endif  // BUILDFLAG(IS_WIN)

#include <algorithm>
#include <array>
#include <iterator>
#include <memory>
#include <utility>

#include "arkweb/build/features/features.h"
#if BUILDFLAG(ARKWEB_REPORT_SYS_EVENT)
#include "arkweb/chromium_ext/third_party/crashpad/crashpad/util/linux/crashpad_dfx.h"
#endif
#include "base/command_line.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/task/bind_post_task.h"
#include "base/trace_event/trace_event.h"
#include "base/version.h"
#include "build/chromecast_buildflags.h"
#include "cc/base/switches.h"
#include "components/viz/common/features.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/media/frameless_media_interface_proxy.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "content/public/browser/gpu_utils.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/config/gpu_blocklist.h"
#include "gpu/config/gpu_driver_bug_list.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_feature_type.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/config/software_rendering_list_autogen.h"
#include "gpu/ipc/common/memory_stats.h"
#include "gpu/ipc/host/gpu_disk_cache.h"
#include "gpu/vulkan/buildflags.h"
#include "media/gpu/gpu_video_accelerator_util.h"
#include "media/media_buildflags.h"
#include "media/mojo/clients/mojo_video_decoder.h"
#include "media/mojo/mojom/video_encode_accelerator.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/display/screen.h"
#include "ui/gfx/switches.h"
#include "ui/gl/buildflags.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_preference.h"
#include "ui/gl/gpu_switching_manager.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/application_status_listener.h"
#endif
#if BUILDFLAG(IS_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif
#if BUILDFLAG(IS_MAC)
#include <ApplicationServices/ApplicationServices.h>
#endif  // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
#include "base/base_paths_win.h"
#include "ui/display/win/screen_win.h"
#include "ui/gfx/mojom/dxgi_info.mojom.h"
#endif  // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_CASTOS)
#include "chromecast/chromecast_buildflags.h"  // nogncheck
#endif                                         // BUILDFLAG(IS_CASTOS)

namespace content {

namespace {

#if BUILDFLAG(IS_ANDROID)
// NOINLINE to ensure this function is used in crash reports.
NOINLINE void FatalGpuProcessLaunchFailureOnBackground() {
  if (!base::android::ApplicationStatusListener::HasVisibleActivities()) {
    // We expect the platform to aggressively kill services when the app is
    // backgrounded. A FATAL error creates a dialog notifying users that the
    // app has crashed which doesn't look good. So we use SIGKILL instead. But
    // still do a crash dump for 1% cases to make sure we're not regressing this
    // case.
    if (base::RandInt(1, 100) == 1)
      base::debug::DumpWithoutCrashing();
    kill(getpid(), SIGKILL);
  }
}
#endif

#if BUILDFLAG(IS_WIN)
// This function checks the created file to ensure it wasn't redirected
// to another location using a symbolic link or a hard link.
bool ValidateFileHandle(HANDLE cache_file_handle,
                        const base::FilePath& cache_file_path) {
  // Check that the file wasn't hardlinked to something else.
  BY_HANDLE_FILE_INFORMATION file_info = {};
  if (!::GetFileInformationByHandle(cache_file_handle, &file_info))
    return false;
  if (file_info.nNumberOfLinks > 1)
    return false;

  // Check the final path matches the expected path.
  wchar_t final_path_buffer[MAX_PATH];
  if (!::GetFinalPathNameByHandle(cache_file_handle, final_path_buffer,
                                  _countof(final_path_buffer),
                                  FILE_NAME_NORMALIZED | VOLUME_NAME_DOS)) {
    return false;
  }
  // Returned string should start with \\?\. If not then fail validation.
  if (!base::StartsWith(final_path_buffer, L"\\\\?\\",
                        base::CompareCase::INSENSITIVE_ASCII)) {
    return false;
  }
  // Expected filename and actual file name must be an exact match.
  return cache_file_path == base::FilePath(&final_path_buffer[4]);
}

// Generate Intel cache file names depending on the app name.
bool GetIntelCacheFileNames(std::vector<base::FilePath::StringType>* names) {
  DCHECK(names);
  DCHECK(names->empty());
  base::FilePath module_path;
  if (!base::PathService::Get(base::FILE_EXE, &module_path))
    return false;
  module_path = module_path.BaseName().RemoveExtension();
  base::FilePath::StringType module_name = module_path.value();
  if (module_name.size() == 0)
    return false;
  // The Intel shader cache files should be appName_[0|1|2].
  names->push_back(module_name + L"_0");
  names->push_back(module_name + L"_1");
  names->push_back(module_name + L"_2");
  return true;
}

void EnableIntelShaderCache() {
  base::FilePath dir;
  if (!base::PathService::Get(base::DIR_COMMON_APP_DATA, &dir))
    return;
  dir = dir.Append(L"Intel").Append(L"ShaderCache");
  if (!base::DirectoryExists(dir))
    return;

  PSECURITY_DESCRIPTOR sd = nullptr;
  ULONG sd_length = 0;
  // Set Full Access to All Users and Administrators, then grant RWX to
  // AppContainers and Low Privilege AppContainers.
  BOOL success = ::ConvertStringSecurityDescriptorToSecurityDescriptor(
      L"D:(A;;FA;;;AU)(A;;FA;;;BA)(A;;GRGWGX;;;S-1-15-2-1)(A;;GRGWGX;;;S-1-15-"
      L"2-2)",
      SDDL_REVISION_1, &sd, &sd_length);
  if (!success)
    return;
  DCHECK(sd);
  DCHECK_LT(0u, sd_length);
  std::unique_ptr<void, decltype(::LocalFree)*> sd_holder(sd, ::LocalFree);
  PACL dacl = nullptr;
  BOOL present = FALSE, defaulted = FALSE;
  success = ::GetSecurityDescriptorDacl(sd, &present, &dacl, &defaulted);
  if (!success)
    return;
  DCHECK(present);
  DCHECK(dacl);
  DCHECK(!defaulted);

  std::vector<base::FilePath::StringType> cache_file_names;
  if (!GetIntelCacheFileNames(&cache_file_names))
    return;
  for (const auto& cache_file_name : cache_file_names) {
    base::FilePath cache_file_path = dir.Append(cache_file_name);
    HANDLE cache_file_handle = ::CreateFileW(
        cache_file_path.value().c_str(), WRITE_DAC,
        FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, 0, nullptr);
    base::win::ScopedHandle handle_holder(cache_file_handle);
    if (cache_file_handle == INVALID_HANDLE_VALUE ||
        !ValidateFileHandle(cache_file_handle, cache_file_path)) {
      continue;
    }

    DWORD result = ::SetSecurityInfo(cache_file_handle, SE_KERNEL_OBJECT,
                                     DACL_SECURITY_INFORMATION, nullptr,
                                     nullptr, dacl, nullptr);
    if (result != ERROR_SUCCESS) {
      LOG(ERROR) << "SetSecurityInfo returned " << result;
    }
  }
}
#endif  // BUILDFLAG(IS_WIN)

// These values are persistent to logs. Entries should not be renumbered and
// numeric values should never be reused.
// This should match enum CanvasOopRasterAndGpuAcceleration in
//  \tools\metrics\histograms\enums.xml
enum class CanvasOopRasterAndGpuAcceleration {
  kAccelOop = 0,
  kAccelNoOop = 1,  // obsolete
  kNoAccelOop = 2,
  kNoAccelNoOop = 3,
  kMaxValue = kNoAccelNoOop,
};

void RecordCanvasAcceleratedOopRasterHistogram(
    const gpu::GpuFeatureInfo& gpu_feature_info,
    bool gpu_compositing_disabled) {
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  bool accelerated_canvas =
      gpu_feature_info
              .status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS] ==
          gpu::kGpuFeatureStatusEnabled &&
      !command_line.HasSwitch(switches::kDisableAccelerated2dCanvas);

  CanvasOopRasterAndGpuAcceleration oop_acceleration_state =
      CanvasOopRasterAndGpuAcceleration::kNoAccelNoOop;
  if (!gpu_compositing_disabled) {
    if (accelerated_canvas) {
      oop_acceleration_state = CanvasOopRasterAndGpuAcceleration::kAccelOop;
    } else {
      oop_acceleration_state = CanvasOopRasterAndGpuAcceleration::kNoAccelOop;
    }
  }
  UMA_HISTOGRAM_ENUMERATION("GPU.CanvasOopRaster.OopRasterAndGpuAcceleration",
                            oop_acceleration_state);
}

// Send UMA histograms about the enabled features and GPU properties.
void UpdateFeatureStats(const gpu::GpuFeatureInfo& gpu_feature_info) {
  constexpr char kGpuBlocklistHistogram[] = "GPU.BlocklistEntriesApplied";

  // Update applied entry stats.
  std::unique_ptr<gpu::GpuBlocklist> blocklist(gpu::GpuBlocklist::Create());
  DCHECK(blocklist.get() && blocklist->max_entry_id() > 0);
  uint32_t max_entry_id = blocklist->max_entry_id();
  // Use entry 0 to capture the total number of times that data
  // was recorded in this histogram in order to have a convenient
  // denominator to compute blocklist percentages for the rest of the
  // entries.
  base::UmaHistogramSparse(kGpuBlocklistHistogram, 0);
  if (!gpu_feature_info.applied_gpu_blocklist_entries.empty()) {
    std::vector<uint32_t> entry_ids = blocklist->GetEntryIDsFromIndices(
        gpu_feature_info.applied_gpu_blocklist_entries);
    DCHECK_EQ(gpu_feature_info.applied_gpu_blocklist_entries.size(),
              entry_ids.size());
    for (auto id : entry_ids) {
      DCHECK_GE(max_entry_id, id);
      base::UmaHistogramSparse(kGpuBlocklistHistogram, id);
    }
  }

  // Update feature status stats.
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  const auto kGpuFeatures = std::to_array<gpu::GpuFeatureType>({
      gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
      gpu::GPU_FEATURE_TYPE_ACCELERATED_GL,
      gpu::GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION,
      gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
      gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
      gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGPU,
  });
  const auto kGpuBlocklistFeatureHistogramNames = std::to_array<std::string>({
      "GPU.BlocklistFeatureTestResults.Accelerated2dCanvas",
      "GPU.BlocklistFeatureTestResults.GpuCompositing",
      "GPU.BlocklistFeatureTestResults.GpuRasterization",
      "GPU.BlocklistFeatureTestResults.Webgl",
      "GPU.BlocklistFeatureTestResults.Webgl2",
      "GPU.BlocklistFeatureTestResults.Webgpu",
  });
  const auto kGpuFeatureUserFlags = std::to_array<bool>({
      command_line.HasSwitch(switches::kDisableAccelerated2dCanvas),
      command_line.HasSwitch(switches::kDisableGpu),
      command_line.HasSwitch(switches::kDisableGpuRasterization),
      command_line.HasSwitch(switches::kDisableWebGL),
      (command_line.HasSwitch(switches::kDisableWebGL) ||
       command_line.HasSwitch(switches::kDisableWebGL2)),
      !command_line.HasSwitch(switches::kEnableUnsafeWebGPU),
  });
  for (size_t i = 0; i < kGpuFeatures.size(); ++i) {
    // We can't use UMA_HISTOGRAM_ENUMERATION here because the same name is
    // expected if the macro is used within a loop.
    gpu::GpuFeatureStatus value =
        gpu_feature_info.status_values[kGpuFeatures[i]];
    if (value == gpu::kGpuFeatureStatusEnabled && kGpuFeatureUserFlags[i])
      value = gpu::kGpuFeatureStatusDisabled;
    base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
        kGpuBlocklistFeatureHistogramNames[i], 1, gpu::kGpuFeatureStatusMax,
        gpu::kGpuFeatureStatusMax + 1,
        base::HistogramBase::kUmaTargetedHistogramFlag);
    histogram_pointer->Add(value);
  }
}

void UpdateDriverBugListStats(const gpu::GpuFeatureInfo& gpu_feature_info) {
  // Use entry 0 to capture the total number of times that data was recorded
  // in this histogram in order to have a convenient denominator to compute
  // driver bug list percentages for the rest of the entries.
  base::UmaHistogramSparse("GPU.DriverBugTestResultsPerEntry", 0);

  if (!gpu_feature_info.applied_gpu_driver_bug_list_entries.empty()) {
    std::unique_ptr<gpu::GpuDriverBugList> bug_list(
        gpu::GpuDriverBugList::Create());
    DCHECK(bug_list.get() && bug_list->max_entry_id() > 0);
    std::vector<uint32_t> entry_ids = bug_list->GetEntryIDsFromIndices(
        gpu_feature_info.applied_gpu_driver_bug_list_entries);
    DCHECK_EQ(gpu_feature_info.applied_gpu_driver_bug_list_entries.size(),
              entry_ids.size());
    for (auto id : entry_ids) {
      DCHECK_GE(bug_list->max_entry_id(), id);
      base::UmaHistogramSparse("GPU.DriverBugTestResultsPerEntry", id);
    }
  }
}

#if BUILDFLAG(IS_MAC)
void DisplayReconfigCallback(CGDirectDisplayID display,
                             CGDisplayChangeSummaryFlags flags,
                             void* gpu_data_manager) {
  if (flags == kCGDisplayBeginConfigurationFlag)
    return;  // This call contains no information about the display change

  GpuDataManagerImpl* manager =
      reinterpret_cast<GpuDataManagerImpl*>(gpu_data_manager);
  DCHECK(manager);

  // Notification about "GPU switches" is only necessary on macOS when
  // using ANGLE's OpenGL backend. Short-circuit the dispatches for
  // all other backends.
  gpu::GPUInfo info = manager->GetGPUInfo();
  gl::GLImplementationParts parts = info.gl_implementation_parts;
  if (!(parts.gl == gl::kGLImplementationEGLANGLE &&
        parts.angle == gl::ANGLEImplementation::kOpenGL)) {
    return;
  }

  // Notification is only necessary if the machine actually has more
  // than one GPU - nowadays, defined by it being AMD switchable.
  if (!info.amd_switchable) {
    return;
  }

  // Dispatch the notification through the system.
  manager->HandleGpuSwitch();
}
#endif  // BUILDFLAG(IS_MAC)

void OnVideoMemoryUsageStats(
    GpuDataManager::VideoMemoryUsageStatsCallback callback,
    const gpu::VideoMemoryUsageStats& stats) {
  GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), stats));
}

void RequestVideoMemoryUsageStats(
    GpuDataManager::VideoMemoryUsageStatsCallback callback,
    GpuProcessHost* host) {
  if (!host)
    return;
  host->gpu_service()->GetVideoMemoryUsageStats(
      base::BindOnce(&OnVideoMemoryUsageStats, std::move(callback)));
}

// Determines if software GL is available as a fallback for WebGL.
bool SoftwareGLAllowed() {
  const base::CommandLine* command_line =
      base::CommandLine::ForCurrentProcess();
  return !command_line->HasSwitch(switches::kDisableSoftwareRasterizer) &&
         features::IsAnySoftwareGLAllowed(command_line);
}

// These values are logged to UMA. Entries should not be renumbered and numeric
// values should never be reused. Please keep in sync with "CompositingMode" in
// src/tools/metrics/histograms/enums.xml.
enum class CompositingMode {
  kSoftware = 0,
  kGL = 1,
  kVulkan = 2,
  kMetal = 3,  // deprecated
  kMaxValue = kMetal
};

#if BUILDFLAG(ARKWEB_REPORT_SYS_EVENT)
const std::string process_type = "browser";
const std::string error_reason = "gpu process is not usable.";
#endif
// Intentionally crash with a very descriptive name.
NOINLINE void IntentionallyCrashBrowserForUnusableGpuProcess() {
#if BUILDFLAG(ARKWEB_REPORT_SYS_EVENT)
  crashpad::CrashpadDfx::ProcessCrashReport(process_type, "", "", error_reason);
#endif
  LOG(FATAL) << "GPU process isn't usable. Goodbye.";
}

#if BUILDFLAG(IS_WIN)
void CollectExtraDevicePerfInfo(const gpu::GPUInfo& gpu_info,
                                gpu::DevicePerfInfo* device_perf_info) {
  device_perf_info->intel_gpu_generation = gpu::GetIntelGpuGeneration(gpu_info);
  const gpu::GPUInfo::GPUDevice& device = gpu_info.active_gpu();
  if (device.vendor_id == 0xffff /* internal flag for software rendering */ ||
      device.vendor_id == 0x15ad /* VMware */ ||
      device.vendor_id == 0x1414 /* Microsoft software renderer */ ||
      gl::IsSoftwareGLImplementation(
          gpu_info.gl_implementation_parts) /* SwiftShader */) {
    device_perf_info->software_rendering = true;
  }
}

// Provides a bridge whereby display::win::ScreenWin can ask the GPU process
// about the HDR status of the system.
class HDRProxy {
 public:
  static void Initialize() {
    display::win::GetScreenWin()->SetRequestHDRStatusCallback(
        base::BindRepeating(&HDRProxy::RequestHDRStatus));
  }

  static void RequestHDRStatus() {
    auto* gpu_process_host =
        GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED, false);
    if (gpu_process_host) {
      auto* gpu_service = gpu_process_host->gpu_host()->gpu_service();
      gpu_service->RequestDXGIInfo(base::BindOnce(&HDRProxy::GotResult));
    } else {
      GotResult(gfx::mojom::DXGIInfo::New());
    }
  }

  static void GotResult(gfx::mojom::DXGIInfoPtr dxgi_info) {
    display::win::GetScreenWin()->SetDXGIInfo(std::move(dxgi_info));
  }
};

#endif  // BUILDFLAG(IS_WIN)
}  // anonymous namespace

GpuDataManagerImplPrivate::GpuDataManagerImplPrivate(GpuDataManagerImpl* owner)
    : owner_(owner),
      observer_list_(base::MakeRefCounted<GpuDataManagerObserverList>()) {
  DCHECK(owner_);
  InitializeGpuModes();
#if BUILDFLAG(IS_WIN)
  EnableIntelShaderCache();
#endif  // BUILDFLAG(IS_WIN)
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kDisableGpuCompositing)) {
    SetGpuCompositingDisabled();
  }

  if (command_line->HasSwitch(switches::kSingleProcess) ||
      command_line->HasSwitch(switches::kInProcessGPU)) {
    AppendGpuCommandLine(command_line, GPU_PROCESS_KIND_SANDBOXED);
  }

#if BUILDFLAG(IS_MAC)
  CGDisplayRegisterReconfigurationCallback(DisplayReconfigCallback, owner_);
#endif  // BUILDFLAG(IS_MAC)

  // For testing only.
  if (command_line->HasSwitch(switches::kDisableDomainBlockingFor3DAPIs))
    domain_blocking_enabled_ = false;
}

GpuDataManagerImplPrivate::~GpuDataManagerImplPrivate() {
#if BUILDFLAG(IS_MAC)
  CGDisplayRemoveReconfigurationCallback(DisplayReconfigCallback, owner_);
#endif
}

void GpuDataManagerImplPrivate::StartUmaTimer() {
  // Do not change kTimerInterval without also changing the UMA histogram name,
  // as histogram data from before/after the change will not be comparable.
  constexpr base::TimeDelta kTimerInterval = base::Minutes(5);
  compositing_mode_timer_.Start(
      FROM_HERE, kTimerInterval, this,
      &GpuDataManagerImplPrivate::RecordCompositingMode);
}

#if BUILDFLAG(IS_OHOS)
void SetVulkanIcdAddress()
{
  // Set the configuration file lookup address of swiftshader
  // used by local libvulkan
  base::FilePath result;
  CHECK(base::PathService::Get(base::DIR_OHOS_APP_DATA, &result));
  if (setenv("OHOS_VULKAN_ICD", result.value().c_str(), 1) != 0) {
    LOG(ERROR) << "set env OHOS_VULKAN_ICD fail";
  }
}
#endif

void GpuDataManagerImplPrivate::InitializeGpuModes() {
  DCHECK_EQ(gpu::GpuMode::UNKNOWN, gpu_mode_);
  // Android and Chrome OS can't switch to software compositing. If the GPU
  // process initialization fails or GPU process is too unstable then crash the
  // browser process to reset everything.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(ARKWEB_OOP_GPU_PROCESS)
  fallback_modes_.push_back(gpu::GpuMode::DISPLAY_COMPOSITOR);
  if (SoftwareGLAllowed()) {
    fallback_modes_.push_back(gpu::GpuMode::SOFTWARE_GL);
  }
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kDisableGpu)) {
    // Chomecast audio-only builds run with the flag --disable-gpu. The GPU
    // process should not access hardware GPU in this case.
#if BUILDFLAG(IS_OHOS)
    SetVulkanIcdAddress();
#endif
#if BUILDFLAG(IS_CASTOS)
#if BUILDFLAG(IS_CAST_AUDIO_ONLY)
    fallback_modes_.clear();
    fallback_modes_.push_back(gpu::GpuMode::DISPLAY_COMPOSITOR);
#endif  // BUILDFLAG(IS_CAST_AUDIO_ONLY)
#endif  // BUILDFLAG(IS_CASTOS)

#if (BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CAST_ANDROID)) || \
    BUILDFLAG(IS_CHROMEOS)
    NOTREACHED() << "GPU acceleration is required on certain platforms!";
#endif
  } else if (features::IsSkiaGraphiteEnabled(command_line)) {
    // If Graphite is enabled, fall back to Ganesh/GL on platforms that do not
    // support software compositing or sometimes fail dawn initialization.
    // TODO(b/323953910): Eliminate this fallback on each platform once Graphite
    // stability is sufficient on that platform.
#if !(BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64))
    fallback_modes_.push_back(gpu::GpuMode::HARDWARE_GL);
#endif
    fallback_modes_.push_back(gpu::GpuMode::HARDWARE_GRAPHITE);
  } else {
    // On Fuchsia Vulkan must be used when it's enabled by the WebEngine
    // embedder. Falling back to SW compositing in that case is not supported.
#if BUILDFLAG(IS_FUCHSIA)
    fallback_modes_.clear();
    fallback_modes_.push_back(gpu::GpuMode::HARDWARE_VULKAN);
#else
    fallback_modes_.push_back(gpu::GpuMode::HARDWARE_GL);
    // Prefer Vulkan over GL if enabled.
    if (features::IsUsingVulkan()) {
      fallback_modes_.push_back(gpu::GpuMode::HARDWARE_VULKAN);
    }
#endif  // BUILDFLAG(IS_FUCHSIA)
  }

  FallBackToNextGpuMode();
}

void GpuDataManagerImplPrivate::BlocklistWebGLForTesting() {
  // This function is for testing only, so disable histograms.
  update_histograms_ = false;

  gpu::GpuFeatureInfo gpu_feature_info;
  for (int ii = 0; ii < gpu::NUMBER_OF_GPU_FEATURE_TYPES; ++ii) {
    if (ii == static_cast<int>(gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL))
      gpu_feature_info.status_values[ii] = gpu::kGpuFeatureStatusBlocklisted;
    else
      gpu_feature_info.status_values[ii] = gpu::kGpuFeatureStatusEnabled;
  }
  UpdateGpuFeatureInfo(gpu_feature_info, std::nullopt);
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::SetSkiaGraphiteEnabledForTesting(bool enabled) {
  // Pretend that HW acceleration is enabled so that we consider GpuFeatureInfo
  // as initialized.
  gpu_feature_info_.status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_GL] =
      gpu::kGpuFeatureStatusEnabled;
  gpu_feature_info_.status_values[gpu::GPU_FEATURE_TYPE_SKIA_GRAPHITE] =
      enabled ? gpu::kGpuFeatureStatusEnabled : gpu::kGpuFeatureStatusDisabled;
}

gpu::GPUInfo GpuDataManagerImplPrivate::GetGPUInfo() const {
  return gpu_info_;
}

gpu::GPUInfo GpuDataManagerImplPrivate::GetGPUInfoForHardwareGpu() const {
  return gpu_info_for_hardware_gpu_;
}

std::vector<std::string> GpuDataManagerImplPrivate::GetDawnInfoList() const {
  return dawn_info_list_;
}

bool GpuDataManagerImplPrivate::GpuAccessAllowed(std::string* reason) const {
  switch (gpu_mode_) {
    case gpu::GpuMode::HARDWARE_GL:
    case gpu::GpuMode::HARDWARE_GRAPHITE:
    case gpu::GpuMode::HARDWARE_VULKAN:
      return true;
    case gpu::GpuMode::SOFTWARE_GL:
      DCHECK(SoftwareGLAllowed());
      return true;
    default:
      if (reason) {
        // If SwiftShader is allowed, then we are here because it was blocked.
        if (SoftwareGLAllowed()) {
          *reason = "GPU process crashed too many times with software GL.";
        } else {
          *reason = "GPU access is disabled ";
          // just running with --disable-gpu only will go to
          // GpuMode::SOFTWARE_GL instead. Adding --disable-gpu and
          // --disable-software-rasterizer makes GpuAccessAllowed false and it
          // comes here.
          if (base::CommandLine::ForCurrentProcess()->HasSwitch(
                  switches::kDisableGpu)) {
            *reason +=
                "through commandline switch --disable-gpu and "
                "--disable-software-rasterizer.";
          } else if (hardware_disabled_explicitly_) {
            *reason += "in chrome://settings.";
          } else {
            *reason += "due to frequent crashes.";
          }
        }
      }
      return false;
  }
}

bool GpuDataManagerImplPrivate::GpuAccessAllowedForHardwareGpu(
    std::string* reason) const {
  if (reason)
    *reason = gpu_access_blocked_reason_for_hardware_gpu_;
  return gpu_access_allowed_for_hardware_gpu_;
}

void GpuDataManagerImplPrivate::RequestDx12VulkanVideoGpuInfoIfNeeded(
    GpuDataManagerImpl::GpuInfoRequest request,
    bool delayed) {
  if (request & GpuDataManagerImpl::kGpuInfoRequestDirectX) {
    RequestGpuSupportedDirectXVersion(delayed);
  }

  if (request & GpuDataManagerImpl::kGpuInfoRequestVulkan)
    RequestGpuSupportedVulkanVersion(delayed);

  if (request & GpuDataManagerImpl::kGpuInfoRequestDawnInfo)
    RequestDawnInfo(delayed, /*collect_metrics=*/false);

  if (request & GpuDataManagerImpl::kGpuInfoRequestVideo) {
    DCHECK(!delayed) << "|delayed| is not supported for Mojo Media requests";
    RequestMojoMediaVideoCapabilities();
  }
}

void GpuDataManagerImplPrivate::RequestGpuSupportedDirectXVersion(
    bool delayed) {
#if BUILDFLAG(IS_WIN)
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  base::TimeDelta delta;
  if (delayed &&
      !command_line->HasSwitch(switches::kNoDelayForDX12VulkanInfoCollection)) {
    delta = base::Seconds(120);
  }

  base::OnceClosure task = base::BindOnce(
      [](base::TimeDelta delta) {
        GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
        if (manager->DirectXRequested()) {
          return;
        }

        base::CommandLine* command_line =
            base::CommandLine::ForCurrentProcess();
        if (command_line->HasSwitch(
                switches::kDisableGpuProcessForDX12InfoCollection)) {
          manager->UpdateDirectXRequestStatus(false);
          return;
        }

        // No info collection for software GL implementation (id == 0xffff) or
        // abnormal situation (id == 0). There are a few crash reports on
        // exit_or_terminate_process() during process teardown. The GPU ID
        // should be available by the time this task starts to run. In the case
        // of no delay, which is for testing only, don't check the GPU ID
        // because the ID is not available yet.
        const gpu::GPUInfo::GPUDevice& gpu = manager->GetGPUInfo().gpu;
        if ((gpu.vendor_id == 0xffff && gpu.device_id == 0xffff) ||
            (!delta.is_zero() && gpu.vendor_id == 0 && gpu.device_id == 0)) {
          manager->UpdateDirectXRequestStatus(false);
          return;
        }

        GpuProcessHost* host = GpuProcessHost::Get(
            GPU_PROCESS_KIND_INFO_COLLECTION, true /* force_create */);
        if (!host) {
          manager->UpdateDirectXRequestStatus(false);
          return;
        }

        manager->UpdateDirectXRequestStatus(true);
        host->info_collection_gpu_service()
            ->GetGpuSupportedDirectXVersionAndDevicePerfInfo(
                base::BindOnce([](uint32_t d3d12_feature_level,
                                  uint32_t highest_shader_model_version,
                                  uint32_t directml_feature_level,
                                  const gpu::DevicePerfInfo& device_perf_info) {
                  GpuDataManagerImpl* manager =
                      GpuDataManagerImpl::GetInstance();
                  manager->UpdateDirectXInfo(d3d12_feature_level,
                                             directml_feature_level);
                  // UpdateDirectXInfo() needs to be called before
                  // UpdateDevicePerfInfo() because only the latter calls
                  // NotifyGpuInfoUpdate().
                  manager->UpdateDevicePerfInfo(device_perf_info);
                  manager->TerminateInfoCollectionGpuProcess();
                  gpu::RecordGpuSupportedDx12VersionHistograms(
                      d3d12_feature_level, highest_shader_model_version);
                }));
      },
      delta);

  GetUIThreadTaskRunner({})->PostDelayedTask(FROM_HERE, std::move(task), delta);
#endif
}

void GpuDataManagerImplPrivate::RequestGpuSupportedVulkanVersion(bool delayed) {
#if BUILDFLAG(IS_WIN)
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  base::TimeDelta delta;
  if (delayed &&
      !command_line->HasSwitch(switches::kNoDelayForDX12VulkanInfoCollection)) {
    delta = base::Seconds(120);
  }

  base::OnceClosure task = base::BindOnce(
      [](base::TimeDelta delta) {
        GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
        if (manager->VulkanRequested())
          return;

        // No info collection for software GL implementation (id == 0xffff) or
        // abnormal situation (id == 0). There are a few crash reports on
        // exit_or_terminate_process() during process teardown. The GPU ID
        // should be available by the time this task starts to run. In the case
        // of no delay, which is for testing only, don't check the GPU ID
        // because the ID is not available yet.
        const gpu::GPUInfo::GPUDevice gpu = manager->GetGPUInfo().gpu;
        if ((gpu.vendor_id == 0xffff && gpu.device_id == 0xffff) ||
            (!delta.is_zero() && gpu.vendor_id == 0 && gpu.device_id == 0)) {
          manager->UpdateVulkanRequestStatus(false);
          return;
        }

        GpuProcessHost* host = GpuProcessHost::Get(
            GPU_PROCESS_KIND_INFO_COLLECTION, true /* force_create */);
        if (!host) {
          manager->UpdateVulkanRequestStatus(false);
          return;
        }

        manager->UpdateVulkanRequestStatus(true);
        host->info_collection_gpu_service()->GetGpuSupportedVulkanVersionInfo(
            base::BindOnce([](uint32_t vulkan_version) {
              GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
              manager->UpdateVulkanInfo(vulkan_version);
              manager->TerminateInfoCollectionGpuProcess();
            }));
      },
      delta);

  GetUIThreadTaskRunner({})->PostDelayedTask(FROM_HERE, std::move(task), delta);
#endif
}

void GpuDataManagerImplPrivate::RequestDawnInfo(bool delayed,
                                                bool collect_metrics) {
  base::TimeDelta delta;
  if (delayed) {
    delta = base::Seconds(120);
  }

  base::OnceClosure task = base::BindOnce(
      [](bool collect_metrics) {
        GpuProcessHost* host = GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED,
                                                   /*force_create=*/false);
        if (!host) {
          return;
        }

        host->gpu_service()->GetDawnInfo(
            collect_metrics,
            base::BindOnce(
                [](bool collect_metrics,
                   const std::vector<std::string>& dawn_info_list) {
                  if (collect_metrics) {
                    // Metrics collection does not populate the info list.
                    return;
                  }
                  GpuDataManagerImpl* manager =
                      GpuDataManagerImpl::GetInstance();
                  manager->UpdateDawnInfo(dawn_info_list);
                },
                collect_metrics));
      },
      collect_metrics);

  GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
      ->PostDelayedTask(FROM_HERE, std::move(task), delta);
}

void GpuDataManagerImplPrivate::RequestMojoMediaVideoCapabilities() {
  base::OnceClosure task = base::BindOnce([]() {
    auto media_interface_proxy =
        std::make_unique<FramelessMediaInterfaceProxy>(nullptr);

    mojo::PendingRemote<media::mojom::VideoDecoder> pending_remote_decoder;
    media_interface_proxy->CreateVideoDecoder(
        pending_remote_decoder.InitWithNewPipeAndPassReceiver(),
        /*dst_video_decoder=*/{});
    DCHECK(pending_remote_decoder.is_valid());

    mojo::Remote<media::mojom::VideoDecoder> remote_decoder(
        std::move(pending_remote_decoder));
    DCHECK(remote_decoder.is_connected());

    auto* remote_decoder_ptr = remote_decoder.get();
    DCHECK(remote_decoder_ptr);
    remote_decoder_ptr->GetSupportedConfigs(base::BindOnce(
        [](mojo::Remote<media::mojom::VideoDecoder> /* remote_decoder */,
           std::unique_ptr<
               FramelessMediaInterfaceProxy> /* media_interface_proxy */,
           const media::SupportedVideoDecoderConfigs& configs,
           media::VideoDecoderType /* decoder_type */) {
          GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
          DCHECK(manager);
          manager->UpdateMojoMediaVideoDecoderCapabilities(configs);
        },
        std::move(remote_decoder), std::move(media_interface_proxy)));
  });

  GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));

  // Query VEA profiles to show in chrome://gpu
  auto update_vea_profiles_callback = base::BindPostTask(
      GetUIThreadTaskRunner({}),
      base::BindOnce([](const media::VideoEncodeAccelerator::SupportedProfiles&
                            supported_profiles) {
        GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
        DCHECK(manager);
        manager->UpdateMojoMediaVideoEncoderCapabilities(supported_profiles);
      }));

  using VEAProfileCallback = base::OnceCallback<void(
      const media::VideoEncodeAccelerator::SupportedProfiles&)>;
  GpuProcessHost::CallOnUI(
      FROM_HERE, GPU_PROCESS_KIND_SANDBOXED, /*force_create=*/false,
      base::BindOnce(
          [](VEAProfileCallback update_vea_profiles_callback,
             GpuProcessHost* host) {
            if (!host)
              return;

            mojo::PendingRemote<media::mojom::VideoEncodeAcceleratorProvider>
                vea_provider_remote;
            host->gpu_service()->CreateVideoEncodeAcceleratorProvider(
                vea_provider_remote.InitWithNewPipeAndPassReceiver());

            mojo::Remote<media::mojom::VideoEncodeAcceleratorProvider>
                vea_provider;
            vea_provider.Bind(std::move(vea_provider_remote));

            // Cache pointer locally since we std::move it into the callback.
            auto* vea_provider_ptr = vea_provider.get();
            vea_provider_ptr->GetVideoEncodeAcceleratorSupportedProfiles(
                base::BindOnce(
                    [](VEAProfileCallback update_vea_profiles_callback,
                       mojo::Remote<
                           media::mojom::VideoEncodeAcceleratorProvider>
                           vea_provider,
                       const media::VideoEncodeAccelerator::SupportedProfiles&
                           supported_profiles) {
                      std::move(update_vea_profiles_callback)
                          .Run(supported_profiles);
                    },
                    std::move(update_vea_profiles_callback),
                    std::move(vea_provider)));
          },
          std::move(update_vea_profiles_callback)));
}

bool GpuDataManagerImplPrivate::IsEssentialGpuInfoAvailable() const {
  // We always update GPUInfo and GpuFeatureInfo from GPU process together.
  return IsGpuFeatureInfoAvailable();
}

bool GpuDataManagerImplPrivate::IsDx12VulkanVersionAvailable() const {
#if BUILDFLAG(IS_WIN)
  // Certain gpu_integration_test needs dx12/Vulkan info. If this info is
  // needed, --no-delay-for-dx12-vulkan-info-collection should be added to the
  // browser command line, so that the collection of this info isn't delayed.
  // This function returns the status of availability to the tests based on
  // whether gpu info has been requested or not.

  return (gpu_info_dx_valid_ && gpu_info_vulkan_valid_) ||
         (!gpu_info_dx_requested_ || !gpu_info_vulkan_requested_) ||
         (gpu_info_dx_request_failed_ || gpu_info_vulkan_request_failed_);
#else
  return true;
#endif
}

bool GpuDataManagerImplPrivate::IsGpuFeatureInfoAvailable() const {
  return gpu_feature_info_.IsInitialized();
}

gpu::GpuFeatureStatus GpuDataManagerImplPrivate::GetFeatureStatus(
    gpu::GpuFeatureType feature) const {
  DCHECK(feature >= 0 && feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES);
  DCHECK(gpu_feature_info_.IsInitialized());
  return gpu_feature_info_.status_values[feature];
}

void GpuDataManagerImplPrivate::RequestVideoMemoryUsageStatsUpdate(
    GpuDataManager::VideoMemoryUsageStatsCallback callback) const {
  GpuProcessHost::CallOnUI(
      FROM_HERE, GPU_PROCESS_KIND_SANDBOXED, /*force_create=*/false ,
      base::BindOnce(&RequestVideoMemoryUsageStats, std::move(callback)));
}

void GpuDataManagerImplPrivate::AddObserver(GpuDataManagerObserver* observer) {
  observer_list_->AddObserver(observer);
}

void GpuDataManagerImplPrivate::RemoveObserver(
    GpuDataManagerObserver* observer) {
  observer_list_->RemoveObserver(observer);
}

void GpuDataManagerImplPrivate::UnblockDomainFrom3DAPIs(const GURL& url) {
  // Remove all instances of this domain from the recent domain
  // blocking events. This may have the side-effect of removing the
  // kAllDomainsBlocked status.

  // Shortcut in the common case where no blocking has occurred. This
  // is important to not regress navigation performance, since this is
  // now called on every user-initiated navigation.
  if (blocked_domains_.empty())
    return;

  std::string domain = GetDomainFromURL(url);
  auto iter = blocked_domains_.begin();
  while (iter != blocked_domains_.end()) {
    if (domain == iter->second.domain) {
      iter = blocked_domains_.erase(iter);
    } else {
      ++iter;
    }
  }

  // If there are have been enough context loss events spread over a
  // long enough time period, it is possible that a given page will be
  // blocked from using 3D APIs because of other domains' entries, and
  // that reloading this page will not allow 3D APIs to run on this
  // page. Compared to an earlier version of these heuristics, it's
  // not clear whether unblocking a domain that doesn't exist in the
  // blocked_domains_ list should clear out the list entirely.
  // Currently, kBlockedDomainExpirationPeriod is set low enough that
  // this should hopefully not be a problem in practice.
}

void GpuDataManagerImplPrivate::UpdateGpuInfo(
    const gpu::GPUInfo& gpu_info,
    const std::optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu) {
#if BUILDFLAG(IS_WIN)
  // If GPU process crashes and launches again, GPUInfo will be sent back from
  // the new GPU process again, and may overwrite the DX12, Vulkan, info we
  // already collected. This is to make sure it doesn't happen.
  uint32_t directml_feature_level = gpu_info_.directml_feature_level;
  uint32_t d3d12_feature_level = gpu_info_.d3d12_feature_level;
  uint32_t vulkan_version = gpu_info_.vulkan_version;
#endif
  gpu_info_ = gpu_info;
  RecordDiscreteGpuHistograms(gpu_info_);
#if BUILDFLAG(ENABLE_VULKAN)
  // Remember the initial hardware_supports_vulkan value so it doesn't change
  // if GPU process restarts as Vulkan might get disabled by GPU mode fallback.
  if (fixed_gpu_info_.hardware_supports_vulkan.has_value()) {
    gpu_info_.hardware_supports_vulkan =
        *fixed_gpu_info_.hardware_supports_vulkan;
  } else {
    fixed_gpu_info_.hardware_supports_vulkan =
        gpu_info.hardware_supports_vulkan;
  }
#endif
#if BUILDFLAG(IS_WIN)
  if (d3d12_feature_level != 0) {
    gpu_info_.d3d12_feature_level = d3d12_feature_level;
  }
  if (vulkan_version != 0) {
    gpu_info_.vulkan_version = vulkan_version;
  }
  if (directml_feature_level != 0) {
    gpu_info_.directml_feature_level = directml_feature_level;
  }
#endif  // BUILDFLAG(IS_WIN)

  bool needs_to_update_gpu_info_for_hardware_gpu =
      !gpu_info_for_hardware_gpu_.IsInitialized();
  if (!needs_to_update_gpu_info_for_hardware_gpu &&
      !gpu_info_.UsesSwiftShader()) {
    // On multi-GPU system, when switching to a different GPU, we want to reset
    // GPUInfo for hardware GPU, because we want to know on which GPU Chrome
    // crashes multiple times and falls back to SwiftShader.
    const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info_.active_gpu();
    const gpu::GPUInfo::GPUDevice& cached_active_gpu =
        gpu_info_for_hardware_gpu_.active_gpu();
#if BUILDFLAG(IS_WIN)
    if (active_gpu.luid.HighPart != cached_active_gpu.luid.HighPart &&
        active_gpu.luid.LowPart != cached_active_gpu.luid.LowPart) {
      needs_to_update_gpu_info_for_hardware_gpu = true;
    }
#else
    if (active_gpu.vendor_id != cached_active_gpu.vendor_id ||
        active_gpu.device_id != cached_active_gpu.device_id) {
      needs_to_update_gpu_info_for_hardware_gpu = true;
    }
#endif  // BUILDFLAG(IS_WIN)
  }

  if (needs_to_update_gpu_info_for_hardware_gpu) {
    if (gpu_info_for_hardware_gpu.has_value()) {
      DCHECK(gpu_info_for_hardware_gpu->IsInitialized());
      bool valid_info = true;
      if (gpu_info_for_hardware_gpu->UsesSwiftShader()) {
        valid_info = false;
      } else if (gpu_info_for_hardware_gpu->gl_renderer.empty() &&
                 gpu_info_for_hardware_gpu->active_gpu().vendor_id == 0u) {
        valid_info = false;
      }
      if (valid_info)
        gpu_info_for_hardware_gpu_ = gpu_info_for_hardware_gpu.value();
    } else {
      if (!gpu_info_.UsesSwiftShader())
        gpu_info_for_hardware_gpu_ = gpu_info_;
    }
  }

  GetContentClient()->SetGpuInfo(gpu_info_);
  NotifyGpuInfoUpdate();
}

#if BUILDFLAG(IS_WIN)

void GpuDataManagerImplPrivate::UpdateDirectXInfo(
    uint32_t d3d12_feature_level,
    uint32_t directml_feature_level) {
  gpu_info_.d3d12_feature_level = d3d12_feature_level;
  gpu_info_.directml_feature_level = directml_feature_level;
  gpu_info_dx_valid_ = true;
  // No need to call NotifyGpuInfoUpdate() because UpdateDirectXInfo() is
  // always called together with UpdateDevicePerfInfo, which calls
  // NotifyGpuInfoUpdate().
}

void GpuDataManagerImplPrivate::UpdateVulkanInfo(uint32_t vulkan_version) {
  gpu_info_.vulkan_version = vulkan_version;
  gpu_info_vulkan_valid_ = true;
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::UpdateDevicePerfInfo(
    const gpu::DevicePerfInfo& device_perf_info) {
  gpu::DevicePerfInfo mutable_device_perf_info = device_perf_info;
  CollectExtraDevicePerfInfo(gpu_info_, &mutable_device_perf_info);
  gpu::SetDevicePerfInfo(mutable_device_perf_info);
  // No need to call GetContentClient()->SetGpuInfo().
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::UpdateOverlayInfo(
    const gpu::OverlayInfo& overlay_info) {
  gpu_info_.overlay_info = overlay_info;

  // No need to call GetContentClient()->SetGpuInfo().
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::UpdateDXGIInfo(
    gfx::mojom::DXGIInfoPtr dxgi_info) {
  // Calling out into HDRProxy::GotResult may end up re-entering us via
  // GpuDataManagerImpl::OnDisplayRemoved/OnDisplayAdded. Both of these
  // take the owner's lock. To avoid recursive locks, we PostTask
  // HDRProxy::GotResult so that it runs outside of the lock.
  GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(&HDRProxy::GotResult, std::move(dxgi_info)));
}

void GpuDataManagerImplPrivate::UpdateDirectXRequestStatus(
    bool request_continues) {
  gpu_info_dx_requested_ = true;
  gpu_info_dx_request_failed_ = !request_continues;

  if (gpu_info_dx_request_failed_) {
    gpu::DevicePerfInfo device_perf_info;
    gpu::CollectDevicePerfInfo(&device_perf_info, /*in_browser_process=*/true);
    UpdateDevicePerfInfo(device_perf_info);
  }
}

void GpuDataManagerImplPrivate::UpdateVulkanRequestStatus(
    bool request_continues) {
  gpu_info_vulkan_requested_ = true;
  gpu_info_vulkan_request_failed_ = !request_continues;
}

bool GpuDataManagerImplPrivate::DirectXRequested() const {
  return gpu_info_dx_requested_;
}

bool GpuDataManagerImplPrivate::VulkanRequested() const {
  return gpu_info_vulkan_requested_;
}

void GpuDataManagerImplPrivate::TerminateInfoCollectionGpuProcess() {
  // Wait until DX12/Vulkan and DevicePerfInfo requests are all complete.
  // gpu_info_dx12_valid_ is always updated before device_perf_info
  if (gpu_info_dx_requested_ && !gpu_info_dx_request_failed_ &&
      !gpu::GetDevicePerfInfo().has_value()) {
    return;
  }

  if (gpu_info_vulkan_requested_ && !gpu_info_vulkan_request_failed_ &&
      !gpu_info_vulkan_valid_)
    return;

  // GpuProcessHost::Get() calls GpuDataManagerImpl functions and causes a
  // re-entry of lock.
  base::AutoUnlock unlock(owner_->lock_);
  // GpuProcessHost::Get() only runs on the IO thread. Get() can be called
  // directly here from TerminateInfoCollectionGpuProcess(), which also runs on
  // the IO thread.
  GpuProcessHost* host = GpuProcessHost::Get(GPU_PROCESS_KIND_INFO_COLLECTION,
                                             /*force_create=*/false );
  if (host)
    host->ForceShutdown();
}
#endif

void GpuDataManagerImplPrivate::PostCreateThreads() {
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  // Launch the info collection GPU process to collect Dawn info.
  // Not to affect Chrome startup, this is done in a delayed mode, i.e., 120
  // seconds after Chrome startup.
  bool delay_dawn_collection =
      !command_line->HasSwitch(switches::kCollectDawnInfoEagerly);
  RequestDawnInfo(/*delayed=*/delay_dawn_collection,
                  /*collect_metrics=*/true);

#if BUILDFLAG(IS_WIN)
  if (command_line->HasSwitch(switches::kNoDelayForDX12VulkanInfoCollection)) {
    // This is for the info collection test of the gpu integration tests.
    RequestDx12VulkanVideoGpuInfoIfNeeded(
        GpuDataManagerImpl::kGpuInfoRequestDirectXVulkan,
        /*delayed=*/false);
  } else {
    // Launch the info collection GPU process to collect DX12 and DirectML
    // support information for UMA at the start of the browser. Not to affect
    // Chrome startup, this is done in a delayed mode,  i.e., 120 seconds after
    // Chrome startup.
    RequestDx12VulkanVideoGpuInfoIfNeeded(
        GpuDataManagerImpl::kGpuInfoRequestDirectX, /*delayed=*/true);
  }

  // Observer for display change.
  display_observer_.emplace(owner_);

  // Initialization for HDR status update.
  HDRProxy::Initialize();
#endif  // BUILDFLAG(IS_WIN)
}

void GpuDataManagerImplPrivate::UpdateDawnInfo(
    const std::vector<std::string>& dawn_info_list) {
  dawn_info_list_ = dawn_info_list;

  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::UpdateGpuFeatureInfo(
    const gpu::GpuFeatureInfo& gpu_feature_info,
    const std::optional<gpu::GpuFeatureInfo>&
        gpu_feature_info_for_hardware_gpu) {
  if (gpu_mode_ == gpu::GpuMode::DISPLAY_COMPOSITOR) {
    // If we're in the display compositor mode, force the feature info to
    // disable everything. UpdateGpuFeatureInfo calls may come at any time so we
    // make it sticky here. The gpu_feature_info will be used to initialize
    // gpu_feature_info_for_hardware_gpu_ below if no
    // gpu_feature_info_for_hardware_gpu was provided.
    gpu_feature_info_ = gpu::ComputeGpuFeatureInfoWithNoGpu();
  } else {
    gpu_feature_info_ = gpu_feature_info;
  }
#if !BUILDFLAG(IS_FUCHSIA)
  // With Vulkan or Graphite, GL might be blocked so don't fallback to it later.
  if (HardwareAccelerationEnabled() &&
      gpu_feature_info_.status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_GL] !=
          gpu::GpuFeatureStatus::kGpuFeatureStatusEnabled) {
    std::erase(fallback_modes_, gpu::GpuMode::HARDWARE_GL);
  }

  // If Vulkan or Graphite initialization fails, the GPU process can silently
  // fallback to GL.
  if (gpu_mode_ == gpu::GpuMode::HARDWARE_VULKAN &&
      gpu_feature_info_.status_values[gpu::GPU_FEATURE_TYPE_VULKAN] !=
          gpu::GpuFeatureStatus::kGpuFeatureStatusEnabled) {
    // TODO(rivr): The GpuMode in GpuProcessHost will still be
    // HARDWARE_VULKAN. This isn't a big issue right now because both GPU modes
    // report to the same histogram. The first fallback will occur after 4
    // crashes, instead of 3.
    FallBackToNextGpuMode();
  } else if (gpu_mode_ == gpu::GpuMode::HARDWARE_GRAPHITE &&
             gpu_feature_info_
                     .status_values[gpu::GPU_FEATURE_TYPE_SKIA_GRAPHITE] !=
                 gpu::GpuFeatureStatus::kGpuFeatureStatusEnabled) {
    FallBackToNextGpuMode();
  }
#endif  // !BUILDFLAG(IS_FUCHSIA)
  if (!gpu_feature_info_for_hardware_gpu_.IsInitialized()) {
    if (gpu_feature_info_for_hardware_gpu.has_value()) {
      DCHECK(gpu_feature_info_for_hardware_gpu->IsInitialized());
      gpu_feature_info_for_hardware_gpu_ =
          gpu_feature_info_for_hardware_gpu.value();
    } else {
      gpu_feature_info_for_hardware_gpu_ = gpu_feature_info;
    }
    is_gpu_compositing_disabled_for_hardware_gpu_ = IsGpuCompositingDisabled();
    gpu_access_allowed_for_hardware_gpu_ =
        GpuAccessAllowed(&gpu_access_blocked_reason_for_hardware_gpu_);
  }
  if (update_histograms_) {
    UpdateFeatureStats(gpu_feature_info_);
    UpdateDriverBugListStats(gpu_feature_info_);
    RecordCanvasAcceleratedOopRasterHistogram(gpu_feature_info_,
                                              IsGpuCompositingDisabled());
  }

  is_gpu_rasterization_for_ui_enabled_ =
      features::IsUiGpuRasterizationEnabled() &&
      gpu_feature_info_
              .status_values[gpu::GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION] ==
          gpu::kGpuFeatureStatusEnabled;
}

void GpuDataManagerImplPrivate::UpdateGpuExtraInfo(
    const gfx::GpuExtraInfo& gpu_extra_info) {
  gpu_extra_info_ = gpu_extra_info;
  observer_list_->Notify(FROM_HERE,
                         &GpuDataManagerObserver::OnGpuExtraInfoUpdate);
}

void GpuDataManagerImplPrivate::UpdateMojoMediaVideoDecoderCapabilities(
    const media::SupportedVideoDecoderConfigs& configs) {
  gpu_info_.video_decode_accelerator_supported_profiles =
      media::GpuVideoAcceleratorUtil::ConvertMediaConfigsToGpuDecodeProfiles(
          configs);
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::UpdateMojoMediaVideoEncoderCapabilities(
    const media::VideoEncodeAccelerator::SupportedProfiles& profiles) {
  gpu_info_.video_encode_accelerator_supported_profiles =
      media::GpuVideoAcceleratorUtil::ConvertMediaToGpuEncodeProfiles(profiles);
  NotifyGpuInfoUpdate();
}

gpu::GpuFeatureInfo GpuDataManagerImplPrivate::GetGpuFeatureInfo() const {
  return gpu_feature_info_;
}

gpu::GpuFeatureInfo GpuDataManagerImplPrivate::GetGpuFeatureInfoForHardwareGpu()
    const {
  return gpu_feature_info_for_hardware_gpu_;
}

gfx::GpuExtraInfo GpuDataManagerImplPrivate::GetGpuExtraInfo() const {
  return gpu_extra_info_;
}

bool GpuDataManagerImplPrivate::IsGpuCompositingDisabled() const {
  return disable_gpu_compositing_ || !HardwareAccelerationEnabled();
}

bool GpuDataManagerImplPrivate::IsGpuCompositingDisabledForHardwareGpu() const {
  return is_gpu_compositing_disabled_for_hardware_gpu_;
}

void GpuDataManagerImplPrivate::SetGpuCompositingDisabled() {
  if (!IsGpuCompositingDisabled()) {
    disable_gpu_compositing_ = true;
    if (gpu_feature_info_.IsInitialized())
      NotifyGpuInfoUpdate();
  }
}

void GpuDataManagerImplPrivate::AppendGpuCommandLine(
    base::CommandLine* command_line,
    GpuProcessKind kind) const {
  DCHECK(command_line);
  const base::CommandLine* browser_command_line =
      base::CommandLine::ForCurrentProcess();

  gpu::GpuPreferences gpu_prefs = GetGpuPreferencesFromCommandLine();
  UpdateGpuPreferences(&gpu_prefs, kind);

  command_line->AppendSwitchASCII(switches::kGpuPreferences,
                                  gpu_prefs.ToSwitchValue());

  std::string use_gl;
  switch (gpu_mode_) {
    case gpu::GpuMode::HARDWARE_GL:
    case gpu::GpuMode::HARDWARE_GRAPHITE:
    case gpu::GpuMode::HARDWARE_VULKAN:
      use_gl = browser_command_line->GetSwitchValueASCII(switches::kUseGL);
      break;
    case gpu::GpuMode::SOFTWARE_GL:
      // On Fuchsia, always force software GL
#if !BUILDFLAG(IS_FUCHSIA)
      if (!gl::HasRequestedSoftwareGLImplementationFromCommandLine(
              command_line))
#endif  // BUILDFLAG(IS_FUCHSIA)
      {
        gl::SetSoftwareWebGLCommandLineSwitches(command_line);
      }
      break;
    default:
      use_gl = gl::kGLImplementationDisabledName;
  }
  if (!use_gl.empty()) {
    command_line->AppendSwitchASCII(switches::kUseGL, use_gl);
  }
}

void GpuDataManagerImplPrivate::UpdateGpuPreferences(
    gpu::GpuPreferences* gpu_preferences,
    GpuProcessKind kind) const {
  DCHECK(gpu_preferences);

  gpu_preferences->gpu_program_cache_size = gpu::GetDefaultGpuDiskCacheSize();

  gpu_preferences->watchdog_starts_backgrounded = !application_is_visible_;

  const base::CommandLine* command_line =
      base::CommandLine::ForCurrentProcess();
  gpu_preferences->gpu_startup_dialog =
#if BUILDFLAG(IS_WIN)
      (kind == GPU_PROCESS_KIND_INFO_COLLECTION &&
       command_line->HasSwitch(switches::kGpu2StartupDialog)) ||
#endif
      (kind == GPU_PROCESS_KIND_SANDBOXED &&
       command_line->HasSwitch(switches::kGpuStartupDialog));

#if BUILDFLAG(IS_WIN)
  if (kind == GPU_PROCESS_KIND_INFO_COLLECTION) {
    gpu_preferences->disable_gpu_watchdog = true;
    gpu_preferences->enable_perf_data_collection = true;
  }
#endif

#if BUILDFLAG(IS_OZONE)
  gpu_preferences->message_pump_type = ui::OzonePlatform::GetInstance()
                                           ->GetPlatformProperties()
                                           .message_pump_type_for_gpu;
#endif

  // Disable loading VulkanImplementation if not using Ganesh/Vulkan.
  if (gpu_mode_ != gpu::GpuMode::HARDWARE_VULKAN) {
    gpu_preferences->use_vulkan = gpu::VulkanImplementationName::kNone;
  }

  if (!HardwareAccelerationEnabled()) {
    gpu_preferences->gr_context_type = gpu::GrContextType::kNone;
  } else if (gpu_mode_ != gpu::GpuMode::HARDWARE_GRAPHITE) {
    // Recompute the `gr_context_type` pref with Graphite explicitly disabled,
    // as it may currently be set to Graphite.
    auto command_line_with_graphite_disabled(*command_line);
    command_line_with_graphite_disabled.AppendSwitch(
        switches::kDisableSkiaGraphite);
    gpu_preferences->gr_context_type =
        gpu::gles2::ParseGrContextType(&command_line_with_graphite_disabled);
  }
}

void GpuDataManagerImplPrivate::DisableHardwareAcceleration() {
#if BUILDFLAG(IS_OHOS)
  SetVulkanIcdAddress();
#endif
  hardware_disabled_explicitly_ = true;
  while (HardwareAccelerationEnabled())
    FallBackToNextGpuMode();
}

bool GpuDataManagerImplPrivate::HardwareAccelerationEnabled() const {
  switch (gpu_mode_) {
    case gpu::GpuMode::HARDWARE_GL:
    case gpu::GpuMode::HARDWARE_GRAPHITE:
    case gpu::GpuMode::HARDWARE_VULKAN:
      return true;
    default:
      return false;
  }
}

bool GpuDataManagerImplPrivate::IsGpuRasterizationForUIEnabled() const {
  return is_gpu_rasterization_for_ui_enabled_;
}

void GpuDataManagerImplPrivate::OnGpuBlocked() {
  std::optional<gpu::GpuFeatureInfo> gpu_feature_info_for_hardware_gpu;
  if (gpu_feature_info_.IsInitialized())
    gpu_feature_info_for_hardware_gpu = gpu_feature_info_;
  UpdateGpuFeatureInfo(gpu_feature_info_, gpu_feature_info_for_hardware_gpu);

  // Some observers might be waiting.
  NotifyGpuInfoUpdate();
}

void GpuDataManagerImplPrivate::AddLogMessage(int level,
                                              const std::string& header,
                                              const std::string& message) {
  // Some clients emit many log messages. This has been observed to consume GBs
  // of memory in the wild
  // https://bugs.chromium.org/p/chromium/issues/detail?id=798012. Use a limit
  // of 1000 messages to prevent excess memory usage.
  const int kLogMessageLimit = 1000;

  log_messages_.push_back(LogMessage(level, header, message));
  if (log_messages_.size() > kLogMessageLimit)
    log_messages_.erase(log_messages_.begin());
}

void GpuDataManagerImplPrivate::ProcessCrashed() {
  observer_list_->Notify(FROM_HERE,
                         &GpuDataManagerObserver::OnGpuProcessCrashed);
}

base::Value::List GpuDataManagerImplPrivate::GetLogMessages() const {
  base::Value::List value;
  for (const auto& log_message : log_messages_) {
    base::Value::Dict dict;
    dict.Set("level", log_message.level);
    dict.Set("header", log_message.header);
    dict.Set("message", log_message.message);
    value.Append(std::move(dict));
  }
  return value;
}

void GpuDataManagerImplPrivate::HandleGpuSwitch() {
  base::AutoUnlock unlock(owner_->lock_);
  // Notify observers in the browser process.
  ui::GpuSwitchingManager::GetInstance()->NotifyGpuSwitched();
  // Pass the notification to the GPU process to notify observers there.
  GpuProcessHost::CallOnUI(FROM_HERE, GPU_PROCESS_KIND_SANDBOXED,
                           /*force_create=*/false ,
                           base::BindOnce([](GpuProcessHost* host) {
                             if (host) {
                               host->gpu_service()->GpuSwitched();
                             }
                           }));
}

void GpuDataManagerImplPrivate::OnDisplayAdded(
    const display::Display& new_display) {
  base::AutoUnlock unlock(owner_->lock_);

  // Notify observers in the browser process.
  ui::GpuSwitchingManager::GetInstance()->NotifyDisplayAdded();
  // Pass the notification to the GPU process to notify observers there.
  GpuProcessHost::CallOnUI(FROM_HERE, GPU_PROCESS_KIND_SANDBOXED,
                           /*force_create=*/false ,
                           base::BindOnce([](GpuProcessHost* host) {
                             if (host)
                               host->gpu_service()->DisplayAdded();
                           }));
}

void GpuDataManagerImplPrivate::OnDisplaysRemoved(
    const display::Displays& removed_displays) {
  base::AutoUnlock unlock(owner_->lock_);

  // Notify observers in the browser process.
  ui::GpuSwitchingManager::GetInstance()->NotifyDisplayRemoved();
  // Pass the notification to the GPU process to notify observers there.
  GpuProcessHost::CallOnUI(FROM_HERE, GPU_PROCESS_KIND_SANDBOXED,
                           /*force_create=*/false ,
                           base::BindOnce([](GpuProcessHost* host) {
                             if (host)
                               host->gpu_service()->DisplayRemoved();
                           }));
}

void GpuDataManagerImplPrivate::OnDisplayMetricsChanged(
    const display::Display& display,
    uint32_t changed_metrics) {
  base::AutoUnlock unlock(owner_->lock_);

  // Notify observers in the browser process.
  ui::GpuSwitchingManager::GetInstance()->NotifyDisplayMetricsChanged();
  // Pass the notification to the GPU process to notify observers there.
  GpuProcessHost::CallOnUI(FROM_HERE, GPU_PROCESS_KIND_SANDBOXED,
                           /*force_create=*/false ,
                           base::BindOnce([](GpuProcessHost* host) {
                             if (host)
                               host->gpu_service()->DisplayMetricsChanged();
                           }));
}

void GpuDataManagerImplPrivate::BlockDomainsFrom3DAPIs(
    const std::set<GURL>& urls,
    gpu::DomainGuilt guilt) {
  BlockDomainsFrom3DAPIsAtTime(urls, guilt, base::Time::Now());
}

bool GpuDataManagerImplPrivate::Are3DAPIsBlocked(const GURL& top_origin_url,
                                                 ThreeDAPIType requester) {
  return Are3DAPIsBlockedAtTime(top_origin_url, base::Time::Now()) !=
         DomainBlockStatus::kNotBlocked;
}

void GpuDataManagerImplPrivate::DisableDomainBlockingFor3DAPIsForTesting() {
  domain_blocking_enabled_ = false;
}

void GpuDataManagerImplPrivate::NotifyGpuInfoUpdate() {
  observer_list_->Notify(FROM_HERE, &GpuDataManagerObserver::OnGpuInfoUpdate);
}

bool GpuDataManagerImplPrivate::IsGpuProcessUsingHardwareGpu() const {
  if (base::StartsWith(gpu_info_.gl_renderer, "Google SwiftShader",
                       base::CompareCase::SENSITIVE)) {
    return false;
  }
  if (base::StartsWith(gpu_info_.gl_renderer, "ANGLE",
                       base::CompareCase::SENSITIVE) &&
      gpu_info_.gl_renderer.find("SwiftShader Device") != std::string::npos) {
    return false;
  }
  if (gpu_info_.gl_renderer == "Disabled") {
    return false;
  }
  return true;
}

void GpuDataManagerImplPrivate::SetApplicationVisible(bool is_visible) {
  application_is_visible_ = is_visible;
}

std::string GpuDataManagerImplPrivate::GetDomainFromURL(const GURL& url) const {
  // For the moment, we just use the host, or its IP address, as the
  // entry in the set, rather than trying to figure out the top-level
  // domain. This does mean that a.foo.com and b.foo.com will be
  // treated independently in the blocking of a given domain, but it
  // would require a third-party library to reliably figure out the
  // top-level domain from a URL.
  if (!url.has_host()) {
    return std::string();
  }

  return url.GetHost();
}

void GpuDataManagerImplPrivate::BlockDomainsFrom3DAPIsAtTime(
    const std::set<GURL>& urls,
    gpu::DomainGuilt guilt,
    base::Time at_time) {
  if (!domain_blocking_enabled_)
    return;

  // The coalescing of multiple entries for the same blocking event is
  // crucially important for the algorithm. Coalescing based on timestamp
  // would introduce flakiness.
  std::set<std::string> domains;
  for (const auto& url : urls) {
    domains.insert(GetDomainFromURL(url));
  }

  for (const auto& domain : domains) {
    blocked_domains_.insert({at_time, {domain, guilt}});
  }
}

static const base::TimeDelta kBlockedDomainExpirationPeriod = base::Minutes(2);

void GpuDataManagerImplPrivate::ExpireOldBlockedDomainsAtTime(
    base::Time at_time) const {
  // After kBlockedDomainExpirationPeriod, un-block a domain previously
  // blocked due to context loss.

  // Uses the fact that "blocked_domains_" is mutable to perform a cleanup.
  base::Time everything_expired_before =
      at_time - kBlockedDomainExpirationPeriod;
  blocked_domains_.erase(
      blocked_domains_.begin(),
      std::lower_bound(blocked_domains_.begin(), blocked_domains_.end(),
                       everything_expired_before,
                       [](const auto& elem, const base::Time& t) {
                         return elem.first < t;
                       }));
}

GpuDataManagerImplPrivate::DomainBlockStatus
GpuDataManagerImplPrivate::Are3DAPIsBlockedAtTime(const GURL& url,
                                                  base::Time at_time) const {
  if (!domain_blocking_enabled_)
    return DomainBlockStatus::kNotBlocked;

  // Note: adjusting the policies in this code will almost certainly
  // require adjusting the associated unit tests.

  // First expire old domain blocks.
  ExpireOldBlockedDomainsAtTime(at_time);

  std::string domain = GetDomainFromURL(url);
  size_t losses_for_domain =
      std::ranges::count(blocked_domains_, domain,
                         [](const auto& entry) { return entry.second.domain; });
  // Allow one context loss per domain, so block if there are two or more.
  if (losses_for_domain > 1)
    return DomainBlockStatus::kBlocked;

  // Look at and cluster the timestamps of recent domain blocking events to
  // see if there are more than the threshold which would cause us to
  // blocklist all domains. GPU process crashes or TDR events are
  // discovered because the blocked domain entries all have the same
  // timestamp.
  //
  // TODO(kbr): make this pay attention to the TDR thresholds in the
  // Windows registry, but make sure it continues to be testable.
  {
    int num_event_clusters = 0;
    base::Time last_time;  // Initialized to the "zero" time.

    // Relies on the domain blocking events being sorted by increasing
    // timestamp.
    for (const auto& elem : blocked_domains_) {
      if (last_time.is_null() || elem.first != last_time) {
        last_time = elem.first;
        ++num_event_clusters;
      }
    }

    const int kMaxNumResetsWithinDuration = 2;

    if (num_event_clusters > kMaxNumResetsWithinDuration)
      return DomainBlockStatus::kAllDomainsBlocked;
  }

  return DomainBlockStatus::kNotBlocked;
}

base::TimeDelta GpuDataManagerImplPrivate::GetDomainBlockingExpirationPeriod()
    const {
  return kBlockedDomainExpirationPeriod;
}

gpu::GpuMode GpuDataManagerImplPrivate::GetGpuMode() const {
  return gpu_mode_;
}

void GpuDataManagerImplPrivate::FallBackToNextGpuMode() {
  if (fallback_modes_.empty()) {
#if BUILDFLAG(IS_ANDROID)
    FatalGpuProcessLaunchFailureOnBackground();
#endif
    IntentionallyCrashBrowserForUnusableGpuProcess();
  }

  gpu_mode_ = fallback_modes_.back();
  fallback_modes_.pop_back();
  DCHECK_NE(gpu_mode_, gpu::GpuMode::UNKNOWN);
  if (gpu_mode_ == gpu::GpuMode::DISPLAY_COMPOSITOR)
    OnGpuBlocked();
}

void GpuDataManagerImplPrivate::FallBackToNextGpuModeDueToCrash() {
  FallBackToNextGpuMode();

  // If we fell back to sofware GL due to crashes and it is disabled with a
  // feature. Fall back again.
  if (gpu_mode_ == gpu::GpuMode::SOFTWARE_GL &&
      !features::IsSoftwareGLFallbackDueToCrashesAllowed(
          base::CommandLine::ForCurrentProcess())) {
    FallBackToNextGpuMode();
    DCHECK_NE(gpu_mode_, gpu::GpuMode::SOFTWARE_GL);
  }
}

void GpuDataManagerImplPrivate::RecordCompositingMode() {
  CompositingMode compositing_mode;
  if (IsGpuCompositingDisabled()) {
    compositing_mode = CompositingMode::kSoftware;
  } else {
    // TODO(penghuang): Record kVulkan here if we're using Vulkan.
    compositing_mode = CompositingMode::kGL;
  }

  UMA_HISTOGRAM_ENUMERATION("GPU.CompositingMode", compositing_mode);
}

#if BUILDFLAG(IS_LINUX)
bool GpuDataManagerImplPrivate::IsGpuMemoryBufferNV12Supported() {
  return gpu_extra_info_.is_gmb_nv12_supported;
}
#endif  // BUILDFLAG(IS_LINUX)

}  // namespace content