| // Copyright 2021 The Dawn Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "common/GPUInfo.h" |
| #include "common/Log.h" |
| #include "common/Platform.h" |
| #include "common/SystemUtils.h" |
| #include "dawn/dawn_proc.h" |
| #include "dawn/webgpu_cpp.h" |
| #include "dawn_native/DawnNative.h" |
| #include "tests/MockCallback.h" |
| |
| #if defined(DAWN_ENABLE_BACKEND_VULKAN) |
| # include "dawn_native/VulkanBackend.h" |
| #endif // defined(DAWN_ENABLE_BACKEND_VULKAN) |
| |
| #if defined(DAWN_ENABLE_BACKEND_D3D12) |
| # include "dawn_native/D3D12Backend.h" |
| #endif // defined(DAWN_ENABLE_BACKEND_D3D12) |
| |
| #if defined(DAWN_ENABLE_BACKEND_METAL) |
| # include "dawn_native/MetalBackend.h" |
| #endif // defined(DAWN_ENABLE_BACKEND_METAL) |
| |
| #if defined(DAWN_ENABLE_BACKEND_METAL) |
| # include "dawn_native/MetalBackend.h" |
| #endif // defined(DAWN_ENABLE_BACKEND_METAL) |
| |
| #if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) || defined(DAWN_ENABLE_BACKEND_OPENGLES) |
| # include "GLFW/glfw3.h" |
| # include "dawn_native/OpenGLBackend.h" |
| #endif // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) || defined(DAWN_ENABLE_BACKEND_OPENGLES) |
| |
| #include <gtest/gtest.h> |
| |
| namespace { |
| |
| using namespace testing; |
| |
| class AdapterDiscoveryTests : public ::testing::Test {}; |
| |
| #if defined(DAWN_ENABLE_BACKEND_VULKAN) |
| // Test only discovering the SwiftShader adapter |
| TEST(AdapterDiscoveryTests, OnlySwiftShader) { |
| dawn::native::Instance instance; |
| |
| dawn::native::vulkan::AdapterDiscoveryOptions options; |
| options.forceSwiftShader = true; |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| EXPECT_LE(adapters.size(), 1u); // 0 or 1 SwiftShader adapters. |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::Vulkan); |
| EXPECT_EQ(properties.adapterType, wgpu::AdapterType::CPU); |
| EXPECT_TRUE(gpu_info::IsSwiftshader(properties.vendorID, properties.deviceID)); |
| } |
| } |
| |
| // Test discovering only Vulkan adapters |
| TEST(AdapterDiscoveryTests, OnlyVulkan) { |
| dawn::native::Instance instance; |
| |
| dawn::native::vulkan::AdapterDiscoveryOptions options; |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::Vulkan); |
| } |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_VULKAN) |
| |
| #if defined(DAWN_ENABLE_BACKEND_D3D12) |
| // Test discovering only D3D12 adapters |
| TEST(AdapterDiscoveryTests, OnlyD3D12) { |
| dawn::native::Instance instance; |
| |
| dawn::native::d3d12::AdapterDiscoveryOptions options; |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::D3D12); |
| } |
| } |
| |
| // Test discovering a D3D12 adapter from a prexisting DXGI adapter |
| TEST(AdapterDiscoveryTests, MatchingDXGIAdapter) { |
| using Microsoft::WRL::ComPtr; |
| |
| ComPtr<IDXGIFactory4> dxgiFactory; |
| HRESULT hr = ::CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory)); |
| ASSERT_EQ(hr, S_OK); |
| |
| for (uint32_t adapterIndex = 0;; ++adapterIndex) { |
| ComPtr<IDXGIAdapter1> dxgiAdapter = nullptr; |
| if (dxgiFactory->EnumAdapters1(adapterIndex, &dxgiAdapter) == DXGI_ERROR_NOT_FOUND) { |
| break; // No more adapters to enumerate. |
| } |
| |
| dawn::native::Instance instance; |
| |
| dawn::native::d3d12::AdapterDiscoveryOptions options; |
| options.dxgiAdapter = std::move(dxgiAdapter); |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::D3D12); |
| } |
| } |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_D3D12) |
| |
| #if defined(DAWN_ENABLE_BACKEND_METAL) |
| // Test discovering only Metal adapters |
| TEST(AdapterDiscoveryTests, OnlyMetal) { |
| dawn::native::Instance instance; |
| |
| dawn::native::metal::AdapterDiscoveryOptions options; |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::Metal); |
| } |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_METAL) |
| |
| #if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) |
| // Test discovering only desktop OpenGL adapters |
| TEST(AdapterDiscoveryTests, OnlyDesktopGL) { |
| if (!glfwInit()) { |
| GTEST_SKIP() << "glfwInit() failed"; |
| } |
| glfwDefaultWindowHints(); |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); |
| glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); |
| glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); |
| glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); |
| |
| GLFWwindow* window = |
| glfwCreateWindow(400, 400, "Dawn OpenGL test window", nullptr, nullptr); |
| glfwMakeContextCurrent(window); |
| |
| dawn::native::Instance instance; |
| |
| dawn::native::opengl::AdapterDiscoveryOptions options; |
| options.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress); |
| instance.DiscoverAdapters(&options); |
| glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::OpenGL); |
| } |
| |
| glfwDestroyWindow(window); |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) |
| |
| #if defined(DAWN_ENABLE_BACKEND_OPENGLES) |
| // Test discovering only OpenGLES adapters |
| TEST(AdapterDiscoveryTests, OnlyOpenGLES) { |
| ScopedEnvironmentVar angleDefaultPlatform; |
| if (GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) { |
| angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", "swiftshader"); |
| } |
| |
| if (!glfwInit()) { |
| GTEST_SKIP() << "glfwInit() failed"; |
| } |
| glfwDefaultWindowHints(); |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); |
| glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); |
| glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); |
| glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); |
| |
| GLFWwindow* window = |
| glfwCreateWindow(400, 400, "Dawn OpenGLES test window", nullptr, nullptr); |
| glfwMakeContextCurrent(window); |
| |
| dawn::native::Instance instance; |
| |
| dawn::native::opengl::AdapterDiscoveryOptionsES options; |
| options.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress); |
| instance.DiscoverAdapters(&options); |
| glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); |
| |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.backendType, wgpu::BackendType::OpenGLES); |
| } |
| |
| glfwDestroyWindow(window); |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_OPENGLES) |
| |
| #if defined(DAWN_ENABLE_BACKEND_METAL) && defined(DAWN_ENABLE_BACKEND_VULKAN) |
| // Test discovering the Metal backend, then the Vulkan backend |
| // does not duplicate adapters. |
| TEST(AdapterDiscoveryTests, OneBackendThenTheOther) { |
| dawn::native::Instance instance; |
| uint32_t metalAdapterCount = 0; |
| { |
| dawn::native::metal::AdapterDiscoveryOptions options; |
| instance.DiscoverAdapters(&options); |
| |
| const auto& adapters = instance.GetAdapters(); |
| metalAdapterCount = adapters.size(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| ASSERT_EQ(properties.backendType, wgpu::BackendType::Metal); |
| } |
| } |
| { |
| dawn::native::vulkan::AdapterDiscoveryOptions options; |
| instance.DiscoverAdapters(&options); |
| |
| uint32_t metalAdapterCount2 = 0; |
| const auto& adapters = instance.GetAdapters(); |
| for (const auto& adapter : adapters) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_TRUE(properties.backendType == wgpu::BackendType::Metal || |
| properties.backendType == wgpu::BackendType::Vulkan); |
| if (properties.backendType == wgpu::BackendType::Metal) { |
| metalAdapterCount2++; |
| } |
| } |
| EXPECT_EQ(metalAdapterCount, metalAdapterCount2); |
| } |
| } |
| #endif // defined(DAWN_ENABLE_BACKEND_VULKAN) && defined(DAWN_ENABLE_BACKEND_METAL) |
| |
| class AdapterCreationTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| dawnProcSetProcs(&dawn_native::GetProcs()); |
| |
| { |
| auto nativeInstance = std::make_unique<dawn_native::Instance>(); |
| nativeInstance->DiscoverDefaultAdapters(); |
| for (dawn_native::Adapter& nativeAdapter : nativeInstance->GetAdapters()) { |
| anyAdapterAvailable = true; |
| |
| wgpu::AdapterProperties properties; |
| nativeAdapter.GetProperties(&properties); |
| swiftShaderAvailable = |
| swiftShaderAvailable || |
| gpu_info::IsSwiftshader(properties.vendorID, properties.deviceID); |
| discreteGPUAvailable = discreteGPUAvailable || |
| properties.adapterType == wgpu::AdapterType::DiscreteGPU; |
| integratedGPUAvailable = |
| integratedGPUAvailable || |
| properties.adapterType == wgpu::AdapterType::IntegratedGPU; |
| } |
| } |
| |
| instance = wgpu::CreateInstance(); |
| } |
| |
| void TearDown() override { |
| instance = nullptr; |
| dawnProcSetProcs(nullptr); |
| } |
| |
| wgpu::Instance instance; |
| bool anyAdapterAvailable = false; |
| bool swiftShaderAvailable = false; |
| bool discreteGPUAvailable = false; |
| bool integratedGPUAvailable = false; |
| }; |
| |
| // Test that requesting the default adapter works |
| TEST_F(AdapterCreationTest, DefaultAdapter) { |
| wgpu::RequestAdapterOptions options = {}; |
| |
| MockCallback<WGPURequestAdapterCallback> cb; |
| |
| WGPUAdapter cAdapter = nullptr; |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this)); |
| |
| wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter != nullptr, anyAdapterAvailable); |
| } |
| |
| // Test that passing nullptr for the options gets the default adapter |
| TEST_F(AdapterCreationTest, NullGivesDefaultAdapter) { |
| wgpu::RequestAdapterOptions options = {}; |
| |
| MockCallback<WGPURequestAdapterCallback> cb; |
| |
| WGPUAdapter cAdapter = nullptr; |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this)); |
| |
| wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter != nullptr, anyAdapterAvailable); |
| |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this + 1)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(nullptr, cb.Callback(), cb.MakeUserdata(this + 1)); |
| |
| wgpu::Adapter adapter2 = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter.Get(), adapter2.Get()); |
| } |
| |
| // Test that requesting the fallback adapter returns SwiftShader. |
| TEST_F(AdapterCreationTest, FallbackAdapter) { |
| wgpu::RequestAdapterOptions options = {}; |
| options.forceFallbackAdapter = true; |
| |
| MockCallback<WGPURequestAdapterCallback> cb; |
| |
| WGPUAdapter cAdapter = nullptr; |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this)); |
| |
| wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter != nullptr, swiftShaderAvailable); |
| if (adapter != nullptr) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| |
| EXPECT_EQ(properties.adapterType, wgpu::AdapterType::CPU); |
| EXPECT_TRUE(gpu_info::IsSwiftshader(properties.vendorID, properties.deviceID)); |
| } |
| } |
| |
| // Test that requesting a high performance GPU works |
| TEST_F(AdapterCreationTest, PreferHighPerformance) { |
| wgpu::RequestAdapterOptions options = {}; |
| options.powerPreference = wgpu::PowerPreference::HighPerformance; |
| |
| MockCallback<WGPURequestAdapterCallback> cb; |
| |
| WGPUAdapter cAdapter = nullptr; |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this)); |
| |
| wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter != nullptr, anyAdapterAvailable); |
| if (discreteGPUAvailable) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| EXPECT_EQ(properties.adapterType, wgpu::AdapterType::DiscreteGPU); |
| } |
| } |
| |
| // Test that requesting a low power GPU works |
| TEST_F(AdapterCreationTest, PreferLowPower) { |
| wgpu::RequestAdapterOptions options = {}; |
| options.powerPreference = wgpu::PowerPreference::LowPower; |
| |
| MockCallback<WGPURequestAdapterCallback> cb; |
| |
| WGPUAdapter cAdapter = nullptr; |
| EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this)) |
| .WillOnce(SaveArg<1>(&cAdapter)); |
| instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this)); |
| |
| wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter); |
| EXPECT_EQ(adapter != nullptr, anyAdapterAvailable); |
| if (integratedGPUAvailable) { |
| wgpu::AdapterProperties properties; |
| adapter.GetProperties(&properties); |
| EXPECT_EQ(properties.adapterType, wgpu::AdapterType::IntegratedGPU); |
| } |
| } |
| |
| } // anonymous namespace |