Make the templates of webgpu_absl_format flexible

Move the manually parts to src/dawn_native/webgpu_absl_format.cpp/h.
Rename the template webgpu_absl_format.cpp/h to api_absl_format.cpp.h .

BUG=dawn:1201, dawn:563
Change-Id: Ibbeea43227f4fcf7f1d6b1d0bc3927226e79e6c3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/74300
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Junwei Fu <junwei.fu@intel.com>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index fa8a1ee..7ecf906 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -923,12 +923,12 @@
                            'src/' + native_dir + '/ChainUtils_autogen.cpp',
                            frontend_params))
             renders.append(
-                FileRender('dawn_native/webgpu_absl_format.h',
-                           'src/dawn_native/webgpu_absl_format_autogen.h',
+                FileRender('dawn_native/api_absl_format.h',
+                           'src/' + native_dir + '/' + api + '_absl_format_autogen.h',
                            frontend_params))
             renders.append(
-                FileRender('dawn_native/webgpu_absl_format.cpp',
-                           'src/dawn_native/webgpu_absl_format_autogen.cpp',
+                FileRender('dawn_native/api_absl_format.cpp',
+                           'src/' + native_dir + '/' + api + '_absl_format_autogen.cpp',
                            frontend_params))
             renders.append(
                 FileRender('dawn_native/ObjectType.h',
diff --git a/generator/templates/dawn_native/api_absl_format.cpp b/generator/templates/dawn_native/api_absl_format.cpp
new file mode 100644
index 0000000..8145990
--- /dev/null
+++ b/generator/templates/dawn_native/api_absl_format.cpp
@@ -0,0 +1,131 @@
+//* Copyright 2021 The Dawn Authors
+//*
+//* Licensed under the Apache License, Version 2.0 (the "License");
+//* you may not use this file except in compliance with the License.
+//* You may obtain a copy of the License at
+//*
+//*     http://www.apache.org/licenses/LICENSE-2.0
+//*
+//* Unless required by applicable law or agreed to in writing, software
+//* distributed under the License is distributed on an "AS IS" BASIS,
+//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//* See the License for the specific language governing permissions and
+//* limitations under the License.
+
+{% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
+{% set native_namespace = Name(metadata.native_namespace).snake_case() %}
+{% set native_dir = impl_dir + native_namespace %}
+{% set api = metadata.api.lower() %}
+#include "{{native_dir}}/{{api}}_absl_format_autogen.h"
+
+#include "{{native_dir}}/ObjectType_autogen.h"
+
+namespace {{native_namespace}} {
+
+    //
+    // Descriptors
+    //
+
+    {% for type in by_category["structure"] %}
+        {% for member in type.members %}
+            {% if member.name.canonical_case() == "label" %}
+                absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+                AbslFormatConvert(const {{as_cppType(type.name)}}* value,
+                                    const absl::FormatConversionSpec& spec,
+                                    absl::FormatSink* s) {
+                    if (value == nullptr) {
+                        s->Append("[null]");
+                        return {true};
+                    }
+                    s->Append("[{{as_cppType(type.name)}}");
+                    if (value->label != nullptr) {
+                        s->Append(absl::StrFormat(" \"%s\"", value->label));
+                    }
+                    s->Append("]");
+                    return {true};
+                }
+            {% endif %}
+        {% endfor %}
+    {% endfor %}
+
+}  // namespace {{native_namespace}}
+
+{% set namespace = metadata.namespace %}
+namespace {{namespace}} {
+
+    //
+    // Enums
+    //
+
+    {% for type in by_category["enum"] %}
+        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+        AbslFormatConvert({{as_cppType(type.name)}} value,
+                            const absl::FormatConversionSpec& spec,
+                            absl::FormatSink* s) {
+            s->Append("{{as_cppType(type.name)}}::");
+            switch (value) {
+            {% for value in type.values %}
+                case {{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}:
+                s->Append("{{as_cppEnum(value.name)}}");
+                break;
+            {% endfor %}
+                default:
+                s->Append(absl::StrFormat("%x", static_cast<typename std::underlying_type<{{as_cppType(type.name)}}>::type>(value)));
+            }
+            return {true};
+        }
+    {% endfor %}
+
+    //
+    // Bitmasks
+    //
+
+    {% for type in by_category["bitmask"] %}
+        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+        AbslFormatConvert({{as_cppType(type.name)}} value,
+                            const absl::FormatConversionSpec& spec,
+                            absl::FormatSink* s) {
+            s->Append("{{as_cppType(type.name)}}::");
+            if (!static_cast<bool>(value)) {
+                {% for value in type.values if value.value == 0 %}
+                    // 0 is often explicitly declared as None.
+                    s->Append("{{as_cppEnum(value.name)}}");
+                {% else %}
+                    s->Append(absl::StrFormat("{{as_cppType(type.name)}}::%x", 0));
+                {% endfor %}
+                return {true};
+            }
+
+            bool moreThanOneBit = !HasZeroOrOneBits(value);
+            if (moreThanOneBit) {
+                s->Append("(");
+            }
+
+            bool first = true;
+            {% for value in type.values if value.value != 0 %}
+                if (value & {{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}) {
+                    if (!first) {
+                        s->Append("|");
+                    }
+                    first = false;
+                    s->Append("{{as_cppEnum(value.name)}}");
+                    value &= ~{{as_cppType(type.name)}}::{{as_cppEnum(value.name)}};
+                }
+            {% endfor %}
+
+            if (static_cast<bool>(value)) {
+                if (!first) {
+                    s->Append("|");
+                }
+                s->Append(absl::StrFormat("{{as_cppType(type.name)}}::%x", static_cast<typename std::underlying_type<{{as_cppType(type.name)}}>::type>(value)));
+            }
+
+            if (moreThanOneBit) {
+                s->Append(")");
+            }
+
+            return {true};
+        }
+    {% endfor %}
+
+}  // namespace {{namespace}}
diff --git a/generator/templates/dawn_native/api_absl_format.h b/generator/templates/dawn_native/api_absl_format.h
new file mode 100644
index 0000000..efca15b
--- /dev/null
+++ b/generator/templates/dawn_native/api_absl_format.h
@@ -0,0 +1,73 @@
+//* Copyright 2021 The Dawn Authors
+//*
+//* Licensed under the Apache License, Version 2.0 (the "License");
+//* you may not use this file except in compliance with the License.
+//* You may obtain a copy of the License at
+//*
+//*     http://www.apache.org/licenses/LICENSE-2.0
+//*
+//* Unless required by applicable law or agreed to in writing, software
+//* distributed under the License is distributed on an "AS IS" BASIS,
+//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//* See the License for the specific language governing permissions and
+//* limitations under the License.
+
+{% set API = metadata.api.upper() %}
+#ifndef {{API}}_ABSL_FORMAT_H_
+#define {{API}}_ABSL_FORMAT_H_
+
+{% set impl_dir = metadata.impl_dir + "/" if metadata.impl_dir else "" %}
+{% set native_namespace = Name(metadata.native_namespace).snake_case() %}
+{% set native_dir = impl_dir + native_namespace %}
+{% set prefix = metadata.proc_table_prefix.lower() %}
+#include "{{native_dir}}/{{prefix}}_platform.h"
+
+#include "absl/strings/str_format.h"
+
+namespace {{native_namespace}} {
+
+    //
+    // Descriptors
+    //
+
+    // Only includes structures that have a 'label' member.
+    {% for type in by_category["structure"] %}
+        {% for member in type.members %}
+            {% if member.name.canonical_case() == "label" %}
+                absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+                AbslFormatConvert(const {{as_cppType(type.name)}}* value,
+                                  const absl::FormatConversionSpec& spec,
+                                  absl::FormatSink* s);
+            {% endif %}
+        {% endfor %}
+    {% endfor %}
+} // namespace {{native_namespace}}
+
+{% set namespace = metadata.namespace %}
+namespace {{namespace}} {
+
+    //
+    // Enums
+    //
+
+    {% for type in by_category["enum"] %}
+        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+        AbslFormatConvert({{as_cppType(type.name)}} value,
+                          const absl::FormatConversionSpec& spec,
+                          absl::FormatSink* s);
+    {% endfor %}
+
+    //
+    // Bitmasks
+    //
+
+    {% for type in by_category["bitmask"] %}
+        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+        AbslFormatConvert({{as_cppType(type.name)}} value,
+                          const absl::FormatConversionSpec& spec,
+                          absl::FormatSink* s);
+    {% endfor %}
+
+}  // namespace {{namespace}}
+
+#endif // {{API}}_ABSL_FORMAT_H_
diff --git a/generator/templates/dawn_native/webgpu_absl_format.cpp b/generator/templates/dawn_native/webgpu_absl_format.cpp
deleted file mode 100644
index ec9f382..0000000
--- a/generator/templates/dawn_native/webgpu_absl_format.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-//* Copyright 2021 The Dawn Authors
-//*
-//* Licensed under the Apache License, Version 2.0 (the "License");
-//* you may not use this file except in compliance with the License.
-//* You may obtain a copy of the License at
-//*
-//*     http://www.apache.org/licenses/LICENSE-2.0
-//*
-//* Unless required by applicable law or agreed to in writing, software
-//* distributed under the License is distributed on an "AS IS" BASIS,
-//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//* See the License for the specific language governing permissions and
-//* limitations under the License.
-
-#include "dawn_native/webgpu_absl_format_autogen.h"
-
-#include "dawn_native/Device.h"
-#include "dawn_native/ObjectBase.h"
-#include "dawn_native/ObjectType_autogen.h"
-#include "dawn_native/Texture.h"
-
-namespace dawn_native {
-
-    //
-    // Structs (Manually written)
-    //
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Color* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append(absl::StrFormat("[Color r:%f, g:%f, b:%f, a:%f]",
-            value->r, value->g, value->b, value->a));
-        return {true};
-    }
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Extent3D* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append(absl::StrFormat("[Extent3D width:%u, height:%u, depthOrArrayLayers:%u]",
-            value->width, value->height, value->depthOrArrayLayers));
-        return {true};
-    }
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Origin3D* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append(absl::StrFormat("[Origin3D x:%u, y:%u, z:%u]",
-            value->x, value->y, value->z));
-        return {true};
-    }
-
-    //
-    // Objects
-    //
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const DeviceBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append("[Device");
-        const std::string& label = value->GetLabel();
-        if (!label.empty()) {
-            s->Append(absl::StrFormat(" \"%s\"", label));
-        }
-        s->Append("]");
-        return {true};
-    }
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const ApiObjectBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append("[");
-        if (value->IsError()) {
-            s->Append("Invalid ");
-        }
-        s->Append(ObjectTypeAsString(value->GetType()));
-        const std::string& label = value->GetLabel();
-        if (!label.empty()) {
-            s->Append(absl::StrFormat(" \"%s\"", label));
-        }
-        s->Append("]");
-        return {true};
-    }
-
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const TextureViewBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s) {
-        if (value == nullptr) {
-            s->Append("[null]");
-            return {true};
-        }
-        s->Append("[");
-        if (value->IsError()) {
-            s->Append("Invalid ");
-        }
-        s->Append(ObjectTypeAsString(value->GetType()));
-        const std::string& label = value->GetLabel();
-        if (!label.empty()) {
-            s->Append(absl::StrFormat(" \"%s\"", label));
-        }
-        const std::string& textureLabel = value->GetTexture()->GetLabel();
-        if (!textureLabel.empty()) {
-            s->Append(absl::StrFormat(" of Texture \"%s\"", textureLabel));
-        }
-        s->Append("]");
-        return {true};
-    }
-
-    //
-    // Descriptors
-    //
-
-    {% for type in by_category["structure"] %}
-        {% for member in type.members %}
-            {% if member.name.canonical_case() == "label" %}
-                absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-                AbslFormatConvert(const {{as_cppType(type.name)}}* value,
-                                    const absl::FormatConversionSpec& spec,
-                                    absl::FormatSink* s) {
-                    if (value == nullptr) {
-                        s->Append("[null]");
-                        return {true};
-                    }
-                    s->Append("[{{as_cppType(type.name)}}");
-                    if (value->label != nullptr) {
-                        s->Append(absl::StrFormat(" \"%s\"", value->label));
-                    }
-                    s->Append("]");
-                    return {true};
-                }
-            {% endif %}
-        {% endfor %}
-    {% endfor %}
-
-}  // namespace dawn_native
-
-namespace wgpu {
-
-    //
-    // Enums
-    //
-
-    {% for type in by_category["enum"] %}
-        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-        AbslFormatConvert({{as_cppType(type.name)}} value,
-                            const absl::FormatConversionSpec& spec,
-                            absl::FormatSink* s) {
-            s->Append("{{as_cppType(type.name)}}::");
-            switch (value) {
-            {% for value in type.values %}
-                case {{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}:
-                s->Append("{{as_cppEnum(value.name)}}");
-                break;
-            {% endfor %}
-                default:
-                s->Append(absl::StrFormat("%x", static_cast<typename std::underlying_type<{{as_cppType(type.name)}}>::type>(value)));
-            }
-            return {true};
-        }
-    {% endfor %}
-
-    //
-    // Bitmasks
-    //
-
-    {% for type in by_category["bitmask"] %}
-        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-        AbslFormatConvert({{as_cppType(type.name)}} value,
-                            const absl::FormatConversionSpec& spec,
-                            absl::FormatSink* s) {
-            s->Append("{{as_cppType(type.name)}}::");
-            if (!static_cast<bool>(value)) {
-                {% for value in type.values if value.value == 0 %}
-                    // 0 is often explicitly declared as None.
-                    s->Append("{{as_cppEnum(value.name)}}");
-                {% else %}
-                    s->Append(absl::StrFormat("{{as_cppType(type.name)}}::%x", 0));
-                {% endfor %}
-                return {true};
-            }
-
-            bool moreThanOneBit = !HasZeroOrOneBits(value);
-            if (moreThanOneBit) {
-                s->Append("(");
-            }
-
-            bool first = true;
-            {% for value in type.values if value.value != 0 %}
-                if (value & {{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}) {
-                    if (!first) {
-                        s->Append("|");
-                    }
-                    first = false;
-                    s->Append("{{as_cppEnum(value.name)}}");
-                    value &= ~{{as_cppType(type.name)}}::{{as_cppEnum(value.name)}};
-                }
-            {% endfor %}
-
-            if (static_cast<bool>(value)) {
-                if (!first) {
-                    s->Append("|");
-                }
-                s->Append(absl::StrFormat("{{as_cppType(type.name)}}::%x", static_cast<typename std::underlying_type<{{as_cppType(type.name)}}>::type>(value)));
-            }
-
-            if (moreThanOneBit) {
-                s->Append(")");
-            }
-
-            return {true};
-        }
-    {% endfor %}
-
-}  // namespace wgpu
diff --git a/generator/templates/dawn_native/webgpu_absl_format.h b/generator/templates/dawn_native/webgpu_absl_format.h
deleted file mode 100644
index a327841..0000000
--- a/generator/templates/dawn_native/webgpu_absl_format.h
+++ /dev/null
@@ -1,118 +0,0 @@
-//* Copyright 2021 The Dawn Authors
-//*
-//* Licensed under the Apache License, Version 2.0 (the "License");
-//* you may not use this file except in compliance with the License.
-//* You may obtain a copy of the License at
-//*
-//*     http://www.apache.org/licenses/LICENSE-2.0
-//*
-//* Unless required by applicable law or agreed to in writing, software
-//* distributed under the License is distributed on an "AS IS" BASIS,
-//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//* See the License for the specific language governing permissions and
-//* limitations under the License.
-
-#ifndef WEBGPU_ABSL_FORMAT_H_
-#define WEBGPU_ABSL_FORMAT_H_
-
-#include "dawn_native/dawn_platform.h"
-
-#include "absl/strings/str_format.h"
-
-namespace dawn_native {
-
-    // TODO(dawn:563):
-    //  - Split the file between autogenerated parts and manually written parts.
-    //  - Forward declare common Dawn enums and have AbslFormatConvert for them.
-    //  - Support AbslFormatConvert for Dawn's typed integers.
-
-    //
-    // Structs (Manually written)
-    //
-
-    struct Color;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Color* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    struct Extent3D;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Extent3D* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    struct Origin3D;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const Origin3D* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    //
-    // Objects
-    //
-
-    class DeviceBase;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const DeviceBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    class ApiObjectBase;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const ApiObjectBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    // Special case for TextureViews, since frequently the texture will be the
-    // thing that's labeled.
-    class TextureViewBase;
-    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-    AbslFormatConvert(const TextureViewBase* value,
-                      const absl::FormatConversionSpec& spec,
-                      absl::FormatSink* s);
-
-    //
-    // Descriptors
-    //
-
-    // Only includes structures that have a 'label' member.
-    {% for type in by_category["structure"] %}
-        {% for member in type.members %}
-            {% if member.name.canonical_case() == "label" %}
-                absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-                AbslFormatConvert(const {{as_cppType(type.name)}}* value,
-                                  const absl::FormatConversionSpec& spec,
-                                  absl::FormatSink* s);
-            {% endif %}
-        {% endfor %}
-    {% endfor %}
-}
-
-namespace wgpu {
-
-    //
-    // Enums
-    //
-
-    {% for type in by_category["enum"] %}
-        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-        AbslFormatConvert({{as_cppType(type.name)}} value,
-                          const absl::FormatConversionSpec& spec,
-                          absl::FormatSink* s);
-    {% endfor %}
-
-    //
-    // Bitmasks
-    //
-
-    {% for type in by_category["bitmask"] %}
-        absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
-        AbslFormatConvert({{as_cppType(type.name)}} value,
-                          const absl::FormatConversionSpec& spec,
-                          absl::FormatSink* s);
-    {% endfor %}
-
-}  // namespace dawn_native
-
-#endif // WEBGPU_ABSL_FORMAT_H_
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index 82913a8..1f2e920 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -331,6 +331,8 @@
     "VertexFormat.cpp",
     "VertexFormat.h",
     "dawn_platform.h",
+    "webgpu_absl_format.cpp",
+    "webgpu_absl_format.h",
     "utils/WGPUHelpers.cpp",
     "utils/WGPUHelpers.h",
   ]
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index b6b4f47..4196e1c 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -176,6 +176,8 @@
     "VertexFormat.cpp"
     "VertexFormat.h"
     "dawn_platform.h"
+    "webgpu_absl_format.cpp"
+    "webgpu_absl_format.h"
     "utils/WGPUHelpers.cpp"
     "utils/WGPUHelpers.h"
 )
diff --git a/src/dawn_native/Error.h b/src/dawn_native/Error.h
index 89296a2..c67fcdd 100644
--- a/src/dawn_native/Error.h
+++ b/src/dawn_native/Error.h
@@ -18,7 +18,7 @@
 #include "absl/strings/str_format.h"
 #include "common/Result.h"
 #include "dawn_native/ErrorData.h"
-#include "dawn_native/webgpu_absl_format_autogen.h"
+#include "dawn_native/webgpu_absl_format.h"
 
 #include <string>
 
diff --git a/src/dawn_native/webgpu_absl_format.cpp b/src/dawn_native/webgpu_absl_format.cpp
new file mode 100644
index 0000000..465fd2c
--- /dev/null
+++ b/src/dawn_native/webgpu_absl_format.cpp
@@ -0,0 +1,133 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/webgpu_absl_format.h"
+
+#include "dawn_native/Device.h"
+#include "dawn_native/ObjectBase.h"
+#include "dawn_native/Texture.h"
+
+namespace dawn_native {
+
+    //
+    // Structs
+    //
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Color* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append(absl::StrFormat("[Color r:%f, g:%f, b:%f, a:%f]",
+            value->r, value->g, value->b, value->a));
+        return {true};
+    }
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Extent3D* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append(absl::StrFormat("[Extent3D width:%u, height:%u, depthOrArrayLayers:%u]",
+            value->width, value->height, value->depthOrArrayLayers));
+        return {true};
+    }
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Origin3D* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append(absl::StrFormat("[Origin3D x:%u, y:%u, z:%u]",
+            value->x, value->y, value->z));
+        return {true};
+    }
+
+    //
+    // Objects
+    //
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const DeviceBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append("[Device");
+        const std::string& label = value->GetLabel();
+        if (!label.empty()) {
+            s->Append(absl::StrFormat(" \"%s\"", label));
+        }
+        s->Append("]");
+        return {true};
+    }
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const ApiObjectBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append("[");
+        if (value->IsError()) {
+            s->Append("Invalid ");
+        }
+        s->Append(ObjectTypeAsString(value->GetType()));
+        const std::string& label = value->GetLabel();
+        if (!label.empty()) {
+            s->Append(absl::StrFormat(" \"%s\"", label));
+        }
+        s->Append("]");
+        return {true};
+    }
+
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const TextureViewBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s) {
+        if (value == nullptr) {
+            s->Append("[null]");
+            return {true};
+        }
+        s->Append("[");
+        if (value->IsError()) {
+            s->Append("Invalid ");
+        }
+        s->Append(ObjectTypeAsString(value->GetType()));
+        const std::string& label = value->GetLabel();
+        if (!label.empty()) {
+            s->Append(absl::StrFormat(" \"%s\"", label));
+        }
+        const std::string& textureLabel = value->GetTexture()->GetLabel();
+        if (!textureLabel.empty()) {
+            s->Append(absl::StrFormat(" of Texture \"%s\"", textureLabel));
+        }
+        s->Append("]");
+        return {true};
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/webgpu_absl_format.h b/src/dawn_native/webgpu_absl_format.h
new file mode 100644
index 0000000..340e106
--- /dev/null
+++ b/src/dawn_native/webgpu_absl_format.h
@@ -0,0 +1,72 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_WEBGPUABSLFORMAT_H_
+#define DAWNNATIVE_WEBGPUABSLFORMAT_H_
+
+#include "absl/strings/str_format.h"
+#include "dawn_native/dawn_platform.h"
+#include "dawn_native/webgpu_absl_format_autogen.h"
+
+namespace dawn_native {
+
+    //
+    // Structs
+    //
+
+    struct Color;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Color* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+    struct Extent3D;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Extent3D* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+    struct Origin3D;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const Origin3D* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+    //
+    // Objects
+    //
+
+    class DeviceBase;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const DeviceBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+    class ApiObjectBase;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const ApiObjectBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+    // Special case for TextureViews, since frequently the texture will be the
+    // thing that's labeled.
+    class TextureViewBase;
+    absl::FormatConvertResult<absl::FormatConversionCharSet::kString>
+    AbslFormatConvert(const TextureViewBase* value,
+                      const absl::FormatConversionSpec& spec,
+                      absl::FormatSink* s);
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_WEBGPUABSLFORMAT_H_