blob: 94f568b17a247a224bdfde53c08e7b14d7e55782 [file] [log] [blame]
// 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