[go: nahoru, domu]

blob: 6de2d30dbc97c5180f48cc0238205b4382a998f4 [file] [log] [blame]
// Copyright 2019 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/command_buffer/service/dawn_context_provider.h"
#include <memory>
#include <vector>
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/debug/dump_without_crashing.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_arguments.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
#include "gpu/command_buffer/service/dawn_instance.h"
#include "gpu/command_buffer/service/dawn_platform.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_switches.h"
#include "third_party/skia/include/gpu/graphite/Context.h"
#include "third_party/skia/include/gpu/graphite/dawn/DawnBackendContext.h"
#include "third_party/skia/include/gpu/graphite/dawn/DawnUtils.h"
#include "ui/gl/gl_implementation.h"
#if BUILDFLAG(IS_WIN)
#include <d3d11_4.h>
#include "third_party/dawn/include/dawn/native/D3D11Backend.h"
#include "ui/gl/direct_composition_support.h"
#include "ui/gl/gl_angle_util_win.h"
#endif
namespace gpu {
namespace {
void LogInfo(WGPULoggingType type, char const* message, void* userdata) {
VLOG(1) << message;
}
void LogError(WGPUErrorType type, char const* message, void* userdata) {
if (type != WGPUErrorType_NoError) {
static_cast<DawnContextProvider*>(userdata)->OnError(type, message);
}
}
void LogDeviceLost(WGPUDeviceLostReason reason,
char const* message,
void* userdata) {
if (reason != WGPUDeviceLostReason_Destroyed) {
static_cast<DawnContextProvider*>(userdata)->OnError(
WGPUErrorType_DeviceLost, message);
}
}
class Platform : public webgpu::DawnPlatform {
public:
using webgpu::DawnPlatform::DawnPlatform;
~Platform() override = default;
const unsigned char* GetTraceCategoryEnabledFlag(
dawn::platform::TraceCategory category) override {
return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
TRACE_DISABLED_BY_DEFAULT("gpu.graphite.dawn"));
}
};
#if BUILDFLAG(IS_WIN)
bool GetANGLED3D11DeviceLUID(LUID* luid) {
// On Windows, query the LUID of ANGLE's adapter.
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
if (!d3d11_device) {
LOG(ERROR) << "Failed to query ID3D11Device from ANGLE.";
return false;
}
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
if (!SUCCEEDED(d3d11_device.As(&dxgi_device))) {
LOG(ERROR) << "Failed to get IDXGIDevice from ANGLE.";
return false;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
if (!SUCCEEDED(dxgi_device->GetAdapter(&dxgi_adapter))) {
LOG(ERROR) << "Failed to get IDXGIAdapter from ANGLE.";
return false;
}
DXGI_ADAPTER_DESC adapter_desc;
if (!SUCCEEDED(dxgi_adapter->GetDesc(&adapter_desc))) {
LOG(ERROR) << "Failed to get DXGI_ADAPTER_DESC from ANGLE.";
return false;
}
*luid = adapter_desc.AdapterLuid;
return true;
}
const char* HRESULTToString(HRESULT result) {
switch (result) {
#define ERROR_CASE(E) \
case E: \
return #E;
ERROR_CASE(DXGI_ERROR_DEVICE_HUNG)
ERROR_CASE(DXGI_ERROR_DEVICE_REMOVED)
ERROR_CASE(DXGI_ERROR_DEVICE_RESET)
ERROR_CASE(DXGI_ERROR_DRIVER_INTERNAL_ERROR)
ERROR_CASE(DXGI_ERROR_INVALID_CALL)
ERROR_CASE(S_OK)
#undef ERROR_CASE
default:
return nullptr;
}
}
#endif // BUILDFLAG(IS_WIN)
} // namespace
std::unique_ptr<DawnContextProvider> DawnContextProvider::Create(
const GpuPreferences& gpu_preferences,
const GpuDriverBugWorkarounds& gpu_driver_workarounds,
webgpu::DawnCachingInterfaceFactory* caching_interface_factory,
CacheBlobCallback callback) {
return DawnContextProvider::CreateWithBackend(
GetDefaultBackendType(), DefaultForceFallbackAdapter(), gpu_preferences,
gpu_driver_workarounds, caching_interface_factory, std::move(callback));
}
std::unique_ptr<DawnContextProvider> DawnContextProvider::CreateWithBackend(
wgpu::BackendType backend_type,
bool force_fallback_adapter,
const GpuPreferences& gpu_preferences,
const GpuDriverBugWorkarounds& gpu_driver_workarounds,
webgpu::DawnCachingInterfaceFactory* caching_interface_factory,
CacheBlobCallback callback) {
auto context_provider =
base::WrapUnique(new DawnContextProvider(caching_interface_factory));
// TODO(rivr): This may return a GPU that is not the active one. Currently
// the only known way to avoid this is platform-specific; e.g. on Mac, create
// a Dawn device, get the actual Metal device from it, and compare against
// MTLCreateSystemDefaultDevice().
if (!context_provider->Initialize(backend_type, force_fallback_adapter,
gpu_preferences, gpu_driver_workarounds,
std::move(callback))) {
context_provider.reset();
}
return context_provider;
}
// static
wgpu::BackendType DawnContextProvider::GetDefaultBackendType() {
const auto switch_value =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kSkiaGraphiteBackend);
if (switch_value == switches::kSkiaGraphiteBackendDawnD3D11) {
return wgpu::BackendType::D3D11;
} else if (switch_value == switches::kSkiaGraphiteBackendDawnD3D12) {
return wgpu::BackendType::D3D12;
} else if (switch_value == switches::kSkiaGraphiteBackendDawnMetal) {
return wgpu::BackendType::Metal;
} else if (switch_value == switches::kSkiaGraphiteBackendDawnSwiftshader ||
switch_value == switches::kSkiaGraphiteBackendDawnVulkan) {
return wgpu::BackendType::Vulkan;
}
if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader) {
return wgpu::BackendType::Vulkan;
}
#if BUILDFLAG(IS_WIN)
return base::FeatureList::IsEnabled(features::kSkiaGraphiteDawnUseD3D12)
? wgpu::BackendType::D3D12
: wgpu::BackendType::D3D11;
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
return wgpu::BackendType::Vulkan;
#elif BUILDFLAG(IS_APPLE)
return wgpu::BackendType::Metal;
#else
NOTREACHED();
return wgpu::BackendType::Null;
#endif
}
// static
bool DawnContextProvider::DefaultForceFallbackAdapter() {
return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kSkiaGraphiteBackend) ==
switches::kSkiaGraphiteBackendDawnSwiftshader ||
gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader;
}
DawnContextProvider::DawnContextProvider(
webgpu::DawnCachingInterfaceFactory* caching_interface_factory)
: caching_interface_factory_(caching_interface_factory) {}
DawnContextProvider::~DawnContextProvider() {
if (device_) {
device_.SetUncapturedErrorCallback(nullptr, nullptr);
device_.SetDeviceLostCallback(nullptr, nullptr);
device_.SetLoggingCallback(nullptr, nullptr);
}
}
bool DawnContextProvider::Initialize(
wgpu::BackendType backend_type,
bool force_fallback_adapter,
const GpuPreferences& gpu_preferences,
const GpuDriverBugWorkarounds& gpu_driver_workarounds,
CacheBlobCallback callback) {
std::unique_ptr<webgpu::DawnCachingInterface> caching_interface;
if (caching_interface_factory_) {
caching_interface = caching_interface_factory_->CreateInstance(
kGraphiteDawnGpuDiskCacheHandle, std::move(callback));
}
platform_ = std::make_unique<Platform>(std::move(caching_interface),
/*uma_prefix=*/"GPU.GraphiteDawn.");
instance_ = webgpu::DawnInstance::Create(platform_.get(), gpu_preferences);
// If a new toggle is added here, ForceDawnTogglesForSkia() which collects
// info for about:gpu should be updated as well.
std::vector<const char*> enabled_toggles;
std::vector<const char*> disabled_toggles;
for (const auto& toggle : gpu_preferences.enabled_dawn_features_list) {
enabled_toggles.push_back(toggle.c_str());
}
for (const auto& toggle : gpu_preferences.disabled_dawn_features_list) {
disabled_toggles.push_back(toggle.c_str());
}
// The following toggles are all device-scoped toggles so it's not necessary
// to pass them when creating the Instance above.
#if DCHECK_IS_ON()
enabled_toggles.push_back("use_user_defined_labels_in_backend");
#else
if (features::kSkiaGraphiteDawnSkipValidation.Get()) {
enabled_toggles.push_back("skip_validation");
}
enabled_toggles.push_back("disable_robustness");
#endif
enabled_toggles.push_back("disable_lazy_clear_for_mapped_at_creation_buffer");
#if BUILDFLAG(IS_APPLE)
// We need MultiPlanarFormatExtendedUsages to copy to/from multiplanar
// texture. And this feature is currently experimental.
enabled_toggles.push_back("allow_unsafe_apis");
#endif // BUILDFLAG(IS_APPLE)
wgpu::DawnTogglesDescriptor toggles_desc;
toggles_desc.enabledToggles = enabled_toggles.data();
toggles_desc.disabledToggles = disabled_toggles.data();
toggles_desc.enabledToggleCount = enabled_toggles.size();
toggles_desc.disabledToggleCount = disabled_toggles.size();
wgpu::DeviceDescriptor descriptor;
descriptor.nextInChain = &toggles_desc;
// TODO(crbug.com/1456492): verify the required features.
std::vector<wgpu::FeatureName> features = {
wgpu::FeatureName::DawnInternalUsages,
wgpu::FeatureName::DawnMultiPlanarFormats,
wgpu::FeatureName::ImplicitDeviceSynchronization,
wgpu::FeatureName::SurfaceCapabilities,
};
wgpu::RequestAdapterOptions adapter_options;
adapter_options.backendType = backend_type;
adapter_options.forceFallbackAdapter = force_fallback_adapter;
if (gpu_driver_workarounds.force_high_performance_gpu) {
adapter_options.powerPreference = wgpu::PowerPreference::HighPerformance;
} else {
adapter_options.powerPreference = wgpu::PowerPreference::LowPower;
}
adapter_options.nextInChain = &toggles_desc;
#if BUILDFLAG(IS_WIN)
if (adapter_options.backendType == wgpu::BackendType::D3D11) {
features.push_back(wgpu::FeatureName::D3D11MultithreadProtected);
}
dawn::native::d3d::RequestAdapterOptionsLUID adapter_options_luid;
if (GetANGLED3D11DeviceLUID(&adapter_options_luid.adapterLUID)) {
// Request the GPU that ANGLE is using if possible.
adapter_options.nextInChain = &adapter_options_luid;
}
dawn::native::d3d11::RequestAdapterOptionsD3D11Device
adapter_options_d3d11_device;
bool share_d3d11_device =
adapter_options.backendType == wgpu::BackendType::D3D11 &&
features::kSkiaGraphiteDawnShareDevice.Get();
if (share_d3d11_device) {
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
CHECK(d3d11_device) << "Query d3d11 device from ANGLE failed.";
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context;
d3d11_device->GetImmediateContext(&d3d11_device_context);
Microsoft::WRL::ComPtr<ID3D11Multithread> d3d11_multithread;
HRESULT hr = d3d11_device_context.As(&d3d11_multithread);
CHECK(SUCCEEDED(hr)) << "Query ID3D11Multithread interface failed: 0x"
<< std::hex << hr;
// Dawn requires enable multithread protection for d3d11 device.
d3d11_multithread->SetMultithreadProtected(TRUE);
adapter_options_d3d11_device.device = std::move(d3d11_device);
adapter_options_d3d11_device.nextInChain = adapter_options.nextInChain;
adapter_options.nextInChain = &adapter_options_d3d11_device;
}
#endif // BUILDFLAG(IS_WIN)
std::vector<dawn::native::Adapter> adapters =
instance_->EnumerateAdapters(&adapter_options);
if (adapters.empty()) {
LOG(ERROR) << "No adapters found.";
return false;
}
const wgpu::FeatureName kOptionalFeatures[] = {
wgpu::FeatureName::DualSourceBlending,
wgpu::FeatureName::MultiPlanarFormatExtendedUsages,
wgpu::FeatureName::MultiPlanarFormatP010,
wgpu::FeatureName::MultiPlanarRenderTargets,
wgpu::FeatureName::Norm16TextureFormats,
wgpu::FeatureName::TransientAttachments,
};
wgpu::Adapter adapter(adapters[0].Get());
for (auto feature : kOptionalFeatures) {
if (!adapter.HasFeature(feature)) {
continue;
}
features.push_back(feature);
// Enabling MSAARenderToSingleSampled causes performance regression without
// TransientAttachments support.
if (feature == wgpu::FeatureName::TransientAttachments &&
adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) {
features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled);
}
}
descriptor.requiredFeatures = features.data();
descriptor.requiredFeatureCount = std::size(features);
std::vector<dawn::native::BackendValidationLevel> backend_validation_levels =
{dawn::native::BackendValidationLevel::Disabled};
if (features::kSkiaGraphiteDawnBackendValidation.Get()) {
backend_validation_levels.push_back(
dawn::native::BackendValidationLevel::Partial);
backend_validation_levels.push_back(
dawn::native::BackendValidationLevel::Full);
}
wgpu::Device device;
// Try create device with backend validation level.
for (auto it = backend_validation_levels.rbegin();
it != backend_validation_levels.rend(); ++it) {
instance_->SetBackendValidationLevel(*it);
device = adapter.CreateDevice(&descriptor);
if (device) {
break;
}
}
if (!device) {
LOG(ERROR) << "Failed to create device.";
return false;
}
device.SetUncapturedErrorCallback(&LogError, static_cast<void*>(this));
device.SetDeviceLostCallback(&LogDeviceLost, static_cast<void*>(this));
device.SetLoggingCallback(&LogInfo, nullptr);
device_ = std::move(device);
backend_type_ = backend_type;
is_vulkan_swiftshader_adapter_ =
backend_type == wgpu::BackendType::Vulkan && force_fallback_adapter;
#if BUILDFLAG(IS_WIN)
// DirectComposition is initialized in ui/gl/init/gl_initializer_win.cc while
// initializing GL. So we need to shutdown it and re-initialize it here with
// the D3D11 device from dawn device.
// TODO(crbug.com/1469283): avoid initializing DirectComposition twice.
if (!share_d3d11_device) {
gl::ShutdownDirectComposition();
if (auto d3d11_device = GetD3D11Device()) {
gl::InitializeDirectComposition(std::move(d3d11_device));
}
}
#endif // BUILDFLAG(IS_WIN)
return true;
}
bool DawnContextProvider::InitializeGraphiteContext(
const skgpu::graphite::ContextOptions& options) {
CHECK(!graphite_context_);
if (device_) {
skgpu::graphite::DawnBackendContext backend_context;
backend_context.fDevice = device_;
backend_context.fQueue = device_.GetQueue();
graphite_context_ =
skgpu::graphite::ContextFactory::MakeDawn(backend_context, options);
}
return !!graphite_context_;
}
wgpu::Instance DawnContextProvider::GetInstance() const {
return instance_->Get();
}
#if BUILDFLAG(IS_WIN)
Microsoft::WRL::ComPtr<ID3D11Device> DawnContextProvider::GetD3D11Device()
const {
if (backend_type() == wgpu::BackendType::D3D11) {
return dawn::native::d3d11::GetD3D11Device(device_.Get());
}
return nullptr;
}
#endif
bool DawnContextProvider::SupportsFeature(wgpu::FeatureName feature) {
if (!device_) {
return false;
}
return device_.HasFeature(feature);
}
absl::optional<error::ContextLostReason> DawnContextProvider::GetResetStatus()
const {
base::AutoLock auto_lock(context_lost_lock_);
return context_lost_reason_;
}
void DawnContextProvider::OnError(WGPUErrorType error_type,
const char* message) {
LOG(ERROR) << message;
static crash_reporter::CrashKeyString<1024> error_key("dawn-error");
error_key.Set(message);
#if BUILDFLAG(IS_WIN)
if (auto d3d11_device = GetD3D11Device()) {
static crash_reporter::CrashKeyString<64> reason_message_key(
"d3d11-device-removed-reason");
HRESULT result = d3d11_device->GetDeviceRemovedReason();
if (const char* result_string = HRESULTToString(result)) {
LOG(ERROR) << "Device removed reason: " << result_string;
reason_message_key.Set(result_string);
} else {
auto unknown_error = base::StringPrintf("Unknown error(0x%08lX)", result);
LOG(ERROR) << "Device removed reason: " << unknown_error;
reason_message_key.Set(unknown_error.c_str());
}
}
#endif
base::debug::DumpWithoutCrashing();
base::AutoLock auto_lock(context_lost_lock_);
if (context_lost_reason_.has_value()) {
return;
}
if (error_type == WGPUErrorType_OutOfMemory) {
context_lost_reason_ = error::kOutOfMemory;
} else if (error_type == WGPUErrorType_Validation) {
context_lost_reason_ = error::kGuilty;
} else {
context_lost_reason_ = error::kUnknown;
}
}
} // namespace gpu