Rename DeviceErrorCallback to ErrorCallback and add ErrorType arg

This same callback will be used for push/pop error scope.

Bug: dawn:153
Change-Id: I2771539e13f8a4e6a59f13c8082689d25ba44905
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10460
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/dawn.json b/dawn.json
index 8f8f8fc..53d1de5 100644
--- a/dawn.json
+++ b/dawn.json
@@ -524,7 +524,7 @@
             {
                 "name": "set error callback",
                 "args": [
-                    {"name": "callback", "type": "device error callback"},
+                    {"name": "callback", "type": "error callback"},
                     {"name": "userdata", "type": "void", "annotation": "*"}
                 ]
             }
@@ -543,9 +543,19 @@
             {"name": "stencil write mask", "type": "uint32_t", "default": "0xFFFFFFFF"}
         ]
     },
-    "device error callback": {
+    "error callback": {
         "category": "natively defined"
     },
+    "error type": {
+        "category": "enum",
+        "values": [
+            {"value": 0, "name": "no error"},
+            {"value": 1, "name": "validation"},
+            {"value": 2, "name": "out of memory"},
+            {"value": 3, "name": "unknown"},
+            {"value": 4, "name": "device lost"}
+        ]
+    },
     "extent 3D": {
         "category": "structure",
         "members": [
diff --git a/dawn_wire.json b/dawn_wire.json
index 55bb452..a7fc103 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -67,6 +67,7 @@
             { "name": "status", "type": "uint32_t" }
         ],
         "device error callback": [
+            { "name": "type", "type": "error type"},
             { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
         ],
         "fence update completed value": [
diff --git a/examples/SampleUtils.cpp b/examples/SampleUtils.cpp
index bac979a..496ed7f 100644
--- a/examples/SampleUtils.cpp
+++ b/examples/SampleUtils.cpp
@@ -31,8 +31,25 @@
 #include <cstring>
 #include <iostream>
 
-void PrintDeviceError(const char* message, void*) {
-    std::cout << "Device error: " << message << std::endl;
+void PrintDeviceError(DawnErrorType errorType, const char* message, void*) {
+    switch (errorType) {
+        case DAWN_ERROR_TYPE_VALIDATION:
+            std::cout << "Validation ";
+            break;
+        case DAWN_ERROR_TYPE_OUT_OF_MEMORY:
+            std::cout << "Out of memory ";
+            break;
+        case DAWN_ERROR_TYPE_UNKNOWN:
+            std::cout << "Unknown ";
+            break;
+        case DAWN_ERROR_TYPE_DEVICE_LOST:
+            std::cout << "Device lost ";
+            break;
+        default:
+            UNREACHABLE();
+            return;
+    }
+    std::cout << "error: " << message << std::endl;
 }
 
 void PrintGLFWError(int code, const char* message) {
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index eaa324c..23f423d 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -430,6 +430,14 @@
     else:
         return as_cType(typ.name)
 
+def as_wireType(typ):
+    if typ.category == 'object':
+        return typ.name.CamelCase() + '*'
+    elif typ.category in ['bitmask', 'enum']:
+        return 'Dawn' + typ.name.CamelCase()
+    else:
+        return as_cppType(typ.name)
+
 def cpp_native_methods(types, typ):
     return typ.methods + typ.native_methods
 
@@ -522,7 +530,8 @@
                 api_params,
                 c_params,
                 {
-                    'as_wireType': lambda typ: typ.name.CamelCase() + '*' if typ.category == 'object' else as_cppType(typ.name)
+                    'as_wireType': as_wireType,
+                    'as_annotated_wireType': lambda arg: annotated(as_wireType(arg.type), arg),
                 },
                 additional_params
             ]
diff --git a/generator/templates/api.h b/generator/templates/api.h
index 7e5add8..4a80cb4 100644
--- a/generator/templates/api.h
+++ b/generator/templates/api.h
@@ -50,7 +50,7 @@
 {% endfor %}
 
 // Custom types depending on the target language
-typedef void (*DawnDeviceErrorCallback)(const char* message, void* userdata);
+typedef void (*DawnErrorCallback)(DawnErrorType type, const char* message, void* userdata);
 typedef void (*DawnBufferCreateMappedCallback)(DawnBufferMapAsyncStatus status,
                                                DawnCreateBufferMappedResult result,
                                                void* userdata);
diff --git a/generator/templates/dawn_wire/client/ClientPrototypes.inc b/generator/templates/dawn_wire/client/ClientPrototypes.inc
index 6e47303..b29f68b 100644
--- a/generator/templates/dawn_wire/client/ClientPrototypes.inc
+++ b/generator/templates/dawn_wire/client/ClientPrototypes.inc
@@ -22,9 +22,9 @@
     bool Do{{command.name.CamelCase()}}(
         {%- for member in command.members -%}
             {%- if member.handle_type -%}
-                {{as_cppType(member.handle_type.name)}}* {{as_varName(member.name)}}
+                {{as_wireType(member.handle_type)}} {{as_varName(member.name)}}
             {%- else -%}
-                {{as_annotated_cppType(member)}}
+                {{as_annotated_wireType(member)}}
             {%- endif -%}
             {%- if not loop.last -%}, {% endif %}
         {%- endfor -%}
diff --git a/generator/templates/mock_api.cpp b/generator/templates/mock_api.cpp
index b41d41e..8a6dc58 100644
--- a/generator/templates/mock_api.cpp
+++ b/generator/templates/mock_api.cpp
@@ -51,7 +51,7 @@
 }
 
 void ProcTableAsClass::DeviceSetErrorCallback(DawnDevice self,
-                                              DawnDeviceErrorCallback callback,
+                                              DawnErrorCallback callback,
                                               void* userdata) {
     auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
     object->deviceErrorCallback = callback;
@@ -102,9 +102,9 @@
     OnFenceOnCompletionCallback(self, value, callback, userdata);
 }
 
-void ProcTableAsClass::CallDeviceErrorCallback(DawnDevice device, const char* message) {
+void ProcTableAsClass::CallDeviceErrorCallback(DawnDevice device, DawnErrorType type, const char* message) {
     auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
-    object->deviceErrorCallback(message, object->userdata1);
+    object->deviceErrorCallback(type, message, object->userdata1);
 }
 void ProcTableAsClass::CallCreateBufferMappedCallback(DawnDevice device, DawnBufferMapAsyncStatus status, DawnCreateBufferMappedResult result) {
     auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
diff --git a/generator/templates/mock_api.h b/generator/templates/mock_api.h
index f075130..803e86e 100644
--- a/generator/templates/mock_api.h
+++ b/generator/templates/mock_api.h
@@ -52,7 +52,7 @@
 
         // Stores callback and userdata and calls the On* methods
         void DeviceSetErrorCallback(DawnDevice self,
-                                    DawnDeviceErrorCallback callback,
+                                    DawnErrorCallback callback,
                                     void* userdata);
         void DeviceCreateBufferMappedAsync(DawnDevice self,
                                            const DawnBufferDescriptor* descriptor,
@@ -71,7 +71,7 @@
 
         // Special cased mockable methods
         virtual void OnDeviceSetErrorCallback(DawnDevice device,
-                                              DawnDeviceErrorCallback callback,
+                                              DawnErrorCallback callback,
                                               void* userdata) = 0;
         virtual void OnDeviceCreateBufferMappedAsyncCallback(DawnDevice self,
                                                              const DawnBufferDescriptor* descriptor,
@@ -89,7 +89,7 @@
                                                  void* userdata) = 0;
 
         // Calls the stored callbacks
-        void CallDeviceErrorCallback(DawnDevice device, const char* message);
+        void CallDeviceErrorCallback(DawnDevice device, DawnErrorType type, const char* message);
         void CallCreateBufferMappedCallback(DawnDevice device, DawnBufferMapAsyncStatus status, DawnCreateBufferMappedResult result);
         void CallMapReadCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, const void* data, uint64_t dataLength);
         void CallMapWriteCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, void* data, uint64_t dataLength);
@@ -97,7 +97,7 @@
 
         struct Object {
             ProcTableAsClass* procs = nullptr;
-            DawnDeviceErrorCallback deviceErrorCallback = nullptr;
+            DawnErrorCallback deviceErrorCallback = nullptr;
             DawnBufferCreateMappedCallback createBufferMappedCallback = nullptr;
             DawnBufferMapReadCallback mapReadCallback = nullptr;
             DawnBufferMapWriteCallback mapWriteCallback = nullptr;
@@ -133,7 +133,7 @@
             MOCK_METHOD1({{as_MethodSuffix(type.name, Name("release"))}}, void({{as_cType(type.name)}} self));
         {% endfor %}
 
-        MOCK_METHOD3(OnDeviceSetErrorCallback, void(DawnDevice device, DawnDeviceErrorCallback callback, void* userdata));
+        MOCK_METHOD3(OnDeviceSetErrorCallback, void(DawnDevice device, DawnErrorCallback callback, void* userdata));
         MOCK_METHOD4(OnDeviceCreateBufferMappedAsyncCallback, void(DawnDevice device, const DawnBufferDescriptor* descriptor, DawnBufferCreateMappedCallback callback, void* userdata));
         MOCK_METHOD3(OnBufferMapReadAsyncCallback, void(DawnBuffer buffer, DawnBufferMapReadCallback callback, void* userdata));
         MOCK_METHOD3(OnBufferMapWriteAsyncCallback, void(DawnBuffer buffer, DawnBufferMapWriteCallback callback, void* userdata));
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 558bb17..b557114 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -88,13 +88,13 @@
         ASSERT(mCaches->shaderModules.empty());
     }
 
-    void DeviceBase::HandleError(const char* message) {
+    void DeviceBase::HandleError(dawn::ErrorType type, const char* message) {
         if (mErrorCallback) {
-            mErrorCallback(message, mErrorUserdata);
+            mErrorCallback(static_cast<DawnErrorType>(type), message, mErrorUserdata);
         }
     }
 
-    void DeviceBase::SetErrorCallback(dawn::DeviceErrorCallback callback, void* userdata) {
+    void DeviceBase::SetErrorCallback(dawn::ErrorCallback callback, void* userdata) {
         mErrorCallback = callback;
         mErrorUserdata = userdata;
     }
@@ -671,7 +671,7 @@
 
     void DeviceBase::ConsumeError(ErrorData* error) {
         ASSERT(error != nullptr);
-        HandleError(error->GetMessage().c_str());
+        HandleError(error->GetType(), error->GetMessage().c_str());
         delete error;
     }
 
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 3ccde37..3d79d15 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -44,7 +44,7 @@
         DeviceBase(AdapterBase* adapter, const DeviceDescriptor* descriptor);
         virtual ~DeviceBase();
 
-        void HandleError(const char* message);
+        void HandleError(dawn::ErrorType type, const char* message);
 
         bool ConsumedError(MaybeError maybeError) {
             if (DAWN_UNLIKELY(maybeError.IsError())) {
@@ -148,7 +148,7 @@
 
         void Tick();
 
-        void SetErrorCallback(dawn::DeviceErrorCallback callback, void* userdata);
+        void SetErrorCallback(dawn::ErrorCallback callback, void* userdata);
         void Reference();
         void Release();
 
@@ -247,7 +247,7 @@
         std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
         std::vector<DeferredCreateBufferMappedAsync> mDeferredCreateBufferMappedAsyncResults;
 
-        dawn::DeviceErrorCallback mErrorCallback = nullptr;
+        dawn::ErrorCallback mErrorCallback = nullptr;
         void* mErrorUserdata = 0;
         uint32_t mRefCount = 1;
 
diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp
index d36ccef..121a890 100644
--- a/src/dawn_native/EncodingContext.cpp
+++ b/src/dawn_native/EncodingContext.cpp
@@ -45,7 +45,7 @@
         return &mIterator;
     }
 
-    void EncodingContext::HandleError(const char* message) {
+    void EncodingContext::HandleError(dawn::ErrorType type, const char* message) {
         if (!IsFinished()) {
             // If the encoding context is not finished, errors are deferred until
             // Finish() is called.
@@ -54,7 +54,7 @@
                 mErrorMessage = message;
             }
         } else {
-            mDevice->HandleError(message);
+            mDevice->HandleError(type, message);
         }
     }
 
diff --git a/src/dawn_native/EncodingContext.h b/src/dawn_native/EncodingContext.h
index a810f50..831db7d 100644
--- a/src/dawn_native/EncodingContext.h
+++ b/src/dawn_native/EncodingContext.h
@@ -18,6 +18,7 @@
 #include "dawn_native/CommandAllocator.h"
 #include "dawn_native/Error.h"
 #include "dawn_native/ErrorData.h"
+#include "dawn_native/dawn_platform.h"
 
 #include <string>
 
@@ -37,10 +38,10 @@
         CommandIterator* GetIterator();
 
         // Functions to handle encoder errors
-        void HandleError(const char* message);
+        void HandleError(dawn::ErrorType type, const char* message);
 
         inline void ConsumeError(ErrorData* error) {
-            HandleError(error->GetMessage().c_str());
+            HandleError(error->GetType(), error->GetMessage().c_str());
             delete error;
         }
 
@@ -57,9 +58,11 @@
             if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
                 if (mCurrentEncoder != mTopLevelEncoder) {
                     // The top level encoder was used when a pass encoder was current.
-                    HandleError("Command cannot be recorded inside a pass");
+                    HandleError(dawn::ErrorType::Validation,
+                                "Command cannot be recorded inside a pass");
                 } else {
-                    HandleError("Recording in an error or already ended pass encoder");
+                    HandleError(dawn::ErrorType::Validation,
+                                "Recording in an error or already ended pass encoder");
                 }
                 return false;
             }
diff --git a/src/dawn_native/Error.cpp b/src/dawn_native/Error.cpp
index 2e09931..195afb2 100644
--- a/src/dawn_native/Error.cpp
+++ b/src/dawn_native/Error.cpp
@@ -18,7 +18,7 @@
 
 namespace dawn_native {
 
-    ErrorData* MakeError(ErrorType type,
+    ErrorData* MakeError(InternalErrorType type,
                          std::string message,
                          const char* file,
                          const char* function,
diff --git a/src/dawn_native/Error.h b/src/dawn_native/Error.h
index 82efbee..172263e 100644
--- a/src/dawn_native/Error.h
+++ b/src/dawn_native/Error.h
@@ -25,7 +25,7 @@
     // file to avoid having all files including headers like <string> and <vector>
     class ErrorData;
 
-    enum class ErrorType : uint32_t { Validation, DeviceLost, Unimplemented, OutOfMemory };
+    enum class InternalErrorType : uint32_t { Validation, DeviceLost, Unimplemented, OutOfMemory };
 
     // MaybeError and ResultOrError are meant to be used as return value for function that are not
     // expected to, but might fail. The handling of error is potentially much slower than successes.
@@ -45,10 +45,10 @@
     //   return DAWN_VALIDATION_ERROR("My error message");
 #define DAWN_MAKE_ERROR(TYPE, MESSAGE) \
     ::dawn_native::MakeError(TYPE, MESSAGE, __FILE__, __func__, __LINE__)
-#define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Validation, MESSAGE)
-#define DAWN_DEVICE_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::DeviceLost, MESSAGE)
-#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Unimplemented, MESSAGE)
-#define DAWN_OUT_OF_MEMORY_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::OutOfMemory, MESSAGE)
+#define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Validation, MESSAGE)
+#define DAWN_DEVICE_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::DeviceLost, MESSAGE)
+#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Unimplemented, MESSAGE)
+#define DAWN_OUT_OF_MEMORY_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::OutOfMemory, MESSAGE)
 
 #define DAWN_CONCAT1(x, y) x##y
 #define DAWN_CONCAT2(x, y) DAWN_CONCAT1(x, y)
@@ -88,7 +88,7 @@
     void AppendBacktrace(ErrorData* error, const char* file, const char* function, int line);
 
     // Implementation detail of DAWN_MAKE_ERROR
-    ErrorData* MakeError(ErrorType type,
+    ErrorData* MakeError(InternalErrorType type,
                          std::string message,
                          const char* file,
                          const char* function,
diff --git a/src/dawn_native/ErrorData.cpp b/src/dawn_native/ErrorData.cpp
index 77a0e3f..06be01e 100644
--- a/src/dawn_native/ErrorData.cpp
+++ b/src/dawn_native/ErrorData.cpp
@@ -14,11 +14,14 @@
 
 #include "dawn_native/ErrorData.h"
 
+#include "dawn_native/Error.h"
+#include "dawn_native/dawn_platform.h"
+
 namespace dawn_native {
 
     ErrorData::ErrorData() = default;
 
-    ErrorData::ErrorData(ErrorType type, std::string message)
+    ErrorData::ErrorData(InternalErrorType type, std::string message)
         : mType(type), mMessage(std::move(message)) {
     }
 
@@ -31,10 +34,23 @@
         mBacktrace.push_back(std::move(record));
     }
 
-    ErrorType ErrorData::GetType() const {
+    InternalErrorType ErrorData::GetInternalType() const {
         return mType;
     }
 
+    dawn::ErrorType ErrorData::GetType() const {
+        switch (mType) {
+            case InternalErrorType::Validation:
+                return dawn::ErrorType::Validation;
+            case InternalErrorType::OutOfMemory:
+                return dawn::ErrorType::OutOfMemory;
+            case InternalErrorType::DeviceLost:
+                return dawn::ErrorType::DeviceLost;
+            default:
+                return dawn::ErrorType::Unknown;
+        }
+    }
+
     const std::string& ErrorData::GetMessage() const {
         return mMessage;
     }
diff --git a/src/dawn_native/ErrorData.h b/src/dawn_native/ErrorData.h
index 0152003..8c56b7a 100644
--- a/src/dawn_native/ErrorData.h
+++ b/src/dawn_native/ErrorData.h
@@ -19,14 +19,18 @@
 #include <string>
 #include <vector>
 
+namespace dawn {
+    enum class ErrorType : uint32_t;
+}
+
 namespace dawn_native {
 
-    enum class ErrorType : uint32_t;
+    enum class InternalErrorType : uint32_t;
 
     class ErrorData {
       public:
         ErrorData();
-        ErrorData(ErrorType type, std::string message);
+        ErrorData(InternalErrorType type, std::string message);
 
         struct BacktraceRecord {
             const char* file;
@@ -35,12 +39,13 @@
         };
         void AppendBacktrace(const char* file, const char* function, int line);
 
-        ErrorType GetType() const;
+        InternalErrorType GetInternalType() const;
+        dawn::ErrorType GetType() const;
         const std::string& GetMessage() const;
         const std::vector<BacktraceRecord>& GetBacktrace() const;
 
       private:
-        ErrorType mType;
+        InternalErrorType mType;
         std::string mMessage;
         std::vector<BacktraceRecord> mBacktrace;
     };
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index c59b3cb..a418420 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -115,7 +115,8 @@
         }
 
         if (resources.push_constant_buffers.size() > 0) {
-            GetDevice()->HandleError("Push constants aren't supported.");
+            GetDevice()->HandleError(dawn::ErrorType::Validation,
+                                     "Push constants aren't supported.");
         }
 
         // Fill in bindingInfo with the SPIRV bindings
@@ -132,7 +133,8 @@
                 uint32_t set = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
 
                 if (binding >= kMaxBindingsPerGroup || set >= kMaxBindGroups) {
-                    GetDevice()->HandleError("Binding over limits in the SPIRV");
+                    GetDevice()->HandleError(dawn::ErrorType::Validation,
+                                             "Binding over limits in the SPIRV");
                     continue;
                 }
 
@@ -159,7 +161,8 @@
                 uint32_t location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
 
                 if (location >= kMaxVertexAttributes) {
-                    device->HandleError("Attribute location over limits in the SPIRV");
+                    device->HandleError(dawn::ErrorType::Validation,
+                                        "Attribute location over limits in the SPIRV");
                     return;
                 }
 
@@ -170,7 +173,8 @@
             // all the location 0, causing a compile error.
             for (const auto& attrib : resources.stage_outputs) {
                 if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
-                    device->HandleError("Need location qualifier on vertex output");
+                    device->HandleError(dawn::ErrorType::Validation,
+                                        "Need location qualifier on vertex output");
                     return;
                 }
             }
@@ -181,7 +185,8 @@
             // all the location 0, causing a compile error.
             for (const auto& attrib : resources.stage_inputs) {
                 if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
-                    device->HandleError("Need location qualifier on fragment input");
+                    device->HandleError(dawn::ErrorType::Validation,
+                                        "Need location qualifier on fragment input");
                     return;
                 }
             }
diff --git a/src/dawn_native/d3d12/SwapChainD3D12.cpp b/src/dawn_native/d3d12/SwapChainD3D12.cpp
index 48c642d..0dffc29 100644
--- a/src/dawn_native/d3d12/SwapChainD3D12.cpp
+++ b/src/dawn_native/d3d12/SwapChainD3D12.cpp
@@ -40,7 +40,7 @@
         DawnSwapChainNextTexture next = {};
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
         if (error) {
-            GetDevice()->HandleError(error);
+            GetDevice()->HandleError(dawn::ErrorType::Unknown, error);
             return nullptr;
         }
 
diff --git a/src/dawn_native/metal/ComputePipelineMTL.mm b/src/dawn_native/metal/ComputePipelineMTL.mm
index a843e31..8f5608a 100644
--- a/src/dawn_native/metal/ComputePipelineMTL.mm
+++ b/src/dawn_native/metal/ComputePipelineMTL.mm
@@ -33,7 +33,7 @@
             [mtlDevice newComputePipelineStateWithFunction:computeData.function error:&error];
         if (error != nil) {
             NSLog(@" error => %@", error);
-            GetDevice()->HandleError("Error creating pipeline state");
+            GetDevice()->HandleError(dawn::ErrorType::DeviceLost, "Error creating pipeline state");
             return;
         }
 
diff --git a/src/dawn_native/metal/RenderPipelineMTL.mm b/src/dawn_native/metal/RenderPipelineMTL.mm
index 1c1c8a9..4758fe2 100644
--- a/src/dawn_native/metal/RenderPipelineMTL.mm
+++ b/src/dawn_native/metal/RenderPipelineMTL.mm
@@ -363,7 +363,8 @@
             [descriptorMTL release];
             if (error != nil) {
                 NSLog(@" error => %@", error);
-                device->HandleError("Error creating rendering pipeline state");
+                device->HandleError(dawn::ErrorType::DeviceLost,
+                                    "Error creating rendering pipeline state");
                 return;
             }
         }
diff --git a/src/dawn_native/metal/SwapChainMTL.mm b/src/dawn_native/metal/SwapChainMTL.mm
index 094e35a..0677ca0 100644
--- a/src/dawn_native/metal/SwapChainMTL.mm
+++ b/src/dawn_native/metal/SwapChainMTL.mm
@@ -37,7 +37,7 @@
         DawnSwapChainNextTexture next = {};
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
         if (error) {
-            GetDevice()->HandleError(error);
+            GetDevice()->HandleError(dawn::ErrorType::Unknown, error);
             return nullptr;
         }
 
diff --git a/src/dawn_native/opengl/SwapChainGL.cpp b/src/dawn_native/opengl/SwapChainGL.cpp
index 2a9fe29..e988bc4 100644
--- a/src/dawn_native/opengl/SwapChainGL.cpp
+++ b/src/dawn_native/opengl/SwapChainGL.cpp
@@ -36,7 +36,7 @@
         DawnSwapChainNextTexture next = {};
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
         if (error) {
-            GetDevice()->HandleError(error);
+            GetDevice()->HandleError(dawn::ErrorType::Unknown, error);
             return nullptr;
         }
         GLuint nativeTexture = next.texture.u32;
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index f244d1b..570760d 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -38,7 +38,7 @@
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
 
         if (error) {
-            GetDevice()->HandleError(error);
+            GetDevice()->HandleError(dawn::ErrorType::Unknown, error);
             return nullptr;
         }
 
diff --git a/src/dawn_wire/client/ApiProcs.cpp b/src/dawn_wire/client/ApiProcs.cpp
index 95d0e54..773a0d3 100644
--- a/src/dawn_wire/client/ApiProcs.cpp
+++ b/src/dawn_wire/client/ApiProcs.cpp
@@ -275,7 +275,8 @@
                                  void* userdata) {
         Fence* fence = reinterpret_cast<Fence*>(cFence);
         if (value > fence->signaledValue) {
-            fence->device->HandleError("Value greater than fence signaled value");
+            fence->device->HandleError(DAWN_ERROR_TYPE_VALIDATION,
+                                       "Value greater than fence signaled value");
             callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata);
             return;
         }
@@ -384,11 +385,13 @@
         Queue* queue = reinterpret_cast<Queue*>(cQueue);
         if (fence->queue != queue) {
             fence->device->HandleError(
+                DAWN_ERROR_TYPE_VALIDATION,
                 "Fence must be signaled on the queue on which it was created.");
             return;
         }
         if (signalValue <= fence->signaledValue) {
-            fence->device->HandleError("Fence value less than or equal to signaled value");
+            fence->device->HandleError(DAWN_ERROR_TYPE_VALIDATION,
+                                       "Fence value less than or equal to signaled value");
             return;
         }
         fence->signaledValue = signalValue;
@@ -411,7 +414,7 @@
     }
 
     void ClientDeviceSetErrorCallback(DawnDevice cSelf,
-                                      DawnDeviceErrorCallback callback,
+                                      DawnErrorCallback callback,
                                       void* userdata) {
         Device* device = reinterpret_cast<Device*>(cSelf);
         device->SetErrorCallback(callback, userdata);
diff --git a/src/dawn_wire/client/Client.h b/src/dawn_wire/client/Client.h
index b729abb..f7b0687 100644
--- a/src/dawn_wire/client/Client.h
+++ b/src/dawn_wire/client/Client.h
@@ -15,6 +15,7 @@
 #ifndef DAWNWIRE_CLIENT_CLIENT_H_
 #define DAWNWIRE_CLIENT_CLIENT_H_
 
+#include <dawn/dawn.h>
 #include <dawn_wire/Wire.h>
 
 #include "dawn_wire/WireClient.h"
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index abaa41c..531d724 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -18,9 +18,18 @@
 
 namespace dawn_wire { namespace client {
 
-    bool Client::DoDeviceErrorCallback(const char* message) {
-        DAWN_ASSERT(message != nullptr);
-        mDevice->HandleError(message);
+    bool Client::DoDeviceErrorCallback(DawnErrorType errorType, const char* message) {
+        switch (errorType) {
+            case DAWN_ERROR_TYPE_NO_ERROR:
+            case DAWN_ERROR_TYPE_VALIDATION:
+            case DAWN_ERROR_TYPE_OUT_OF_MEMORY:
+            case DAWN_ERROR_TYPE_UNKNOWN:
+            case DAWN_ERROR_TYPE_DEVICE_LOST:
+                break;
+            default:
+                return false;
+        }
+        mDevice->HandleError(errorType, message);
         return true;
     }
 
diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp
index 7c55cd3..2ada62f 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -25,13 +25,13 @@
         return mClient;
     }
 
-    void Device::HandleError(const char* message) {
+    void Device::HandleError(DawnErrorType errorType, const char* message) {
         if (mErrorCallback) {
-            mErrorCallback(message, mErrorUserdata);
+            mErrorCallback(errorType, message, mErrorUserdata);
         }
     }
 
-    void Device::SetErrorCallback(DawnDeviceErrorCallback errorCallback, void* errorUserdata) {
+    void Device::SetErrorCallback(DawnErrorCallback errorCallback, void* errorUserdata) {
         mErrorCallback = errorCallback;
         mErrorUserdata = errorUserdata;
     }
diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h
index 96f2262..eb725ff 100644
--- a/src/dawn_wire/client/Device.h
+++ b/src/dawn_wire/client/Device.h
@@ -28,12 +28,12 @@
         Device(Client* client, uint32_t refcount, uint32_t id);
 
         Client* GetClient();
-        void HandleError(const char* message);
-        void SetErrorCallback(DawnDeviceErrorCallback errorCallback, void* errorUserdata);
+        void HandleError(DawnErrorType errorType, const char* message);
+        void SetErrorCallback(DawnErrorCallback errorCallback, void* errorUserdata);
 
       private:
         Client* mClient = nullptr;
-        DawnDeviceErrorCallback mErrorCallback = nullptr;
+        DawnErrorCallback mErrorCallback = nullptr;
         void* mErrorUserdata;
     };
 
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index 153dd72..76e0093 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -54,7 +54,7 @@
         void* GetCmdSpace(size_t size);
 
         // Forwarding callbacks
-        static void ForwardDeviceError(const char* message, void* userdata);
+        static void ForwardDeviceError(DawnErrorType type, const char* message, void* userdata);
         static void ForwardBufferMapReadAsync(DawnBufferMapAsyncStatus status,
                                               const void* ptr,
                                               uint64_t dataLength,
@@ -66,7 +66,7 @@
         static void ForwardFenceCompletedValue(DawnFenceCompletionStatus status, void* userdata);
 
         // Error callbacks
-        void OnDeviceError(const char* message);
+        void OnDeviceError(DawnErrorType type, const char* message);
         void OnBufferMapReadAsyncCallback(DawnBufferMapAsyncStatus status,
                                           const void* ptr,
                                           uint64_t dataLength,
diff --git a/src/dawn_wire/server/ServerDevice.cpp b/src/dawn_wire/server/ServerDevice.cpp
index 507f518..06cf1f8 100644
--- a/src/dawn_wire/server/ServerDevice.cpp
+++ b/src/dawn_wire/server/ServerDevice.cpp
@@ -16,13 +16,14 @@
 
 namespace dawn_wire { namespace server {
 
-    void Server::ForwardDeviceError(const char* message, void* userdata) {
+    void Server::ForwardDeviceError(DawnErrorType type, const char* message, void* userdata) {
         auto server = static_cast<Server*>(userdata);
-        server->OnDeviceError(message);
+        server->OnDeviceError(type, message);
     }
 
-    void Server::OnDeviceError(const char* message) {
+    void Server::OnDeviceError(DawnErrorType type, const char* message) {
         ReturnDeviceErrorCallbackCmd cmd;
+        cmd.type = type;
         cmd.message = message;
 
         size_t requiredSize = cmd.GetRequiredSize();
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 737aa90..a062df5 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -482,7 +482,8 @@
 }
 
 // static
-void DawnTest::OnDeviceError(const char* message, void* userdata) {
+void DawnTest::OnDeviceError(DawnErrorType type, const char* message, void* userdata) {
+    ASSERT(type != DAWN_ERROR_TYPE_NO_ERROR);
     DawnTest* self = static_cast<DawnTest*>(userdata);
 
     ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h
index 4633716..f111abe 100644
--- a/src/tests/DawnTest.h
+++ b/src/tests/DawnTest.h
@@ -212,7 +212,7 @@
     std::unique_ptr<utils::TerribleCommandBuffer> mS2cBuf;
 
     // Tracking for validation errors
-    static void OnDeviceError(const char* message, void* userdata);
+    static void OnDeviceError(DawnErrorType type, const char* message, void* userdata);
     bool mExpectError = false;
     bool mError = false;
 
diff --git a/src/tests/unittests/validation/ValidationTest.cpp b/src/tests/unittests/validation/ValidationTest.cpp
index d9979e3..a1f0841 100644
--- a/src/tests/unittests/validation/ValidationTest.cpp
+++ b/src/tests/unittests/validation/ValidationTest.cpp
@@ -84,7 +84,8 @@
 }
 
 // static
-void ValidationTest::OnDeviceError(const char* message, void* userdata) {
+void ValidationTest::OnDeviceError(DawnErrorType type, const char* message, void* userdata) {
+    ASSERT(type != DAWN_ERROR_TYPE_NO_ERROR);
     auto self = static_cast<ValidationTest*>(userdata);
     self->mDeviceErrorMessage = message;
 
diff --git a/src/tests/unittests/validation/ValidationTest.h b/src/tests/unittests/validation/ValidationTest.h
index 5deb019..3d0b59c 100644
--- a/src/tests/unittests/validation/ValidationTest.h
+++ b/src/tests/unittests/validation/ValidationTest.h
@@ -59,7 +59,7 @@
     std::unique_ptr<dawn_native::Instance> instance;
 
   private:
-    static void OnDeviceError(const char* message, void* userdata);
+    static void OnDeviceError(DawnErrorType type, const char* message, void* userdata);
     std::string mDeviceErrorMessage;
     bool mExpectError = false;
     bool mError = false;
diff --git a/src/tests/unittests/wire/WireErrorCallbackTests.cpp b/src/tests/unittests/wire/WireErrorCallbackTests.cpp
index 04d81f9..f9958ea 100644
--- a/src/tests/unittests/wire/WireErrorCallbackTests.cpp
+++ b/src/tests/unittests/wire/WireErrorCallbackTests.cpp
@@ -22,12 +22,12 @@
     // Mock classes to add expectations on the wire calling callbacks
     class MockDeviceErrorCallback {
       public:
-        MOCK_METHOD2(Call, void(const char* message, void* userdata));
+        MOCK_METHOD3(Call, void(DawnErrorType type, const char* message, void* userdata));
     };
 
     std::unique_ptr<StrictMock<MockDeviceErrorCallback>> mockDeviceErrorCallback;
-    void ToMockDeviceErrorCallback(const char* message, void* userdata) {
-        mockDeviceErrorCallback->Call(message, userdata);
+    void ToMockDeviceErrorCallback(DawnErrorType type, const char* message, void* userdata) {
+        mockDeviceErrorCallback->Call(type, message, userdata);
     }
 
 }  // anonymous namespace
@@ -66,9 +66,9 @@
 
     // Calling the callback on the server side will result in the callback being called on the
     // client side
-    api.CallDeviceErrorCallback(apiDevice, "Some error message");
+    api.CallDeviceErrorCallback(apiDevice, DAWN_ERROR_TYPE_VALIDATION, "Some error message");
 
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), this)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, StrEq("Some error message"), this)).Times(1);
 
     FlushServer();
 }
diff --git a/src/tests/unittests/wire/WireFenceTests.cpp b/src/tests/unittests/wire/WireFenceTests.cpp
index 8acbf00..c255826 100644
--- a/src/tests/unittests/wire/WireFenceTests.cpp
+++ b/src/tests/unittests/wire/WireFenceTests.cpp
@@ -22,12 +22,12 @@
     // Mock classes to add expectations on the wire calling callbacks
     class MockDeviceErrorCallback {
       public:
-        MOCK_METHOD2(Call, void(const char* message, void* userdata));
+        MOCK_METHOD3(Call, void(DawnErrorType type, const char* message, void* userdata));
     };
 
     std::unique_ptr<StrictMock<MockDeviceErrorCallback>> mockDeviceErrorCallback;
-    void ToMockDeviceErrorCallback(const char* message, void* userdata) {
-        mockDeviceErrorCallback->Call(message, userdata);
+    void ToMockDeviceErrorCallback(DawnErrorType type, const char* message, void* userdata) {
+        mockDeviceErrorCallback->Call(type, message, userdata);
     }
 
     class MockFenceOnCompletionCallback {
@@ -121,7 +121,7 @@
 // signaled value
 TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) {
     dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(0);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _, _)).Times(0);
 
     dawnQueueSignal(queue, fence, 2u);
     dawnQueueSignal(queue, fence, 4u);
@@ -133,19 +133,19 @@
 TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) {
     dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr);
 
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, _)).Times(1);
     dawnQueueSignal(queue, fence, 0u);  // Error
     EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
 
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, _)).Times(1);
     dawnQueueSignal(queue, fence, 1u);  // Error
     EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
 
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(0);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _, _)).Times(0);
     dawnQueueSignal(queue, fence, 4u);  // Success
     EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
 
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, _)).Times(1);
     dawnQueueSignal(queue, fence, 3u);  // Error
     EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
 }
@@ -221,7 +221,7 @@
 
     EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, this + 0))
         .Times(1);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, this + 1)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, this + 1)).Times(1);
 
     dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, this + 0);
 }
@@ -263,7 +263,7 @@
     FlushClient();
 
     dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, _)).Times(1);
     dawnQueueSignal(queue2, fence, 2u);  // error
 }
 
@@ -275,7 +275,7 @@
     FlushClient();
 
     dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(DAWN_ERROR_TYPE_VALIDATION, _, _)).Times(1);
     dawnQueueSignal(queue2, fence, 2u);  // error
 
     // Fence value should be unchanged.