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

#include "gpu/config/gpu_finch_features.h"

#include <string_view>

#include "base/byte_size.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/synchronization/atomic_flag.h"
#include "build/build_config.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_switches.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_utils.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/android_info.h"
#include "base/android/device_info.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "ui/gfx/android/android_surface_control_compat.h"
#endif  // BUILDFLAG(IS_ANDROID)

namespace features {
namespace {

SkiaGraphiteFeatureParams g_skia_graphite_feature_params;

base::AtomicFlag& GetGraphiteParamsInitFlag() {
  static base::NoDestructor<base::AtomicFlag> flag;
  return *flag;
}

void InitSkiaGraphiteFeatureParams(const base::Feature* feature) {
  if (GetGraphiteParamsInitFlag().IsSet()) {
    return;
  }

  if (!feature) {
    g_skia_graphite_feature_params = SkiaGraphiteFeatureParams();
    GetGraphiteParamsInitFlag().Set();
    return;
  }

  g_skia_graphite_feature_params.dawn_skip_validation =
      base::FeatureParam<bool>(
          feature, "dawn_skip_validation",
          g_skia_graphite_feature_params.dawn_skip_validation)
          .Get();
  g_skia_graphite_feature_params.dawn_backend_validation =
      base::FeatureParam<bool>(
          feature, "dawn_backend_validation",
          g_skia_graphite_feature_params.dawn_backend_validation)
          .Get();
  g_skia_graphite_feature_params.dawn_backend_debug_labels =
      base::FeatureParam<bool>(
          feature, "dawn_backend_debug_labels",
          g_skia_graphite_feature_params.dawn_backend_debug_labels)
          .Get();
  g_skia_graphite_feature_params.dawn_enable_auto_map =
      base::FeatureParam<bool>(
          feature, "dawn_enable_auto_map",
          g_skia_graphite_feature_params.dawn_enable_auto_map)
          .Get();
  g_skia_graphite_feature_params.max_pending_recordings =
      base::FeatureParam<int>(
          feature, "max_pending_recordings",
          g_skia_graphite_feature_params.max_pending_recordings)
          .Get();
  g_skia_graphite_feature_params.enable_deferred_submit =
      base::FeatureParam<bool>(
          feature, "enable_deferred_submit",
          g_skia_graphite_feature_params.enable_deferred_submit)
          .Get();
  g_skia_graphite_feature_params.enable_msaa_on_newer_intel =
      base::FeatureParam<bool>(
          feature, "enable_msaa_on_newer_intel",
          g_skia_graphite_feature_params.enable_msaa_on_newer_intel)
          .Get();
#if BUILDFLAG(IS_WIN)
  g_skia_graphite_feature_params.dawn_dumpwc_d3d_errors =
      base::FeatureParam<bool>(
          feature, "dawn_dumpwc_d3d_errors",
          g_skia_graphite_feature_params.dawn_dumpwc_d3d_errors)
          .Get();
  g_skia_graphite_feature_params.dawn_disable_d3d_shader_optimizations =
      base::FeatureParam<bool>(
          feature, "dawn_disable_d3d_shader_optimizations",
          g_skia_graphite_feature_params.dawn_disable_d3d_shader_optimizations)
          .Get();
  g_skia_graphite_feature_params.dawn_d3d11_delay_flush =
      base::FeatureParam<bool>(
          feature, "dawn_d3d11_delay_flush",
          g_skia_graphite_feature_params.dawn_d3d11_delay_flush)
          .Get();
  g_skia_graphite_feature_params.flush_d3d11_tile_raster_commands_to_driver =
      base::FeatureParam<bool>(
          feature, "flush_d3d11_tile_raster_commands_to_driver",
          g_skia_graphite_feature_params
              .flush_d3d11_tile_raster_commands_to_driver)
          .Get();
  g_skia_graphite_feature_params.triple_buffered_dcomp_root_surface =
      base::FeatureParam<bool>(
          feature, "triple_buffered_dcomp_root_surface",
          g_skia_graphite_feature_params.triple_buffered_dcomp_root_surface)
          .Get();
#endif

  GetGraphiteParamsInitFlag().Set();
}

#if BUILDFLAG(IS_ANDROID)
bool IsDeviceBlocked(std::string_view field, std::string_view block_list) {
  auto disable_patterns = base::SplitString(
      block_list, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  for (const auto& disable_pattern : disable_patterns) {
    if (base::MatchPattern(field, disable_pattern))
      return true;
  }
  return false;
}
#endif  // BUILDFLAG(IS_ANDROID)

}  // namespace

// More aggressive behavior for the shader cache: increase size, and do not
// purge as much in case of memory pressure.
BASE_FEATURE(kAggressiveShaderCacheLimits, base::FEATURE_DISABLED_BY_DEFAULT);

#if BUILDFLAG(IS_ANDROID)
// Use android SurfaceControl API for managing display compositor's buffer queue
// and using overlays on Android. Also used by webview to disable surface
// SurfaceControl.
BASE_FEATURE(kAndroidSurfaceControl, base::FEATURE_ENABLED_BY_DEFAULT);

// Hardware Overlays for WebView.
BASE_FEATURE(kWebViewSurfaceControl, base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kWebViewSurfaceControlForTV, base::FEATURE_ENABLED_BY_DEFAULT);

// This is used as default state because it's different for webview and chrome.
// WebView hardcodes this as enabled in AwMainDelegate.
BASE_FEATURE(kWebViewThreadSafeMediaDefault, base::FEATURE_DISABLED_BY_DEFAULT);

// Used to limit AImageReader max queue size to 1 since many devices especially
// android Tv devices do not support more than 1 images.
BASE_FEATURE(kLimitAImageReaderMaxSizeToOne, base::FEATURE_ENABLED_BY_DEFAULT);

// List of devices on which to limit AImageReader max queue size to 1.
const base::FeatureParam<std::string> kLimitAImageReaderMaxSizeToOneBlocklist{
    &kLimitAImageReaderMaxSizeToOne, "LimitAImageReaderMaxSizeToOneBlocklist",
    "MIBOX|*ODROID*"};

// Used to relax the limit of AImageReader max queue size to 1 for Android Tvs.
// Currently for all android tv except the ones in this list will have max
// queue size of 1 image.
BASE_FEATURE(kRelaxLimitAImageReaderMaxSizeToOne,
             base::FEATURE_ENABLED_BY_DEFAULT);

// List of devices on which to relax the restriction of max queue size of 1 for
// AImageReader.
const base::FeatureParam<std::string>
    kRelaxLimitAImageReaderMaxSizeToOneSoCBlocklist{
        &kRelaxLimitAImageReaderMaxSizeToOne,
        "RelaxLimitAImageReaderMaxSizeToOneSoCBlocklist", "*Broadcom*"};
const base::FeatureParam<std::string>
    kRelaxLimitAImageReaderMaxSizeToOneManufacturerBlocklist{
        &kRelaxLimitAImageReaderMaxSizeToOne,
        "RelaxLimitAImageReaderMaxSizeToOneManufacturerBlocklist",
        "*Broadcom*|*Google*"};
const base::FeatureParam<std::string>
    kRelaxLimitAImageReaderMaxSizeToOneDeviceBlocklist{
        &kRelaxLimitAImageReaderMaxSizeToOne,
        "RelaxLimitAImageReaderMaxSizeToOneDeviceBlocklist",
        "G08|G10|G17|BRAVIA_CT1"};
const base::FeatureParam<std::string>
    kRelaxLimitAImageReaderMaxSizeToOneModelBlocklist{
        &kRelaxLimitAImageReaderMaxSizeToOne,
        "RelaxLimitAImageReaderMaxSizeToOneModelBlocklist", ""};

#endif

// When enabled, gives GpuChannel/Host its own dedicated Mojo pipe instead
// of associating with an unused IPC::Channel.
BASE_FEATURE(kRemoveGPULegacyIPC, base::FEATURE_DISABLED_BY_DEFAULT);

#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
// Feature flag to control whether SharedImageStub sequence uses high priority
// on ChromeOS and Linux. Enabled by default.
BASE_FEATURE(kSharedImageStubHighPriority, base::FEATURE_DISABLED_BY_DEFAULT);
#endif

// Enable GPU Rasterization by default. This can still be overridden by
// --enable-gpu-rasterization or --disable-gpu-rasterization.
// DefaultEnableGpuRasterization has launched on Mac, Windows, ChromeOS,
// Android and Linux.
BASE_FEATURE(kDefaultEnableGpuRasterization,
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || \
    BUILDFLAG(IS_ANDROID) || BUILDFLAG(USE_WEBGPU_ON_VULKAN_VIA_GL_INTEROP)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

// Enables the use of MSAA in skia on Ice Lake and later intel architectures.

BASE_FEATURE(kEnableMSAAOnNewIntelGPUs,
#if BUILDFLAG(IS_ANDROID)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

#if BUILDFLAG(IS_WIN)
BASE_FEATURE(kNoUndamagedOverlayPromotion, base::FEATURE_DISABLED_BY_DEFAULT);
#endif

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_IOS)
// If enabled, the TASK_CATEGORY_POLICY value of the GPU process will be
// adjusted to match the one from the browser process every time it changes.
BASE_FEATURE(kAdjustGpuProcessPriority, base::FEATURE_DISABLED_BY_DEFAULT);
#endif

// When enabled, Grshader disk cache will be cleared on startup if any cache
// entry prefix does not match with the current prefix. prefix is made up of
// various parameters like chrome version, driver version etc.
BASE_FEATURE(kClearGrShaderDiskCacheOnInvalidPrefix,
             base::FEATURE_DISABLED_BY_DEFAULT);

// When enabled, Chrome will use the shader disk cache. This feature provides a
// kill-switch for working around issues with the disk cache and assessing the
// performance value of the disk cache. The --disable-gpu-shader-disk-cache flag
// overrides this feature and forces the disk cache to be disabled.
BASE_FEATURE(kGpuShaderDiskCache, base::FEATURE_ENABLED_BY_DEFAULT);

bool IsShaderDiskCacheEnabled(const base::CommandLine* command_line) {
  if (command_line->HasSwitch(switches::kDisableGpuShaderDiskCache)) {
    return false;
  }

  return base::FeatureList::IsEnabled(kGpuShaderDiskCache);
}

// Enable Vulkan graphics backend for compositing and rasterization. Defaults to
// native implementation if --use-vulkan flag is not used. Otherwise
// --use-vulkan will be followed.
// Note Android WebView uses kWebViewDrawFunctorUsesVulkan instead of this.
BASE_FEATURE(kVulkan,
#if BUILDFLAG(IS_ANDROID)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

// Force enable WebGPU interop when enabled. When disabled the webgpu interop
// mechanism will default to auto detection in 'GetWebGPUOnVulkanViaGLInterop'
// function.
BASE_FEATURE(kForceEnableWebGpuInterop, base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kEnableDrDc,
#if BUILDFLAG(IS_ANDROID)
             base::FEATURE_ENABLED_BY_DEFAULT
#elif BUILDFLAG(IS_MAC)
             // DrDC will not be running if Graphite is disabled on Mac.
             base::FEATURE_DISABLED_BY_DEFAULT
#else
             // NOT SUPPORTED. DO NOT ENABLE!
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

// Enable WebGPU on gpu service side only. This is used with origin trial and
// enabled by default on supported platforms.
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || \
    BUILDFLAG(IS_ANDROID) || BUILDFLAG(USE_WEBGPU_ON_VULKAN_VIA_GL_INTEROP)
#define WEBGPU_ENABLED base::FEATURE_ENABLED_BY_DEFAULT
#else
#define WEBGPU_ENABLED base::FEATURE_DISABLED_BY_DEFAULT
#endif
BASE_FEATURE(kWebGPUService, WEBGPU_ENABLED);
BASE_FEATURE(kWebGPUBlobCache, WEBGPU_ENABLED);
#undef WEBGPU_ENABLED

// Feature enforces WebGPU security in Android Advanced Protection Mode.
// Disable feature by default for Finch testing.
BASE_FEATURE(kAAPMBlocksWebGPU, base::FEATURE_ENABLED_BY_DEFAULT);

// List of Dawn toggles for WebGPU, delimited by ,
// The FeatureParam may be overridden via Finch config, or via the command line
// For example:
//   --enable-field-trial-config \
//   --force-fieldtrial-params=WebGPU.Enabled:DisabledToggles/toggle1%2Ctoggle2
// Note that the comma should be URL-encoded.
const base::FeatureParam<std::string> kWebGPUDisabledToggles{
    &kWebGPUService, "DisabledToggles", ""};
const base::FeatureParam<std::string> kWebGPUEnabledToggles{
    &kWebGPUService, "EnabledToggles", ""};
// List of WebGPU feature names, delimited by ,
// The FeatureParam may be overridden via Finch config, or via the command line
// For example:
//   --enable-field-trial-config \
//   --force-fieldtrial-params=WebGPU.Enabled:UnsafeFeatures/timestamp-query%2Cshader-f16
// Note that the comma should be URL-encoded.
const base::FeatureParam<std::string> kWebGPUUnsafeFeatures{
    &kWebGPUService, "UnsafeFeatures", ""};
// Whether to enable Dawn's spontaneous wire mode on the server side for faster
// async resolution and timed wait any on the client side.
const base::FeatureParam<bool> kWebGPUSpontaneousWireServer{
    &kWebGPUService, "DawnSpontaneousWireServer", true};
// List of WGSL feature names, delimited by ,
// The FeatureParam may be overridden via Finch config, or via the command line
// For example:
//   --enable-field-trial-config \
//   --force-fieldtrial-params=WebGPU.Enabled:UnsafeWGSLFeatures/feature_1%2Cfeature_2
// Note that the comma should be URL-encoded.
const base::FeatureParam<std::string> kWGSLUnsafeFeatures{
    &kWebGPUService, "UnsafeWGSLFeatures", ""};

BASE_FEATURE(kWebGPUEnableRangeAnalysisForRobustness,
             base::FEATURE_ENABLED_BY_DEFAULT);

BASE_FEATURE(kWebGPUUseSpirv14, base::FEATURE_ENABLED_BY_DEFAULT);

BASE_FEATURE(kWebGPUDecomposeUniformBuffers, base::FEATURE_ENABLED_BY_DEFAULT);

BASE_FEATURE(kWebGPUUseHLSL2021, base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kWebGPUUseSpirvReconvergenceMode,
             base::FEATURE_DISABLED_BY_DEFAULT);

#if BUILDFLAG(IS_ANDROID)

// Blocklists meant for DrDc.
// crbug.com/1294648, crbug.com/1397578: the screen flickers.
const base::FeatureParam<std::string> kDrDcBlockListByDevice{
    &kEnableDrDc, "BlockListByDevice",
    "LF9810_2GB|amber|chopin|secret|a03|SO-51B|on7xelte|j7xelte|F41B|doha|"
    "rk322x_box|a20s|HWMAR|HWSTK-HF|HWPOT-H|b2q|channel|galahad|a32|ellis|"
    "dandelion|tonga|RMX3231|ASUS_I006D|ASUS_I004D|bacon"};

// crbug.com/1340059, crbug.com/1340064
const base::FeatureParam<std::string> kDrDcBlockListByModel{
    &kEnableDrDc, "BlockListByModel",
    "SM-J400M|SM-J415F|ONEPLUS A3003|OCTAStream*"};

const base::FeatureParam<std::string> kDrDcBlockListByHardware{
    &kEnableDrDc, "BlockListByHardware", ""};

const base::FeatureParam<std::string> kDrDcBlockListByBrand{
    &kEnableDrDc, "BlockListByBrand", "HONOR"};

const base::FeatureParam<std::string> kDrDcBlockListByAndroidBuildId{
    &kEnableDrDc, "BlockListByAndroidBuildId", ""};

const base::FeatureParam<std::string> kDrDcBlockListByManufacturer{
    &kEnableDrDc, "BlockListByManufacturer", ""};

const base::FeatureParam<std::string> kDrDcBlockListByBoard{
    &kEnableDrDc, "BlockListByBoard", ""};

const base::FeatureParam<std::string> kDrDcBlockListByAndroidBuildFP{
    &kEnableDrDc, "BlockListByAndroidBuildFP", ""};
#endif  // BUILDFLAG(IS_ANDROID)

// Enable Skia Graphite with the platform's default Dawn backend.
// Note: This can be overridden by --enable-skia-graphite and
// --disable-skia-graphite which take precedence over the feature flag, and the
// Dawn backend can be overridden with the --skia-graphite-dawn-backend flag.
BASE_FEATURE(kSkiaGraphite,
#if BUILDFLAG(IS_APPLE)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

// Controls Skia Graphite specifically for Intel GPUs on Windows.
// On Windows, the status of Graphite on Intel GPUs won't be controlled
// by the standard SkiaGraphite feature, but by this feature flag
// instead. This feature only works if `kLateGraphiteFeatureCheck` is
// also enabled.
BASE_FEATURE(kSkiaGraphiteWinIntel,
             base::FEATURE_DISABLED_BY_DEFAULT);

// Allows CompoundImageBacking to allocate backings during runtime if a
// compatible backing to serve clients requested usage is not already present.
BASE_FEATURE(kUseDynamicBackingAllocations, base::FEATURE_DISABLED_BY_DEFAULT);

// When enabled, this feature allows ClientSharedImage to store and use a
// scoped_refptr to SharedImageInterface, instead of the raw_ptr as used in
// SharedImageInterfaceHolder.
BASE_FEATURE(kUseStrongRefToSharedImageInterface,
             base::FEATURE_DISABLED_BY_DEFAULT);

// When enabled, this feature lets ClientSharedImage handle all SyncToken
// management (i.e. generation, waiting and storing) internally. All SyncTokens
// that clients obtain from ClientSharedImage will be empty in this situation.
BASE_FEATURE(kUseAutomaticSyncTokenManagement,
             base::FEATURE_DISABLED_BY_DEFAULT);

// Enable atlasing of small paths on Skia Graphite. Only meaningful if
// SkiaGraphite is also enabled.
BASE_FEATURE(kSkiaGraphiteSmallPathAtlas, base::FEATURE_DISABLED_BY_DEFAULT);

// When enabled, the Graphite feature check (including blocklist) is deferred to
// the GPU process rather than evaluated in the browser process.
BASE_FEATURE(kLateGraphiteFeatureCheck,
#if BUILDFLAG(IS_WIN)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

// Enable Skia Graphite's Pipeline precompilation feature.
// Note: This is only meaningful when Skia Graphite is enabled but can then also
// be overridden by
// --enable-skia-graphite-precompilation and
// --disable-skia-graphite-precompilation.
BASE_FEATURE(kSkiaGraphitePrecompilation, base::FEATURE_DISABLED_BY_DEFAULT);

// Whether to use PersistentCache for Skia Graphite's pipeline cache.
BASE_FEATURE(kSkiaGraphiteUsePersistentCache,
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
             base::FEATURE_ENABLED_BY_DEFAULT
#else
             base::FEATURE_DISABLED_BY_DEFAULT
#endif
);

bool SkiaGraphiteUsesPersistentCache() {
  return base::FeatureList::IsEnabled(kSkiaGraphiteUsePersistentCache);
}

BASE_FEATURE(kConditionallySkipGpuChannelFlush,
// To enable on ChromeOS, test failures must be investigated
// (crrev.com/c/5435673).
#if BUILDFLAG(IS_CHROMEOS)
             base::FEATURE_DISABLED_BY_DEFAULT
#else
             base::FEATURE_ENABLED_BY_DEFAULT
#endif
);

const SkiaGraphiteFeatureParams& GetSkiaGraphiteFeatureParams() {
  DCHECK(GetGraphiteParamsInitFlag().IsSet());
  return g_skia_graphite_feature_params;
}

void InitSkiaGraphiteDefaultParamsForTesting() {
  InitSkiaGraphiteFeatureParams(nullptr);
}

const base::FeatureParam<int> kSkiaGraphiteMinPathSizeForMsaa{
    &kSkiaGraphiteSmallPathAtlas, "min_path_size_for_msaa", 0};

// Whether to use the GpuPersistentCache for caching GPU process shader blobs.
// Usage for Graphite is controlled independently with
// kSkiaGraphiteDawnUsePersistentCache.
BASE_FEATURE(kGpuPersistentCache,
#if BUILDFLAG(IS_WIN)
             base::FEATURE_DISABLED_BY_DEFAULT);
#else
             base::FEATURE_ENABLED_BY_DEFAULT);
#endif

BASE_FEATURE(kGpuPersistentCacheMetadata, base::FEATURE_DISABLED_BY_DEFAULT);

const base::FeatureParam<int> kGpuPersistentCacheMetadataPreloadCount{
    &kGpuPersistentCacheMetadata, "preload_count", 50};

// Use a 100-command limit before forcing context switch per command buffer
// instead of 20.
BASE_FEATURE(kIncreasedCmdBufferParseSlice, base::FEATURE_DISABLED_BY_DEFAULT);

// Prune transfer cache entries not accessed recently. This also turns off
// similar logic in cc::GpuImageDecodeCache which is the largest (often single)
// client of transfer cache.
BASE_FEATURE(kPruneOldTransferCacheEntries, base::FEATURE_ENABLED_BY_DEFAULT);

// On platforms with delegated compositing, try to release overlays later, when
// no new frames are swapped.
BASE_FEATURE(kDeferredOverlaysRelease,
             "DeferredOverlayRelease",
             base::FEATURE_ENABLED_BY_DEFAULT);

// This feature allows enabling specific entries in
// software_rendering_list.json, via experimentation. The entries must have
// test_group property and test_group feature parameter should be set in the
// experiment for the entries that need to be enabled.
BASE_FEATURE(kGPUBlockListTestGroup, base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<int> kGPUBlockListTestGroupId{&kGPUBlockListTestGroup,
                                                       "test_group", 0};

// This feature allows enabling specific entries in gpu_driver_bug_list.json,
// via experimentation. The entries must have test_group property and
// test_group feature parameter should be set in the experiment for the entries
// that need to be enabled.
BASE_FEATURE(kGPUDriverBugListTestGroup, base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<int> kGPUDriverBugListTestGroupId{
    &kGPUDriverBugListTestGroup, "test_group", 0};

#if BUILDFLAG(IS_LINUX)
bool IsForceEnableWebGpuInterop() {
  return base::FeatureList::IsEnabled(kForceEnableWebGpuInterop);
}
#endif

bool IsUsingVulkan() {
#if BUILDFLAG(IS_ANDROID)
  // Force on if Vulkan feature is enabled from command line.
  base::FeatureList* feature_list = base::FeatureList::GetInstance();
  if (feature_list &&
      feature_list->IsFeatureOverriddenFromCommandLine(
          features::kVulkan.name, base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
    return true;
  }

  // WebView checks, which do not use (and disables) kVulkan.
  // Do this above the Android version check because there are test devices
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kWebViewDrawFunctorUsesVulkan)) {
    return true;
  }
#endif

#if BUILDFLAG(ENABLE_VULKAN)
  return base::FeatureList::IsEnabled(kVulkan);
#else
  return false;
#endif
}

bool IsUsingThreadSafeMediaForWebView() {
#if BUILDFLAG(IS_ANDROID)
  // Thread safe media code currently
  // requires AImageReader max size to be at least 2 since one image could be
  // accessed by each gpu thread in webview.
  if (LimitAImageReaderMaxSizeToOne()) {
    return false;
  }

  return base::FeatureList::IsEnabled(kWebViewThreadSafeMediaDefault);
#else
  return false;
#endif
}

// Note that DrDc is also disabled on some of the gpus (crbug.com/1354201).
// Thread safe media will still be used on those gpus which should be fine for
// now as the lock shouldn't have much overhead and is limited to only few gpus.
// This should be fixed/updated later to account for disabled gpus.
bool NeedThreadSafeAndroidMedia() {
  // If GpuFeatureInfo is available, replace ShouldEnableDrDc() with
  // IsDrDcEnabled(gpu_feature_info) which is set after checking drdc
  // workarounds;
  return ShouldEnableDrDc() || IsUsingThreadSafeMediaForWebView();
}

namespace {
bool IsSkiaGraphiteSupportedByDevice(const base::CommandLine* command_line) {
#if BUILDFLAG(IS_APPLE)
  // Graphite only works well with ANGLE Metal on Mac or iOS.
  // TODO(https://crbug.com/40063538): Remove this after ANGLE Metal launches
  // fully.
  const bool is_angle_metal_selected =
      base::FeatureList::IsEnabled(features::kDefaultANGLEMetal) ||
      command_line->GetSwitchValueASCII(switches::kUseANGLE) ==
          gl::kANGLEImplementationMetalName;
  return UsePassthroughCommandDecoder() && is_angle_metal_selected;
#elif BUILDFLAG(IS_ANDROID)
  // Desktop Android isn't ready to pick up the fieldtrial_testing_config.json
  // change that enables graphite. However, it's the same platform as regular
  // Android and does. Skip enabling the feature there for now.
  if (base::android::device_info::is_desktop()) {
    return false;
  }

  // Graphite on Android uses the Dawn Vulkan backend. Only enable Graphite if
  // device would already be using Ganesh/Vulkan.
  return IsUsingVulkan();
#elif BUILDFLAG(IS_CHROMEOS)
  // Graphite on ChromeOS uses the Dawn Vulkan backend. Only enable Graphite if
  // device would already be using Ganesh/Vulkan.
  return IsUsingVulkan();
#elif BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)
  // Graphite on Windows ARM requires further research.
  return false;
#elif BUILDFLAG(IS_WIN)
  return true;
#else
  // Disallow Graphite from being enabled via the base::Feature on
  // not-yet-supported platforms to avoid users experiencing undefined behavior,
  // including behavior that might prevent them from being able to return to
  // chrome://flags to disable the feature.
  if (base::FeatureList::IsEnabled(features::kSkiaGraphite)) {
    LOG(ERROR) << "Enabling Graphite on a not-yet-supported platform is "
                  "disallowed for safety";
  }
  return false;
#endif
}
}  // namespace

bool IsSkiaGraphiteEnabled(const base::CommandLine* command_line) {
  // Force disabling graphite if --disable-skia-graphite flag is specified.
  if (command_line->HasSwitch(switches::kDisableSkiaGraphite)) {
    return false;
  }

  // Force Graphite on if --enable-skia-graphite flag is specified.
  if (command_line->HasSwitch(switches::kEnableSkiaGraphite)) {
    // The flag enable-skia-graphite is for testing purpose, so set the params
    // to default values.
    InitSkiaGraphiteFeatureParams(nullptr);
    return true;
  }

  if (!IsSkiaGraphiteSupportedByDevice(command_line)) {
    // Return early before checking "SkiaGraphite" feature so that devices
    // which don't support graphite are not included in the finch study.
    return false;
  }

  if (base::FeatureList::IsEnabled(kSkiaGraphite)) {
    InitSkiaGraphiteFeatureParams(&kSkiaGraphite);
    return true;
  }
  return false;
}

bool IsSkiaGraphiteWinIntelEnabled() {
  if (base::FeatureList::IsEnabled(kSkiaGraphiteWinIntel)) {
    InitSkiaGraphiteFeatureParams(&kSkiaGraphiteWinIntel);
    return true;
  }
  return false;
}

bool IsDrDcEnabled(const gpu::GpuFeatureInfo& gpu_feature_info) {
  return gpu_feature_info.status_values
             [gpu::GPU_FEATURE_TYPE_DIRECT_RENDERING_DISPLAY_COMPOSITOR] ==
         gpu::kGpuFeatureStatusEnabled;
}

bool ShouldEnableDrDc() {
#if BUILDFLAG(IS_ANDROID)
  // Enabled on android P+.
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_P) {
    return false;
  }

  // DrDc requires AImageReader max size to be
  // at least 2 for each gpu thread. Hence DrDc is disabled on devices which has
  // only 1 image.
  if (LimitAImageReaderMaxSizeToOne()) {
    return false;
  }

  // Check block list against build info.
  if (IsDeviceBlocked(base::android::android_info::device(),
                      kDrDcBlockListByDevice.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::model(),
                      kDrDcBlockListByModel.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::hardware(),
                      kDrDcBlockListByHardware.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::brand(),
                      kDrDcBlockListByBrand.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::android_build_id(),
                      kDrDcBlockListByAndroidBuildId.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::manufacturer(),
                      kDrDcBlockListByManufacturer.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::board(),
                      kDrDcBlockListByBoard.Get())) {
    return false;
  }
  if (IsDeviceBlocked(base::android::android_info::android_build_fp(),
                      kDrDcBlockListByAndroidBuildFP.Get())) {
    return false;
  }
#endif

  return base::FeatureList::IsEnabled(kEnableDrDc);
}

bool IsSkiaGraphitePrecompilationEnabled(
    const base::CommandLine* command_line) {
  // Force disabling Graphite Precompilation if
  // --disable-skia-graphite-precompilation flag is specified.
  if (command_line->HasSwitch(switches::kDisableSkiaGraphitePrecompilation)) {
    return false;
  }

  // Force Graphite Precompilation on if --enable-skia-graphite-precompilation
  // flag is specified.
  if (command_line->HasSwitch(switches::kEnableSkiaGraphitePrecompilation)) {
    return true;
  }

  return base::FeatureList::IsEnabled(features::kSkiaGraphitePrecompilation);
}

bool EnablePruneOldTransferCacheEntries() {
  return base::FeatureList::IsEnabled(kPruneOldTransferCacheEntries);
}

bool IsLegacyIpcDisabled() {
  return base::FeatureList::IsEnabled(kRemoveGPULegacyIPC);
}

#if BUILDFLAG(IS_ANDROID)
bool IsAndroidSurfaceControlEnabled() {
  if (base::android::android_info::sdk_int() <=
          base::android::android_info::SDK_VERSION_S &&
      (IsDeviceBlocked(base::android::android_info::device(), "capri|caprip") ||
       IsDeviceBlocked(base::android::android_info::model(),
                       "SM-F9*|SM-W202?|SCV44|SCG05|SCG11|SC-55B"))) {
    return false;
  }

  if (!gfx::SurfaceControl::IsSupported())
    return false;

  // SurfaceControl requires at least 3 frames in flight.
  if (LimitAImageReaderMaxSizeToOne())
    return false;

  // Check conditions that egl_android_native_fence_sync is enabled.
  // LINT.IfChange(AndroidSurfaceControlCondition)
  if (base::SysInfo::GetAndroidHardwareEGL() == "swiftshader" ||
      base::SysInfo::GetAndroidHardwareEGL() == "emulation") {
    return false;
  }
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableAndroidNativeFenceSyncForTesting)) {
    return false;
  }
  // LINT.ThenChange(//ui/gl/gl_display.cc:AndroidSurfaceControlCondition)

  // On WebView we require thread-safe media to use SurfaceControl
  if (IsUsingThreadSafeMediaForWebView()) {
    // MediaTek devices have problems of importing overlay-able images to the
    // Vulkan. Don't use SurfaceControl there until this is resolved.
    if (base::StartsWith(base::android::android_info::model(), "mt") &&
        base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kWebViewDrawFunctorUsesVulkan)) {
      return false;
    }

    // We decouple experiments between ATV and the rest of the users by using
    // different flags here.
    if (base::android::device_info::is_tv()) {
      return base::FeatureList::IsEnabled(kWebViewSurfaceControlForTV);
    } else {
      return base::FeatureList::IsEnabled(kWebViewSurfaceControl);
    }
  }

  return base::FeatureList::IsEnabled(kAndroidSurfaceControl);
}

// Many devices do not support more than 1 image to be acquired from the
// AImageReader.(crbug.com/1051705). This method returns true for those
// devices. Currently the list of device model names are sent from server side
// via a finch config file. There is a known device MIBOX for which max size
// should be 1 irrespecticve of the feature LimitAImageReaderMaxSizeToOne
// enabled or not. Get() returns default value even if the feature is disabled.
bool LimitAImageReaderMaxSizeToOne() {
  // The feature is enabled by default, if it was overridden by user we should
  // not limit regardless if it will work or not.
  base::FeatureList* feature_list = base::FeatureList::GetInstance();
  if (feature_list && feature_list->IsFeatureOverriddenFromCommandLine(
                          kLimitAImageReaderMaxSizeToOne.name)) {
    return base::FeatureList::IsEnabled(kLimitAImageReaderMaxSizeToOne);
  }

  // Always limit image reader to 1 frame for Android TV. Many TVs doesn't work
  // with more than 1 frame and it's very hard to localize which models do.
  if (base::android::device_info::is_tv()) {
    // For the android Tvs which are in the below list, we are relaxing this
    // restrictions as those are able to create AImageReader with more than 1
    // images. This helps in removing the flickering seen which can happen with
    // only 1 image. Also note that we should use soc_manufacturer instead of
    // manufacturer when available as sometimes manufacturer field gets
    // modified by vendors.

    if (IsDeviceBlocked(
            base::android::android_info::soc_manufacturer(),
            kRelaxLimitAImageReaderMaxSizeToOneSoCBlocklist.Get())) {
      return false;
    }
    if (IsDeviceBlocked(
            base::android::android_info::manufacturer(),
            kRelaxLimitAImageReaderMaxSizeToOneManufacturerBlocklist.Get())) {
      return false;
    }
    if (IsDeviceBlocked(
            base::android::android_info::device(),
            kRelaxLimitAImageReaderMaxSizeToOneDeviceBlocklist.Get())) {
      return false;
    }
    if (IsDeviceBlocked(
            base::android::android_info::model(),
            kRelaxLimitAImageReaderMaxSizeToOneModelBlocklist.Get())) {
      return false;
    }

    return true;
  }

  return (IsDeviceBlocked(base::android::android_info::model(),
                          kLimitAImageReaderMaxSizeToOneBlocklist.Get()));
}

bool IncreaseBufferCountForHighFrameRate() {
  // TODO(crbug.com/40767562): We don't have a way to dynamically adjust number
  // of buffers. So these checks, espeically the RAM one, is to limit the impact
  // of more buffers to devices that can handle them.
  // 8GB of ram with large margin for error.
  constexpr base::ByteSize RAM_8GB_CUTOFF = base::MiB(7200);
  static bool increase =
      base::android::android_info::sdk_int() >=
          base::android::android_info::SDK_VERSION_R &&
      IsAndroidSurfaceControlEnabled() &&
      base::SysInfo::AmountOfTotalPhysicalMemory() > RAM_8GB_CUTOFF;
  return increase;
}

#endif

// When this flag is enabled, stops using gpu::SyncPointOrderData for sync point
// validation, uses gpu::TaskGraph instead.
// Graph-based validation doesn't require sync point releases are submitted to
// the scheduler prior to their corresponding waits. Therefore it allows to
// remove the synchronous flush done by VerifySyncTokens().
//
// TODO(b/324276400): Work in progress.
BASE_FEATURE(kSyncPointGraphValidation, base::FEATURE_DISABLED_BY_DEFAULT);

bool IsSyncPointGraphValidationEnabled() {
  return base::FeatureList::IsEnabled(kSyncPointGraphValidation);
}

BASE_FEATURE(kANGLEPerContextBlobCache, base::FEATURE_DISABLED_BY_DEFAULT);

// Support thread safety for graphite::context by sharing the same
// graphite::context as well as its wrapper class GraphiteSharedContext between
// GpuMain and CompositorGpuThread. Note: When this feature is disabled,
// each thread creates its own graphite::context and the context wrapper.
BASE_FEATURE(kGraphiteContextIsThreadSafe,
             base::FEATURE_DISABLED_BY_DEFAULT);

bool IsGraphiteContextThreadSafe() {
  return base::FeatureList::IsEnabled(features::kGraphiteContextIsThreadSafe);
}

BASE_FEATURE(kWebGPUCompatibilityMode, base::FEATURE_ENABLED_BY_DEFAULT);

BASE_FEATURE(kWebGPUAndroidOpenGLES, base::FEATURE_ENABLED_BY_DEFAULT);

#if BUILDFLAG(IS_WIN)
BASE_FEATURE(kWebGPUQualcommWindows, base::FEATURE_ENABLED_BY_DEFAULT);
#endif

// Enables runtime configuration of the GPU watchdog timeout via
// experimentation.
BASE_FEATURE(kConfigurableGPUWatchdogTimeout,
             base::FEATURE_DISABLED_BY_DEFAULT);
const base::FeatureParam<int> kConfigurableGPUWatchdogTimeoutSeconds{
    &kConfigurableGPUWatchdogTimeout, "watchdog_timeout_seconds", 30};

// Enables the optimization where GPU channels are sent to renderer processes
// early when the renderer process is being initialized, instead of waiting
// for the renderer to request the GPU channel to the browser process.
BASE_FEATURE(kSendGPUChannelEarly, base::FEATURE_DISABLED_BY_DEFAULT);
// If true, only enable the early GPU channel optimization for topchrome WebUI
// renderers.
const base::FeatureParam<bool> kSendGPUChannelEarlyTopChromeOnly{
    &kSendGPUChannelEarly, "for_topchrome_webui_only", false};

}  // namespace features
