dawn_node: add "enable/disable-dawn-features=<comma-separated-values>"

Same as Chrome's args, this allows us to set the
DeviceDescriptor::forceEnabledToggles and forceDisabledToggles when
creating the GPUDevice.

Example: node cmdline.ts ... --gpu-provider-flag=enable-dawn-features=dump_shaders ...

Bug: dawn:1163
Change-Id: Ib5db71355f72e5d08f8fe87313c5e3d63ee236c3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/66963
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn_node/binding/GPU.cpp b/src/dawn_node/binding/GPU.cpp
index 5f1d4e7..50eb020 100644
--- a/src/dawn_node/binding/GPU.cpp
+++ b/src/dawn_node/binding/GPU.cpp
@@ -143,7 +143,7 @@
             }
         }
 
-        auto adapter = GPUAdapter::Create<GPUAdapter>(env, adapters[adapterIndex]);
+        auto adapter = GPUAdapter::Create<GPUAdapter>(env, adapters[adapterIndex], flags_);
         promise.Resolve(std::optional<interop::Interface<interop::GPUAdapter>>(adapter));
         return promise;
     }
diff --git a/src/dawn_node/binding/GPUAdapter.cpp b/src/dawn_node/binding/GPUAdapter.cpp
index c49e442..9ddca1e 100644
--- a/src/dawn_node/binding/GPUAdapter.cpp
+++ b/src/dawn_node/binding/GPUAdapter.cpp
@@ -16,9 +16,38 @@
 
 #include <unordered_set>
 
+#include "src/dawn_node/binding/Flags.h"
 #include "src/dawn_node/binding/GPUDevice.h"
 #include "src/dawn_node/binding/GPUSupportedLimits.h"
 
+namespace {
+    // TODO(amaiorano): Move to utility header
+    std::vector<std::string> Split(const std::string& s, char delim) {
+        if (s.empty())
+            return {};
+
+        std::vector<std::string> result;
+        const size_t lastIndex = s.length() - 1;
+        size_t startIndex = 0;
+        size_t i = startIndex;
+
+        while (i <= lastIndex) {
+            if (s[i] == delim) {
+                auto token = s.substr(startIndex, i - startIndex);
+                if (!token.empty())  // Discard empty tokens
+                    result.push_back(token);
+                startIndex = i + 1;
+            } else if (i == lastIndex) {
+                auto token = s.substr(startIndex, i - startIndex + 1);
+                if (!token.empty())  // Discard empty tokens
+                    result.push_back(token);
+            }
+            ++i;
+        }
+        return result;
+    }
+}  // namespace
+
 namespace wgpu { namespace binding {
 
     namespace {
@@ -79,7 +108,8 @@
     // wgpu::bindings::GPUAdapter
     // TODO(crbug.com/dawn/1133): This is a stub implementation. Properly implement.
     ////////////////////////////////////////////////////////////////////////////////
-    GPUAdapter::GPUAdapter(dawn_native::Adapter a) : adapter_(a) {
+    GPUAdapter::GPUAdapter(dawn_native::Adapter a, const Flags& flags)
+        : adapter_(a), flags_(flags) {
     }
 
     std::string GPUAdapter::getName(Napi::Env) {
@@ -163,6 +193,25 @@
             UNIMPLEMENTED("required: ", required);
         }
 
+        // Propogate enabled/disabled dawn features
+        // Note: DeviceDescriptor::forceEnabledToggles and forceDisabledToggles are vectors of
+        // 'const char*', so we make sure the parsed strings survive the CreateDevice() call by
+        // storing them on the stack.
+        std::vector<std::string> enabledToggles;
+        std::vector<std::string> disabledToggles;
+        if (auto values = flags_.Get("enable-dawn-features")) {
+            enabledToggles = Split(*values, ',');
+            for (auto& t : enabledToggles) {
+                desc.forceEnabledToggles.emplace_back(t.c_str());
+            }
+        }
+        if (auto values = flags_.Get("disable-dawn-features")) {
+            disabledToggles = Split(*values, ',');
+            for (auto& t : disabledToggles) {
+                desc.forceDisabledToggles.emplace_back(t.c_str());
+            }
+        }
+
         auto wgpu_device = adapter_.CreateDevice(&desc);
         if (wgpu_device) {
             promise.Resolve(interop::GPUDevice::Create<GPUDevice>(env, env, wgpu_device));
diff --git a/src/dawn_node/binding/GPUAdapter.h b/src/dawn_node/binding/GPUAdapter.h
index 1b44f57..6f837c8 100644
--- a/src/dawn_node/binding/GPUAdapter.h
+++ b/src/dawn_node/binding/GPUAdapter.h
@@ -21,11 +21,12 @@
 #include "src/dawn_node/interop/WebGPU.h"
 
 namespace wgpu { namespace binding {
+    class Flags;
 
     // GPUAdapter is an implementation of interop::GPUAdapter that wraps a dawn_native::Adapter.
     class GPUAdapter final : public interop::GPUAdapter {
       public:
-        GPUAdapter(dawn_native::Adapter a);
+        GPUAdapter(dawn_native::Adapter a, const Flags& flags);
 
         // interop::GPUAdapter interface compliance
         std::string getName(Napi::Env) override;
@@ -38,6 +39,7 @@
 
       private:
         dawn_native::Adapter adapter_;
+        const Flags& flags_;
     };
 
 }}  // namespace wgpu::binding