// Copyright 2012 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/compositor_util.h"

#include <stddef.h>

#include <algorithm>
#include <memory>
#include <utility>

#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/system/sys_info.h"
#include "base/values.h"
#include "build/build_config.h"
#include "cc/base/features.h"
#include "cc/base/switches.h"
#include "components/viz/common/features.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.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/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_type.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/vulkan/buildflags.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
#include "skia/buildflags.h"
#include "third_party/blink/public/common/switches.h"
#include "ui/base/ui_base_features.h"
#include "ui/gl/gl_switches.h"

#if BUILDFLAG(ARKWEB_NWEB_EX)
#include "arkweb/chromium_ext/content/browser/gpu/ark_web_compositor_util.h"
#endif

namespace content {

namespace {

const int kMinRasterThreads = 1;
const int kMaxRasterThreads = 4;

const int kMinMSAASampleCount = 0;

enum class GpuFeatureInfoType { kCurrent, kForHardwareGpu };

struct DisableInfo {
  // The feature being disabled will be listed as a problem with |description|.
  static DisableInfo Problem(const std::string& description) {
    return DisableInfo{true, description};
  }

  // The feature being disabled will not be listed as a problem.
  static DisableInfo NotProblem() { return DisableInfo{false, ""}; }

  bool is_problem;
  std::string description;
};

struct GpuFeatureData {
  std::string name;
  gpu::GpuFeatureStatus status;
  DisableInfo disabled_info = DisableInfo::NotProblem();
  bool fallback_to_software = false;
};

// Returns enabled/disabled based on a bool for when there is no GpuFeatureType.
gpu::GpuFeatureStatus GetFakeFeatureStatus(bool enabled) {
  return enabled ? gpu::kGpuFeatureStatusEnabled
                 : gpu::kGpuFeatureStatusDisabled;
}

// `force_disabled` should only be true when the feature status from
// `gpu_feature_info` is missing information. In general this should be avoided
// and GpuFeatureInfo should be made to be correct.
gpu::GpuFeatureStatus SafeGetFeatureStatus(
    const gpu::GpuFeatureInfo& gpu_feature_info,
    gpu::GpuFeatureType feature,
    bool force_disabled = false) {
  if (force_disabled) {
    return gpu::kGpuFeatureStatusDisabled;
  }

  if (!gpu_feature_info.IsInitialized()) {
    // The GPU process probably crashed during startup, but we can't
    // assert this as the test bots are slow, and recording the crash
    // is racy. Be robust and just say that all features are disabled.
    return gpu::kGpuFeatureStatusDisabled;
  }
  DCHECK(feature >= 0 && feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES);
  return gpu_feature_info.status_values[feature];
}

std::vector<GpuFeatureData> GetGpuFeatureData(
    const gpu::GpuFeatureInfo& gpu_feature_info,
    bool is_gpu_compositing_disabled) {
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();

#if BUILDFLAG(IS_OHOS)
  const GpuFeatureData kGpuFeatureData[] = {
    {"2d_canvas",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
          command_line.HasSwitch(switches::kDisableAccelerated2dCanvas)),
      DisableInfo::Problem(
          "Accelerated 2D canvas is unavailable: either disabled "
          "via blocklist or the command line."),
      true},
    {"gpu_compositing",
      // TODO(rivr): Replace with a check to see which backend is used for
      // compositing; do the same for GPU rasterization if it's enabled. For
      // now assume that if GL is blocklisted, then Vulkan is also. Check GL to
      // see if GPU compositing is disabled.
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_ACCELERATED_GL,
                           is_gpu_compositing_disabled),
      DisableInfo::Problem(
          "Gpu compositing has been disabled, either via blocklist, "
          "about:flags "
          "or the command line. The browser will fall back to software "
          "compositing and hardware acceleration will be unavailable."),
      true},
    {"webgl",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
                           command_line.HasSwitch(switches::kDisableWebGL)),
      DisableInfo::Problem(
          "WebGL has been disabled via blocklist or the command line."),
      false},
    {"video_decode",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
          command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode)),
      DisableInfo::Problem(
          "Accelerated video decode has been disabled, either via blocklist, "
          "about:flags or the command line."),
      true},
    {"rasterization",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION),
      DisableInfo::Problem(
          "Accelerated rasterization has been disabled, either via blocklist, "
          "about:flags or the command line."),
      true},
    {"opengl", SafeGetFeatureStatus(gpu_feature_info,
                                    gpu::GPU_FEATURE_TYPE_ACCELERATED_GL)},
#if BUILDFLAG(ENABLE_VULKAN)
    {"vulkan",
      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_VULKAN)},
#endif
    {"multiple_raster_threads",
      GetFakeFeatureStatus(NumberOfRendererRasterThreads() > 1)},
    {"webgl2",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
          command_line.HasSwitch(switches::kDisableWebGL) ||
              command_line.HasSwitch(switches::kDisableWebGL2)),
      DisableInfo::Problem(
          "WebGL2 has been disabled via blocklist or the command line."),
      false},
    {"raw_draw",
     GetFakeFeatureStatus(features::IsUsingRawDraw())},
    {"direct_rendering_display_compositor",
     GetFakeFeatureStatus(features::IsDrDcEnabled(gpu_feature_info))},
    {"webgpu",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGPU,
          !command_line.HasSwitch(switches::kEnableUnsafeWebGPU) &&
              !base::FeatureList::IsEnabled(::features::kWebGPUService)),
      DisableInfo::Problem(
          "WebGPU has been disabled via blocklist or the command line."),
      false},
    {"skia_graphite",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_SKIA_GRAPHITE)},
    {"webnn",
      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_WEBNN)}
  };
  std::vector<GpuFeatureData> features(
      kGpuFeatureData,
      kGpuFeatureData + sizeof(kGpuFeatureData) / sizeof(kGpuFeatureData[0]));
#else
  std::vector<GpuFeatureData> features;
  features.emplace_back(
      "2d_canvas",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
          command_line.HasSwitch(switches::kDisableAccelerated2dCanvas)),
      DisableInfo::Problem(
          "Accelerated 2D canvas is unavailable: either disabled "
          "via blocklist or the command line."),
      true);
  features.emplace_back(
      "gpu_compositing",
      // TODO(rivr): Replace with a check to see which backend is used for
      // compositing; do the same for GPU rasterization if it's enabled. For
      // now assume that if GL is blocklisted, then Vulkan is also. Check GL to
      // see if GPU compositing is disabled.
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_ACCELERATED_GL,
                           is_gpu_compositing_disabled),
      DisableInfo::Problem(
          "Gpu compositing has been disabled, either via blocklist, "
          "about:flags "
          "or the command line. The browser will fall back to software "
          "compositing and hardware acceleration will be unavailable."),
      true);
  features.emplace_back(
      "webgl",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
                           command_line.HasSwitch(switches::kDisableWebGL)),
      DisableInfo::Problem(
          "WebGL has been disabled via blocklist or the command line."),
      false);
  features.emplace_back(
      "video_decode",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
#if BUILDFLAG(IS_LINUX)
          !base::FeatureList::IsEnabled(media::kAcceleratedVideoDecodeLinux) ||
#endif  // BUILDFLAG(IS_LINUX)
              command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode)),
      DisableInfo::Problem(
          "Accelerated video decode has been disabled, either via blocklist, "
          "about:flags or the command line."),
      true);
  features.emplace_back(
      "video_encode",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_ENCODE,
#if BUILDFLAG(IS_LINUX)
          !base::FeatureList::IsEnabled(media::kAcceleratedVideoEncodeLinux)),
#else
          command_line.HasSwitch(switches::kDisableAcceleratedVideoEncode)),
#endif  // BUILDFLAG(IS_LINUX)
      DisableInfo::Problem(
          "Accelerated video encode has been disabled, either via blocklist, "
          "about:flags or the command line."),
      true);
  features.emplace_back(
      "rasterization",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION),
      DisableInfo::Problem(
          "Accelerated rasterization has been disabled, either via blocklist, "
          "about:flags or the command line."),
      true);
  features.emplace_back(
      "opengl", SafeGetFeatureStatus(gpu_feature_info,
                                     gpu::GPU_FEATURE_TYPE_ACCELERATED_GL));
#if BUILDFLAG(ENABLE_VULKAN)
  features.emplace_back(
      "vulkan",
      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_VULKAN));
#endif
  features.emplace_back(
      "multiple_raster_threads",
      GetFakeFeatureStatus(NumberOfRendererRasterThreads() > 1));
#if BUILDFLAG(IS_ANDROID)
  features.emplace_back("surface_control",
                        features::IsAndroidSurfaceControlEnabled()
                            ? gpu::kGpuFeatureStatusEnabled
                            : gpu::kGpuFeatureStatusDisabled);
#endif
  features.emplace_back("raw_draw",
                        GetFakeFeatureStatus(::features::IsUsingRawDraw()));
  features.emplace_back(
      "direct_rendering_display_compositor",
      SafeGetFeatureStatus(
          gpu_feature_info,
          gpu::GPU_FEATURE_TYPE_DIRECT_RENDERING_DISPLAY_COMPOSITOR));
  features.emplace_back(
      "webgpu",
      SafeGetFeatureStatus(
          gpu_feature_info, gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGPU,
          !command_line.HasSwitch(switches::kEnableUnsafeWebGPU) &&
              !base::FeatureList::IsEnabled(::features::kWebGPUService)),
      DisableInfo::Problem(
          "WebGPU has been disabled via blocklist or the command line."),
      false);
  features.emplace_back(
      "skia_graphite",
      SafeGetFeatureStatus(gpu_feature_info,
                           gpu::GPU_FEATURE_TYPE_SKIA_GRAPHITE));
  features.emplace_back(
      "webnn",
      SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_WEBNN));
  features.emplace_back("trees_in_viz",
                        base::FeatureList::IsEnabled(::features::kTreesInViz)
                            ? gpu::kGpuFeatureStatusEnabled
                            : gpu::kGpuFeatureStatusDisabled);
#endif
  return features;
}

base::Value GetFeatureStatusImpl(GpuFeatureInfoType type) {
  GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
  std::string gpu_access_blocked_reason;
  bool gpu_access_blocked;
  gpu::GpuFeatureInfo gpu_feature_info;
  bool is_gpu_compositing_disabled;
  if (type == GpuFeatureInfoType::kCurrent) {
    gpu_access_blocked = !manager->GpuAccessAllowed(&gpu_access_blocked_reason);
    gpu_feature_info = manager->GetGpuFeatureInfo();
    is_gpu_compositing_disabled = manager->IsGpuCompositingDisabled();
  } else {
    gpu_access_blocked =
        !manager->GpuAccessAllowedForHardwareGpu(&gpu_access_blocked_reason);
    gpu_feature_info = manager->GetGpuFeatureInfoForHardwareGpu();
    is_gpu_compositing_disabled =
        manager->IsGpuCompositingDisabledForHardwareGpu();
  }

  base::Value::Dict feature_status_dict;

  for (auto& gpu_feature_data :
       GetGpuFeatureData(gpu_feature_info, is_gpu_compositing_disabled)) {
    std::string status;
    // Features undergoing a finch controlled roll out.
    if (gpu_feature_data.name == "raw_draw" ||
        gpu_feature_data.name == "direct_rendering_display_compositor") {
      status = gpu_feature_data.status == gpu::kGpuFeatureStatusEnabled
                   ? "enabled_on"
                   : "disabled_off_ok";
    } else if (gpu_access_blocked ||
               gpu_feature_data.status == gpu::kGpuFeatureStatusDisabled) {
      status = "disabled";
      if (gpu_feature_data.fallback_to_software)
        status += "_software";
      else
        status += "_off";
    } else if (gpu_feature_data.status == gpu::kGpuFeatureStatusBlocklisted) {
      status = "unavailable_off";
    } else if (gpu_feature_data.status == gpu::kGpuFeatureStatusSoftware) {
      status = "unavailable_software";
    } else {
      status = "enabled";
      if (gpu_feature_data.name == "canvas_oop_rasterization") {
        status += "_on";
      }
      if ((gpu_feature_data.name == "webgl" ||
           gpu_feature_data.name == "webgpu") &&
          is_gpu_compositing_disabled)
        status += "_readback";
      if (gpu_feature_data.name == "rasterization") {
        const base::CommandLine& command_line =
            *base::CommandLine::ForCurrentProcess();
        if (command_line.HasSwitch(switches::kEnableGpuRasterization))
          status += "_force";
      }
      if (gpu_feature_data.name == "multiple_raster_threads") {
        const base::CommandLine& command_line =
            *base::CommandLine::ForCurrentProcess();
        if (command_line.HasSwitch(switches::kNumRasterThreads)) {
          status += "_force";
        }
        status += "_on";
      }
      if (gpu_feature_data.name == "opengl" ||
          gpu_feature_data.name == "metal" ||
          gpu_feature_data.name == "vulkan" ||
          gpu_feature_data.name == "skia_graphite" ||
          gpu_feature_data.name == "surface_control" ||
          gpu_feature_data.name == "webnn" ||
          gpu_feature_data.name == "trees_in_viz") {
        status += "_on";
      }
    }
    feature_status_dict.Set(gpu_feature_data.name, status);
  }
  return base::Value(std::move(feature_status_dict));
}

base::Value GetProblemsImpl(GpuFeatureInfoType type) {
  GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
  std::string gpu_access_blocked_reason;
  bool gpu_access_blocked;
  gpu::GpuFeatureInfo gpu_feature_info;
  bool is_gpu_compositing_disabled;
  if (type == GpuFeatureInfoType::kCurrent) {
    gpu_access_blocked = !manager->GpuAccessAllowed(&gpu_access_blocked_reason);
    gpu_feature_info = manager->GetGpuFeatureInfo();
    is_gpu_compositing_disabled = manager->IsGpuCompositingDisabled();
  } else {
    gpu_access_blocked =
        !manager->GpuAccessAllowedForHardwareGpu(&gpu_access_blocked_reason);
    gpu_feature_info = manager->GetGpuFeatureInfoForHardwareGpu();
    is_gpu_compositing_disabled =
        manager->IsGpuCompositingDisabledForHardwareGpu();
  }

  base::Value::List problem_list;
  if (!gpu_feature_info.applied_gpu_blocklist_entries.empty()) {
    std::unique_ptr<gpu::GpuBlocklist> blocklist(gpu::GpuBlocklist::Create());
    blocklist->GetReasons(problem_list, "disabledFeatures",
                          gpu_feature_info.applied_gpu_blocklist_entries);
  }
  if (!gpu_feature_info.applied_gpu_driver_bug_list_entries.empty()) {
    std::unique_ptr<gpu::GpuDriverBugList> bug_list(
        gpu::GpuDriverBugList::Create());
    bug_list->GetReasons(problem_list, "workarounds",
                         gpu_feature_info.applied_gpu_driver_bug_list_entries);
  }

  if (gpu_access_blocked) {
    base::Value::Dict problem;
    problem.Set("description",
                "GPU process was unable to boot: " + gpu_access_blocked_reason);
    problem.Set("crBugs", base::Value::List());
    base::Value::List disabled_features;
    disabled_features.Append("all");
    problem.Set("affectedGpuSettings", std::move(disabled_features));
    problem.Set("tag", "disabledFeatures");
    problem_list.Insert(problem_list.begin(), base::Value(std::move(problem)));
  }

  for (auto& gpu_feature_data :
       GetGpuFeatureData(gpu_feature_info, is_gpu_compositing_disabled)) {
    if (gpu_feature_data.status != gpu::kGpuFeatureStatusEnabled &&
        gpu_feature_data.disabled_info.is_problem) {
      base::Value::Dict problem;
      problem.Set("description", gpu_feature_data.disabled_info.description);
      problem.Set("crBugs", base::Value::List());
      base::Value::List disabled_features;
      disabled_features.Append(gpu_feature_data.name);
      problem.Set("affectedGpuSettings", std::move(disabled_features));
      problem.Set("tag", "disabledFeatures");
      problem_list.Insert(problem_list.begin(),
                          base::Value(std::move(problem)));
    }
  }
  return base::Value(std::move(problem_list));
}

std::vector<std::string> GetDriverBugWorkaroundsImpl(GpuFeatureInfoType type) {
  GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
  const gpu::GpuFeatureInfo gpu_feature_info =
      type == GpuFeatureInfoType::kCurrent
          ? manager->GetGpuFeatureInfo()
          : manager->GetGpuFeatureInfoForHardwareGpu();

  std::vector<std::string> workarounds;
  for (auto workaround : gpu_feature_info.enabled_gpu_driver_bug_workarounds) {
    workarounds.push_back(gpu::GpuDriverBugWorkaroundTypeToString(
        static_cast<gpu::GpuDriverBugWorkaroundType>(workaround)));
  }
  // Tell clients about the disabled extensions and disabled WebGL
  // extensions as well, to avoid confusion. Do this in a way that's
  // compatible with the current reporting of driver bug workarounds
  // to DevTools and Telemetry, and from there to the GPU tests.
  //
  // This code must be kept in sync with
  // GpuBenchmarking::GetGpuDriverBugWorkarounds.
  for (auto ext : base::SplitString(gpu_feature_info.disabled_extensions,
                                    " ",
                                    base::TRIM_WHITESPACE,
                                    base::SPLIT_WANT_NONEMPTY)) {
    workarounds.push_back("disabled_extension_" + ext);
  }
  for (auto ext : base::SplitString(gpu_feature_info.disabled_webgl_extensions,
                                    " ",
                                    base::TRIM_WHITESPACE,
                                    base::SPLIT_WANT_NONEMPTY)) {
    workarounds.push_back("disabled_webgl_extension_" + ext);
  }
  return workarounds;
}

}  // namespace

int NumberOfRendererRasterThreads() {
  int num_processors = base::SysInfo::NumberOfProcessors();

#if BUILDFLAG(IS_ANDROID) || \
    (BUILDFLAG(IS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY))
  // Android and ChromeOS ARM devices may report 6 to 8 CPUs for big.LITTLE
  // configurations. Limit the number of raster threads based on maximum of
  // 4 big cores.
  num_processors = std::min(num_processors, 4);
#endif

  int num_raster_threads = num_processors / 2;

#if BUILDFLAG(IS_ANDROID)
  // Limit the number of raster threads to 1 on Android.
  // TODO(reveman): Remove this when we have a better mechanims to prevent
  // pre-paint raster work from slowing down non-raster work. crbug.com/504515
  num_raster_threads = 1;
#endif

  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();

  if (command_line.HasSwitch(switches::kNumRasterThreads)) {
    std::string string_value =
        command_line.GetSwitchValueASCII(switches::kNumRasterThreads);
    if (!base::StringToInt(string_value, &num_raster_threads)) {
      DLOG(WARNING) << "Failed to parse switch " << switches::kNumRasterThreads
                    << ": " << string_value;
    }
  }

  return std::clamp(num_raster_threads, kMinRasterThreads, kMaxRasterThreads);
}

bool IsZeroCopyUploadEnabled() {
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
#if BUILDFLAG(IS_MAC)
  return !command_line.HasSwitch(blink::switches::kDisableZeroCopy);
#else
  return command_line.HasSwitch(blink::switches::kEnableZeroCopy);
#endif
}

bool IsPartialRasterEnabled() {
  // Partial raster is not supported with RawDraw.
  if (::features::IsUsingRawDraw()) {
    return false;
  }
  const auto& command_line = *base::CommandLine::ForCurrentProcess();
  return !command_line.HasSwitch(blink::switches::kDisablePartialRaster);
}

bool IsGpuMemoryBufferCompositorResourcesEnabled() {
  // To use Raw Draw, the Raw Draw shared image backing should be used, so
  // not use GPU memory buffer shared image backings for compositor resources.
  if (::features::IsUsingRawDraw()) {
    return false;
  }
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(
          blink::switches::kEnableGpuMemoryBufferCompositorResources)) {
    return true;
  }
  if (command_line.HasSwitch(
          switches::kDisableGpuMemoryBufferCompositorResources)) {
    return false;
  }

#if BUILDFLAG(IS_APPLE)
  return true;
#elif BUILDFLAG(IS_WIN)
  return features::IsDelegatedCompositingEnabled() &&
         features::kDelegatedCompositingModeParam.Get() ==
             features::DelegatedCompositingMode::kFull;
#else
  return false;
#endif
}

int GpuRasterizationMSAASampleCount() {
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();

  if (!command_line.HasSwitch(
          blink::switches::kGpuRasterizationMSAASampleCount))
#if BUILDFLAG(IS_ANDROID)
    return 4;
#else
    // Desktop platforms will compute this automatically based on DPI.
    return -1;
#endif
  std::string string_value = command_line.GetSwitchValueASCII(
      blink::switches::kGpuRasterizationMSAASampleCount);
  int msaa_sample_count = 0;
  if (base::StringToInt(string_value, &msaa_sample_count) &&
      msaa_sample_count >= kMinMSAASampleCount) {
    return msaa_sample_count;
  } else {
    DLOG(WARNING) << "Failed to parse switch "
                  << blink::switches::kGpuRasterizationMSAASampleCount << ": "
                  << string_value;
    return 0;
  }
}

bool IsMainFrameBeforeActivationEnabled() {
  if (base::SysInfo::NumberOfProcessors() < 4)
    return false;

  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableMainFrameBeforeActivation)) {
    return false;
  }

  return true;
}

base::Value GetFeatureStatus() {
  return GetFeatureStatusImpl(GpuFeatureInfoType::kCurrent);
}

base::Value GetProblems() {
  return GetProblemsImpl(GpuFeatureInfoType::kCurrent);
}

std::vector<std::string> GetDriverBugWorkarounds() {
  return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kCurrent);
}

base::Value GetFeatureStatusForHardwareGpu() {
  return GetFeatureStatusImpl(GpuFeatureInfoType::kForHardwareGpu);
}

base::Value GetProblemsForHardwareGpu() {
  return GetProblemsImpl(GpuFeatureInfoType::kForHardwareGpu);
}

std::vector<std::string> GetDriverBugWorkaroundsForHardwareGpu() {
  return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kForHardwareGpu);
}

}  // namespace content