DawnTest: Gather adapters on environment creation for filtering backends

This moves the creation of the Instance and Adapter discover to
environment creation. It allows programatically filtering test
parameterizations based on what is available on the system instead of
relying on what is enabled at compile time.

Because of an issue with the Vulkan validation layers, the instance and
adapters are created twice. Once in environment creation, and once on
environment set up. The Vulkan validation layers use static global
mutexes which are unsafe when Chromium's test launcher forks the
launcher process between environment creation and SetUp.

Bug: dawn:396
Change-Id: Id79f0d274331e4ba95f75b2ca4e896ad0f7a31a8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21762
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 53805fe..e64af58 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -128,6 +128,11 @@
                          forceDisabledWorkarounds);
 }
 
+TestAdapterProperties::TestAdapterProperties(const wgpu::AdapterProperties& properties,
+                                             bool selected)
+    : wgpu::AdapterProperties(properties), adapterName(properties.name), selected(selected) {
+}
+
 std::ostream& operator<<(std::ostream& os, const DawnTestParam& param) {
     os << ParamName(param.backendType);
     for (const char* forceEnabledWorkaround : param.forceEnabledWorkarounds) {
@@ -152,6 +157,21 @@
 }
 
 DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) {
+    ParseArgs(argc, argv);
+
+    // Create a temporary instance to gather adapter properties. This is done before
+    // test instantiation so FilterBackends can generate test parameterizations only on available
+    // backends. We drop the instance at the end of this function because the Vulkan validation
+    // layers use static global mutexes which behave badly when Chromium's test launcher forks the
+    // test process. The instance will be recreated on test environment setup.
+    std::unique_ptr<dawn_native::Instance> instance = CreateInstanceAndDiscoverAdapters();
+    ASSERT(instance);
+
+    GatherAdapterProperties(instance.get());
+    PrintTestConfigurationAndAdapterInfo();
+}
+
+void DawnTestEnvironment::ParseArgs(int argc, char** argv) {
     size_t argLen = 0;  // Set when parsing --arg=X arguments
     for (int i = 1; i < argc; ++i) {
         if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
@@ -319,6 +339,30 @@
     return instance;
 }
 
+void DawnTestEnvironment::GatherAdapterProperties(const dawn_native::Instance* instance) {
+    for (const dawn_native::Adapter& adapter : instance->GetAdapters()) {
+        wgpu::AdapterProperties properties;
+        adapter.GetProperties(&properties);
+
+        mAdapterProperties.emplace_back(
+            properties, mHasVendorIdFilter && mVendorIdFilter == properties.vendorID);
+    }
+}
+
+std::vector<DawnTestParam> DawnTestEnvironment::FilterBackends(const DawnTestParam* params,
+                                                               size_t numParams) const {
+    std::vector<DawnTestParam> backends;
+    for (size_t i = 0; i < numParams; ++i) {
+        for (const auto& adapterProperties : mAdapterProperties) {
+            if (params[i].backendType == adapterProperties.backendType) {
+                backends.push_back(params[i]);
+                break;
+            }
+        }
+    }
+    return backends;
+}
+
 void DawnTestEnvironment::PrintTestConfigurationAndAdapterInfo() const {
     dawn::LogMessage log = dawn::InfoLog();
     log << "Testing configuration\n"
@@ -344,9 +388,7 @@
            "\n"
         << "System adapters: \n";
 
-    for (const dawn_native::Adapter& adapter : mInstance->GetAdapters()) {
-        wgpu::AdapterProperties properties;
-        adapter.GetProperties(&properties);
+    for (const TestAdapterProperties& properties : mAdapterProperties) {
         std::ostringstream vendorId;
         std::ostringstream deviceId;
         vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
@@ -357,19 +399,17 @@
         // Preparing for outputting hex numbers
         log << std::showbase << std::hex << std::setfill('0') << std::setw(4)
 
-            << " - \"" << properties.name << "\"\n"
+            << " - \"" << properties.adapterName << "\"\n"
             << "   type: " << AdapterTypeName(properties.adapterType)
             << ", backend: " << ParamName(properties.backendType) << "\n"
             << "   vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str()
-            << (mHasVendorIdFilter && mVendorIdFilter == properties.vendorID ? " [Selected]" : "")
-            << "\n";
+            << (properties.selected ? " [Selected]" : "") << "\n";
     }
 }
 
 void DawnTestEnvironment::SetUp() {
     mInstance = CreateInstanceAndDiscoverAdapters();
     ASSERT(mInstance);
-    PrintTestConfigurationAndAdapterInfo();
 }
 
 void DawnTestEnvironment::TearDown() {
@@ -987,39 +1027,9 @@
 }
 
 namespace detail {
-    bool IsBackendAvailable(wgpu::BackendType type) {
-        switch (type) {
-#if defined(DAWN_ENABLE_BACKEND_D3D12)
-            case wgpu::BackendType::D3D12:
-#endif
-#if defined(DAWN_ENABLE_BACKEND_METAL)
-            case wgpu::BackendType::Metal:
-#endif
-#if defined(DAWN_ENABLE_BACKEND_NULL)
-            case wgpu::BackendType::Null:
-#endif
-#if defined(DAWN_ENABLE_BACKEND_OPENGL)
-            case wgpu::BackendType::OpenGL:
-#endif
-#if defined(DAWN_ENABLE_BACKEND_VULKAN)
-            case wgpu::BackendType::Vulkan:
-#endif
-                return true;
-
-            default:
-                return false;
-        }
-    }
-
     std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams) {
-        std::vector<DawnTestParam> backends;
-
-        for (size_t i = 0; i < numParams; ++i) {
-            if (IsBackendAvailable(params[i].backendType)) {
-                backends.push_back(params[i]);
-            }
-        }
-        return backends;
+        ASSERT(gTestEnv != nullptr);
+        return gTestEnv->FilterBackends(params, numParams);
     }
 
     // Helper classes to set expectations
diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h
index 45c3cbd..0ed0836 100644
--- a/src/tests/DawnTest.h
+++ b/src/tests/DawnTest.h
@@ -89,6 +89,16 @@
 };
 std::ostream& operator<<(std::ostream& stream, const RGBA8& color);
 
+struct TestAdapterProperties : wgpu::AdapterProperties {
+    TestAdapterProperties(const wgpu::AdapterProperties& properties, bool selected);
+    std::string adapterName;
+    bool selected;
+
+  private:
+    // This may be temporary, so it is copied into |adapterName| and made private.
+    using wgpu::AdapterProperties::name;
+};
+
 struct DawnTestParam {
     DawnTestParam(wgpu::BackendType backendType,
                   std::initializer_list<const char*> forceEnabledWorkarounds = {},
@@ -140,6 +150,8 @@
 
     static void SetEnvironment(DawnTestEnvironment* env);
 
+    std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams) const;
+
     void SetUp() override;
     void TearDown() override;
 
@@ -157,7 +169,9 @@
     std::unique_ptr<dawn_native::Instance> mInstance;
 
   private:
+    void ParseArgs(int argc, char** argv);
     std::unique_ptr<dawn_native::Instance> CreateInstanceAndDiscoverAdapters() const;
+    void GatherAdapterProperties(const dawn_native::Instance* instance);
     void PrintTestConfigurationAndAdapterInfo() const;
 
     bool mUseWire = false;
@@ -171,6 +185,7 @@
     bool mHasVendorIdFilter = false;
     uint32_t mVendorIdFilter = 0;
     std::string mWireTraceDir;
+    std::vector<TestAdapterProperties> mAdapterProperties;
 };
 
 class DawnTestBase {
diff --git a/src/tests/End2EndTestsMain.cpp b/src/tests/End2EndTestsMain.cpp
index 69a2041..da7bbbe 100644
--- a/src/tests/End2EndTestsMain.cpp
+++ b/src/tests/End2EndTestsMain.cpp
@@ -15,7 +15,7 @@
 #include "tests/DawnTest.h"
 
 int main(int argc, char** argv) {
-    testing::InitGoogleTest(&argc, argv);
     InitDawnEnd2EndTestEnvironment(argc, argv);
+    testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }
diff --git a/src/tests/PerfTestsMain.cpp b/src/tests/PerfTestsMain.cpp
index d928c9a..8852896 100644
--- a/src/tests/PerfTestsMain.cpp
+++ b/src/tests/PerfTestsMain.cpp
@@ -15,7 +15,7 @@
 #include "tests/perf_tests/DawnPerfTest.h"
 
 int main(int argc, char** argv) {
-    testing::InitGoogleTest(&argc, argv);
     InitDawnPerfTestEnvironment(argc, argv);
+    testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }