// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpu/config/gpu_util.h" #if defined(OS_WIN) #include // Must be included after windows.h. #include #endif // OS_WIN #include #include #include #include #include #include #include "base/base_paths.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/notreached.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/system/sys_info.h" #include "build/build_config.h" #include "gpu/config/device_perf_info.h" #include "gpu/config/gpu_blocklist.h" #include "gpu/config/gpu_crash_keys.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_info.h" #include "gpu/config/gpu_info_collector.h" #include "gpu/config/gpu_preferences.h" #include "gpu/config/gpu_switches.h" #include "gpu/vulkan/buildflags.h" #include "ui/gfx/extension_set.h" #include "ui/gl/buildflags.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_switches.h" #if defined(OS_ANDROID) #include "base/no_destructor.h" #include "base/synchronization/lock.h" #include "ui/gfx/android/android_surface_control_compat.h" #include "ui/gl/gl_surface_egl.h" #include "ui/gl/init/gl_factory.h" #endif // OS_ANDROID namespace gpu { namespace { #if defined(OS_WIN) // These values are persistent to logs. Entries should not be renumbered and // numeric values should never be reused. // This should match enum D3D11FeatureLevel in // \tools\metrics\histograms\enums.xml enum class D3D11FeatureLevel { kUnknown = 0, k9_1 = 4, k9_2 = 5, k9_3 = 6, k10_0 = 7, k10_1 = 8, k11_0 = 9, k11_1 = 10, k12_0 = 11, k12_1 = 12, kMaxValue = k12_1, }; inline D3D11FeatureLevel ConvertToHistogramD3D11FeatureLevel( D3D_FEATURE_LEVEL d3d11_feature_level) { switch (d3d11_feature_level) { case D3D_FEATURE_LEVEL_1_0_CORE: return D3D11FeatureLevel::kUnknown; case D3D_FEATURE_LEVEL_9_1: return D3D11FeatureLevel::k9_1; case D3D_FEATURE_LEVEL_9_2: return D3D11FeatureLevel::k9_2; case D3D_FEATURE_LEVEL_9_3: return D3D11FeatureLevel::k9_3; case D3D_FEATURE_LEVEL_10_0: return D3D11FeatureLevel::k10_0; case D3D_FEATURE_LEVEL_10_1: return D3D11FeatureLevel::k10_1; case D3D_FEATURE_LEVEL_11_0: return D3D11FeatureLevel::k11_0; case D3D_FEATURE_LEVEL_11_1: return D3D11FeatureLevel::k11_1; case D3D_FEATURE_LEVEL_12_0: return D3D11FeatureLevel::k12_0; case D3D_FEATURE_LEVEL_12_1: return D3D11FeatureLevel::k12_1; default: NOTREACHED(); return D3D11FeatureLevel::kUnknown; } } #endif // OS_WIN GpuFeatureStatus GetAndroidSurfaceControlFeatureStatus( const std::set& blocklisted_features, const GpuPreferences& gpu_preferences) { #if !defined(OS_ANDROID) return kGpuFeatureStatusDisabled; #else if (!gpu_preferences.enable_android_surface_control) return kGpuFeatureStatusDisabled; // SurfaceControl as used by Chrome requires using GpuFence for // synchronization, this is based on Android native fence sync // support. If that is unavailable, i.e. on emulator or SwiftShader, // don't claim SurfaceControl support. if (!gl::GLSurfaceEGL::IsAndroidNativeFenceSyncSupported()) return kGpuFeatureStatusDisabled; DCHECK(gfx::SurfaceControl::IsSupported()); return kGpuFeatureStatusEnabled; #endif } GpuFeatureStatus GetMetalFeatureStatus( const std::set& blocklisted_features, const GpuPreferences& gpu_preferences) { #if defined(OS_MAC) if (blocklisted_features.count(GPU_FEATURE_TYPE_METAL)) return kGpuFeatureStatusBlocklisted; if (!gpu_preferences.enable_metal) return kGpuFeatureStatusDisabled; return kGpuFeatureStatusEnabled; #else return kGpuFeatureStatusDisabled; #endif } GpuFeatureStatus GetVulkanFeatureStatus( const std::set& blocklisted_features, const GpuPreferences& gpu_preferences) { #if BUILDFLAG(ENABLE_VULKAN) // Only blocklist native vulkan. if (gpu_preferences.use_vulkan == VulkanImplementationName::kNative && blocklisted_features.count(GPU_FEATURE_TYPE_VULKAN)) return kGpuFeatureStatusBlocklisted; if (gpu_preferences.use_vulkan == VulkanImplementationName::kNone) return kGpuFeatureStatusDisabled; return kGpuFeatureStatusEnabled; #else return kGpuFeatureStatusDisabled; #endif } GpuFeatureStatus GetGpuRasterizationFeatureStatus( const std::set& blocklisted_features, const base::CommandLine& command_line) { if (command_line.HasSwitch(switches::kDisableGpuRasterization)) return kGpuFeatureStatusDisabled; else if (command_line.HasSwitch(switches::kEnableGpuRasterization)) return kGpuFeatureStatusEnabled; if (blocklisted_features.count(GPU_FEATURE_TYPE_GPU_RASTERIZATION)) return kGpuFeatureStatusBlocklisted; // Enable gpu rasterization for vulkan, unless it is overridden by // commandline. if (features::IsUsingVulkan() && !base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine( features::kDefaultEnableGpuRasterization.name, base::FeatureList::OVERRIDE_DISABLE_FEATURE)) { return kGpuFeatureStatusEnabled; } // Gpu Rasterization on platforms that are not fully enabled is controlled by // a finch experiment. if (!base::FeatureList::IsEnabled(features::kDefaultEnableGpuRasterization)) return kGpuFeatureStatusDisabled; return kGpuFeatureStatusEnabled; } GpuFeatureStatus GetOopRasterizationFeatureStatus( const std::set& blocklisted_features, const base::CommandLine& command_line, const GpuPreferences& gpu_preferences, const GPUInfo& gpu_info) { // OOP rasterization requires GPU rasterization, so if blocklisted or // disabled, report the same. auto status = GetGpuRasterizationFeatureStatus(blocklisted_features, command_line); if (status != kGpuFeatureStatusEnabled) return status; // If we can't create a GrContext for whatever reason, don't enable oop // rasterization. if (!gpu_info.oop_rasterization_supported) return kGpuFeatureStatusDisabled; if (gpu_preferences.disable_oop_rasterization) return kGpuFeatureStatusDisabled; else if (gpu_preferences.enable_oop_rasterization) return kGpuFeatureStatusEnabled; // Enable OOP rasterization for vulkan, unless it is overridden by // commandline. if (features::IsUsingVulkan() && !base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine( features::kDefaultEnableOopRasterization.name, base::FeatureList::OVERRIDE_DISABLE_FEATURE)) { return kGpuFeatureStatusEnabled; } // OOP Rasterization on platforms that are not fully enabled is controlled by // a finch experiment. if (!base::FeatureList::IsEnabled(features::kDefaultEnableOopRasterization)) return kGpuFeatureStatusDisabled; return kGpuFeatureStatusEnabled; } GpuFeatureStatus GetWebGLFeatureStatus( const std::set& blocklisted_features, bool use_swift_shader) { if (use_swift_shader) return kGpuFeatureStatusEnabled; if (blocklisted_features.count(GPU_FEATURE_TYPE_ACCELERATED_WEBGL)) return kGpuFeatureStatusBlocklisted; return kGpuFeatureStatusEnabled; } GpuFeatureStatus GetWebGL2FeatureStatus( const std::set& blocklisted_features, bool use_swift_shader) { if (use_swift_shader) return kGpuFeatureStatusEnabled; if (blocklisted_features.count(GPU_FEATURE_TYPE_ACCELERATED_WEBGL2)) return kGpuFeatureStatusBlocklisted; return kGpuFeatureStatusEnabled; } GpuFeatureStatus Get2DCanvasFeatureStatus( const std::set& blocklisted_features, bool use_swift_shader) { if (use_swift_shader) { // This is for testing only. Chrome should exercise the GPU accelerated // path on top of SwiftShader driver. return kGpuFeatureStatusEnabled; } if (blocklisted_features.count(GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS)) return kGpuFeatureStatusSoftware; return kGpuFeatureStatusEnabled; } GpuFeatureStatus GetAcceleratedVideoDecodeFeatureStatus( const std::set& blocklisted_features, bool use_swift_shader) { if (use_swift_shader) { // This is for testing only. Chrome should exercise the GPU accelerated // path on top of SwiftShader driver. return kGpuFeatureStatusEnabled; } if (blocklisted_features.count(GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE)) return kGpuFeatureStatusBlocklisted; return kGpuFeatureStatusEnabled; } GpuFeatureStatus GetGLFeatureStatus(const std::set& blocklisted_features, bool use_swift_shader) { if (use_swift_shader) { // This is for testing only. Chrome should exercise the GPU accelerated // path on top of SwiftShader driver. return kGpuFeatureStatusEnabled; } if (blocklisted_features.count(GPU_FEATURE_TYPE_ACCELERATED_GL)) return kGpuFeatureStatusBlocklisted; return kGpuFeatureStatusEnabled; } void AppendWorkaroundsToCommandLine(const GpuFeatureInfo& gpu_feature_info, base::CommandLine* command_line) { if (gpu_feature_info.IsWorkaroundEnabled(DISABLE_D3D11)) { command_line->AppendSwitch(switches::kDisableD3D11); } if (gpu_feature_info.IsWorkaroundEnabled(DISABLE_ES3_GL_CONTEXT)) { command_line->AppendSwitch(switches::kDisableES3GLContext); } if (gpu_feature_info.IsWorkaroundEnabled( DISABLE_ES3_GL_CONTEXT_FOR_TESTING)) { command_line->AppendSwitch(switches::kDisableES3GLContextForTesting); } #if defined(OS_WIN) if (gpu_feature_info.IsWorkaroundEnabled(DISABLE_DIRECT_COMPOSITION)) { command_line->AppendSwitch(switches::kDisableDirectComposition); } if (gpu_feature_info.IsWorkaroundEnabled( DISABLE_DIRECT_COMPOSITION_VIDEO_OVERLAYS)) { command_line->AppendSwitch( switches::kDisableDirectCompositionVideoOverlays); } #endif } // Adjust gpu feature status based on enabled gpu driver bug workarounds. void AdjustGpuFeatureStatusToWorkarounds(GpuFeatureInfo* gpu_feature_info) { if (gpu_feature_info->IsWorkaroundEnabled(DISABLE_D3D11) || gpu_feature_info->IsWorkaroundEnabled(DISABLE_ES3_GL_CONTEXT)) { gpu_feature_info->status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL2] = kGpuFeatureStatusBlocklisted; } } // Estimates roughly user total disk space by counting in the drives where // the exe is, where the temporary space is, where the user home is. // If total space and free space are of the same size, they are considered // the same drive. There could be corner cases this estimation is far from // the actual total disk space, but for histogram purpose, limited numbers // of outliers do not matter. uint32_t EstimateAmountOfTotalDiskSpaceMB() { const base::BasePathKey kPathKeys[] = {base::DIR_EXE, base::DIR_TEMP, base::DIR_HOME}; std::vector total_space_vector, free_space_vector; uint32_t sum = 0; for (const auto& path_key : kPathKeys) { base::FilePath path; if (base::PathService::Get(path_key, &path)) { uint32_t total_space = static_cast( base::SysInfo::AmountOfTotalDiskSpace(path) / 1024 / 1024); uint32_t free_space = static_cast( base::SysInfo::AmountOfFreeDiskSpace(path) / 1024 / 1024); bool duplicated = false; for (size_t ii = 0; ii < total_space_vector.size(); ++ii) { if (total_space == total_space_vector[ii] && free_space == free_space_vector[ii]) { duplicated = true; break; } } if (!duplicated) { total_space_vector.push_back(total_space); free_space_vector.push_back(free_space); sum += total_space; } } } return sum; } // Only record Nvidia and AMD GPUs. void RecordGpuHistogram(uint32_t vendor_id, uint32_t device_id) { switch (vendor_id) { case 0x10de: base::SparseHistogram::FactoryGet( "GPU.MultiGpu.Nvidia", base::HistogramBase::kUmaTargetedHistogramFlag) ->Add(device_id); break; case 0x1002: base::SparseHistogram::FactoryGet( "GPU.MultiGpu.AMD", base::HistogramBase::kUmaTargetedHistogramFlag) ->Add(device_id); break; default: // Do nothing if it's not Nvidia/AMD. break; } } #if defined(OS_WIN) uint32_t GetSystemCommitLimitMb() { PERFORMANCE_INFORMATION perf_info = {sizeof(perf_info)}; if (::GetPerformanceInfo(&perf_info, sizeof(perf_info))) { uint64_t limit = perf_info.CommitLimit; limit *= perf_info.PageSize; limit /= 1024 * 1024; return static_cast(limit); } return 0u; } #endif // OS_WIN GPUInfo* g_gpu_info_cache = nullptr; GpuFeatureInfo* g_gpu_feature_info_cache = nullptr; } // namespace GpuFeatureInfo ComputeGpuFeatureInfoWithHardwareAccelerationDisabled() { GpuFeatureInfo gpu_feature_info; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL2] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_GL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_METAL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_VULKAN] = kGpuFeatureStatusDisabled; #if DCHECK_IS_ON() for (int ii = 0; ii < NUMBER_OF_GPU_FEATURE_TYPES; ++ii) { DCHECK_NE(kGpuFeatureStatusUndefined, gpu_feature_info.status_values[ii]); } #endif return gpu_feature_info; } GpuFeatureInfo ComputeGpuFeatureInfoWithNoGpu() { GpuFeatureInfo gpu_feature_info; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL2] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_GL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_METAL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_VULKAN] = kGpuFeatureStatusDisabled; #if DCHECK_IS_ON() for (int ii = 0; ii < NUMBER_OF_GPU_FEATURE_TYPES; ++ii) { DCHECK_NE(kGpuFeatureStatusUndefined, gpu_feature_info.status_values[ii]); } #endif return gpu_feature_info; } GpuFeatureInfo ComputeGpuFeatureInfoForSwiftShader() { GpuFeatureInfo gpu_feature_info; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL2] = kGpuFeatureStatusSoftware; gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_GL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_METAL] = kGpuFeatureStatusDisabled; gpu_feature_info.status_values[GPU_FEATURE_TYPE_VULKAN] = kGpuFeatureStatusDisabled; #if DCHECK_IS_ON() for (int ii = 0; ii < NUMBER_OF_GPU_FEATURE_TYPES; ++ii) { DCHECK_NE(kGpuFeatureStatusUndefined, gpu_feature_info.status_values[ii]); } #endif return gpu_feature_info; } GpuFeatureInfo ComputeGpuFeatureInfo(const GPUInfo& gpu_info, const GpuPreferences& gpu_preferences, base::CommandLine* command_line, bool* needs_more_info) { DCHECK(!needs_more_info || !(*needs_more_info)); bool use_swift_shader = false; if (command_line->HasSwitch(switches::kUseGL)) { std::string use_gl = command_line->GetSwitchValueASCII(switches::kUseGL); if (use_gl == gl::kGLImplementationSwiftShaderName) use_swift_shader = true; else if (use_gl == gl::kGLImplementationANGLEName) { if (command_line->HasSwitch(switches::kUseANGLE)) { std::string use_angle = command_line->GetSwitchValueASCII(switches::kUseANGLE); if (use_angle == gl::kANGLEImplementationSwiftShaderName) use_swift_shader = true; else if (use_angle == gl::kANGLEImplementationSwiftShaderForWebGLName) return ComputeGpuFeatureInfoForSwiftShader(); } } else if (use_gl == gl::kGLImplementationSwiftShaderForWebGLName) return ComputeGpuFeatureInfoForSwiftShader(); else if (use_gl == gl::kGLImplementationDisabledName) return ComputeGpuFeatureInfoWithNoGpu(); } if (gpu_preferences.use_vulkan == gpu::VulkanImplementationName::kSwiftshader) { use_swift_shader = true; } GpuFeatureInfo gpu_feature_info; std::set blocklisted_features; if (!gpu_preferences.ignore_gpu_blocklist && !command_line->HasSwitch(switches::kUseGpuInTests)) { std::unique_ptr list(GpuBlocklist::Create()); if (gpu_preferences.log_gpu_control_list_decisions) list->EnableControlListLogging("gpu_blocklist"); unsigned target_test_group = 0u; if (command_line->HasSwitch(switches::kGpuBlocklistTestGroup)) { std::string test_group_string = command_line->GetSwitchValueASCII(switches::kGpuBlocklistTestGroup); if (!base::StringToUint(test_group_string, &target_test_group)) target_test_group = 0u; } blocklisted_features = list->MakeDecision( GpuControlList::kOsAny, std::string(), gpu_info, target_test_group); gpu_feature_info.applied_gpu_blocklist_entries = list->GetActiveEntries(); if (needs_more_info) { *needs_more_info = list->needs_more_info(); } } gpu_feature_info.status_values[GPU_FEATURE_TYPE_GPU_RASTERIZATION] = GetGpuRasterizationFeatureStatus(blocklisted_features, *command_line); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL] = GetWebGLFeatureStatus(blocklisted_features, use_swift_shader); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL2] = GetWebGL2FeatureStatus(blocklisted_features, use_swift_shader); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS] = Get2DCanvasFeatureStatus(blocklisted_features, use_swift_shader); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE] = GetAcceleratedVideoDecodeFeatureStatus(blocklisted_features, use_swift_shader); gpu_feature_info.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] = GetOopRasterizationFeatureStatus(blocklisted_features, *command_line, gpu_preferences, gpu_info); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] = GetAndroidSurfaceControlFeatureStatus(blocklisted_features, gpu_preferences); gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_GL] = GetGLFeatureStatus(blocklisted_features, use_swift_shader); gpu_feature_info.status_values[GPU_FEATURE_TYPE_METAL] = GetMetalFeatureStatus(blocklisted_features, gpu_preferences); gpu_feature_info.status_values[GPU_FEATURE_TYPE_VULKAN] = GetVulkanFeatureStatus(blocklisted_features, gpu_preferences); #if DCHECK_IS_ON() for (int ii = 0; ii < NUMBER_OF_GPU_FEATURE_TYPES; ++ii) { DCHECK_NE(kGpuFeatureStatusUndefined, gpu_feature_info.status_values[ii]); } #endif gfx::ExtensionSet all_disabled_extensions; std::string disabled_gl_extensions_value = command_line->GetSwitchValueASCII(switches::kDisableGLExtensions); if (!disabled_gl_extensions_value.empty()) { std::vector command_line_disabled_extensions = base::SplitStringPiece(disabled_gl_extensions_value, ", ;", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); all_disabled_extensions.insert(command_line_disabled_extensions.begin(), command_line_disabled_extensions.end()); } std::set enabled_driver_bug_workarounds; std::vector driver_bug_disabled_extensions; if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) { std::unique_ptr list(GpuDriverBugList::Create()); unsigned target_test_group = 0u; if (command_line->HasSwitch(switches::kGpuDriverBugListTestGroup)) { std::string test_group_string = command_line->GetSwitchValueASCII( switches::kGpuDriverBugListTestGroup); if (!base::StringToUint(test_group_string, &target_test_group)) target_test_group = 0u; } enabled_driver_bug_workarounds = list->MakeDecision( GpuControlList::kOsAny, std::string(), gpu_info, target_test_group); gpu_feature_info.applied_gpu_driver_bug_list_entries = list->GetActiveEntries(); driver_bug_disabled_extensions = list->GetDisabledExtensions(); all_disabled_extensions.insert(driver_bug_disabled_extensions.begin(), driver_bug_disabled_extensions.end()); // Disabling WebGL extensions only occurs via the blocklist, so // the logic is simpler. gfx::ExtensionSet disabled_webgl_extensions; std::vector disabled_webgl_extension_list = list->GetDisabledWebGLExtensions(); disabled_webgl_extensions.insert(disabled_webgl_extension_list.begin(), disabled_webgl_extension_list.end()); gpu_feature_info.disabled_webgl_extensions = gfx::MakeExtensionString(disabled_webgl_extensions); } gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine( &enabled_driver_bug_workarounds, *command_line); gpu_feature_info.enabled_gpu_driver_bug_workarounds.insert( gpu_feature_info.enabled_gpu_driver_bug_workarounds.begin(), enabled_driver_bug_workarounds.begin(), enabled_driver_bug_workarounds.end()); if (all_disabled_extensions.size()) { gpu_feature_info.disabled_extensions = gfx::MakeExtensionString(all_disabled_extensions); } AdjustGpuFeatureStatusToWorkarounds(&gpu_feature_info); // TODO(zmo): Find a better way to communicate these settings to bindings // initialization than commandline switches. AppendWorkaroundsToCommandLine(gpu_feature_info, command_line); return gpu_feature_info; } void SetKeysForCrashLogging(const GPUInfo& gpu_info) { const GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu(); #if !defined(OS_ANDROID) crash_keys::gpu_vendor_id.Set( base::StringPrintf("0x%04x", active_gpu.vendor_id)); crash_keys::gpu_device_id.Set( base::StringPrintf("0x%04x", active_gpu.device_id)); #endif // !OS_ANDROID #if defined(OS_WIN) crash_keys::gpu_sub_sys_id.Set( base::StringPrintf("0x%08x", active_gpu.sub_sys_id)); crash_keys::gpu_revision.Set(base::StringPrintf("%u", active_gpu.revision)); #endif // OS_WIN crash_keys::gpu_driver_version.Set(active_gpu.driver_version); crash_keys::gpu_pixel_shader_version.Set(gpu_info.pixel_shader_version); crash_keys::gpu_vertex_shader_version.Set(gpu_info.vertex_shader_version); crash_keys::gpu_generation_intel.Set( base::StringPrintf("%d", GetIntelGpuGeneration(gpu_info))); #if defined(OS_MAC) crash_keys::gpu_gl_version.Set(gpu_info.gl_version); #elif defined(OS_POSIX) crash_keys::gpu_vendor.Set(gpu_info.gl_vendor); crash_keys::gpu_renderer.Set(gpu_info.gl_renderer); #endif } void CacheGPUInfo(const GPUInfo& gpu_info) { DCHECK(!g_gpu_info_cache); g_gpu_info_cache = new GPUInfo; *g_gpu_info_cache = gpu_info; } bool PopGPUInfoCache(GPUInfo* gpu_info) { if (!g_gpu_info_cache) return false; *gpu_info = *g_gpu_info_cache; delete g_gpu_info_cache; g_gpu_info_cache = nullptr; return true; } void CacheGpuFeatureInfo(const GpuFeatureInfo& gpu_feature_info) { DCHECK(!g_gpu_feature_info_cache); g_gpu_feature_info_cache = new GpuFeatureInfo; *g_gpu_feature_info_cache = gpu_feature_info; } bool PopGpuFeatureInfoCache(GpuFeatureInfo* gpu_feature_info) { if (!g_gpu_feature_info_cache) return false; *gpu_feature_info = *g_gpu_feature_info_cache; delete g_gpu_feature_info_cache; g_gpu_feature_info_cache = nullptr; return true; } #if defined(OS_ANDROID) bool InitializeGLThreadSafe(base::CommandLine* command_line, const GpuPreferences& gpu_preferences, GPUInfo* out_gpu_info, GpuFeatureInfo* out_gpu_feature_info) { static base::NoDestructor gl_bindings_initialization_lock; base::AutoLock auto_lock(*gl_bindings_initialization_lock); DCHECK(command_line); DCHECK(out_gpu_info && out_gpu_feature_info); bool gpu_info_cached = PopGPUInfoCache(out_gpu_info); bool gpu_feature_info_cached = PopGpuFeatureInfoCache(out_gpu_feature_info); DCHECK_EQ(gpu_info_cached, gpu_feature_info_cached); if (gpu_info_cached) { // GL bindings have already been initialized in another thread. DCHECK_NE(gl::kGLImplementationNone, gl::GetGLImplementation()); return true; } if (gl::GetGLImplementation() == gl::kGLImplementationNone) { // Some tests initialize bindings by themselves. if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true)) { VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed"; return false; } } CollectContextGraphicsInfo(out_gpu_info); *out_gpu_feature_info = ComputeGpuFeatureInfo(*out_gpu_info, gpu_preferences, command_line, nullptr); if (!out_gpu_feature_info->disabled_extensions.empty()) { gl::init::SetDisabledExtensionsPlatform( out_gpu_feature_info->disabled_extensions); } if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) { VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed"; return false; } CacheGPUInfo(*out_gpu_info); CacheGpuFeatureInfo(*out_gpu_feature_info); return true; } #endif // OS_ANDROID bool EnableSwiftShaderIfNeeded(base::CommandLine* command_line, const GpuFeatureInfo& gpu_feature_info, bool disable_software_rasterizer, bool blocklist_needs_more_info) { #if BUILDFLAG(ENABLE_SWIFTSHADER) if (disable_software_rasterizer || blocklist_needs_more_info) return false; // Don't overwrite user preference. if (command_line->HasSwitch(switches::kUseGL)) return false; if (gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_WEBGL] != kGpuFeatureStatusEnabled || gpu_feature_info.status_values[GPU_FEATURE_TYPE_ACCELERATED_GL] != kGpuFeatureStatusEnabled) { bool legacy_software_gl = true; #if defined(OS_LINUX) || defined(OS_WIN) // This setting makes WebGL run on SwANGLE instead of SwiftShader GL. legacy_software_gl = false; #endif gl::SetSoftwareWebGLCommandLineSwitches(command_line, legacy_software_gl); return true; } return false; #else return false; #endif } IntelGpuSeriesType GetIntelGpuSeriesType(uint32_t vendor_id, uint32_t device_id) { // Note that this function's output should only depend on vendor_id and // device_id of a GPU. This is because we record a histogram on the output // and we don't want to expose an extra bit other than the already recorded // vendor_id and device_id. if (vendor_id == 0x8086) { // Intel // The device IDs of Intel GPU are based on following Mesa source files: // include/pci_ids/i965_pci_ids.h and iris_pci_ids.h uint32_t masked_device_id = device_id & 0xFF00; switch (masked_device_id) { case 0x2900: return IntelGpuSeriesType::kBroadwater; case 0x2A00: if (device_id == 0x2A02 || device_id == 0x2A12) return IntelGpuSeriesType::kBroadwater; if (device_id == 0x2A42) return IntelGpuSeriesType::kEaglelake; break; case 0x2E00: return IntelGpuSeriesType::kEaglelake; case 0x0000: return IntelGpuSeriesType::kIronlake; case 0x0100: if (device_id == 0x0152 || device_id == 0x0156 || device_id == 0x015A || device_id == 0x0162 || device_id == 0x0166 || device_id == 0x016A) return IntelGpuSeriesType::kIvybridge; if (device_id == 0x0155 || device_id == 0x0157) return IntelGpuSeriesType::kBaytrail; return IntelGpuSeriesType::kSandybridge; case 0x0F00: return IntelGpuSeriesType::kBaytrail; case 0x0A00: if (device_id == 0x0A84) return IntelGpuSeriesType::kApollolake; return IntelGpuSeriesType::kHaswell; case 0x0400: case 0x0C00: case 0x0D00: return IntelGpuSeriesType::kHaswell; case 0x2200: return IntelGpuSeriesType::kCherrytrail; case 0x1600: return IntelGpuSeriesType::kBroadwell; case 0x5A00: if (device_id == 0x5A85 || device_id == 0x5A84) return IntelGpuSeriesType::kApollolake; return IntelGpuSeriesType::kCannonlake; case 0x1900: return IntelGpuSeriesType::kSkylake; case 0x1A00: return IntelGpuSeriesType::kApollolake; case 0x3100: return IntelGpuSeriesType::kGeminilake; case 0x5900: return IntelGpuSeriesType::kKabylake; case 0x8700: if (device_id == 0x87C0) return IntelGpuSeriesType::kKabylake; if (device_id == 0x87CA) return IntelGpuSeriesType::kCoffeelake; break; case 0x3E00: if (device_id == 0x3EA0 || device_id == 0x3EA1 || device_id == 0x3EA2 || device_id == 0x3EA4 || device_id == 0x3EA3) return IntelGpuSeriesType::kWhiskeylake; return IntelGpuSeriesType::kCoffeelake; case 0x9B00: return IntelGpuSeriesType::kCometlake; case 0x8A00: return IntelGpuSeriesType::kIcelake; case 0x4500: return IntelGpuSeriesType::kElkhartlake; case 0x4E00: return IntelGpuSeriesType::kJasperlake; case 0x9A00: return IntelGpuSeriesType::kTigerlake; default: break; } } return IntelGpuSeriesType::kUnknown; } std::string GetIntelGpuGeneration(uint32_t vendor_id, uint32_t device_id) { if (vendor_id == 0x8086) { IntelGpuSeriesType gpu_series = GetIntelGpuSeriesType(vendor_id, device_id); switch (gpu_series) { case IntelGpuSeriesType::kBroadwater: case IntelGpuSeriesType::kEaglelake: return "4"; case IntelGpuSeriesType::kIronlake: return "5"; case IntelGpuSeriesType::kSandybridge: return "6"; case IntelGpuSeriesType::kBaytrail: case IntelGpuSeriesType::kIvybridge: case IntelGpuSeriesType::kHaswell: return "7"; case IntelGpuSeriesType::kCherrytrail: case IntelGpuSeriesType::kBroadwell: return "8"; case IntelGpuSeriesType::kApollolake: case IntelGpuSeriesType::kSkylake: case IntelGpuSeriesType::kGeminilake: case IntelGpuSeriesType::kKabylake: case IntelGpuSeriesType::kCoffeelake: case IntelGpuSeriesType::kWhiskeylake: case IntelGpuSeriesType::kCometlake: return "9"; case IntelGpuSeriesType::kCannonlake: return "10"; case IntelGpuSeriesType::kIcelake: case IntelGpuSeriesType::kElkhartlake: case IntelGpuSeriesType::kJasperlake: return "11"; case IntelGpuSeriesType::kTigerlake: return "12"; default: break; } } return ""; } IntelGpuGeneration GetIntelGpuGeneration(const GPUInfo& gpu_info) { const uint32_t kIntelVendorId = 0x8086; IntelGpuGeneration latest = IntelGpuGeneration::kNonIntel; std::vector intel_device_ids; if (gpu_info.gpu.vendor_id == kIntelVendorId) intel_device_ids.push_back(gpu_info.gpu.device_id); for (const auto& gpu : gpu_info.secondary_gpus) { if (gpu.vendor_id == kIntelVendorId) intel_device_ids.push_back(gpu.device_id); } if (intel_device_ids.empty()) return latest; latest = IntelGpuGeneration::kUnknownIntel; for (uint32_t device_id : intel_device_ids) { std::string gen_str = gpu::GetIntelGpuGeneration(kIntelVendorId, device_id); int gen_int = 0; if (gen_str.empty() || !base::StringToInt(gen_str, &gen_int)) continue; DCHECK_GE(gen_int, static_cast(IntelGpuGeneration::kUnknownIntel)); DCHECK_LE(gen_int, static_cast(IntelGpuGeneration::kMaxValue)); if (gen_int > static_cast(latest)) latest = static_cast(gen_int); } return latest; } void CollectDevicePerfInfo(DevicePerfInfo* device_perf_info, bool in_browser_process) { DCHECK(device_perf_info); device_perf_info->total_physical_memory_mb = static_cast(base::SysInfo::AmountOfPhysicalMemoryMB()); if (!in_browser_process) device_perf_info->total_disk_space_mb = EstimateAmountOfTotalDiskSpaceMB(); device_perf_info->hardware_concurrency = static_cast(std::thread::hardware_concurrency()); #if defined(OS_WIN) device_perf_info->system_commit_limit_mb = GetSystemCommitLimitMb(); if (!in_browser_process) { D3D_FEATURE_LEVEL d3d11_feature_level = D3D_FEATURE_LEVEL_1_0_CORE; bool has_discrete_gpu = false; if (CollectD3D11FeatureInfo(&d3d11_feature_level, &has_discrete_gpu)) { device_perf_info->d3d11_feature_level = d3d11_feature_level; device_perf_info->has_discrete_gpu = has_discrete_gpu ? HasDiscreteGpu::kYes : HasDiscreteGpu::kNo; } } #endif } void RecordDevicePerfInfoHistograms() { absl::optional device_perf_info = GetDevicePerfInfo(); if (!device_perf_info.has_value()) return; UMA_HISTOGRAM_COUNTS_1000("Hardware.TotalDiskSpace", device_perf_info->total_disk_space_mb / 1024); UMA_HISTOGRAM_COUNTS_100("Hardware.Concurrency", device_perf_info->hardware_concurrency); #if defined(OS_WIN) UMA_HISTOGRAM_COUNTS_100("Memory.Total.SystemCommitLimit", device_perf_info->system_commit_limit_mb / 1024); UMA_HISTOGRAM_ENUMERATION("GPU.D3D11FeatureLevel", ConvertToHistogramD3D11FeatureLevel( device_perf_info->d3d11_feature_level)); UMA_HISTOGRAM_ENUMERATION("GPU.HasDiscreteGpu", device_perf_info->has_discrete_gpu); #endif // OS_WIN UMA_HISTOGRAM_ENUMERATION("GPU.IntelGpuGeneration", device_perf_info->intel_gpu_generation); UMA_HISTOGRAM_BOOLEAN("GPU.SoftwareRendering", device_perf_info->software_rendering); } void RecordDiscreteGpuHistograms(const GPUInfo& gpu_info) { if (gpu_info.GpuCount() < 2) return; // To simplify logic, if there are multiple GPUs identified on a device, // assume AMD or Nvidia is the discrete GPU. RecordGpuHistogram(gpu_info.gpu.vendor_id, gpu_info.gpu.device_id); for (const auto& gpu : gpu_info.secondary_gpus) RecordGpuHistogram(gpu.vendor_id, gpu.device_id); } #if defined(OS_WIN) std::string D3DFeatureLevelToString(uint32_t d3d_feature_level) { if (d3d_feature_level == 0) { return "Not supported"; } else { return base::StringPrintf("D3D %d.%d", (d3d_feature_level >> 12) & 0xF, (d3d_feature_level >> 8) & 0xF); } } std::string VulkanVersionToString(uint32_t vulkan_version) { if (vulkan_version == 0) { return "Not supported"; } else { // Vulkan version number VK_MAKE_VERSION(major, minor, patch) // (((major) << 22) | ((minor) << 12) | (patch)) return base::StringPrintf( "Vulkan API %d.%d.%d", (vulkan_version >> 22) & 0x3FF, (vulkan_version >> 12) & 0x3FF, vulkan_version & 0xFFF); } } #endif // OS_WIN } // namespace gpu