Add wgpuDeviceGetLimits. Split Required/Supported limit structs

Bug: dawn:685
Change-Id: Ibb5dd0479f5e887d4b2ca864c014ebaafb674dba
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64443
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 859df44..91107ca 100644
--- a/dawn.json
+++ b/dawn.json
@@ -20,6 +20,7 @@
     "adapter properties": {
         "category": "structure",
         "extensible": true,
+        "output": true,
         "members": [
             {"name": "device ID", "type": "uint32_t"},
             {"name": "vendor ID", "type": "uint32_t"},
@@ -735,6 +736,13 @@
                 ]
             },
             {
+                "name": "create external texture",
+                "returns": "external texture",
+                "args": [
+                    {"name": "external texture descriptor", "type": "external texture descriptor", "annotation": "const*"}
+                ]
+            },
+            {
                 "name": "create pipeline layout",
                 "returns": "pipeline layout",
                 "args": [
@@ -801,15 +809,15 @@
                 ]
             },
             {
-                "name": "get queue",
-                "returns": "queue"
+                "name": "get limits",
+                "returns": "bool",
+                "args": [
+                    {"name": "limits", "type": "supported limits", "annotation": "*"}
+                ]
             },
             {
-                "name": "create external texture",
-                "returns": "external texture",
-                "args": [
-                    {"name": "external texture descriptor", "type": "external texture descriptor", "annotation": "const*"}
-                ]
+                "name": "get queue",
+                "returns": "queue"
             },
             {
                 "name": "inject error",
@@ -885,7 +893,7 @@
             {"name": "depth clamping", "type": "bool", "default": "false"},
             {"name": "invalid extension", "type": "bool", "default": "false"},
             {"name": "dawn internal usages", "type": "bool", "default": "false"},
-            {"name": "limits", "type": "limits"}
+            {"name": "limits", "type": "supported limits"}
         ]
     },
     "double": {
@@ -901,7 +909,6 @@
     },
     "limits": {
         "category": "structure",
-        "extensible": true,
         "members": [
             {"name": "max texture dimension 1D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
             {"name": "max texture dimension 2D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"},
@@ -931,6 +938,21 @@
             {"name": "max compute workgroups per dimension", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"}
         ]
     },
+    "required limits": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "limits", "type": "limits"}
+        ]
+    },
+    "supported limits": {
+        "category": "structure",
+        "extensible": true,
+        "output": true,
+        "members": [
+            {"name": "limits", "type": "limits"}
+        ]
+    },
     "logging callback": {
         "category": "callback",
         "args": [
diff --git a/dawn_wire.json b/dawn_wire.json
index 5f881c1..7d4b89c 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -153,6 +153,7 @@
             "DeviceCreateBuffer",
             "DeviceCreateComputePipelineAsync",
             "DeviceCreateRenderPipelineAsync",
+            "DeviceGetLimits",
             "DevicePopErrorScope",
             "DeviceSetDeviceLostCallback",
             "DeviceSetUncapturedErrorCallback",
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index c6268ed..23558e6 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -206,6 +206,7 @@
         Type.__init__(self, name, json_data)
         self.chained = json_data.get("chained", False)
         self.extensible = json_data.get("extensible", False)
+        self.output = json_data.get("output", False)
         # Chained structs inherit from wgpu::ChainedStruct, which has
         # nextInChain, so setting both extensible and chained would result in
         # two nextInChain members.
diff --git a/generator/templates/dawn_wire/WireCmd.cpp b/generator/templates/dawn_wire/WireCmd.cpp
index 41809a1..c9dc5e2 100644
--- a/generator/templates/dawn_wire/WireCmd.cpp
+++ b/generator/templates/dawn_wire/WireCmd.cpp
@@ -496,13 +496,22 @@
         }
 
         size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct);
-        DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
+        DAWN_NO_DISCARD WireResult SerializeChainedStruct(const WGPUChainedStruct* chainedStruct,
                                                           SerializeBuffer* buffer,
                                                           const ObjectIdProvider& provider);
         WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
-                                                   DeserializeBuffer* deserializeBuffer,
-                                                   DeserializeAllocator* allocator,
-                                                   const ObjectIdResolver& resolver);
+                                            DeserializeBuffer* deserializeBuffer,
+                                            DeserializeAllocator* allocator,
+                                            const ObjectIdResolver& resolver);
+
+        size_t GetChainedStructExtraRequiredSize(WGPUChainedStructOut* chainedStruct);
+        DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStructOut* chainedStruct,
+                                                          SerializeBuffer* buffer,
+                                                          const ObjectIdProvider& provider);
+        WireResult DeserializeChainedStruct(WGPUChainedStructOut** outChainNext,
+                                            DeserializeBuffer* deserializeBuffer,
+                                            DeserializeAllocator* allocator,
+                                            const ObjectIdResolver& resolver);
 
         //* Output structure [de]serialization first because it is used by commands.
         {% for type in by_category["structure"] %}
@@ -513,12 +522,19 @@
             {% endif %}
         {% endfor %}
 
-        size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct) {
+{% macro make_chained_struct_serialization_helpers(out) %}
+        {% set ChainedStructPtr = "WGPUChainedStructOut*" if out else "const WGPUChainedStruct*" %}
+        {% set ChainedStruct = "WGPUChainedStructOut" if out else "WGPUChainedStruct" %}
+        size_t GetChainedStructExtraRequiredSize({{ChainedStructPtr}} chainedStruct) {
             ASSERT(chainedStruct != nullptr);
             size_t result = 0;
             while (chainedStruct != nullptr) {
                 switch (chainedStruct->sType) {
-                    {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
+                    {% for sType in types["s type"].values if (
+                            sType.valid and
+                            (sType.name.CamelCase() not in client_side_structures) and
+                            (types[sType.name.get()].output == out)
+                    ) %}
                         case {{as_cEnum(types["s type"].name, sType.name)}}: {
                             const auto& typedStruct = *reinterpret_cast<{{as_cType(sType.name)}} const *>(chainedStruct);
                             result += sizeof({{as_cType(sType.name)}}Transfer);
@@ -527,6 +543,8 @@
                             break;
                         }
                     {% endfor %}
+                    // Explicitly list the Invalid enum. MSVC complains about no case labels.
+                    case WGPUSType_Invalid:
                     default:
                         // Invalid enum. Reserve space just for the transfer header (sType and hasNext).
                         result += sizeof(WGPUChainedStructTransfer);
@@ -537,14 +555,18 @@
             return result;
         }
 
-        DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct,
+        DAWN_NO_DISCARD WireResult SerializeChainedStruct({{ChainedStructPtr}} chainedStruct,
                                                           SerializeBuffer* buffer,
                                                           const ObjectIdProvider& provider) {
             ASSERT(chainedStruct != nullptr);
             ASSERT(buffer != nullptr);
             do {
                 switch (chainedStruct->sType) {
-                    {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
+                    {% for sType in types["s type"].values if (
+                            sType.valid and
+                            (sType.name.CamelCase() not in client_side_structures) and
+                            (types[sType.name.get()].output == out)
+                    ) %}
                         {% set CType = as_cType(sType.name) %}
                         case {{as_cEnum(types["s type"].name, sType.name)}}: {
 
@@ -562,6 +584,8 @@
                             chainedStruct = chainedStruct->next;
                         } break;
                     {% endfor %}
+                    // Explicitly list the Invalid enum. MSVC complains about no case labels.
+                    case WGPUSType_Invalid:
                     default: {
                         // Invalid enum. Serialize just the transfer header with Invalid as the sType.
                         // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
@@ -583,17 +607,21 @@
             return WireResult::Success;
         }
 
-        WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext,
-                                                   DeserializeBuffer* deserializeBuffer,
-                                                   DeserializeAllocator* allocator,
-                                                   const ObjectIdResolver& resolver) {
+        WireResult DeserializeChainedStruct({{ChainedStructPtr}}* outChainNext,
+                                            DeserializeBuffer* deserializeBuffer,
+                                            DeserializeAllocator* allocator,
+                                            const ObjectIdResolver& resolver) {
             bool hasNext;
             do {
                 const volatile WGPUChainedStructTransfer* header;
                 WIRE_TRY(deserializeBuffer->Peek(&header));
                 WGPUSType sType = header->sType;
                 switch (sType) {
-                    {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %}
+                    {% for sType in types["s type"].values if (
+                            sType.valid and
+                            (sType.name.CamelCase() not in client_side_structures) and
+                            (types[sType.name.get()].output == out)
+                    ) %}
                         {% set CType = as_cType(sType.name) %}
                         case {{as_cEnum(types["s type"].name, sType.name)}}: {
                             const volatile {{CType}}Transfer* transfer;
@@ -616,6 +644,8 @@
                             hasNext = transfer->chain.hasNext;
                         } break;
                     {% endfor %}
+                    // Explicitly list the Invalid enum. MSVC complains about no case labels.
+                    case WGPUSType_Invalid:
                     default: {
                         // Invalid enum. Deserialize just the transfer header with Invalid as the sType.
                         // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded.
@@ -626,8 +656,8 @@
                         const volatile WGPUChainedStructTransfer* transfer;
                         WIRE_TRY(deserializeBuffer->Read(&transfer));
 
-                        WGPUChainedStruct* outStruct;
-                        WIRE_TRY(GetSpace(allocator, sizeof(WGPUChainedStruct), &outStruct));
+                        {{ChainedStruct}}* outStruct;
+                        WIRE_TRY(GetSpace(allocator, sizeof({{ChainedStruct}}), &outStruct));
                         outStruct->sType = WGPUSType_Invalid;
                         outStruct->next = nullptr;
 
@@ -642,6 +672,10 @@
 
             return WireResult::Success;
         }
+{% endmacro %}
+
+{{ make_chained_struct_serialization_helpers(False) }}
+{{ make_chained_struct_serialization_helpers(True) }}
 
         //* Output [de]serialization helpers for commands
         {% for command in cmd_records["command"] %}
@@ -730,4 +764,38 @@
                                                    nullptr, resolver) == WireResult::Success;
         }
 
+        size_t SerializedWGPUSupportedLimitsSize(const WGPUSupportedLimits* supportedLimits) {
+            return sizeof(WGPUSupportedLimits) +
+                   WGPUSupportedLimitsGetExtraRequiredSize(*supportedLimits);
+        }
+
+        void SerializeWGPUSupportedLimits(
+            const WGPUSupportedLimits* supportedLimits,
+            char* buffer) {
+            SerializeBuffer serializeBuffer(buffer, SerializedWGPUSupportedLimitsSize(supportedLimits));
+
+            WGPUSupportedLimitsTransfer* transfer;
+
+            WireResult result = serializeBuffer.Next(&transfer);
+            ASSERT(result == WireResult::Success);
+
+            ErrorObjectIdProvider provider;
+            result = WGPUSupportedLimitsSerialize(*supportedLimits, transfer, &serializeBuffer, provider);
+            ASSERT(result == WireResult::Success);
+        }
+
+        bool DeserializeWGPUSupportedLimits(WGPUSupportedLimits* supportedLimits,
+                                            const volatile char* buffer,
+                                            size_t size) {
+            const volatile WGPUSupportedLimitsTransfer* transfer;
+            DeserializeBuffer deserializeBuffer(buffer, size);
+            if (deserializeBuffer.Read(&transfer) != WireResult::Success) {
+                return false;
+            }
+
+            ErrorObjectIdResolver resolver;
+            return WGPUSupportedLimitsDeserialize(supportedLimits, transfer, &deserializeBuffer,
+                                                  nullptr, resolver) == WireResult::Success;
+        }
+
 }  // namespace dawn_wire
diff --git a/generator/templates/webgpu.h b/generator/templates/webgpu.h
index 3b91306..e42c7ac 100644
--- a/generator/templates/webgpu.h
+++ b/generator/templates/webgpu.h
@@ -106,13 +106,20 @@
     WGPUSType sType;
 } WGPUChainedStruct;
 
+typedef struct WGPUChainedStructOut {
+    struct WGPUChainedStructOut * next;
+    WGPUSType sType;
+} WGPUChainedStructOut;
+
 {% for type in by_category["structure"] %}
     typedef struct {{as_cType(type.name)}} {
+        {% set Out = "Out" if type.output else "" %}
+        {% set const = "const" if not type.output else "" %}
         {% if type.extensible %}
-            WGPUChainedStruct const * nextInChain;
+            WGPUChainedStruct{{Out}} {{const}} * nextInChain;
         {% endif %}
         {% if type.chained %}
-            WGPUChainedStruct chain;
+            WGPUChainedStruct{{Out}} chain;
         {% endif %}
         {% for member in type.members %}
             {{as_annotated_cType(member)}};
diff --git a/generator/templates/webgpu_cpp.h b/generator/templates/webgpu_cpp.h
index dd99b11..fbb386d 100644
--- a/generator/templates/webgpu_cpp.h
+++ b/generator/templates/webgpu_cpp.h
@@ -206,9 +206,16 @@
         SType sType = SType::Invalid;
     };
 
+    struct ChainedStructOut {
+        ChainedStruct * nextInChain = nullptr;
+        SType sType = SType::Invalid;
+    };
+
     {% for type in by_category["structure"] %}
+        {% set Out = "Out" if type.output else "" %}
+        {% set const = "const" if not type.output else "" %}
         {% if type.chained %}
-            struct {{as_cppType(type.name)}} : ChainedStruct {
+            struct {{as_cppType(type.name)}} : ChainedStruct{{Out}} {
                 {{as_cppType(type.name)}}() {
                     sType = SType::{{type.name.CamelCase()}};
                 }
@@ -216,13 +223,13 @@
             struct {{as_cppType(type.name)}} {
         {% endif %}
             {% if type.extensible %}
-                ChainedStruct const * nextInChain = nullptr;
+                ChainedStruct{{Out}} {{const}} * nextInChain = nullptr;
             {% endif %}
             {% for member in type.members %}
                 {% set member_declaration = as_annotated_cppType(member) + render_cpp_default_value(member) %}
                 {% if type.chained and loop.first %}
                     //* Align the first member to ChainedStruct to match the C struct layout.
-                    alignas(ChainedStruct) {{member_declaration}};
+                    alignas(ChainedStruct{{Out}}) {{member_declaration}};
                 {% else %}
                     {{member_declaration}};
                 {% endif %}
diff --git a/src/dawn_native/Adapter.cpp b/src/dawn_native/Adapter.cpp
index 87b5f9a..5c0d873 100644
--- a/src/dawn_native/Adapter.cpp
+++ b/src/dawn_native/Adapter.cpp
@@ -20,7 +20,6 @@
 
     AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend)
         : mInstance(instance), mBackend(backend) {
-        mLimits.v1.nextInChain = nullptr;
         GetDefaultLimits(&mLimits.v1);
         mSupportedExtensions.EnableExtension(Extension::DawnInternalUsages);
     }
@@ -74,16 +73,16 @@
         // to store them (ex. by calling GetLimits directly instead). Currently,
         // we keep this function as it's only used internally in Chromium to
         // send the adapter properties across the wire.
-        GetLimits(reinterpret_cast<wgpu::Limits*>(&adapterProperties.limits));
+        GetLimits(reinterpret_cast<SupportedLimits*>(&adapterProperties.limits));
         return adapterProperties;
     }
 
-    bool AdapterBase::GetLimits(wgpu::Limits* limits) const {
+    bool AdapterBase::GetLimits(SupportedLimits* limits) const {
         ASSERT(limits != nullptr);
         if (limits->nextInChain != nullptr) {
             return false;
         }
-        *limits = mLimits.v1;
+        limits->limits = mLimits.v1;
         return true;
     }
 
@@ -125,7 +124,8 @@
 
         if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
             DAWN_TRY(ValidateLimits(
-                mLimits.v1, *reinterpret_cast<const wgpu::Limits*>(descriptor->requiredLimits)));
+                mLimits.v1,
+                reinterpret_cast<const RequiredLimits*>(descriptor->requiredLimits)->limits));
 
             if (descriptor->requiredLimits->nextInChain != nullptr) {
                 return DAWN_VALIDATION_ERROR("Unsupported limit extension struct");
diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h
index 894a7f8..32cdd71 100644
--- a/src/dawn_native/Adapter.h
+++ b/src/dawn_native/Adapter.h
@@ -52,7 +52,7 @@
             const std::vector<const char*>& requestedExtensions) const;
         WGPUDeviceProperties GetAdapterProperties() const;
 
-        bool GetLimits(wgpu::Limits* limits) const;
+        bool GetLimits(SupportedLimits* limits) const;
 
         virtual bool SupportsExternalImages() const = 0;
 
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp
index 906df18..8cbda6f 100644
--- a/src/dawn_native/DawnNative.cpp
+++ b/src/dawn_native/DawnNative.cpp
@@ -106,8 +106,8 @@
         return mImpl->GetAdapterProperties();
     }
 
-    bool Adapter::GetLimits(WGPULimits* limits) const {
-        return mImpl->GetLimits(reinterpret_cast<wgpu::Limits*>(limits));
+    bool Adapter::GetLimits(WGPUSupportedLimits* limits) const {
+        return mImpl->GetLimits(reinterpret_cast<SupportedLimits*>(limits));
     }
 
     bool Adapter::SupportsExternalImages() const {
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 7ade6ab..c209cd1 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -175,7 +175,7 @@
 
         if (descriptor != nullptr && descriptor->requiredLimits != nullptr) {
             mLimits.v1 = ReifyDefaultLimits(
-                *reinterpret_cast<const wgpu::Limits*>(descriptor->requiredLimits));
+                reinterpret_cast<const RequiredLimits*>(descriptor->requiredLimits)->limits);
         } else {
             GetDefaultLimits(&mLimits.v1);
         }
@@ -1093,6 +1093,15 @@
         }
     }
 
+    bool DeviceBase::APIGetLimits(SupportedLimits* limits) {
+        ASSERT(limits != nullptr);
+        if (limits->nextInChain != nullptr) {
+            return false;
+        }
+        limits->limits = mLimits.v1;
+        return true;
+    }
+
     void DeviceBase::APIInjectError(wgpu::ErrorType type, const char* message) {
         if (ConsumedError(ValidateErrorType(type))) {
             return;
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 3688ee1..5ec6f4a 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -209,6 +209,7 @@
 
         QueueBase* APIGetQueue();
 
+        bool APIGetLimits(SupportedLimits* limits);
         void APIInjectError(wgpu::ErrorType type, const char* message);
         bool APITick();
 
diff --git a/src/dawn_native/Limits.cpp b/src/dawn_native/Limits.cpp
index 98e067f..33cffdc 100644
--- a/src/dawn_native/Limits.cpp
+++ b/src/dawn_native/Limits.cpp
@@ -94,15 +94,15 @@
 
     }  // namespace
 
-    void GetDefaultLimits(wgpu::Limits* limits) {
+    void GetDefaultLimits(Limits* limits) {
         ASSERT(limits != nullptr);
 #define X(Better, limitName, defaultValue) limits->limitName = defaultValue;
         LIMITS(X)
 #undef X
     }
 
-    wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits) {
-        wgpu::Limits out;
+    Limits ReifyDefaultLimits(const Limits& limits) {
+        Limits out;
 #define X(Better, limitName, defaultValue)     \
     if (!IsLimitUndefined(limits.limitName)) { \
         out.limitName = limits.limitName;      \
@@ -114,8 +114,7 @@
         return out;
     }
 
-    MaybeError ValidateLimits(const wgpu::Limits& supportedLimits,
-                              const wgpu::Limits& requiredLimits) {
+    MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) {
 #define X(Better, limitName, defaultValue)                                                    \
     if (!IsLimitUndefined(requiredLimits.limitName)) {                                        \
         DAWN_TRY(CheckLimit<LimitBetterDirection::Better>::Invoke(supportedLimits.limitName,  \
diff --git a/src/dawn_native/Limits.h b/src/dawn_native/Limits.h
index c0358d9..76f8ec3 100644
--- a/src/dawn_native/Limits.h
+++ b/src/dawn_native/Limits.h
@@ -21,19 +21,18 @@
 namespace dawn_native {
 
     struct CombinedLimits {
-        wgpu::Limits v1;
+        Limits v1;
     };
 
     // Populate |limits| with the default limits.
-    void GetDefaultLimits(wgpu::Limits* limits);
+    void GetDefaultLimits(Limits* limits);
 
     // Returns a copy of |limits| where all undefined values are replaced
     // with their defaults.
-    wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits);
+    Limits ReifyDefaultLimits(const Limits& limits);
 
     // Validate that |requiredLimits| are no better than |supportedLimits|.
-    MaybeError ValidateLimits(const wgpu::Limits& supportedLimits,
-                              const wgpu::Limits& requiredLimits);
+    MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits);
 
 }  // namespace dawn_native
 
diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp
index 6ed4698..21df71e 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -196,6 +196,12 @@
         return Buffer::CreateError(this);
     }
 
+    bool Device::GetLimits(WGPUSupportedLimits* limits) {
+        // Not implemented in the wire.
+        UNREACHABLE();
+        return false;
+    }
+
     WGPUQueue Device::GetQueue() {
         // The queue is lazily created because if a Device is created by
         // Reserve/Inject, we cannot send the GetQueue message until
diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h
index 849364f..ae2d9fd 100644
--- a/src/dawn_wire/client/Device.h
+++ b/src/dawn_wire/client/Device.h
@@ -64,6 +64,7 @@
                                                  WGPUCreatePipelineAsyncStatus status,
                                                  const char* message);
 
+        bool GetLimits(WGPUSupportedLimits* limits);
         WGPUQueue GetQueue();
 
         void CancelCallbacksForDisconnect() override;
diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h
index cb8de7c..b070030 100644
--- a/src/include/dawn_native/DawnNative.h
+++ b/src/include/dawn_native/DawnNative.h
@@ -67,7 +67,7 @@
         std::vector<const char*> forceEnabledToggles;
         std::vector<const char*> forceDisabledToggles;
 
-        const WGPULimits* requiredLimits = nullptr;
+        const WGPURequiredLimits* requiredLimits = nullptr;
     };
 
     // A struct to record the information of a toggle. A toggle is a code path in Dawn device that
@@ -110,7 +110,7 @@
 
         std::vector<const char*> GetSupportedExtensions() const;
         WGPUDeviceProperties GetAdapterProperties() const;
-        bool GetLimits(WGPULimits* limits) const;
+        bool GetLimits(WGPUSupportedLimits* limits) const;
 
         // Check that the Adapter is able to support importing external images. This is necessary
         // to implement the swapchain and interop APIs in Chromium.
diff --git a/src/include/dawn_wire/Wire.h b/src/include/dawn_wire/Wire.h
index 4d69c95..0c11d91 100644
--- a/src/include/dawn_wire/Wire.h
+++ b/src/include/dawn_wire/Wire.h
@@ -61,6 +61,16 @@
                                                           const volatile char* deserializeBuffer,
                                                           size_t deserializeBufferSize);
 
+    DAWN_WIRE_EXPORT size_t
+    SerializedWGPUSupportedLimitsSize(const WGPUSupportedLimits* supportedLimits);
+
+    DAWN_WIRE_EXPORT void SerializeWGPUSupportedLimits(const WGPUSupportedLimits* supportedLimits,
+                                                       char* serializeBuffer);
+
+    DAWN_WIRE_EXPORT bool DeserializeWGPUSupportedLimits(WGPUSupportedLimits* supportedLimits,
+                                                         const volatile char* deserializeBuffer,
+                                                         size_t deserializeBufferSize);
+
 }  // namespace dawn_wire
 
 #endif  // DAWNWIRE_WIRE_H_
diff --git a/src/tests/unittests/validation/RequestDeviceValidationTests.cpp b/src/tests/unittests/validation/RequestDeviceValidationTests.cpp
index c456122..5e8bd9b 100644
--- a/src/tests/unittests/validation/RequestDeviceValidationTests.cpp
+++ b/src/tests/unittests/validation/RequestDeviceValidationTests.cpp
@@ -29,6 +29,10 @@
         EXPECT_EQ(status, WGPURequestDeviceStatus_Success);
         EXPECT_NE(device, nullptr);
         EXPECT_STREQ(message, nullptr);
+        if (userdata != nullptr) {
+            CallCheckDevice(static_cast<std::function<void(wgpu::Device)>*>(userdata),
+                            std::move(device));
+        }
     }
 
     static void ExpectRequestDeviceError(WGPURequestDeviceStatus status,
@@ -40,73 +44,136 @@
         EXPECT_EQ(device, nullptr);
         EXPECT_STRNE(message, nullptr);
     }
+
+    template <typename F>
+    static void* CheckDevice(F&& f) {
+        return new std::function<void(wgpu::Device)>(f);
+    }
+
+    static void CallCheckDevice(std::function<void(wgpu::Device)>* f, wgpu::Device d) {
+        (*f)(std::move(d));
+        delete f;
+    }
 };
 
 // Test that requesting a device without specifying limits is valid.
 TEST_F(RequestDeviceValidationTest, NoRequiredLimits) {
     dawn_native::DeviceDescriptor descriptor;
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
+                          CheckDevice([](wgpu::Device device) {
+                              // Check one of the default limits.
+                              wgpu::SupportedLimits limits;
+                              device.GetLimits(&limits);
+                              EXPECT_EQ(limits.limits.maxBindGroups, 4u);
+                          }));
 }
 
 // Test that requesting a device with the default limits is valid.
 TEST_F(RequestDeviceValidationTest, DefaultLimits) {
-    wgpu::Limits limits = {};
+    wgpu::RequiredLimits limits = {};
     dawn_native::DeviceDescriptor descriptor;
-    descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    descriptor.requiredLimits = reinterpret_cast<const WGPURequiredLimits*>(&limits);
+    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
+                          CheckDevice([](wgpu::Device device) {
+                              // Check one of the default limits.
+                              wgpu::SupportedLimits limits;
+                              device.GetLimits(&limits);
+                              EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+                          }));
 }
 
 // Test that requesting a device where a required limit is above the maximum value.
 TEST_F(RequestDeviceValidationTest, HigherIsBetter) {
-    wgpu::Limits limits = {};
+    wgpu::RequiredLimits limits = {};
     dawn_native::DeviceDescriptor descriptor;
-    descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
+    descriptor.requiredLimits = reinterpret_cast<const WGPURequiredLimits*>(&limits);
 
-    wgpu::Limits supportedLimits;
-    EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPULimits*>(&supportedLimits)));
+    wgpu::SupportedLimits supportedLimits;
+    EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPUSupportedLimits*>(&supportedLimits)));
 
     // Test below the max.
-    limits.maxBindGroups = supportedLimits.maxBindGroups - 1;
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups - 1;
+    adapter.RequestDevice(
+        &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+            wgpu::SupportedLimits limits;
+            device.GetLimits(&limits);
+
+            // Check we got exactly the request.
+            EXPECT_EQ(limits.limits.maxBindGroups, supportedLimits.limits.maxBindGroups - 1);
+            // Check another default limit.
+            EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+        }));
 
     // Test the max.
-    limits.maxBindGroups = supportedLimits.maxBindGroups;
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups;
+    adapter.RequestDevice(
+        &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) {
+            wgpu::SupportedLimits limits;
+            device.GetLimits(&limits);
+
+            // Check we got exactly the request.
+            EXPECT_EQ(limits.limits.maxBindGroups, supportedLimits.limits.maxBindGroups);
+            // Check another default limit.
+            EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+        }));
 
     // Test above the max.
-    limits.maxBindGroups = supportedLimits.maxBindGroups + 1;
+    limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups + 1;
     adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
 }
 
 // Test that requesting a device where a required limit is below the minimum value.
 TEST_F(RequestDeviceValidationTest, LowerIsBetter) {
-    wgpu::Limits limits = {};
+    wgpu::RequiredLimits limits = {};
     dawn_native::DeviceDescriptor descriptor;
-    descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
+    descriptor.requiredLimits = reinterpret_cast<const WGPURequiredLimits*>(&limits);
 
-    wgpu::Limits supportedLimits;
-    EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPULimits*>(&supportedLimits)));
+    wgpu::SupportedLimits supportedLimits;
+    EXPECT_TRUE(adapter.GetLimits(reinterpret_cast<WGPUSupportedLimits*>(&supportedLimits)));
 
     // Test below the min.
-    limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment / 2;
+    limits.limits.minUniformBufferOffsetAlignment =
+        supportedLimits.limits.minUniformBufferOffsetAlignment / 2;
     adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
 
     // Test the min.
-    limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    limits.limits.minUniformBufferOffsetAlignment =
+        supportedLimits.limits.minUniformBufferOffsetAlignment;
+    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
+                          CheckDevice([&](wgpu::Device device) {
+                              wgpu::SupportedLimits limits;
+                              device.GetLimits(&limits);
+
+                              // Check we got exactly the request.
+                              EXPECT_EQ(limits.limits.minUniformBufferOffsetAlignment,
+                                        supportedLimits.limits.minUniformBufferOffsetAlignment);
+                              // Check another default limit.
+                              EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+                          }));
 
     // Test above the min.
-    limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment * 2;
-    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr);
+    limits.limits.minUniformBufferOffsetAlignment =
+        supportedLimits.limits.minUniformBufferOffsetAlignment * 2;
+    adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess,
+                          CheckDevice([&](wgpu::Device device) {
+                              wgpu::SupportedLimits limits;
+                              device.GetLimits(&limits);
+
+                              // Check we got exactly the request.
+                              EXPECT_EQ(limits.limits.minUniformBufferOffsetAlignment,
+                                        supportedLimits.limits.minUniformBufferOffsetAlignment * 2);
+                              // Check another default limit.
+                              EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u);
+                          }));
 }
 
 // Test that it is an error to request limits with an invalid chained struct
 TEST_F(RequestDeviceValidationTest, InvalidChainedStruct) {
     wgpu::PrimitiveDepthClampingState depthClamp = {};
-    wgpu::Limits limits = {};
+    wgpu::RequiredLimits limits = {};
     limits.nextInChain = &depthClamp;
 
     dawn_native::DeviceDescriptor descriptor;
-    descriptor.requiredLimits = reinterpret_cast<const WGPULimits*>(&limits);
+    descriptor.requiredLimits = reinterpret_cast<const WGPURequiredLimits*>(&limits);
     adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr);
 }