node: Throw a TypeError for unsupported GPUTextureFormats

The WebGPU spec requires this so that an extension not being enabled or
not implemented by the browser both behave the same.

Bug: None
Change-Id: Iba449c1f67d266aa53b924501577d646cd944f55
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112421
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index b42a8ea..b4614bb 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -14,6 +14,8 @@
 
 #include "src/dawn/node/binding/Converter.h"
 
+#include <cassert>
+
 #include "src/dawn/node/binding/GPUBuffer.h"
 #include "src/dawn/node/binding/GPUPipelineLayout.h"
 #include "src/dawn/node/binding/GPUQuerySet.h"
@@ -31,6 +33,13 @@
     }
 }
 
+bool Converter::HasFeature(wgpu::FeatureName feature) {
+    // Not all uses of the converter will have a device (for example for adapter-related
+    // conversions).
+    assert(device.Get() != nullptr);
+    return device.HasFeature(feature);
+}
+
 bool Converter::Convert(wgpu::Extent3D& out, const interop::GPUExtent3D& in) {
     out = {};
     if (auto* dict = std::get_if<interop::GPUExtent3DDict>(&in)) {
@@ -172,6 +181,7 @@
 
 bool Converter::Convert(wgpu::TextureFormat& out, const interop::GPUTextureFormat& in) {
     out = wgpu::TextureFormat::Undefined;
+    wgpu::FeatureName requiredFeature = wgpu::FeatureName::Undefined;
     switch (in) {
         case interop::GPUTextureFormat::kR8Unorm:
             out = wgpu::TextureFormat::R8Unorm;
@@ -296,168 +306,234 @@
         case interop::GPUTextureFormat::kDepth32Float:
             out = wgpu::TextureFormat::Depth32Float;
             return true;
+
         case interop::GPUTextureFormat::kDepth32FloatStencil8:
             out = wgpu::TextureFormat::Depth32FloatStencil8;
-            return true;
+            requiredFeature = wgpu::FeatureName::Depth32FloatStencil8;
+            break;
         case interop::GPUTextureFormat::kBc1RgbaUnorm:
             out = wgpu::TextureFormat::BC1RGBAUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc1RgbaUnormSrgb:
             out = wgpu::TextureFormat::BC1RGBAUnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc2RgbaUnorm:
             out = wgpu::TextureFormat::BC2RGBAUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc2RgbaUnormSrgb:
             out = wgpu::TextureFormat::BC2RGBAUnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc3RgbaUnorm:
             out = wgpu::TextureFormat::BC3RGBAUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc3RgbaUnormSrgb:
             out = wgpu::TextureFormat::BC3RGBAUnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc4RUnorm:
             out = wgpu::TextureFormat::BC4RUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc4RSnorm:
             out = wgpu::TextureFormat::BC4RSnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc5RgUnorm:
             out = wgpu::TextureFormat::BC5RGUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc5RgSnorm:
             out = wgpu::TextureFormat::BC5RGSnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc6HRgbUfloat:
             out = wgpu::TextureFormat::BC6HRGBUfloat;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc6HRgbFloat:
             out = wgpu::TextureFormat::BC6HRGBFloat;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc7RgbaUnorm:
             out = wgpu::TextureFormat::BC7RGBAUnorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kBc7RgbaUnormSrgb:
             out = wgpu::TextureFormat::BC7RGBAUnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionBC;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgb8Unorm:
             out = wgpu::TextureFormat::ETC2RGB8Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgb8UnormSrgb:
             out = wgpu::TextureFormat::ETC2RGB8UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgb8A1Unorm:
             out = wgpu::TextureFormat::ETC2RGB8A1Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgb8A1UnormSrgb:
             out = wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgba8Unorm:
             out = wgpu::TextureFormat::ETC2RGBA8Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEtc2Rgba8UnormSrgb:
             out = wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEacR11Unorm:
             out = wgpu::TextureFormat::EACR11Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEacR11Snorm:
             out = wgpu::TextureFormat::EACR11Snorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEacRg11Unorm:
             out = wgpu::TextureFormat::EACRG11Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kEacRg11Snorm:
             out = wgpu::TextureFormat::EACRG11Snorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionETC2;
+            break;
         case interop::GPUTextureFormat::kAstc4X4Unorm:
             out = wgpu::TextureFormat::ASTC4x4Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc4X4UnormSrgb:
             out = wgpu::TextureFormat::ASTC4x4UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc5X4Unorm:
             out = wgpu::TextureFormat::ASTC5x4Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc5X4UnormSrgb:
             out = wgpu::TextureFormat::ASTC5x4UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc5X5Unorm:
             out = wgpu::TextureFormat::ASTC5x5Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc5X5UnormSrgb:
             out = wgpu::TextureFormat::ASTC5x5UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc6X5Unorm:
             out = wgpu::TextureFormat::ASTC6x5Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc6X5UnormSrgb:
             out = wgpu::TextureFormat::ASTC6x5UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc6X6Unorm:
             out = wgpu::TextureFormat::ASTC6x6Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc6X6UnormSrgb:
             out = wgpu::TextureFormat::ASTC6x6UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X5Unorm:
             out = wgpu::TextureFormat::ASTC8x5Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X5UnormSrgb:
             out = wgpu::TextureFormat::ASTC8x5UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X6Unorm:
             out = wgpu::TextureFormat::ASTC8x6Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X6UnormSrgb:
             out = wgpu::TextureFormat::ASTC8x6UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X8Unorm:
             out = wgpu::TextureFormat::ASTC8x8Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc8X8UnormSrgb:
             out = wgpu::TextureFormat::ASTC8x8UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X5Unorm:
             out = wgpu::TextureFormat::ASTC10x5Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X5UnormSrgb:
             out = wgpu::TextureFormat::ASTC10x5UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X6Unorm:
             out = wgpu::TextureFormat::ASTC10x6Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X6UnormSrgb:
             out = wgpu::TextureFormat::ASTC10x6UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X8Unorm:
             out = wgpu::TextureFormat::ASTC10x8Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X8UnormSrgb:
             out = wgpu::TextureFormat::ASTC10x8UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X10Unorm:
             out = wgpu::TextureFormat::ASTC10x10Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc10X10UnormSrgb:
             out = wgpu::TextureFormat::ASTC10x10UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc12X10Unorm:
             out = wgpu::TextureFormat::ASTC12x10Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc12X10UnormSrgb:
             out = wgpu::TextureFormat::ASTC12x10UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc12X12Unorm:
             out = wgpu::TextureFormat::ASTC12x12Unorm;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
         case interop::GPUTextureFormat::kAstc12X12UnormSrgb:
             out = wgpu::TextureFormat::ASTC12x12UnormSrgb;
-            return true;
+            requiredFeature = wgpu::FeatureName::TextureCompressionASTC;
+            break;
+
+        default:
+            Napi::Error::New(env, "invalid value for GPUTextureFormat")
+                .ThrowAsJavaScriptException();
+            return false;
     }
-    Napi::Error::New(env, "invalid value for GPUTextureFormat").ThrowAsJavaScriptException();
-    return false;
+
+    assert(requiredFeature != wgpu::FeatureName::Undefined);
+    if (!HasFeature(requiredFeature)) {
+        Napi::TypeError::New(env, "invalid value for GPUTextureFormat")
+            .ThrowAsJavaScriptException();
+        return false;
+    }
+
+    return true;
 }
 
 bool Converter::Convert(interop::GPUTextureFormat& out, wgpu::TextureFormat in) {
diff --git a/src/dawn/node/binding/Converter.h b/src/dawn/node/binding/Converter.h
index 452cc2e..564b8a6 100644
--- a/src/dawn/node/binding/Converter.h
+++ b/src/dawn/node/binding/Converter.h
@@ -69,6 +69,8 @@
 class Converter {
   public:
     explicit Converter(Napi::Env e) : env(e) {}
+    Converter(Napi::Env e, wgpu::Device extensionDevice)
+        : env(e), device(std::move(extensionDevice)) {}
     ~Converter();
 
     // Conversion function. Converts the interop type IN to the Dawn type OUT.
@@ -408,6 +410,9 @@
     }
 
     Napi::Env env;
+    wgpu::Device device = nullptr;
+
+    bool HasFeature(wgpu::FeatureName feature);
 
     // Allocate() allocates and constructs an array of 'n' elements, and returns a pointer to
     // the first element. The array is freed when the Converter is destructed.
diff --git a/src/dawn/node/binding/GPUCommandEncoder.cpp b/src/dawn/node/binding/GPUCommandEncoder.cpp
index e5ffa14..8e38168 100644
--- a/src/dawn/node/binding/GPUCommandEncoder.cpp
+++ b/src/dawn/node/binding/GPUCommandEncoder.cpp
@@ -31,12 +31,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUCommandEncoder
 ////////////////////////////////////////////////////////////////////////////////
-GPUCommandEncoder::GPUCommandEncoder(wgpu::CommandEncoder enc) : enc_(std::move(enc)) {}
+GPUCommandEncoder::GPUCommandEncoder(wgpu::Device device, wgpu::CommandEncoder enc)
+    : device_(std::move(device)), enc_(std::move(enc)) {}
 
 interop::Interface<interop::GPURenderPassEncoder> GPUCommandEncoder::beginRenderPass(
     Napi::Env env,
     interop::GPURenderPassDescriptor descriptor) {
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::RenderPassDescriptor desc{};
     wgpu::RenderPassDescriptorMaxDrawCount maxDrawCountDesc{};
diff --git a/src/dawn/node/binding/GPUCommandEncoder.h b/src/dawn/node/binding/GPUCommandEncoder.h
index a3878c6..3573274 100644
--- a/src/dawn/node/binding/GPUCommandEncoder.h
+++ b/src/dawn/node/binding/GPUCommandEncoder.h
@@ -28,7 +28,7 @@
 // wgpu::CommandEncoder.
 class GPUCommandEncoder final : public interop::GPUCommandEncoder {
   public:
-    explicit GPUCommandEncoder(wgpu::CommandEncoder enc);
+    GPUCommandEncoder(wgpu::Device device, wgpu::CommandEncoder enc);
 
     // interop::GPUCommandEncoder interface compliance
     interop::Interface<interop::GPURenderPassEncoder> beginRenderPass(
@@ -78,6 +78,7 @@
     void setLabel(Napi::Env, std::string value) override;
 
   private:
+    wgpu::Device device_;
     wgpu::CommandEncoder enc_;
 };
 
diff --git a/src/dawn/node/binding/GPUDevice.cpp b/src/dawn/node/binding/GPUDevice.cpp
index 4b512c8..b540d50 100644
--- a/src/dawn/node/binding/GPUDevice.cpp
+++ b/src/dawn/node/binding/GPUDevice.cpp
@@ -225,7 +225,7 @@
 interop::Interface<interop::GPUTexture> GPUDevice::createTexture(
     Napi::Env env,
     interop::GPUTextureDescriptor descriptor) {
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::TextureDescriptor desc{};
     if (!conv(desc.label, descriptor.label) || !conv(desc.usage, descriptor.usage) ||  //
@@ -237,7 +237,7 @@
         !conv(desc.viewFormats, desc.viewFormatCount, descriptor.viewFormats)) {
         return {};
     }
-    return interop::GPUTexture::Create<GPUTexture>(env, device_.CreateTexture(&desc));
+    return interop::GPUTexture::Create<GPUTexture>(env, device_, device_.CreateTexture(&desc));
 }
 
 interop::Interface<interop::GPUSampler> GPUDevice::createSampler(
@@ -271,7 +271,7 @@
 interop::Interface<interop::GPUBindGroupLayout> GPUDevice::createBindGroupLayout(
     Napi::Env env,
     interop::GPUBindGroupLayoutDescriptor descriptor) {
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::BindGroupLayoutDescriptor desc{};
     if (!conv(desc.label, descriptor.label) ||
@@ -345,7 +345,7 @@
 interop::Interface<interop::GPURenderPipeline> GPUDevice::createRenderPipeline(
     Napi::Env env,
     interop::GPURenderPipelineDescriptor descriptor) {
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::RenderPipelineDescriptor desc{};
     if (!conv(desc, descriptor)) {
@@ -404,7 +404,7 @@
                                      interop::GPURenderPipelineDescriptor descriptor) {
     using Promise = interop::Promise<interop::Interface<interop::GPURenderPipeline>>;
 
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::RenderPipelineDescriptor desc{};
     if (!conv(desc, descriptor)) {
@@ -447,13 +447,13 @@
     interop::GPUCommandEncoderDescriptor descriptor) {
     wgpu::CommandEncoderDescriptor desc{};
     return interop::GPUCommandEncoder::Create<GPUCommandEncoder>(
-        env, device_.CreateCommandEncoder(&desc));
+        env, device_, device_.CreateCommandEncoder(&desc));
 }
 
 interop::Interface<interop::GPURenderBundleEncoder> GPUDevice::createRenderBundleEncoder(
     Napi::Env env,
     interop::GPURenderBundleEncoderDescriptor descriptor) {
-    Converter conv(env);
+    Converter conv(env, device_);
 
     wgpu::RenderBundleEncoderDescriptor desc{};
     if (!conv(desc.label, descriptor.label) ||
diff --git a/src/dawn/node/binding/GPUTexture.cpp b/src/dawn/node/binding/GPUTexture.cpp
index 24c682d..7702c81 100644
--- a/src/dawn/node/binding/GPUTexture.cpp
+++ b/src/dawn/node/binding/GPUTexture.cpp
@@ -26,7 +26,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 // wgpu::bindings::GPUTexture
 ////////////////////////////////////////////////////////////////////////////////
-GPUTexture::GPUTexture(wgpu::Texture texture) : texture_(std::move(texture)) {}
+GPUTexture::GPUTexture(wgpu::Device device, wgpu::Texture texture)
+    : device_(std::move(device)), texture_(std::move(texture)) {}
 
 interop::Interface<interop::GPUTextureView> GPUTexture::createView(
     Napi::Env env,
@@ -37,7 +38,7 @@
     }
 
     wgpu::TextureViewDescriptor desc{};
-    Converter conv(env);
+    Converter conv(env, device_);
     if (!conv(desc.baseMipLevel, descriptor.baseMipLevel) ||        //
         !conv(desc.mipLevelCount, descriptor.mipLevelCount) ||      //
         !conv(desc.baseArrayLayer, descriptor.baseArrayLayer) ||    //
diff --git a/src/dawn/node/binding/GPUTexture.h b/src/dawn/node/binding/GPUTexture.h
index e108d78..d3b2075 100644
--- a/src/dawn/node/binding/GPUTexture.h
+++ b/src/dawn/node/binding/GPUTexture.h
@@ -27,7 +27,7 @@
 // GPUTexture is an implementation of interop::GPUTexture that wraps a wgpu::Texture.
 class GPUTexture final : public interop::GPUTexture {
   public:
-    explicit GPUTexture(wgpu::Texture texture);
+    explicit GPUTexture(wgpu::Device device, wgpu::Texture texture);
 
     // Implicit cast operator to Dawn GPU object
     inline operator const wgpu::Texture&() const { return texture_; }
@@ -49,6 +49,7 @@
     void setLabel(Napi::Env, std::string value) override;
 
   private:
+    wgpu::Device device_;
     wgpu::Texture texture_;
 };