| // Copyright 2023 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/webgpu_blocklist_impl.h" |
| |
| #include <array> |
| #include <sstream> |
| #include <string_view> |
| |
| #include "base/notreached.h" |
| #include "base/strings/pattern.h" |
| #include "base/strings/string_split.h" |
| #include "build/build_config.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "base/mac/mac_util.h" |
| #endif |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "base/android/android_info.h" |
| #endif |
| |
| #include "third_party/dawn/include/dawn/webgpu_cpp.h" |
| |
| namespace gpu { |
| |
| namespace detail { |
| |
| WebGPUBlocklistReason GetWebGPUAdapterBlocklistReason( |
| const wgpu::AdapterInfo& info, |
| const WebGPUBlocklistOptions& options) { |
| WebGPUBlocklistReason reason = WebGPUBlocklistReason::None; |
| #if BUILDFLAG(IS_MAC) |
| constexpr uint32_t kAMDVendorID = 0x1002; |
| if (base::mac::MacOSMajorVersion() < 13 && info.vendorID == kAMDVendorID && |
| info.backendType == wgpu::BackendType::Metal) { |
| reason = reason | WebGPUBlocklistReason::DynamicArrayIndexInStruct; |
| } |
| #endif |
| |
| if (info.backendType == wgpu::BackendType::D3D12) { |
| #if defined(ARCH_CPU_X86) |
| constexpr uint32_t kNVIDIAVendorID = 0x10de; |
| if (info.vendorID == kNVIDIAVendorID) { |
| reason = reason | WebGPUBlocklistReason::IndirectComputeRootConstants; |
| } |
| #endif // defined(ARCH_CPU_X86) |
| #if defined(ARCH_CPU_ARM_FAMILY) |
| reason = reason | WebGPUBlocklistReason::WindowsARM; |
| #endif // defined(ARCH_CPU_ARM_FAMILY) |
| } |
| |
| #if BUILDFLAG(IS_ANDROID) |
| constexpr uint32_t kARMVendorID = 0x13B5; |
| constexpr uint32_t kQualcommVendorID = 0x5143; |
| constexpr uint32_t kIntelVendorID = 0x8086; |
| constexpr uint32_t kImgTecVendorID = 0x1010; |
| |
| switch (info.vendorID) { |
| case kARMVendorID: |
| case kQualcommVendorID: |
| case kIntelVendorID: |
| // ARM, Qualcomm, and Intel GPUs are supported on Android 12+ on Vulkan |
| if (info.backendType == wgpu::BackendType::Vulkan && |
| (base::android::android_info::sdk_int() < |
| base::android::android_info::SDK_VERSION_S)) { |
| reason = reason | WebGPUBlocklistReason::AndroidLimitedSupport; |
| } |
| // and Android 10+ on OpenGLES (currently Chrome's minimum, so no version |
| // check here) |
| break; |
| |
| case kImgTecVendorID: |
| // Imagination GPUs are supported on Android 16+ on Vulkan |
| if (info.backendType == wgpu::BackendType::Vulkan && |
| (base::android::android_info::sdk_int() < |
| base::android::android_info::SDK_VERSION_BAKLAVA)) { |
| reason = reason | WebGPUBlocklistReason::AndroidLimitedSupport; |
| } |
| // and Android 13+ on OpenGLES |
| if (info.backendType == wgpu::BackendType::OpenGLES && |
| (base::android::android_info::sdk_int() < |
| base::android::android_info::SDK_VERSION_T)) { |
| reason = reason | WebGPUBlocklistReason::AndroidLimitedSupport; |
| } |
| break; |
| |
| default: |
| // Other OS versions/GPU vendor combinations may be fine, but have not had |
| // sufficient testing yet. |
| reason = reason | WebGPUBlocklistReason::AndroidLimitedSupport; |
| break; |
| } |
| #endif // BUILDFLAG(IS_ANDROID) |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| constexpr uint32_t kAMDVendorID = 0x1002; |
| if (info.vendorID == kAMDVendorID && info.deviceID == 0x98e4) { |
| reason = reason | WebGPUBlocklistReason::AMDMissingDrmFormatModifier; |
| } |
| #endif |
| |
| if (info.adapterType == wgpu::AdapterType::CPU) { |
| reason = reason | WebGPUBlocklistReason::CPUAdapter; |
| } |
| |
| if (info.backendType == wgpu::BackendType::D3D11) { |
| reason = reason | WebGPUBlocklistReason::D3D11; |
| } |
| |
| for (auto* chain = info.nextInChain; chain != nullptr; |
| chain = chain->nextInChain) { |
| switch (chain->sType) { |
| case wgpu::SType::AdapterPropertiesD3D: |
| #if defined(ARCH_CPU_X86) |
| if (static_cast<const wgpu::AdapterPropertiesD3D*>(chain) |
| ->shaderModel >= 60) { |
| reason = reason | WebGPUBlocklistReason::Consteval22ndBit; |
| } |
| #endif // defined(ARCH_CPU_X86) |
| break; |
| default: |
| break; |
| } |
| } |
| |
| auto U32ToHexString = [](uint32_t value) { |
| std::ostringstream o; |
| o << std::hex << value; |
| return o.str(); |
| }; |
| |
| auto blocked_patterns = |
| base::SplitString(options.blocklist_string, "|", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_ALL); |
| |
| for (const auto& blocked_pattern : blocked_patterns) { |
| std::vector<std::string> segments = base::SplitString( |
| blocked_pattern, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| // Check if the vendorID matches the first segment. |
| if (segments.size() >= 1) { |
| if (!base::MatchPattern(U32ToHexString(info.vendorID), segments[0])) { |
| continue; |
| } |
| } |
| |
| // Check if the deviceID or architecture matches the second segment. |
| if (segments.size() >= 2) { |
| if (!base::MatchPattern(U32ToHexString(info.deviceID), segments[1]) && |
| !base::MatchPattern(info.architecture, segments[1])) { |
| continue; |
| } |
| } |
| |
| // Check if the driver description matches the third segment. |
| if (segments.size() >= 3) { |
| if (!base::MatchPattern(info.description, segments[2])) { |
| continue; |
| } |
| } |
| |
| // Adapter is blocked. |
| reason = reason | WebGPUBlocklistReason::StringPattern; |
| } |
| return reason; |
| } |
| |
| std::string BlocklistReasonToString(WebGPUBlocklistReason reason) { |
| std::string result; |
| bool first = true; |
| static constexpr std::array< |
| std::pair<WebGPUBlocklistReason, std::string_view>, 10> |
| kKnownReasons = {{ |
| {WebGPUBlocklistReason::Consteval22ndBit, |
| "crbug.com/42250788: Invalid consteval interpretation of 22nd bit " |
| "on Windows x86 with SM 6.0+."}, |
| {WebGPUBlocklistReason::D3D11, |
| "crbug.com/41479539: D3D11 backend not fully implemented."}, |
| {WebGPUBlocklistReason::CPUAdapter, |
| "crbug.com/40057808: CPU adapters not fully tested or conformant."}, |
| {WebGPUBlocklistReason::AMDMissingDrmFormatModifier, |
| "b/331922614: AMD driver doesn't support " |
| "VK_EXT_image_drm_format_modifier."}, |
| {WebGPUBlocklistReason::AndroidLimitedSupport, |
| "crbug.com/40643150: Limited support / testing currently " |
| "available on Android."}, |
| {WebGPUBlocklistReason::WindowsARM, |
| "crbug.com/42242119: Not supported on Windows arm yet."}, |
| {WebGPUBlocklistReason::IndirectComputeRootConstants, |
| "crbug.com/42240193: Indirect root constants in compute pass " |
| "broken on Windows NVIDIA x86."}, |
| {WebGPUBlocklistReason::DynamicArrayIndexInStruct, |
| "crbug.com/40643701: Metal compiler errors for dynamic indexing " |
| "of arrays in structures."}, |
| {WebGPUBlocklistReason::StringPattern, |
| "Blocklisted by vendor/device/driver string pattern."}, |
| }}; |
| for (const auto& [flag, description] : kKnownReasons) { |
| if ((reason & flag) != flag) { |
| continue; |
| } |
| reason &= ~flag; |
| if (!first) { |
| result += " | "; |
| } |
| first = false; |
| result += description; |
| } |
| // If this triggers you need to add an entry to kKnownReasons. |
| CHECK(reason == WebGPUBlocklistReason::None); |
| return result; |
| } |
| |
| } // namespace detail |
| |
| WebGPUBlocklistResultImpl IsWebGPUAdapterBlocklisted( |
| const wgpu::Adapter& adapter, |
| WebGPUBlocklistOptions options) { |
| wgpu::AdapterInfo info; |
| wgpu::AdapterPropertiesD3D d3dProperties; |
| if (adapter.HasFeature(wgpu::FeatureName::AdapterPropertiesD3D)) { |
| info.nextInChain = &d3dProperties; |
| } |
| adapter.GetInfo(&info); |
| return IsWebGPUAdapterBlocklisted(info, options); |
| } |
| |
| WebGPUBlocklistResultImpl IsWebGPUAdapterBlocklisted( |
| const wgpu::AdapterInfo& info, |
| WebGPUBlocklistOptions options) { |
| auto blocklistReason = detail::GetWebGPUAdapterBlocklistReason(info, options); |
| bool blocked = |
| (~options.ignores & blocklistReason) != WebGPUBlocklistReason::None; |
| if (!blocked) { |
| return {.blocked = blocked, .reason = ""}; |
| } |
| return {.blocked = blocked, |
| .reason = detail::BlocklistReasonToString(blocklistReason)}; |
| } |
| |
| } // namespace gpu |