[wire] Cpp-ify the Dawn wire client API so that we can spanify.

Bug: 526537254
Change-Id: Ibb4e98127464152b337642f9c2ae05d0dfbbcc22
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/326956
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 2c85853..3e9828c 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -2022,6 +2022,10 @@
                     'src/dawn/wire/client/' + metadata.namespace +
                     '_structs_autogen.cpp', wire_params))
             renders.append(
+                FileRender('dawn/wire/client/dawn_platform.h',
+                           'src/dawn/wire/client/' + prefix + '_platform.h',
+                           wire_params))
+            renders.append(
                 FileRender('dawn/wire/server/ServerBase.h',
                            'src/dawn/wire/server/ServerBase_autogen.h',
                            wire_params))
diff --git a/generator/templates/dawn/wire/client/ApiObjects.h b/generator/templates/dawn/wire/client/ApiObjects.h
index faddfc6..e945a4a 100644
--- a/generator/templates/dawn/wire/client/ApiObjects.h
+++ b/generator/templates/dawn/wire/client/ApiObjects.h
@@ -50,13 +50,6 @@
             };
         {% endif %}
 
-        inline {{Type}}* FromAPI(WGPU{{Type}} obj) {
-            return reinterpret_cast<{{Type}}*>(obj);
-        }
-        inline WGPU{{Type}} ToAPI({{Type}}* obj) {
-            return reinterpret_cast<WGPU{{Type}}>(obj);
-        }
-
         template <>
         inline constexpr ObjectType ObjectTypeToTypeEnum<{{Type}}> = ObjectType::{{Type}};
 
diff --git a/generator/templates/dawn/wire/client/ApiProcs.cpp b/generator/templates/dawn/wire/client/ApiProcs.cpp
index e92564b..b3aea8e 100644
--- a/generator/templates/dawn/wire/client/ApiProcs.cpp
+++ b/generator/templates/dawn/wire/client/ApiProcs.cpp
@@ -25,6 +25,8 @@
 //* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 //* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+{% set Prefix = metadata.proc_table_prefix %}
+{% set prefix = Prefix.lower() %}
 #include <algorithm>
 #include <cstring>
 #include <string_view>
@@ -32,9 +34,70 @@
 #include <utility>
 #include <vector>
 
+#include "dawn/wire/client/{{prefix}}_platform.h"
 #include "dawn/wire/client/webgpu.h"
 #include "dawn/dawn_version.h"
 #include "src/dawn/wire/client/Client.h"
+#include "src/utils/span.h"
+
+{%- macro convert_arguments_and_call(function, suffix, call_receiver, first_arg = None) -%}
+    {% set cppify = not suffix in function_cpp_blocklist %}
+    {% set client = "dawn::wire::client" %}
+
+    {% for arg in function.arguments %}
+        {% set varName = as_varName(arg.name) %}
+        {% if cppify and arg.is_length %}
+            //* Skip as it's included in the span just below.
+        {% elif cppify and arg.length and arg.length != "constant" %}
+            // TODO(https://crbug.com/524405497): Support fixed-length spans.
+            {% if arg.type.name.canonical_case() == "void" %}
+                using {{varName}}SpanT = {% if arg.annotation == "const*" %}const {% endif %}std::byte;
+                auto {{varName}}Ptr = reinterpret_cast<{{varName}}SpanT*>({{varName}});
+            {% else %}
+                using {{varName}}SpanT = std::remove_pointer_t<{% if arg.type.category == "object" %}{{client}}::{% endif %}{{decorate(as_wire_clientType(arg.type), arg)}}>;
+                auto {{varName}}Ptr = {{client}}::FromAPI({{varName}});
+            {% endif %}
+            // SAFETY: The webgpu.h user is required to pass valid ranges of objects.
+            auto {{varName}}_ = DAWN_UNSAFE_BUFFERS(dawn::Span<{{varName}}SpanT>({{varName}}Ptr, {{as_varName(arg.length.name)}}));
+        {% elif cppify and arg.type.category == "structure" %}
+            auto {{varName}}_ = {{client}}::FromAPI({{varName}});
+        {% elif cppify and arg.type.category in ["enum", "bitmask"] and arg.annotation == "value" %}
+            auto {{varName}}_ = static_cast<{{as_wire_clientType(arg.type)}}>({{varName}});
+        {% elif cppify and arg.type.category == "object" %}
+            auto {{varName}}_ = {{client}}::FromAPI({{varName}});
+        {% elif cppify and arg.annotation != "value" %}
+            auto {{varName}}_ = reinterpret_cast<{{decorate(as_wire_clientType(arg.type), arg)}}>({{varName}});
+        {% else %}
+            auto {{varName}}_ = {{as_varName(arg.name)}};
+        {% endif %}
+    {%- endfor-%}
+
+    {% if function.returns %}
+        auto result =
+    {%- endif %}
+    {{call_receiver}}(
+        {%- if first_arg -%}
+            {{first_arg}} {%- if len(function.arguments) != 0 %}, {% endif -%}
+        {%- endif -%}
+        {%- for arg in function.arguments if (not cppify or not arg.is_length) -%}
+            {%- if not loop.first %}, {% endif -%}
+            {{as_varName(arg.name)}}_
+        {%- endfor -%}
+    );
+    {% if function.returns %}
+        {% if cppify %}
+            {% if function.returns.type.category in ["object", "enum", "bitmask"] %}
+                return {{client}}::ToAPI(result);
+            {% elif function.returns.type.category in ["structure"] %}
+                return *{{client}}::ToAPI(&result);
+            {% else %}
+                return result;
+            {% endif %}
+        {% else %}
+            return result;
+        {% endif %}
+    {% endif %}
+{%- endmacro %}
 
 namespace dawn::wire::client {
 
@@ -58,8 +121,9 @@
 
 //* Implementation of the client API functions.
 {% for (type, methods) in c_methods_sorted_by_parent %}
-    {%- set Type = "dawn::wire::client::" + type.name.CamelCase() -%}
+    {%- set Type = type.name.CamelCase() -%}
     {%- set cType = as_cType(type.name) -%}
+    {%- set client = "dawn::wire::client" -%}
 
     {% for method in methods %}
         {% set Suffix = as_MethodSuffix(type.name, method.name) %}
@@ -71,7 +135,7 @@
             {%- endfor -%}
         ) {
             {% if Suffix not in client_handwritten_commands %}
-                auto self = reinterpret_cast<dawn::wire::client::{{as_wireType(type)}}>(cSelf);
+                auto self = {{client}}::FromAPI(cSelf);
                 dawn::wire::{{Suffix}}Cmd cmd;
 
                 //* Create the structure going on the wire on the stack and fill it with the value
@@ -80,8 +144,8 @@
 
                 //* For object creation, store the object ID the client will use for the result.
                 {% if method.returns and method.returns.type.category == "object" %}
-                    {% set ReturnObj = "dawn::wire::client::" + method.returns.type.name.CamelCase() %}
-                    {{ReturnObj}}* returnObject = dawn::wire::client::Create<dawn::wire::client::{{as_wireType(type)}}, {{ReturnObj}}>(self
+                    {% set ReturnObj = client + "::" + method.returns.type.name.CamelCase() %}
+                    {{ReturnObj}}* returnObject = {{client}}::Create<{{client}}::{{as_wireType(type)}}, {{ReturnObj}}>(self
                         {%- for arg in method.arguments -%}
                                 , {{as_varName(arg.name)}}
                         {%- endfor -%}
@@ -99,16 +163,13 @@
                 self->GetClient()->SerializeCommand(cmd);
 
                 {% if method.returns and method.returns.type.category == "object" %}
-                    return ToAPI(returnObject);
+                    return {{client}}::ToAPI(returnObject);
                 {% endif %}
             {% elif type.category == "object" %}
-                auto self = reinterpret_cast<dawn::wire::client::{{as_wireType(type)}}>(cSelf);
-                return self->API{{method.name.CamelCase()}}(
-                    {%- for arg in method.arguments -%}
-                        {%if not loop.first %}, {% endif %} {{as_varName(arg.name)}}
-                    {%- endfor -%});
+                auto self = {{client}}::FromAPI(cSelf);
+                {{convert_arguments_and_call(method, Suffix, "self->API" + method.name.CamelCase())}}
             {% elif type.category == "structure" %}
-                return dawn::wire::client::API{{method.name.CamelCase()}}(cSelf);
+                return {{client}}::API{{method.name.CamelCase()}}(cSelf);
             {% endif %}
         }
     {% endfor %}
diff --git a/generator/templates/dawn/wire/client/dawn_platform.h b/generator/templates/dawn/wire/client/dawn_platform.h
new file mode 100644
index 0000000..b7726cc
--- /dev/null
+++ b/generator/templates/dawn/wire/client/dawn_platform.h
@@ -0,0 +1,127 @@
+//* Copyright 2026 The Dawn & Tint Authors
+//*
+//* Redistribution and use in source and binary forms, with or without
+//* modification, are permitted provided that the following conditions are met:
+//*
+//* 1. Redistributions of source code must retain the above copyright notice, this
+//*    list of conditions and the following disclaimer.
+//*
+//* 2. Redistributions in binary form must reproduce the above copyright notice,
+//*    this list of conditions and the following disclaimer in the documentation
+//*    and/or other materials provided with the distribution.
+//*
+//* 3. Neither the name of the copyright holder nor the names of its
+//*    contributors may be used to endorse or promote products derived from
+//*    this software without specific prior written permission.
+//*
+//* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//* TODO(https://crbug.com/526537254): It might be possible to merge this with the
+//* dawn_platform.h in native once we finish migrating both native and wire.
+
+{% set PREFIX = metadata.proc_table_prefix.upper() %}
+#ifndef DAWNWIRE_CLIENT_{{PREFIX}}_PLATFORM_AUTOGEN_H_
+#define DAWNWIRE_CLIENT_{{PREFIX}}_PLATFORM_AUTOGEN_H_
+
+{% set api = metadata.api.lower() %}
+#include "dawn/{{api}}_cpp.h"
+
+{% set namespace = metadata.namespace %}
+#include "dawn/wire/client/{{namespace}}_structs_autogen.h"
+
+namespace dawn::wire::client {
+    {% for type in by_category["object"] %}
+        {% if type.name.CamelCase() in client_special_objects %}
+            class {{type.name.CamelCase()}};
+        {% else %}
+            struct {{type.name.CamelCase()}};
+        {% endif %}
+    {% endfor %}
+}
+
+{% macro render_structure_conversions(name) -%}
+    inline const {{as_cType(name)}}* ToAPI(const {{as_cppType(name)}}* rhs) {
+        return reinterpret_cast<const {{as_cType(name)}}*>(rhs);
+    }
+    inline {{as_cType(name)}}* ToAPI({{as_cppType(name)}}* rhs) {
+        return reinterpret_cast<{{as_cType(name)}}*>(rhs);
+    }
+    inline const {{as_cppType(name)}}* FromAPI(const {{as_cType(name)}}* rhs) {
+        return reinterpret_cast<const {{as_cppType(name)}}*>(rhs);
+    }
+    inline {{as_cppType(name)}}* FromAPI({{as_cType(name)}}* rhs) {
+        return reinterpret_cast<{{as_cppType(name)}}*>(rhs);
+    }
+    inline const {{as_cppType(name)}}& FromAPI(const {{as_cType(name)}}& rhs) {
+        return *reinterpret_cast<const {{as_cppType(name)}}*>(&rhs);
+    }
+    inline {{as_cppType(name)}}& FromAPI({{as_cType(name)}}& rhs) {
+        return *reinterpret_cast<{{as_cppType(name)}}*>(&rhs);
+    }
+{%- endmacro %}
+
+namespace dawn::wire::client {
+
+    {% set ChainedStructName = Name("chained struct") %}
+    {{render_structure_conversions(ChainedStructName)|indent}}
+
+    {% for type in by_category["structure"] if type.name.get() != "string view" %}
+        {{render_structure_conversions(type.name)|indent}}
+
+    {% endfor %}
+
+    {% for type in by_category["object"] %}
+        {% set Type = type.name.CamelCase() %}
+        inline WGPU{{Type}} ToAPI({{Type}}* rhs) {
+            return reinterpret_cast<WGPU{{Type}}>(rhs);
+        }
+
+        inline {{Type}}* FromAPI(WGPU{{Type}} rhs) {
+            return reinterpret_cast<{{Type}}*>(rhs);
+        }
+        inline const {{Type}}* const* FromAPI(const WGPU{{Type}}* rhs) {
+            return reinterpret_cast<const {{Type}}* const*>(rhs);
+        }
+        inline {{Type}}* const* FromAPI(WGPU{{Type}}* rhs) {
+            return reinterpret_cast<{{Type}}* const*>(rhs);
+        }
+    {% endfor %}
+
+    //* Special structs that we want to allow copies for since they are trivial.
+    inline WGPUFuture ToAPI(Future rhs) {
+        return {rhs.id};
+    }
+    inline Future FromAPI(WGPUFuture rhs) {
+        return Future{rhs.id};
+    }
+    inline WGPUStringView ToAPI(StringView rhs) {
+        return {rhs.data, rhs.length};
+    }
+    inline StringView FromAPI(WGPUStringView rhs) {
+        return StringView(rhs);
+    }
+
+    {% for type in by_category["enum"] + by_category["bitmask"] %}
+        inline {{as_cType(type.name)}} ToAPI({{namespace}}::{{as_cppType(type.name)}} rhs) {
+            return static_cast<{{as_cType(type.name)}}>(rhs);
+        }
+    {% endfor %}
+
+    {% for type in by_category["enum"] %}
+        inline {{namespace}}::{{as_cppType(type.name)}} FromAPI({{as_cType(type.name)}} rhs) {
+            return static_cast<{{namespace}}::{{as_cppType(type.name)}}>(rhs);
+        }
+    {% endfor %}
+
+}  // namespace dawn::wire::client
+
+#endif  // DAWNWIRE_CLIENT_{{PREFIX}}_PLATFORM_AUTOGEN_H_
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 9cd3baa..3865997 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -245,6 +245,63 @@
         ]
     },
     "special items": {
+        "_function_cpp_blocklist_comment": "// TODO(https://crbug.com/526537254): Drive this list down.",
+        "function_cpp_blocklist": [
+            "AdapterCreateDevice",
+            "AdapterGetFeatures",
+            "AdapterGetFormatCapabilities",
+            "AdapterGetInfo",
+            "AdapterGetLimits",
+            "AdapterGetInstance",
+            "AdapterHasFeature",
+            "AdapterRequestDevice",
+            "BufferGetMapState",
+            "BufferGetUsage",
+            "BufferMapAsync",
+            "BufferReadMappedRange",
+            "BufferWriteMappedRange",
+            "ComputePassEncoderSetImmediates",
+            "DeviceCreateBuffer",
+            "DeviceCreateComputePipelineAsync",
+            "DeviceCreateErrorBuffer",
+            "DeviceCreateErrorTexture",
+            "DeviceCreateRenderPipelineAsync",
+            "DeviceCreateResourceTable",
+            "DeviceCreateTexture",
+            "DeviceGetAdapter",
+            "DeviceGetAdapterInfo",
+            "DeviceGetFeatures",
+            "DeviceGetLimits",
+            "DeviceGetLostFuture",
+            "DeviceGetQueue",
+            "DeviceHasFeature",
+            "DeviceInjectError",
+            "DevicePopErrorScope",
+            "InstanceCreateSurface",
+            "InstanceGetWGSLLanguageFeatures",
+            "InstanceHasWGSLLanguageFeature",
+            "InstanceRequestAdapter",
+            "InstanceWaitAny",
+            "QuerySetGetType",
+            "QueueOnSubmittedWorkDone",
+            "QueueSubmit",
+            "QueueWriteBuffer",
+            "QueueWriteTexture",
+            "RenderBundleEncoderSetImmediates",
+            "RenderPassEncoderSetImmediates",
+            "ResourceTableInsert",
+            "ResourceTableRemove",
+            "ResourceTableUpdate",
+            "ShaderModuleGetCompilationInfo",
+            "SurfaceConfigure",
+            "SurfaceGetCapabilities",
+            "SurfaceGetCurrentTexture",
+            "SurfacePresent",
+            "TextureGetDimension",
+            "TextureGetFormat",
+            "TextureGetTextureBindingViewDimension",
+            "TextureGetUsage"
+        ],
         "client_side_structures": [
             "FutureWaitInfo",
             "SurfaceDescriptorFromWindowsCoreWindow",
diff --git a/src/dawn/wire/BUILD.gn b/src/dawn/wire/BUILD.gn
index b415a9a..78e9bf9 100644
--- a/src/dawn/wire/BUILD.gn
+++ b/src/dawn/wire/BUILD.gn
@@ -58,6 +58,7 @@
     "${dawn_template_dir}/dawn/wire/client/ClientPrototypes.inc",
     "${dawn_template_dir}/dawn/wire/client/api_structs.h",
     "${dawn_template_dir}/dawn/wire/client/api_structs.cpp",
+    "${dawn_template_dir}/dawn/wire/client/dawn_platform.h",
     "${dawn_template_dir}/dawn/wire/server/ServerBase.h",
     "${dawn_template_dir}/dawn/wire/server/ServerDoers.cpp",
     "${dawn_template_dir}/dawn/wire/server/ServerHandlers.cpp",
@@ -75,6 +76,7 @@
     "src/dawn/wire/client/ClientPrototypes_autogen.inc",
     "src/dawn/wire/client/wgpu_structs_autogen.h",
     "src/dawn/wire/client/wgpu_structs_autogen.cpp",
+    "src/dawn/wire/client/dawn_platform.h",
     "src/dawn/wire/server/ServerBase_autogen.h",
     "src/dawn/wire/server/ServerDoers_autogen.cpp",
     "src/dawn/wire/server/ServerHandlers_autogen.cpp",
diff --git a/src/dawn/wire/client/ObjectBase.h b/src/dawn/wire/client/ObjectBase.h
index c9b9e93..d59bf84 100644
--- a/src/dawn/wire/client/ObjectBase.h
+++ b/src/dawn/wire/client/ObjectBase.h
@@ -31,6 +31,7 @@
 #include <webgpu/webgpu.h>
 
 #include "dawn/wire/ObjectType_autogen.h"
+#include "dawn/wire/client/dawn_platform.h"
 #include "partition_alloc/pointers/raw_ptr.h"
 #include "src/dawn/common/Ref.h"
 #include "src/dawn/common/RefCounted.h"