| // 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 <algorithm> | |
| #include <atomic> | |
| #include <memory> | |
| #include <optional> | |
| #include <string_view> | |
| #include <variant> | |
| #include <vector> | |
| #include "base/check_op.h" | |
| #include "base/command_line.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/dcheck_is_on.h" | |
| #include "base/debug/crash_logging.h" | |
| #include "base/debug/dump_without_crashing.h" | |
| #include "base/feature_list.h" | |
| #include "base/functional/bind.h" | |
| #include "base/logging.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/metrics/histogram_functions.h" | |
| #include "base/no_destructor.h" | |
| #include "base/notreached.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/task/single_thread_task_runner.h" | |
| #include "base/thread_annotations.h" | |
| #include "base/threading/platform_thread.h" | |
| #include "base/threading/thread_checker.h" | |
| #include "base/trace_event/memory_dump_manager.h" | |
| #include "base/trace_event/memory_dump_provider.h" | |
| #include "base/trace_event/process_memory_dump.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/command_buffer/service/skia_utils.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 "gpu/config/gpu_util.h" | |
| #include "third_party/abseil-cpp/absl/functional/overload.h" | |
| #include "third_party/dawn/include/dawn/webgpu_cpp_print.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_features.h" | |
| #include "ui/gl/gl_implementation.h" | |
| #if BUILDFLAG(IS_WIN) | |
| #include <d3d11_4.h> | |
| #include <d3d11on12.h> | |
| #include "third_party/dawn/include/dawn/native/D3D11Backend.h" | |
| #include "third_party/dawn/include/dawn/native/D3D12Backend.h" | |
| #include "ui/gl/direct_composition_support.h" | |
| #include "ui/gl/gl_angle_util_win.h" | |
| #endif | |
| #if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) | |
| #include "third_party/dawn/include/dawn/native/OpenGLBackend.h" | |
| #include "ui/gl/gl_bindings.h" | |
| #include "ui/gl/gl_context.h" | |
| #include "ui/gl/gl_surface_egl.h" | |
| #endif | |
| namespace gpu { | |
| namespace { | |
| // Used as a flag to test dawn initialization failure. | |
| BASE_FEATURE(kForceDawnInitializeFailure, base::FEATURE_DISABLED_BY_DEFAULT); | |
| // Controls if ShaderF16 Dawn feature is enabled for Graphite. | |
| BASE_FEATURE(kSkiaGraphiteF16, base::FEATURE_DISABLED_BY_DEFAULT); | |
| // Sets crash key in thread safe manner. This should be used for any crash keys | |
| // set from dawn error or device lost callbacks that may run on multiple | |
| // threads. | |
| template <uint32_t KeySize> | |
| void SetCrashKeyThreadSafe(crash_reporter::CrashKeyString<KeySize>& crash_key, | |
| std::string_view message) { | |
| static base::NoDestructor<base::Lock> lock; | |
| base::AutoLock auto_lock(*lock.get()); | |
| crash_key.Set(message); | |
| } | |
| void AppendDawnErrorCrashKey(std::string_view message) { | |
| static base::NoDestructor<base::Lock> lock; | |
| static base::NoDestructor<std::string> error_msg; | |
| static crash_reporter::CrashKeyString<1024> error_key("dawn-error"); | |
| base::AutoLock auto_lock(*lock.get()); | |
| if (!error_msg->empty()) { | |
| error_msg->append("\n"); | |
| } | |
| error_msg->append(message); | |
| error_key.Set(*error_msg); | |
| } | |
| // Different versions of DumpWithoutCrashing for different reasons. | |
| // Deliberately prevent inlining so that the crash report's call stack can | |
| // distinguish between them. | |
| #if BUILDFLAG(IS_WIN) | |
| NOINLINE NOOPT void DumpWithoutCrashingOnDXGIError(wgpu::ErrorType error_type, | |
| std::string_view message) { | |
| LOG(ERROR) << "DXGI Error: " << message; | |
| if (features::SkiaGraphiteDawnDumpWCOnD3DError()) { | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| } | |
| NOINLINE NOOPT void DumpWithoutCrashingOnD3D11DebugLayerError( | |
| wgpu::ErrorType error_type, | |
| std::string_view message) { | |
| LOG(ERROR) << message; | |
| if (features::SkiaGraphiteDawnDumpWCOnD3DError()) { | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| } | |
| #endif | |
| NOINLINE NOOPT void DumpWithoutCrashingOnGenericError( | |
| wgpu::ErrorType error_type, | |
| std::string_view message) { | |
| LOG(ERROR) << message; | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| // These values are persisted to logs. Entries should not be renumbered and | |
| // numeric values should never be reused. | |
| // | |
| // LINT.IfChange(GraphiteDawnError) | |
| enum class GraphiteDawnError { | |
| kOther = 0, | |
| kDXGIError = 1, | |
| kOutOfMemory = 2, | |
| kValidation = 3, | |
| kMaxValue = kValidation, | |
| }; | |
| // LINT.ThenChange(//tools/metrics/histograms/metadata/gpu/enums.xml:GraphiteDawnError) | |
| void EmitErrorReport(wgpu::ErrorType error_type, std::string_view message) { | |
| AppendDawnErrorCrashKey(message); | |
| std::optional<GraphiteDawnError> uma_error; | |
| #if BUILDFLAG(IS_WIN) | |
| // Errors reported after a DXGI error are generally fallout from device | |
| // removal and should not generate additional crash reports. | |
| static std::atomic_flag dxgi_error_received = ATOMIC_FLAG_INIT; | |
| if (message.find("DXGI_ERROR") != std::string_view::npos) { | |
| uma_error = GraphiteDawnError::kDXGIError; | |
| if (!dxgi_error_received.test_and_set()) { | |
| DumpWithoutCrashingOnDXGIError(error_type, message); | |
| } | |
| } else if (!dxgi_error_received.test()) { | |
| if (message.find("The D3D11 debug layer") != std::string_view::npos) { | |
| DumpWithoutCrashingOnD3D11DebugLayerError(error_type, message); | |
| } else { | |
| DumpWithoutCrashingOnGenericError(error_type, message); | |
| } | |
| } | |
| #else | |
| DumpWithoutCrashingOnGenericError(error_type, message); | |
| #endif | |
| // Emit only the 1st error to UMA. | |
| static std::atomic_flag uma_error_emitted = ATOMIC_FLAG_INIT; | |
| if (uma_error_emitted.test_and_set()) { | |
| return; | |
| } | |
| if (!uma_error.has_value()) { | |
| switch (error_type) { | |
| case wgpu::ErrorType::OutOfMemory: | |
| uma_error = GraphiteDawnError::kOutOfMemory; | |
| break; | |
| case wgpu::ErrorType::Validation: | |
| uma_error = GraphiteDawnError::kValidation; | |
| break; | |
| default: | |
| uma_error = GraphiteDawnError::kOther; | |
| break; | |
| } | |
| } | |
| base::UmaHistogramEnumeration("GPU.GraphiteDawn.Error", *uma_error); | |
| } | |
| // NOTE: Update the toggles in GpuInfoCollector whenever a toggle is disabled | |
| // here. | |
| std::vector<const char*> GetDisabledToggles( | |
| const GpuPreferences& gpu_preferences, | |
| const GpuDriverBugWorkarounds& workarounds) { | |
| std::vector<const char*> disabled_toggles; | |
| for (const auto& toggle : gpu_preferences.disabled_dawn_features_list) { | |
| disabled_toggles.push_back(toggle.c_str()); | |
| } | |
| if (!features::SkiaGraphiteDawnBackendDebugLabels()) { | |
| // Note: This toggle needs to be explicitly enabled or disabled. | |
| disabled_toggles.push_back("use_user_defined_labels_in_backend"); | |
| } | |
| // TODO(crbug.com/486866985): Remove this once dynamic rendering perf | |
| // regressions are investigated. | |
| disabled_toggles.push_back("vulkan_use_dynamic_rendering"); | |
| if (features::SkiaGraphiteDawnSkipValidation()) { | |
| disabled_toggles.push_back("enable_spirv_validation"); | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| if (!features::SkiaGraphiteDawnD3D11DelayFlush() || | |
| workarounds.disable_graphite_d3d11_delay_flush) { | |
| disabled_toggles.push_back("d3d11_delay_flush_to_gpu"); | |
| } | |
| #endif | |
| return disabled_toggles; | |
| } | |
| // NOTE: Update the toggles in GpuInfoCollector whenever a toggle is enabled | |
| // here. | |
| std::vector<const char*> GetEnabledToggles( | |
| wgpu::BackendType backend_type, | |
| bool force_fallback_adapter, | |
| const GpuPreferences& 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; | |
| for (const auto& toggle : gpu_preferences.enabled_dawn_features_list) { | |
| enabled_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 (features::SkiaGraphiteDawnBackendDebugLabels()) { | |
| // Note: This toggle needs to be explicitly enabled or disabled. | |
| enabled_toggles.push_back("use_user_defined_labels_in_backend"); | |
| } | |
| if (features::SkiaGraphiteDawnSkipValidation()) { | |
| enabled_toggles.push_back("skip_validation"); | |
| } | |
| if (features::SkiaGraphiteDawnEnableAutoMap()) { | |
| // Tell Dawn to automatically map buffers when they are not in use by the | |
| // GPU. This allows Skia to access buffer contents on the CPU without | |
| // blocking, making operations faster. | |
| enabled_toggles.push_back("auto_map_backend_buffer"); | |
| } | |
| enabled_toggles.push_back("disable_robustness"); | |
| enabled_toggles.push_back("disable_lazy_clear_for_mapped_at_creation_buffer"); | |
| enabled_toggles.push_back("dump_shaders_on_failure"); | |
| #if BUILDFLAG(IS_WIN) | |
| if (backend_type == wgpu::BackendType::D3D11) { | |
| // Use packed D24_UNORM_S8_UINT DXGI format for Depth24PlusStencil8 | |
| // format. | |
| enabled_toggles.push_back("use_packed_depth24_unorm_stencil8_format"); | |
| if (features::SkiaGraphiteDawnD3D11DelayFlush()) { | |
| // Tell Dawn to defer sending commands to GPU until swapchain's Present. | |
| // This will batch the commands better. | |
| enabled_toggles.push_back("d3d11_delay_flush_to_gpu"); | |
| } | |
| } | |
| if (backend_type == wgpu::BackendType::D3D11 || | |
| backend_type == wgpu::BackendType::D3D12) { | |
| if (features::SkiaGraphiteDawnDisableD3DShaderOptimizations()) { | |
| enabled_toggles.push_back("d3d_skip_shader_optimizations"); | |
| } | |
| } | |
| #endif | |
| if (backend_type == wgpu::BackendType::Vulkan) { | |
| #if BUILDFLAG(IS_ANDROID) | |
| // Enable this toggle for all Android devices suspecting vulkan image size | |
| // mismatch causing SharedTextureMemory creation failures, leading to | |
| // promise image creation failures. See https://crbug.com/377935752 for | |
| // details. | |
| enabled_toggles.push_back( | |
| "ignore_imported_ahardwarebuffer_vulkan_image_size"); | |
| #endif | |
| // Use a single VkPipelineCache inside dawn. | |
| enabled_toggles.push_back("vulkan_monolithic_pipeline_cache"); | |
| } | |
| // Skip expensive swiftshader vkCmdDraw* for tests. | |
| // TODO(penghuang): rename kDisableGLDrawingForTests to | |
| // kDisableGpuDrawingForTests | |
| // TODO(crbug.com/407497928): Enable this toggle over GpuInfoCollector. | |
| auto* command_line = base::CommandLine::ForCurrentProcess(); | |
| if (backend_type == wgpu::BackendType::Vulkan && force_fallback_adapter && | |
| command_line->HasSwitch(switches::kDisableGLDrawingForTests)) { | |
| enabled_toggles.push_back("vulkan_skip_draw"); | |
| } | |
| return enabled_toggles; | |
| } | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures( | |
| wgpu::BackendType backend_type, | |
| wgpu::Adapter adapter) { | |
| std::vector<wgpu::FeatureName> features = { | |
| wgpu::FeatureName::DawnInternalUsages, | |
| wgpu::FeatureName::ImplicitDeviceSynchronization, | |
| #if BUILDFLAG(IS_ANDROID) | |
| wgpu::FeatureName::TextureCompressionETC2, | |
| #endif | |
| }; | |
| if (backend_type == wgpu::BackendType::Vulkan) { | |
| #if BUILDFLAG(IS_ANDROID) | |
| features.push_back(wgpu::FeatureName::StaticSamplers); | |
| features.push_back(wgpu::FeatureName::YCbCrVulkanSamplers); | |
| #endif | |
| features.push_back(wgpu::FeatureName::DawnDeviceAllocatorControl); | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| if (backend_type == wgpu::BackendType::D3D11) { | |
| features.push_back(wgpu::FeatureName::D3D11MultithreadProtected); | |
| } | |
| #endif | |
| if (backend_type == wgpu::BackendType::OpenGLES && | |
| gl::GetANGLEImplementation() == gl::ANGLEImplementation::kOpenGL) { | |
| features.push_back(wgpu::FeatureName::ANGLETextureSharing); | |
| } | |
| if (base::FeatureList::IsEnabled(kSkiaGraphiteF16) && | |
| adapter.HasFeature(wgpu::FeatureName::ShaderF16)) { | |
| features.push_back(wgpu::FeatureName::ShaderF16); | |
| } | |
| constexpr wgpu::FeatureName kOptionalFeatures[] = { | |
| wgpu::FeatureName::BGRA8UnormStorage, | |
| wgpu::FeatureName::BufferMapExtendedUsages, | |
| wgpu::FeatureName::DawnAllowUndefinedLoadStoreOp, | |
| wgpu::FeatureName::DawnMultiPlanarFormats, | |
| wgpu::FeatureName::DualSourceBlending, | |
| wgpu::FeatureName::FramebufferFetch, | |
| wgpu::FeatureName::MultiPlanarFormatExtendedUsages, | |
| wgpu::FeatureName::MultiPlanarFormatNv16, | |
| wgpu::FeatureName::MultiPlanarFormatNv24, | |
| wgpu::FeatureName::MultiPlanarFormatP010, | |
| wgpu::FeatureName::MultiPlanarFormatP210, | |
| wgpu::FeatureName::MultiPlanarFormatP410, | |
| wgpu::FeatureName::MultiPlanarFormatNv12a, | |
| wgpu::FeatureName::MultiPlanarRenderTargets, | |
| wgpu::FeatureName::Unorm16TextureFormats, | |
| // The following features are always supported by the the Metal backend on | |
| // the Mac versions on which Chrome runs. | |
| wgpu::FeatureName::SharedTextureMemoryIOSurface, | |
| wgpu::FeatureName::SharedFenceMTLSharedEvent, | |
| // The following features are always supported when running on the Vulkan | |
| // backend on Android. | |
| wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer, | |
| wgpu::FeatureName::SharedFenceSyncFD, | |
| wgpu::FeatureName::RenderPassRenderArea, | |
| wgpu::FeatureName::OpaqueYCbCrAndroidForExternalTexture, | |
| // The following features are always supported by the the D3D backends. | |
| wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D, | |
| wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle, | |
| wgpu::FeatureName::SharedFenceDXGISharedHandle, | |
| wgpu::FeatureName::SharedTextureMemoryD3D12Resource, | |
| // The following feature is always supported by the the D3D12 backend. | |
| wgpu::FeatureName::SharedBufferMemoryD3D12Resource, | |
| wgpu::FeatureName::TransientAttachments, | |
| wgpu::FeatureName::DawnTexelCopyBufferRowAlignment, | |
| wgpu::FeatureName::FlexibleTextureViews, | |
| }; | |
| for (auto feature : kOptionalFeatures) { | |
| if (!adapter.HasFeature(feature)) { | |
| continue; | |
| } | |
| features.push_back(feature); | |
| } | |
| // Both MSAARenderToSingleSampled and DawnLoadResolveTexture are used to | |
| // unresolve a single-sampled texture for multi-sampled rendering. | |
| // We prefer the former if it's available, | |
| if (adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) { | |
| features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled); | |
| } else if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) { | |
| features.push_back(wgpu::FeatureName::DawnLoadResolveTexture); | |
| if (adapter.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture)) { | |
| features.push_back(wgpu::FeatureName::DawnPartialLoadResolveTexture); | |
| } | |
| } | |
| return features; | |
| } | |
| #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; | |
| HRESULT hr = d3d11_device.As(&dxgi_device); | |
| CHECK_EQ(hr, S_OK); | |
| Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter; | |
| hr = dxgi_device->GetAdapter(&dxgi_adapter); | |
| CHECK_EQ(hr, S_OK); | |
| 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; | |
| } | |
| bool IsD3D11DebugLayerEnabled( | |
| const Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device) { | |
| Microsoft::WRL::ComPtr<ID3D11Debug> d3d11_debug; | |
| return SUCCEEDED(d3d11_device.As(&d3d11_debug)); | |
| } | |
| 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) | |
| const char* BackendTypeToString(wgpu::BackendType backend_type) { | |
| switch (backend_type) { | |
| case wgpu::BackendType::D3D11: | |
| return "D3D11"; | |
| case wgpu::BackendType::D3D12: | |
| return "D3D12"; | |
| case wgpu::BackendType::Metal: | |
| return "Metal"; | |
| case wgpu::BackendType::OpenGL: | |
| return "OpenGL"; | |
| case wgpu::BackendType::OpenGLES: | |
| return "OpenGLES"; | |
| case wgpu::BackendType::Vulkan: | |
| return "Vulkan"; | |
| default: | |
| CHECK(false); | |
| } | |
| } | |
| } // namespace | |
| // static | |
| wgpu::BackendType DawnContextProvider::GetDefaultBackendType() { | |
| const auto switch_value = | |
| base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| switches::kSkiaGraphiteDawnBackend); | |
| if (switch_value == switches::kSkiaGraphiteDawnBackendD3D11) { | |
| return wgpu::BackendType::D3D11; | |
| } else if (switch_value == switches::kSkiaGraphiteDawnBackendD3D12) { | |
| return wgpu::BackendType::D3D12; | |
| } else if (switch_value == switches::kSkiaGraphiteDawnBackendMetal) { | |
| return wgpu::BackendType::Metal; | |
| } else if (switch_value == switches::kSkiaGraphiteDawnBackendOpenGLES) { | |
| return wgpu::BackendType::OpenGLES; | |
| } else if (switch_value == switches::kSkiaGraphiteDawnBackendSwiftshader || | |
| switch_value == switches::kSkiaGraphiteDawnBackendVulkan) { | |
| return wgpu::BackendType::Vulkan; | |
| } | |
| if (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader) { | |
| return wgpu::BackendType::Vulkan; | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| return wgpu::BackendType::D3D11; | |
| #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) | |
| return wgpu::BackendType::Vulkan; | |
| #elif BUILDFLAG(IS_APPLE) | |
| return wgpu::BackendType::Metal; | |
| #else | |
| NOTREACHED(); | |
| #endif | |
| } | |
| // static | |
| bool DawnContextProvider::DefaultForceFallbackAdapter() { | |
| return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| switches::kSkiaGraphiteDawnBackend) == | |
| switches::kSkiaGraphiteDawnBackendSwiftshader || | |
| gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader; | |
| } | |
| // static | |
| bool DawnContextProvider::DefaultValidateAdapterFn(wgpu::BackendType, | |
| wgpu::Adapter) { | |
| return true; | |
| } | |
| // Owns the dawn instance/adapter/device so that it's lifetime is not linked to | |
| // a specific DawnContextProvider. | |
| class DawnSharedContext : public base::RefCountedThreadSafe<DawnSharedContext>, | |
| public base::trace_event::MemoryDumpProvider, | |
| public GraphiteSharedContext::Delegate { | |
| public: | |
| DawnSharedContext(gl::ProgressReporter* progress_reporter, | |
| bool thread_safe_graphite_context); | |
| bool Initialize(wgpu::BackendType backend_type, | |
| bool force_fallback_adapter, | |
| const GpuPreferences& gpu_preferences, | |
| const GpuDriverBugWorkarounds& workarounds, | |
| DawnContextProvider::ValidateAdapterFn validate_adapter_fn); | |
| void SetCachingInterface( | |
| std::unique_ptr<webgpu::DawnInMemoryCache> dawn_caching_interface); | |
| void SetCachingInterface(scoped_refptr<GpuPersistentCache> persistent_cache); | |
| wgpu::Device GetDevice() const { return device_; } | |
| wgpu::BackendType backend_type() const { return backend_type_; } | |
| bool is_vulkan_swiftshader_adapter() const { | |
| return is_vulkan_swiftshader_adapter_; | |
| } | |
| wgpu::Adapter GetAdapter() const { return adapter_; } | |
| wgpu::Instance GetInstance() const { return instance_->Get(); } | |
| webgpu::DawnPlatform* GetDawnPlatform() { return &platform_; } | |
| #if BUILDFLAG(IS_WIN) | |
| Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const { | |
| if (backend_type() == wgpu::BackendType::D3D11) { | |
| return dawn::native::d3d11::GetD3D11Device(device_.Get()); | |
| } | |
| // Even if the backend is D3D12, use D3D11On12 to get a D3D11 device. This | |
| // will bridge the gap in places where SharedImage code uses D3D11 for | |
| // internal operations like copying to staging textures. | |
| // TODO(crbug.com/425864542) Transition shared image copy code to D3D12. | |
| if (backend_type() == wgpu::BackendType::D3D12 && | |
| base::FeatureList::IsEnabled(features::kDCompOnD3D12)) { | |
| // If the backend is D3D12, we can use D3D11On12 device. | |
| Microsoft::WRL::ComPtr<ID3D11On12Device> d3d11on12_device = | |
| dawn::native::d3d12::GetOrCreateD3D11On12Device(device_.Get()); | |
| // If the user's platform version does not support ID3D11On12Device2, | |
| // return nullptr. This will prevent Chromium from using | |
| // D3D11On12 APIs. | |
| Microsoft::WRL::ComPtr<ID3D11On12Device2> d3d11on12_device_2; | |
| if (FAILED(d3d11on12_device.As(&d3d11on12_device_2))) { | |
| return nullptr; | |
| } | |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device; | |
| HRESULT hr = d3d11on12_device.As(&d3d11_device); | |
| CHECK_EQ(hr, S_OK); | |
| return d3d11_device; | |
| } | |
| return nullptr; | |
| } | |
| Microsoft::WRL::ComPtr<ID3D12CommandQueue> GetD3D12CommandQueue() const { | |
| if (backend_type() == wgpu::BackendType::D3D12) { | |
| return dawn::native::d3d12::GetD3D12CommandQueue(device_.Get()); | |
| } | |
| return nullptr; | |
| } | |
| void FlushD3D11CommandsIfDelayed() const { | |
| if (backend_type() != wgpu::BackendType::D3D11) { | |
| return; | |
| } | |
| // This function is meant for delayed flush option. | |
| if (!use_d3d11_delay_flush_) { | |
| return; | |
| } | |
| TRACE_EVENT0("gpu", "DawnSharedContext::FlushD3D11Commands"); | |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = | |
| dawn::native::d3d11::GetD3D11Device(device_.Get()); | |
| if (!d3d11_device) { | |
| return; | |
| } | |
| Microsoft::WRL::ComPtr<ID3D11DeviceContext> context; | |
| d3d11_device->GetImmediateContext(&context); | |
| context->Flush(); | |
| } | |
| #endif | |
| // GraphiteSharedContext::Delegate implementation: | |
| void MarkContextLost(error::ContextLostReason reason) override; | |
| bool IsContextLost() const override; | |
| void FlushBackend() override; | |
| void ReportProgress() override; | |
| std::optional<error::ContextLostReason> GetResetStatus() const; | |
| std::unique_ptr<GraphiteSharedContext> CreateGraphiteSharedContext( | |
| const skgpu::graphite::ContextOptions& options, | |
| GpuProcessShmCount* use_shader_cache_shm_count, | |
| bool is_thread_safe) { | |
| if (!device_) { | |
| return nullptr; | |
| } | |
| skgpu::graphite::DawnBackendContext backend_context; | |
| backend_context.fInstance = GetInstance(); | |
| backend_context.fDevice = device_; | |
| backend_context.fQueue = device_.GetQueue(); | |
| std::unique_ptr<skgpu::graphite::Context> graphite_context = | |
| skgpu::graphite::ContextFactory::MakeDawn(backend_context, options); | |
| if (!graphite_context) { | |
| return nullptr; | |
| } | |
| return std::make_unique<GraphiteSharedContext>( | |
| std::move(graphite_context), use_shader_cache_shm_count, is_thread_safe, | |
| features::SkiaGraphiteMaxPendingRecordings(), | |
| features::SkiaGraphiteMaxTimeBetweenSubmits(), | |
| // DawnSharedContext is guaranteed to outlive GraphiteSharedContext. | |
| this); | |
| } | |
| bool use_thread_safe_graphite_context() const { | |
| return use_thread_safe_graphite_context_; | |
| } | |
| GraphiteSharedContext* GetThreadSafeGraphiteSharedContext() const { | |
| CHECK(use_thread_safe_graphite_context()); | |
| return thread_safe_graphite_shared_context_.get(); | |
| } | |
| bool InitializeThreadSafeGraphiteContext( | |
| const skgpu::graphite::ContextOptions& options, | |
| GpuProcessShmCount* use_shader_cache_shm_count) { | |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_); | |
| CHECK(use_thread_safe_graphite_context()); | |
| CHECK(!thread_safe_graphite_shared_context_); | |
| thread_safe_graphite_shared_context_ = CreateGraphiteSharedContext( | |
| options, use_shader_cache_shm_count, /*is_thread_safe=*/true); | |
| return !!thread_safe_graphite_shared_context_; | |
| } | |
| private: | |
| friend class base::RefCountedThreadSafe<DawnSharedContext>; | |
| // Provided to wgpu::Device as logging callback. | |
| static void DeviceLogInfo(wgpu::LoggingType type, | |
| wgpu::StringView message, | |
| DawnSharedContext* shared_context) { | |
| CHECK(shared_context); | |
| std::string_view view = {message.data, message.length}; | |
| switch (static_cast<wgpu::LoggingType>(type)) { | |
| case wgpu::LoggingType::Warning: | |
| LOG(WARNING) << view; | |
| break; | |
| case wgpu::LoggingType::Error: | |
| // Trigger context loss. | |
| shared_context->OnError(wgpu::ErrorType::Internal, view); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| // Provided to wgpu::Instance as logging callback. | |
| static void InstanceLogInfo(wgpu::LoggingType type, | |
| wgpu::StringView message, | |
| DawnSharedContext* shared_context) { | |
| CHECK(shared_context); | |
| std::string_view view = {message.data, message.length}; | |
| if (!shared_context->device_) { | |
| // If device hasn't been created yet. Saving the message so that if there | |
| // is any init failure afterward, we can include the messages in the | |
| // LogInitFailure()'s report. | |
| shared_context->StoreInitLoggingMessage(view); | |
| } | |
| switch (static_cast<wgpu::LoggingType>(type)) { | |
| case wgpu::LoggingType::Warning: | |
| LOG(WARNING) << view; | |
| break; | |
| case wgpu::LoggingType::Error: | |
| LOG(ERROR) << view; | |
| if (shared_context->device_) { | |
| // Only DwC if the device is already created. | |
| // We don't need to DwC for any error before the device is initialized | |
| // because LogInitFailure() would handle them instead. | |
| // Note: We don't trigger context loss here for now since most of the | |
| // errors reported via instance callback is related to Surface | |
| // creation. In that case, instead of triggering context loss, we | |
| // should let the call sites handle them gracefully. | |
| AppendDawnErrorCrashKey(view); | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| ~DawnSharedContext() override; | |
| void OnError(wgpu::ErrorType error_type, wgpu::StringView message); | |
| void StoreInitLoggingMessage(std::string_view message) { | |
| init_logging_msgs_.append(message); | |
| init_logging_msgs_.append("\n"); | |
| } | |
| void LogInitFailure(std::string_view reason, | |
| bool generate_crash_report, | |
| wgpu::BackendType backend_type, | |
| bool force_fallback_adapter) { | |
| LOG(ERROR) << reason; | |
| if (!generate_crash_report) { | |
| return; | |
| } | |
| SCOPED_CRASH_KEY_STRING256("dawn-shared-context", "init-failure", reason); | |
| SCOPED_CRASH_KEY_STRING32("dawn-shared-context", "backend-type", | |
| BackendTypeToString(backend_type)); | |
| SCOPED_CRASH_KEY_BOOL("dawn-shared-context", "fallback-adapter", | |
| force_fallback_adapter); | |
| // Also include any logging messages collected during the initialization. | |
| SCOPED_CRASH_KEY_STRING1024("dawn-shared-context", "init-logging-msgs", | |
| init_logging_msgs_); | |
| init_logging_msgs_.clear(); | |
| base::debug::DumpWithoutCrashing(); | |
| } | |
| // base::trace_event::MemoryDumpProvider implementation: | |
| bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, | |
| base::trace_event::ProcessMemoryDump* pmd) override; | |
| webgpu::DawnCachingInterface* GetCache() const { | |
| return std::visit<webgpu::DawnCachingInterface*>( | |
| absl::Overload{ | |
| [](std::monostate) { return nullptr; }, | |
| [](const auto& cache) { return cache.get(); }, | |
| }, | |
| cache_); | |
| } | |
| std::variant<std::monostate, | |
| std::unique_ptr<webgpu::DawnInMemoryCache>, | |
| scoped_refptr<GpuPersistentCache>> | |
| cache_; | |
| webgpu::DawnPlatform platform_; | |
| std::unique_ptr<webgpu::DawnInstance> instance_; | |
| wgpu::Adapter adapter_; | |
| wgpu::Device device_; | |
| wgpu::BackendType backend_type_; | |
| // Store logging messages collected during device initialization. | |
| std::string init_logging_msgs_; | |
| bool is_vulkan_swiftshader_adapter_ = false; | |
| bool registered_memory_dump_provider_ = false; | |
| #if BUILDFLAG(IS_WIN) | |
| bool use_d3d11_delay_flush_ = false; | |
| #endif | |
| // If true, both GpuMain and CompositorGpuThread share the same | |
| // GraphiteSharedContext which is created lazily. If false, | |
| // DawnContextProvider owns GraphiteSharedContext and each DawnContextProvider | |
| // (i.e. each thread) has its own GraphiteSharedContext. | |
| const bool use_thread_safe_graphite_context_; | |
| std::unique_ptr<GraphiteSharedContext> thread_safe_graphite_shared_context_; | |
| mutable base::Lock context_lost_lock_; | |
| std::optional<error::ContextLostReason> context_lost_reason_ | |
| GUARDED_BY(context_lost_lock_); | |
| THREAD_CHECKER(main_thread_checker_); | |
| }; | |
| DawnSharedContext::DawnSharedContext(gl::ProgressReporter* progress_reporter, | |
| bool use_thread_safe_graphite_context) | |
| : platform_(progress_reporter, | |
| /*uma_prefix=*/"GPU.GraphiteDawn.", | |
| /*record_cache_count_uma=*/true), | |
| use_thread_safe_graphite_context_(use_thread_safe_graphite_context) { | |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_); | |
| } | |
| DawnSharedContext::~DawnSharedContext() { | |
| DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_); | |
| if (device_) { | |
| if (registered_memory_dump_provider_) { | |
| base::trace_event::MemoryDumpManager::GetInstance() | |
| ->UnregisterDumpProvider(this); | |
| } | |
| device_.SetLoggingCallback([](wgpu::LoggingType, wgpu::StringView) {}); | |
| // Destroy GraphiteSharedContext and skgpu::graphite::Context before | |
| // device_, on which skgpu::graphite::Context is created. | |
| thread_safe_graphite_shared_context_ = nullptr; | |
| // Destroy the device now so that the lost callback, which references this | |
| // class, is fired now before we clean up the rest of this class. | |
| device_.Destroy(); | |
| } | |
| if (instance_) { | |
| instance_->DisconnectDawnPlatform(); | |
| } | |
| } | |
| namespace { | |
| // Dawn Graphite adapter feature level for metrics. | |
| // | |
| // See also: webgpu.h:WGPUFeatureLevel | |
| // | |
| // These values are persisted to logs. Entries should not be renumbered and | |
| // numeric values should never be reused. | |
| // | |
| // LINT.IfChange(DawnAdapterFeatureLevel) | |
| enum class DawnAdapterFeatureLevel { | |
| kUnknown = 0, | |
| kCompatibility = 1, | |
| kCore = 2, | |
| kMaxValue = kCore, | |
| }; | |
| // LINT.ThenChange(//tools/metrics/histograms/metadata/gpu/enums.xml:DawnAdapterFeatureLevel) | |
| DawnAdapterFeatureLevel DawnAdapterFeatureLevelFromWGPU( | |
| wgpu::FeatureLevel level) { | |
| switch (level) { | |
| case wgpu::FeatureLevel::Compatibility: | |
| return DawnAdapterFeatureLevel::kCompatibility; | |
| case wgpu::FeatureLevel::Core: | |
| return DawnAdapterFeatureLevel::kCore; | |
| default: | |
| return DawnAdapterFeatureLevel::kUnknown; | |
| } | |
| } | |
| } // namespace | |
| bool DawnSharedContext::Initialize( | |
| wgpu::BackendType backend_type, | |
| bool force_fallback_adapter, | |
| const GpuPreferences& gpu_preferences, | |
| const GpuDriverBugWorkarounds& workarounds, | |
| DawnContextProvider::ValidateAdapterFn validate_adapter_fn) { | |
| // Make Dawn experimental API and WGSL features available since access to this | |
| // instance doesn't exit the GPU process. | |
| // LogInfo will be used to receive instance level errors. For example failures | |
| // of loading libraries, initializing backend, etc | |
| dawn::native::DawnInstanceDescriptor dawn_instance_desc; | |
| dawn_instance_desc.SetLoggingCallback(&DawnSharedContext::InstanceLogInfo, | |
| this); | |
| instance_ = webgpu::DawnInstance::Create(&platform_, gpu_preferences, | |
| webgpu::SafetyLevel::kUnsafe, | |
| &dawn_instance_desc); | |
| std::vector<const char*> enabled_toggles = | |
| GetEnabledToggles(backend_type, force_fallback_adapter, gpu_preferences); | |
| std::vector<const char*> disabled_toggles = | |
| GetDisabledToggles(gpu_preferences, workarounds); | |
| 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::RequestAdapterOptions adapter_options; | |
| adapter_options.backendType = backend_type; | |
| adapter_options.forceFallbackAdapter = force_fallback_adapter; | |
| if (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) | |
| dawn::native::d3d::RequestAdapterOptionsLUID adapter_options_luid; | |
| if ((adapter_options.backendType == wgpu::BackendType::D3D11 || | |
| adapter_options.backendType == wgpu::BackendType::D3D12) && | |
| GetANGLED3D11DeviceLUID(&adapter_options_luid.adapterLUID)) { | |
| // Request the GPU that ANGLE is using if possible. | |
| adapter_options_luid.nextInChain = adapter_options.nextInChain; | |
| adapter_options.nextInChain = &adapter_options_luid; | |
| } | |
| // Share D3D11 device with ANGLE to reduce synchronization overhead. | |
| dawn::native::d3d11::RequestAdapterOptionsD3D11Device | |
| adapter_options_d3d11_device; | |
| if (adapter_options.backendType == wgpu::BackendType::D3D11) { | |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device = | |
| gl::QueryD3D11DeviceObjectFromANGLE(); | |
| CHECK(d3d11_device) << "Query d3d11 device from ANGLE failed."; | |
| static crash_reporter::CrashKeyString<16> feature_level_key( | |
| "d3d11-feature-level"); | |
| std::string feature_level = | |
| D3DFeatureLevelToString(d3d11_device->GetFeatureLevel()); | |
| feature_level_key.Set(feature_level.c_str()); | |
| 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) | |
| #if BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) | |
| dawn::native::opengl::RequestAdapterOptionsGetGLProc | |
| adapter_options_get_gl_proc = {}; | |
| dawn::native::opengl::RequestAdapterOptionsAngleVirtualizationGroup group = | |
| {}; | |
| if (adapter_options.backendType == wgpu::BackendType::OpenGLES) { | |
| adapter_options_get_gl_proc.getProc = gl::GetGLProcAddress; | |
| gl::GLDisplayEGL* gl_display = gl::GLSurfaceEGL::GetGLDisplayEGL(); | |
| if (gl_display) { | |
| adapter_options_get_gl_proc.display = gl_display->GetDisplay(); | |
| } else { | |
| adapter_options_get_gl_proc.display = EGL_NO_DISPLAY; | |
| } | |
| adapter_options_get_gl_proc.nextInChain = adapter_options.nextInChain; | |
| adapter_options.nextInChain = &adapter_options_get_gl_proc; | |
| if (gl_display->ext->b_EGL_ANGLE_context_virtualization) { | |
| group.angleVirtualizationGroup = static_cast<GLuint>( | |
| gl::AngleContextVirtualizationGroup::kGraphiteDawnSharedContext); | |
| group.nextInChain = adapter_options.nextInChain; | |
| adapter_options.nextInChain = &group; | |
| } | |
| } | |
| #endif | |
| adapter_options.featureLevel = wgpu::FeatureLevel::Core; | |
| std::vector<dawn::native::Adapter> adapters = | |
| instance_->EnumerateAdapters(&adapter_options); | |
| if (adapters.empty()) { | |
| LOG(ERROR) << "No adapters found for non compatibility mode."; | |
| adapter_options.featureLevel = wgpu::FeatureLevel::Compatibility; | |
| adapters = instance_->EnumerateAdapters(&adapter_options); | |
| } | |
| if (adapters.empty()) { | |
| // On Android, it's expected that some devices might not support Dawn atm. | |
| // So don't generate report for it. | |
| LogInitFailure("No adapters found.", | |
| /*generate_crash_report=*/!BUILDFLAG(IS_ANDROID), | |
| backend_type, force_fallback_adapter); | |
| return false; | |
| } | |
| adapter_ = wgpu::Adapter(adapters[0].Get()); | |
| if (!validate_adapter_fn(backend_type, adapter_)) { | |
| LogInitFailure("Validate adapter failed.", | |
| /*generate_crash_report=*/!BUILDFLAG(IS_ANDROID), | |
| backend_type, force_fallback_adapter); | |
| return false; | |
| } | |
| // Start initializing dawn device here. | |
| wgpu::DawnCacheDeviceDescriptor cache_desc; | |
| cache_desc.SetDawnLoadCacheDataCallback( | |
| [](std::string_view key, base::span<uint8_t> value, | |
| DawnSharedContext* context) -> size_t { | |
| if (auto* cache = context->GetCache()) { | |
| if (value.empty()) { | |
| return cache->FindKey(key); | |
| } | |
| return cache->LoadData(key, value); | |
| } | |
| return 0; | |
| }, | |
| this); | |
| cache_desc.SetDawnStoreCacheDataCallback( | |
| [](std::string_view key, base::span<const uint8_t> value, | |
| DawnSharedContext* context) { | |
| if (auto* cache = context->GetCache()) { | |
| cache->StoreData(key, value); | |
| } | |
| }, | |
| this); | |
| cache_desc.nextInChain = &toggles_desc; | |
| wgpu::DawnDeviceAllocatorControl allocator_desc; | |
| wgpu::DeviceDescriptor descriptor; | |
| if (backend_type == wgpu::BackendType::Vulkan) { | |
| // Use a 256kb heap block size in the Vulkan backend to minimize | |
| // fragmentation. | |
| allocator_desc.allocatorHeapBlockSize = 256 * 1024; | |
| allocator_desc.nextInChain = &cache_desc; | |
| descriptor.nextInChain = &allocator_desc; | |
| } else { | |
| descriptor.nextInChain = &cache_desc; | |
| } | |
| descriptor.SetUncapturedErrorCallback( | |
| [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message, | |
| DawnSharedContext* state) { | |
| if (type != wgpu::ErrorType::NoError) { | |
| state->OnError(type, message); | |
| } | |
| }, | |
| this); | |
| descriptor.SetDeviceLostCallback( | |
| wgpu::CallbackMode::AllowSpontaneous, | |
| [](const wgpu::Device&, wgpu::DeviceLostReason reason, | |
| wgpu::StringView message, DawnSharedContext* state) { | |
| if (reason != wgpu::DeviceLostReason::Destroyed) { | |
| state->OnError(wgpu::ErrorType::Unknown, message); | |
| } | |
| }, | |
| this); | |
| std::vector<wgpu::FeatureName> features = | |
| GetRequiredFeatures(backend_type, adapter_); | |
| descriptor.requiredFeatures = features.data(); | |
| descriptor.requiredFeatureCount = std::size(features); | |
| // Use best limits for the device. | |
| wgpu::Limits supportedLimits = {}; | |
| if (adapter_.GetLimits(&supportedLimits) != wgpu::Status::Success) { | |
| LogInitFailure("Failed to call adapter.GetLimits().", | |
| /*generate_crash_report=*/true, backend_type, | |
| force_fallback_adapter); | |
| return false; | |
| } | |
| descriptor.requiredLimits = &supportedLimits; | |
| // ANGLE always tries creating D3D11 device with debug layer when dcheck is | |
| // on, so tries creating dawn device with backend validation as well. | |
| constexpr bool enable_backend_validation = | |
| DCHECK_IS_ON() && BUILDFLAG(IS_WIN); | |
| std::vector<dawn::native::BackendValidationLevel> backend_validation_levels = | |
| {dawn::native::BackendValidationLevel::Disabled}; | |
| if (features::SkiaGraphiteDawnBackendValidation() || | |
| enable_backend_validation) { | |
| backend_validation_levels.push_back( | |
| dawn::native::BackendValidationLevel::Partial); | |
| backend_validation_levels.push_back( | |
| dawn::native::BackendValidationLevel::Full); | |
| } | |
| if (base::FeatureList::IsEnabled(kForceDawnInitializeFailure)) { | |
| LOG(ERROR) << "DawnSharedContext creation failed for testing"; | |
| return false; | |
| } | |
| // Try create device with backend validation level. | |
| for (auto it = backend_validation_levels.rbegin(); | |
| it != backend_validation_levels.rend(); ++it) { | |
| auto level = *it; | |
| instance_->SetBackendValidationLevel(level); | |
| device_ = adapter_.CreateDevice(&descriptor); | |
| if (device_) { | |
| break; | |
| } | |
| } | |
| if (!device_) { | |
| LogInitFailure("Failed to create device.", /*generate_crash_report=*/true, | |
| backend_type, force_fallback_adapter); | |
| return false; | |
| } | |
| device_.SetLoggingCallback(&DawnSharedContext::DeviceLogInfo, this); | |
| backend_type_ = backend_type; | |
| is_vulkan_swiftshader_adapter_ = | |
| backend_type == wgpu::BackendType::Vulkan && force_fallback_adapter; | |
| #if BUILDFLAG(IS_WIN) | |
| use_d3d11_delay_flush_ = | |
| std::ranges::contains(dawn::native::GetTogglesUsed(device_.Get()), | |
| std::string_view("d3d11_delay_flush_to_gpu")); | |
| if (auto d3d11_device = GetD3D11Device()) { | |
| static auto* crash_key = base::debug::AllocateCrashKeyString( | |
| "d3d11-debug-layer", base::debug::CrashKeySize::Size32); | |
| const bool enabled = IsD3D11DebugLayerEnabled(d3d11_device); | |
| base::debug::SetCrashKeyString(crash_key, enabled ? "enabled" : "disabled"); | |
| } | |
| #endif // BUILDFLAG(IS_WIN) | |
| if (base::SingleThreadTaskRunner::HasCurrentDefault()) { | |
| base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
| this, "DawnSharedContext", | |
| base::SingleThreadTaskRunner::GetCurrentDefault()); | |
| registered_memory_dump_provider_ = true; | |
| } | |
| base::UmaHistogramEnumeration( | |
| "GPU.Dawn.AdapterFeatureLevel", | |
| DawnAdapterFeatureLevelFromWGPU(adapter_options.featureLevel)); | |
| return true; | |
| } | |
| void DawnSharedContext::SetCachingInterface( | |
| std::unique_ptr<webgpu::DawnInMemoryCache> dawn_caching_interface) { | |
| CHECK(std::holds_alternative<std::monostate>(cache_)); | |
| cache_ = std::move(dawn_caching_interface); | |
| } | |
| void DawnSharedContext::SetCachingInterface( | |
| scoped_refptr<GpuPersistentCache> persistent_cache) { | |
| CHECK(std::holds_alternative<std::monostate>(cache_)); | |
| cache_ = std::move(persistent_cache); | |
| } | |
| std::optional<error::ContextLostReason> DawnSharedContext::GetResetStatus() | |
| const { | |
| base::AutoLock auto_lock(context_lost_lock_); | |
| return context_lost_reason_; | |
| } | |
| void DawnSharedContext::MarkContextLost(error::ContextLostReason reason) { | |
| base::AutoLock auto_lock(context_lost_lock_); | |
| if (!context_lost_reason_.has_value()) { | |
| context_lost_reason_ = reason; | |
| } | |
| } | |
| bool DawnSharedContext::IsContextLost() const { | |
| return GetResetStatus().has_value(); | |
| } | |
| void DawnSharedContext::FlushBackend() { | |
| #if BUILDFLAG(IS_WIN) | |
| FlushD3D11CommandsIfDelayed(); | |
| #endif | |
| } | |
| void DawnSharedContext::ReportProgress() { | |
| platform_.ReportProgress(); | |
| } | |
| void DawnSharedContext::OnError(wgpu::ErrorType error_type, | |
| wgpu::StringView 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; | |
| SetCrashKeyThreadSafe(reason_message_key, result_string); | |
| } else { | |
| auto unknown_error = base::StringPrintf("Unknown error(0x%08lX)", result); | |
| LOG(ERROR) << "Device removed reason: " << unknown_error; | |
| SetCrashKeyThreadSafe(reason_message_key, unknown_error.c_str()); | |
| } | |
| } | |
| #endif | |
| EmitErrorReport(error_type, static_cast<std::string_view>(message)); | |
| error::ContextLostReason reason = error::kUnknown; | |
| switch (error_type) { | |
| case wgpu::ErrorType::OutOfMemory: | |
| reason = error::kOutOfMemory; | |
| break; | |
| case wgpu::ErrorType::Validation: | |
| reason = error::kGuilty; | |
| break; | |
| default: | |
| reason = error::kUnknown; | |
| break; | |
| } | |
| MarkContextLost(reason); | |
| } | |
| namespace { | |
| static constexpr char kDawnMemoryDumpPrefix[] = "gpu/dawn"; | |
| static constexpr char kAllocatorMemoryDumpPrefix[] = | |
| "gpu/vulkan/graphite_allocator"; | |
| class DawnMemoryDump : public dawn::native::MemoryDump { | |
| public: | |
| explicit DawnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) | |
| : pmd_(pmd) { | |
| CHECK(pmd_); | |
| } | |
| ~DawnMemoryDump() override = default; | |
| void AddScalar(const char* name, | |
| const char* key, | |
| const char* units, | |
| uint64_t value) override { | |
| pmd_->GetOrCreateAllocatorDump( | |
| base::JoinString({kDawnMemoryDumpPrefix, name}, "/")) | |
| ->AddScalar(key, units, value); | |
| } | |
| void AddString(const char* name, | |
| const char* key, | |
| const std::string& value) override { | |
| pmd_->GetOrCreateAllocatorDump( | |
| base::JoinString({kDawnMemoryDumpPrefix, name}, "/")) | |
| ->AddString(key, "", value); | |
| } | |
| private: | |
| const raw_ptr<base::trace_event::ProcessMemoryDump> pmd_; | |
| }; | |
| } // namespace | |
| bool DawnSharedContext::OnMemoryDump( | |
| const base::trace_event::MemoryDumpArgs& args, | |
| base::trace_event::ProcessMemoryDump* pmd) { | |
| using base::trace_event::MemoryAllocatorDump; | |
| if (args.level_of_detail == | |
| base::trace_event::MemoryDumpLevelOfDetail::kBackground) { | |
| const dawn::native::MemoryUsageInfo mem_usage = | |
| dawn::native::ComputeEstimatedMemoryUsageInfo(device_.Get()); | |
| pmd->GetOrCreateAllocatorDump(kDawnMemoryDumpPrefix) | |
| ->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, mem_usage.totalUsage); | |
| pmd->GetOrCreateAllocatorDump( | |
| base::JoinString({kDawnMemoryDumpPrefix, "textures"}, "/")) | |
| ->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, mem_usage.texturesUsage); | |
| pmd | |
| ->GetOrCreateAllocatorDump(base::JoinString( | |
| {kDawnMemoryDumpPrefix, "textures/depth_stencil"}, "/")) | |
| ->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, | |
| mem_usage.depthStencilTexturesUsage); | |
| auto* msaa_dump = pmd->GetOrCreateAllocatorDump( | |
| base::JoinString({kDawnMemoryDumpPrefix, "textures/msaa"}, "/")); | |
| msaa_dump->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, | |
| mem_usage.msaaTexturesUsage); | |
| msaa_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount, | |
| MemoryAllocatorDump::kUnitsObjects, | |
| mem_usage.msaaTexturesCount); | |
| msaa_dump->AddScalar("biggest_size", MemoryAllocatorDump::kUnitsBytes, | |
| mem_usage.largestMsaaTextureUsage); | |
| pmd->GetOrCreateAllocatorDump( | |
| base::JoinString({kDawnMemoryDumpPrefix, "buffers"}, "/")) | |
| ->AddScalar(MemoryAllocatorDump::kNameSize, | |
| MemoryAllocatorDump::kUnitsBytes, mem_usage.buffersUsage); | |
| } else { | |
| DawnMemoryDump dawnMemoryDump(pmd); | |
| dawn::native::DumpMemoryStatistics(device_.Get(), &dawnMemoryDump); | |
| } | |
| if (backend_type() == wgpu::BackendType::Vulkan) { | |
| // For Graphite-Vulkan backend, report vulkan allocator dumps and | |
| // statistics. | |
| auto* dump = pmd->GetOrCreateAllocatorDump(kAllocatorMemoryDumpPrefix); | |
| const dawn::native::AllocatorMemoryInfo allocator_usage = | |
| dawn::native::GetAllocatorMemoryInfo(device_.Get()); | |
| // `allocated_size` is memory allocated from the device, used is what is | |
| // actually used. | |
| dump->AddScalar("allocated_size", MemoryAllocatorDump::kUnitsBytes, | |
| allocator_usage.totalAllocatedMemory - | |
| allocator_usage.totalLazyAllocatedMemory); | |
| dump->AddScalar( | |
| "used_size", MemoryAllocatorDump::kUnitsBytes, | |
| allocator_usage.totalUsedMemory - allocator_usage.totalLazyUsedMemory); | |
| dump->AddScalar( | |
| "fragmentation_size", MemoryAllocatorDump::kUnitsBytes, | |
| allocator_usage.totalAllocatedMemory - allocator_usage.totalUsedMemory); | |
| dump->AddScalar("lazy_allocated_size", MemoryAllocatorDump::kUnitsBytes, | |
| allocator_usage.totalLazyAllocatedMemory); | |
| dump->AddScalar("lazy_used_size", MemoryAllocatorDump::kUnitsBytes, | |
| allocator_usage.totalLazyUsedMemory); | |
| } | |
| return true; | |
| } | |
| std::unique_ptr<DawnContextProvider> DawnContextProvider::Create( | |
| const GpuPreferences& gpu_preferences, | |
| const GpuFeatureInfo& gpu_feature_info, | |
| gl::ProgressReporter* progress_reporter, | |
| ValidateAdapterFn validate_adapter_fn) { | |
| return DawnContextProvider::CreateWithBackend( | |
| GetDefaultBackendType(), DefaultForceFallbackAdapter(), gpu_preferences, | |
| gpu_feature_info, progress_reporter, validate_adapter_fn); | |
| } | |
| std::unique_ptr<DawnContextProvider> DawnContextProvider::CreateWithBackend( | |
| wgpu::BackendType backend_type, | |
| bool force_fallback_adapter, | |
| const GpuPreferences& gpu_preferences, | |
| const GpuFeatureInfo& gpu_feature_info, | |
| gl::ProgressReporter* progress_reporter, | |
| ValidateAdapterFn validate_adapter_fn) { | |
| bool use_thread_safe_graphite_context = | |
| features::IsDrDcEnabled(gpu_feature_info) && | |
| features::IsGraphiteContextThreadSafe(); | |
| auto dawn_shared_context = base::MakeRefCounted<DawnSharedContext>( | |
| progress_reporter, use_thread_safe_graphite_context); | |
| GpuDriverBugWorkarounds workarounds( | |
| gpu_feature_info.enabled_gpu_driver_bug_workarounds); | |
| if (!dawn_shared_context->Initialize(backend_type, force_fallback_adapter, | |
| gpu_preferences, workarounds, | |
| validate_adapter_fn)) { | |
| return nullptr; | |
| } | |
| return base::WrapUnique( | |
| new DawnContextProvider(std::move(dawn_shared_context))); | |
| } | |
| std::unique_ptr<DawnContextProvider> | |
| DawnContextProvider::CreateWithSharedDevice( | |
| const DawnContextProvider* existing) { | |
| CHECK(existing); | |
| CHECK(existing->dawn_shared_context_); | |
| return base::WrapUnique( | |
| new DawnContextProvider(existing->dawn_shared_context_)); | |
| } | |
| DawnContextProvider::DawnContextProvider( | |
| scoped_refptr<DawnSharedContext> dawn_shared_context) | |
| : dawn_shared_context_(std::move(dawn_shared_context)) { | |
| CHECK(dawn_shared_context_); | |
| } | |
| DawnContextProvider::~DawnContextProvider() = default; | |
| wgpu::Device DawnContextProvider::GetDevice() const { | |
| return dawn_shared_context_->GetDevice(); | |
| } | |
| wgpu::BackendType DawnContextProvider::backend_type() const { | |
| return dawn_shared_context_->backend_type(); | |
| } | |
| bool DawnContextProvider::is_vulkan_swiftshader_adapter() const { | |
| return dawn_shared_context_->is_vulkan_swiftshader_adapter(); | |
| } | |
| wgpu::Adapter DawnContextProvider::GetAdapter() const { | |
| return dawn_shared_context_->GetAdapter(); | |
| } | |
| wgpu::Instance DawnContextProvider::GetInstance() const { | |
| return dawn_shared_context_->GetInstance(); | |
| } | |
| bool DawnContextProvider::use_thread_safe_shared_context() const { | |
| return dawn_shared_context_->use_thread_safe_graphite_context(); | |
| } | |
| void DawnContextProvider::InitializeThreadSafeGraphiteContext( | |
| const skgpu::graphite::ContextOptions& options, | |
| GpuProcessShmCount* use_shader_cache_shm_count) { | |
| dawn_shared_context_->InitializeThreadSafeGraphiteContext( | |
| options, use_shader_cache_shm_count); | |
| } | |
| bool DawnContextProvider::InitializeGraphiteContext( | |
| const skgpu::graphite::ContextOptions& options, | |
| GpuProcessShmCount* use_shader_cache_shm_count) { | |
| if (dawn_shared_context_->use_thread_safe_graphite_context()) { | |
| return !!dawn_shared_context_->GetThreadSafeGraphiteSharedContext(); | |
| } | |
| graphite_shared_context_ = dawn_shared_context_->CreateGraphiteSharedContext( | |
| options, use_shader_cache_shm_count, /*is_thread_safe=*/false); | |
| return !!graphite_shared_context_; | |
| } | |
| void DawnContextProvider::SetCachingInterface( | |
| std::unique_ptr<webgpu::DawnInMemoryCache> dawn_caching_interface) { | |
| CHECK(dawn_shared_context_->HasOneRef()); | |
| CHECK(!graphite_shared_context_); | |
| dawn_shared_context_->SetCachingInterface(std::move(dawn_caching_interface)); | |
| } | |
| void DawnContextProvider::SetCachingInterface( | |
| scoped_refptr<GpuPersistentCache> persistent_cache) { | |
| CHECK(dawn_shared_context_->HasOneRef()); | |
| CHECK(!graphite_shared_context_); | |
| dawn_shared_context_->SetCachingInterface(std::move(persistent_cache)); | |
| } | |
| #if BUILDFLAG(IS_WIN) | |
| Microsoft::WRL::ComPtr<ID3D11Device> DawnContextProvider::GetD3D11Device() | |
| const { | |
| return dawn_shared_context_->GetD3D11Device(); | |
| } | |
| Microsoft::WRL::ComPtr<ID3D12CommandQueue> | |
| DawnContextProvider::GetD3D12CommandQueue() const { | |
| return dawn_shared_context_->GetD3D12CommandQueue(); | |
| } | |
| #endif | |
| bool DawnContextProvider::SupportsFeature(wgpu::FeatureName feature) { | |
| return dawn_shared_context_->GetDevice().HasFeature(feature); | |
| } | |
| std::optional<error::ContextLostReason> DawnContextProvider::GetResetStatus() | |
| const { | |
| return dawn_shared_context_->GetResetStatus(); | |
| } | |
| GraphiteSharedContext* DawnContextProvider::GetGraphiteSharedContext() const { | |
| if (dawn_shared_context_->use_thread_safe_graphite_context()) { | |
| // Both threads shares the same GraphiteSharedContext. DawnSharedContext | |
| // owns GraphiteSharedContext | |
| return dawn_shared_context_->GetThreadSafeGraphiteSharedContext(); | |
| } else { | |
| // Each DawnContextProvider owns its own GraphiteSharedContext and | |
| // skgpu::graphite::Context | |
| return graphite_shared_context_.get(); | |
| } | |
| } | |
| webgpu::DawnPlatform* DawnContextProvider::GetDawnPlatform() { | |
| return dawn_shared_context_->GetDawnPlatform(); | |
| } | |
| } // namespace gpu |