Separate device lost from internal errors.

The effect to the user is the same, the Dawn device gets lost. However
we need to make the difference internally because when the backend
device is lost we can clean up immediately. On the contrary on internal
errors, the backend device is still alive and processing commands so we
need to gracefully shut it down.

Bug: dawn:269

Change-Id: Ie13b33a4f9ac2e1f5f98b3723d83cf1c6205c988
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/17965
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index d53239f..6973a94 100644
--- a/dawn.json
+++ b/dawn.json
@@ -32,7 +32,7 @@
             {"value": 0, "name": "discrete GPU"},
             {"value": 1, "name": "integrated GPU"},
             {"value": 2, "name": "CPU"},
-            {"value": 3, "name": "Unknown"}
+            {"value": 3, "name": "unknown"}
         ]
     },
     "address mode": {
diff --git a/generator/templates/opengl/OpenGLFunctionsBase.cpp b/generator/templates/opengl/OpenGLFunctionsBase.cpp
index bf5246f..79fc5b1 100644
--- a/generator/templates/opengl/OpenGLFunctionsBase.cpp
+++ b/generator/templates/opengl/OpenGLFunctionsBase.cpp
@@ -20,7 +20,7 @@
 MaybeError OpenGLFunctionsBase::LoadProc(GetProcAddress getProc, T* memberProc, const char* name) {
     *memberProc = reinterpret_cast<T>(getProc(name));
     if (DAWN_UNLIKELY(memberProc == nullptr)) {
-        return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't load GL proc: ") + name);
+        return DAWN_INTERNAL_ERROR(std::string("Couldn't load GL proc: ") + name);
     }
     return {};
 }
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 185d659..fa3cd9b 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -109,23 +109,41 @@
         mLossStatus = LossStatus::AlreadyLost;
     }
 
-    void DeviceBase::HandleError(wgpu::ErrorType type, const char* message) {
-        if (type == wgpu::ErrorType::DeviceLost) {
+    void DeviceBase::HandleError(InternalErrorType type, const char* message) {
+        // If we receive an internal error, assume the backend can't recover and proceed with
+        // device destruction. We first wait for all previous commands to be completed so that
+        // backend objects can be freed immediately, before handling the loss.
+        if (type == InternalErrorType::Internal) {
+            mLossStatus = LossStatus::BeingLost;
+            // Assert that errors are device loss so that we can continue with destruction.
+            AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
             HandleLoss(message);
         }
-        // Still forward device loss to error scope so it can reject them all
-        mCurrentErrorScope->HandleError(type, message);
+
+        // The device was lost for real, call the loss handler because all the backend objects are
+        // as if no longer in use.
+        if (type == InternalErrorType::DeviceLost) {
+            HandleLoss(message);
+        }
+
+        // Still forward device loss and internal errors to the error scopes so they all reject.
+        mCurrentErrorScope->HandleError(ToWGPUErrorType(type), message);
     }
 
     void DeviceBase::InjectError(wgpu::ErrorType type, const char* message) {
         if (ConsumedError(ValidateErrorType(type))) {
             return;
         }
-        if (DAWN_UNLIKELY(type == wgpu::ErrorType::NoError)) {
-            HandleError(wgpu::ErrorType::Validation, "Invalid injected error NoError");
+
+        // This method should only be used to make error scope reject. For DeviceLost there is the
+        // LoseForTesting function that can be used instead.
+        if (type != wgpu::ErrorType::Validation && type != wgpu::ErrorType::OutOfMemory) {
+            HandleError(InternalErrorType::Validation,
+                        "Invalid injected error, must be Validation or OutOfMemory");
             return;
         }
-        HandleError(type, message);
+
+        HandleError(FromWGPUErrorType(type), message);
     }
 
     void DeviceBase::ConsumeError(std::unique_ptr<ErrorData> error) {
@@ -200,10 +218,7 @@
             return;
         }
 
-        mLossStatus = LossStatus::BeingLost;
-        // Assert that errors are device loss so that we can continue with destruction
-        AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
-        HandleError(wgpu::ErrorType::DeviceLost, "Device lost for testing");
+        HandleError(InternalErrorType::Internal, "Device lost for testing");
     }
 
     bool DeviceBase::IsLost() const {
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 235ccd2..e967f3e 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(wgpu::ErrorType type, const char* message);
+        void HandleError(InternalErrorType type, const char* message);
 
         bool ConsumedError(MaybeError maybeError) {
             if (DAWN_UNLIKELY(maybeError.IsError())) {
diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp
index b8be069..8ecf7b2 100644
--- a/src/dawn_native/EncodingContext.cpp
+++ b/src/dawn_native/EncodingContext.cpp
@@ -53,7 +53,7 @@
         }
     }
 
-    void EncodingContext::HandleError(wgpu::ErrorType type, const char* message) {
+    void EncodingContext::HandleError(InternalErrorType type, const char* message) {
         if (!IsFinished()) {
             // If the encoding context is not finished, errors are deferred until
             // Finish() is called.
diff --git a/src/dawn_native/EncodingContext.h b/src/dawn_native/EncodingContext.h
index 1eef6c5..3142bd0 100644
--- a/src/dawn_native/EncodingContext.h
+++ b/src/dawn_native/EncodingContext.h
@@ -39,7 +39,7 @@
         CommandIterator* GetIterator();
 
         // Functions to handle encoder errors
-        void HandleError(wgpu::ErrorType type, const char* message);
+        void HandleError(InternalErrorType type, const char* message);
 
         inline void ConsumeError(std::unique_ptr<ErrorData> error) {
             HandleError(error->GetType(), error->GetMessage().c_str());
@@ -58,10 +58,10 @@
             if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
                 if (mCurrentEncoder != mTopLevelEncoder) {
                     // The top level encoder was used when a pass encoder was current.
-                    HandleError(wgpu::ErrorType::Validation,
+                    HandleError(InternalErrorType::Validation,
                                 "Command cannot be recorded inside a pass");
                 } else {
-                    HandleError(wgpu::ErrorType::Validation,
+                    HandleError(InternalErrorType::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 79c2024..13db32e 100644
--- a/src/dawn_native/Error.cpp
+++ b/src/dawn_native/Error.cpp
@@ -18,10 +18,43 @@
 #include "dawn_native/dawn_platform.h"
 
 namespace dawn_native {
+
     void AssertAndIgnoreDeviceLossError(MaybeError maybeError) {
         if (maybeError.IsError()) {
             std::unique_ptr<ErrorData> errorData = maybeError.AcquireError();
-            ASSERT(errorData->GetType() == wgpu::ErrorType::DeviceLost);
+            ASSERT(errorData->GetType() == InternalErrorType::DeviceLost);
         }
     }
+
+    wgpu::ErrorType ToWGPUErrorType(InternalErrorType type) {
+        switch (type) {
+            case InternalErrorType::Validation:
+                return wgpu::ErrorType::Validation;
+            case InternalErrorType::OutOfMemory:
+                return wgpu::ErrorType::OutOfMemory;
+
+            // There is no equivalent of Internal errors in the WebGPU API. Internal errors cause
+            // the device at the API level to be lost, so treat it like a DeviceLost error.
+            case InternalErrorType::Internal:
+            case InternalErrorType::DeviceLost:
+                return wgpu::ErrorType::DeviceLost;
+
+            default:
+                return wgpu::ErrorType::Unknown;
+        }
+    }
+
+    InternalErrorType FromWGPUErrorType(wgpu::ErrorType type) {
+        switch (type) {
+            case wgpu::ErrorType::Validation:
+                return InternalErrorType::Validation;
+            case wgpu::ErrorType::OutOfMemory:
+                return InternalErrorType::OutOfMemory;
+            case wgpu::ErrorType::DeviceLost:
+                return InternalErrorType::DeviceLost;
+            default:
+                return InternalErrorType::Internal;
+        }
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/Error.h b/src/dawn_native/Error.h
index 87ac540..41a1eff 100644
--- a/src/dawn_native/Error.h
+++ b/src/dawn_native/Error.h
@@ -22,7 +22,13 @@
 
 namespace dawn_native {
 
-    enum class InternalErrorType : uint32_t { Validation, DeviceLost, Unimplemented, OutOfMemory };
+    enum class InternalErrorType : uint32_t {
+        Validation,
+        DeviceLost,
+        Internal,
+        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.
@@ -40,11 +46,37 @@
     //
     // but shorthand version for specific error types are preferred:
     //   return DAWN_VALIDATION_ERROR("My error message");
+    //
+    // There are different types of errors that should be used for different purpose:
+    //
+    //   - Validation: these are errors that show the user did something bad, which causes the
+    //     whole call to be a no-op. It's most commonly found in the frontend but there can be some
+    //     backend specific validation in non-conformant backends too.
+    //
+    //   - Out of memory: creation of a Buffer or Texture failed because there isn't enough memory.
+    //     This is similar to validation errors in that the call becomes a no-op and returns an
+    //     error object, but is reported separated from validation to the user.
+    //
+    //   - Device loss: the backend driver reported that the GPU has been lost, which means all
+    //     previous commands magically disappeared and the only thing left to do is clean up.
+    //     Note: Device loss should be used rarely and in most case you want to use Internal
+    //     instead.
+    //
+    //   - Internal: something happened that the backend didn't expect, and it doesn't know
+    //     how to recover from that situation. This causes the device to be lost, but is separate
+    //     from device loss, because the GPU execution is still happening so we need to clean up
+    //     more gracefully.
+    //
+    //   - Unimplemented: same as Internal except it puts "unimplemented" in the error message for
+    //     more clarity.
+
 #define DAWN_MAKE_ERROR(TYPE, MESSAGE) \
     ::dawn_native::ErrorData::Create(TYPE, MESSAGE, __FILE__, __func__, __LINE__)
 #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_INTERNAL_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Internal, MESSAGE)
+#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) \
+    DAWN_MAKE_ERROR(InternalErrorType::Internal, std::string("Unimplemented: ") + MESSAGE)
 #define DAWN_OUT_OF_MEMORY_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::OutOfMemory, MESSAGE)
 
 #define DAWN_CONCAT1(x, y) x##y
@@ -84,6 +116,9 @@
     // Assert that errors are device loss so that we can continue with destruction
     void AssertAndIgnoreDeviceLossError(MaybeError maybeError);
 
+    wgpu::ErrorType ToWGPUErrorType(InternalErrorType type);
+    InternalErrorType FromWGPUErrorType(wgpu::ErrorType type);
+
 }  // namespace dawn_native
 
 #endif  // DAWNNATIVE_ERROR_H_
diff --git a/src/dawn_native/ErrorData.cpp b/src/dawn_native/ErrorData.cpp
index bdfed7f..41d0c29 100644
--- a/src/dawn_native/ErrorData.cpp
+++ b/src/dawn_native/ErrorData.cpp
@@ -42,23 +42,10 @@
         mBacktrace.push_back(std::move(record));
     }
 
-    InternalErrorType ErrorData::GetInternalType() const {
+    InternalErrorType ErrorData::GetType() const {
         return mType;
     }
 
-    wgpu::ErrorType ErrorData::GetType() const {
-        switch (mType) {
-            case InternalErrorType::Validation:
-                return wgpu::ErrorType::Validation;
-            case InternalErrorType::OutOfMemory:
-                return wgpu::ErrorType::OutOfMemory;
-            case InternalErrorType::DeviceLost:
-                return wgpu::ErrorType::DeviceLost;
-            default:
-                return wgpu::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 9c8ee83..0248602 100644
--- a/src/dawn_native/ErrorData.h
+++ b/src/dawn_native/ErrorData.h
@@ -49,8 +49,7 @@
         };
         void AppendBacktrace(const char* file, const char* function, int line);
 
-        InternalErrorType GetInternalType() const;
-        wgpu::ErrorType GetType() const;
+        InternalErrorType GetType() const;
         const std::string& GetMessage() const;
         const std::vector<BacktraceRecord>& GetBacktrace() const;
 
diff --git a/src/dawn_native/d3d12/AdapterD3D12.cpp b/src/dawn_native/d3d12/AdapterD3D12.cpp
index 487cef9..9f64134 100644
--- a/src/dawn_native/d3d12/AdapterD3D12.cpp
+++ b/src/dawn_native/d3d12/AdapterD3D12.cpp
@@ -63,7 +63,7 @@
         const PlatformFunctions* functions = GetBackend()->GetFunctions();
         if (FAILED(functions->d3d12CreateDevice(GetHardwareAdapter(), D3D_FEATURE_LEVEL_11_0,
                                                 _uuidof(ID3D12Device), &mD3d12Device))) {
-            return DAWN_DEVICE_LOST_ERROR("D3D12CreateDevice failed");
+            return DAWN_INTERNAL_ERROR("D3D12CreateDevice failed");
         }
 
         DXGI_ADAPTER_DESC1 adapterDesc;
diff --git a/src/dawn_native/d3d12/BackendD3D12.cpp b/src/dawn_native/d3d12/BackendD3D12.cpp
index 40e424d..b7a3f82 100644
--- a/src/dawn_native/d3d12/BackendD3D12.cpp
+++ b/src/dawn_native/d3d12/BackendD3D12.cpp
@@ -61,7 +61,7 @@
             }
 
             if (FAILED(functions->createDxgiFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)))) {
-                return DAWN_DEVICE_LOST_ERROR("Failed to create a DXGI factory");
+                return DAWN_INTERNAL_ERROR("Failed to create a DXGI factory");
             }
 
             ASSERT(factory != nullptr);
diff --git a/src/dawn_native/d3d12/D3D12Error.cpp b/src/dawn_native/d3d12/D3D12Error.cpp
index 2cd4627..f92b93a 100644
--- a/src/dawn_native/d3d12/D3D12Error.cpp
+++ b/src/dawn_native/d3d12/D3D12Error.cpp
@@ -23,7 +23,7 @@
         }
 
         std::string message = std::string(context) + " failed with " + std::to_string(result);
-        return DAWN_DEVICE_LOST_ERROR(message);
+        return DAWN_INTERNAL_ERROR(message);
     }
 
     MaybeError CheckOutOfMemoryHRESULT(HRESULT result, const char* context) {
@@ -33,4 +33,4 @@
         return CheckHRESULT(result, context);
     }
 
-}}  // namespace dawn_native::d3d12
\ No newline at end of file
+}}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/PlatformFunctions.cpp b/src/dawn_native/d3d12/PlatformFunctions.cpp
index ae25dc1..1e79f88 100644
--- a/src/dawn_native/d3d12/PlatformFunctions.cpp
+++ b/src/dawn_native/d3d12/PlatformFunctions.cpp
@@ -45,7 +45,7 @@
                                "D3D12SerializeVersionedRootSignature", &error) ||
             !mD3D12Lib.GetProc(&d3d12CreateVersionedRootSignatureDeserializer,
                                "D3D12CreateVersionedRootSignatureDeserializer", &error)) {
-            return DAWN_DEVICE_LOST_ERROR(error.c_str());
+            return DAWN_INTERNAL_ERROR(error.c_str());
         }
 
         return {};
@@ -55,7 +55,7 @@
         std::string error;
         if (!mD3D11Lib.Open("d3d11.dll", &error) ||
             !mD3D11Lib.GetProc(&d3d11on12CreateDevice, "D3D11On12CreateDevice", &error)) {
-            return DAWN_DEVICE_LOST_ERROR(error.c_str());
+            return DAWN_INTERNAL_ERROR(error.c_str());
         }
 
         return {};
@@ -66,7 +66,7 @@
         if (!mDXGILib.Open("dxgi.dll", &error) ||
             !mDXGILib.GetProc(&dxgiGetDebugInterface1, "DXGIGetDebugInterface1", &error) ||
             !mDXGILib.GetProc(&createDxgiFactory2, "CreateDXGIFactory2", &error)) {
-            return DAWN_DEVICE_LOST_ERROR(error.c_str());
+            return DAWN_INTERNAL_ERROR(error.c_str());
         }
 
         return {};
@@ -76,7 +76,7 @@
         std::string error;
         if (!mD3DCompilerLib.Open("d3dcompiler_47.dll", &error) ||
             !mD3DCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) {
-            return DAWN_DEVICE_LOST_ERROR(error.c_str());
+            return DAWN_INTERNAL_ERROR(error.c_str());
         }
 
         return {};
diff --git a/src/dawn_native/d3d12/SwapChainD3D12.cpp b/src/dawn_native/d3d12/SwapChainD3D12.cpp
index 32c1da6..45ec25b 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(wgpu::ErrorType::Unknown, error);
+            GetDevice()->HandleError(InternalErrorType::Internal, error);
             return nullptr;
         }
 
diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm
index 7c626987..2ec8410 100644
--- a/src/dawn_native/metal/BackendMTL.mm
+++ b/src/dawn_native/metal/BackendMTL.mm
@@ -57,7 +57,7 @@
             }
 
             if (vendorId == 0) {
-                return DAWN_DEVICE_LOST_ERROR("Failed to find vendor id with the device");
+                return DAWN_INTERNAL_ERROR("Failed to find vendor id with the device");
             }
 
             // Set vendor id with 0
@@ -108,7 +108,7 @@
             // Get a matching dictionary for the IOGraphicsAccelerator2
             CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching([device registryID]);
             if (matchingDict == nullptr) {
-                return DAWN_DEVICE_LOST_ERROR("Failed to create the matching dict for the device");
+                return DAWN_INTERNAL_ERROR("Failed to create the matching dict for the device");
             }
 
             // IOServiceGetMatchingService will consume the reference on the matching dictionary,
@@ -116,7 +116,7 @@
             io_registry_entry_t acceleratorEntry =
                 IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
             if (acceleratorEntry == IO_OBJECT_NULL) {
-                return DAWN_DEVICE_LOST_ERROR(
+                return DAWN_INTERNAL_ERROR(
                     "Failed to get the IO registry entry for the accelerator");
             }
 
@@ -125,7 +125,7 @@
             if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) !=
                 kIOReturnSuccess) {
                 IOObjectRelease(acceleratorEntry);
-                return DAWN_DEVICE_LOST_ERROR("Failed to get the IO registry entry for the device");
+                return DAWN_INTERNAL_ERROR("Failed to get the IO registry entry for the device");
             }
 
             ASSERT(deviceEntry != IO_OBJECT_NULL);
diff --git a/src/dawn_native/metal/ComputePipelineMTL.mm b/src/dawn_native/metal/ComputePipelineMTL.mm
index 5e08cf6..e9252af 100644
--- a/src/dawn_native/metal/ComputePipelineMTL.mm
+++ b/src/dawn_native/metal/ComputePipelineMTL.mm
@@ -43,7 +43,7 @@
             [mtlDevice newComputePipelineStateWithFunction:computeData.function error:&error];
         if (error != nil) {
             NSLog(@" error => %@", error);
-            return DAWN_DEVICE_LOST_ERROR("Error creating pipeline state");
+            return DAWN_INTERNAL_ERROR("Error creating pipeline state");
         }
 
         // Copy over the local workgroup size as it is passed to dispatch explicitly in Metal
diff --git a/src/dawn_native/metal/RenderPipelineMTL.mm b/src/dawn_native/metal/RenderPipelineMTL.mm
index 6bf8f07..575140f 100644
--- a/src/dawn_native/metal/RenderPipelineMTL.mm
+++ b/src/dawn_native/metal/RenderPipelineMTL.mm
@@ -391,7 +391,7 @@
             [descriptorMTL release];
             if (error != nil) {
                 NSLog(@" error => %@", error);
-                return DAWN_DEVICE_LOST_ERROR("Error creating rendering pipeline state");
+                return DAWN_INTERNAL_ERROR("Error creating rendering pipeline state");
             }
         }
 
diff --git a/src/dawn_native/metal/StagingBufferMTL.mm b/src/dawn_native/metal/StagingBufferMTL.mm
index 491b35c..390f00c 100644
--- a/src/dawn_native/metal/StagingBufferMTL.mm
+++ b/src/dawn_native/metal/StagingBufferMTL.mm
@@ -32,7 +32,7 @@
 
         mMappedPointer = [mBuffer contents];
         if (mMappedPointer == nullptr) {
-            return DAWN_DEVICE_LOST_ERROR("Unable to map staging buffer.");
+            return DAWN_INTERNAL_ERROR("Unable to map staging buffer.");
         }
 
         return {};
diff --git a/src/dawn_native/metal/SwapChainMTL.mm b/src/dawn_native/metal/SwapChainMTL.mm
index b0dcdc8..2a5ffb1 100644
--- a/src/dawn_native/metal/SwapChainMTL.mm
+++ b/src/dawn_native/metal/SwapChainMTL.mm
@@ -43,7 +43,7 @@
         DawnSwapChainNextTexture next = {};
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
         if (error) {
-            GetDevice()->HandleError(wgpu::ErrorType::Unknown, error);
+            GetDevice()->HandleError(InternalErrorType::Internal, error);
             return nullptr;
         }
 
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index 43b38bf..dbb40ec 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -211,7 +211,7 @@
     MaybeError Device::IncrementMemoryUsage(size_t bytes) {
         static_assert(kMaxMemoryUsage <= std::numeric_limits<size_t>::max() / 2, "");
         if (bytes > kMaxMemoryUsage || mMemoryUsage + bytes > kMaxMemoryUsage) {
-            return DAWN_DEVICE_LOST_ERROR("Out of memory.");
+            return DAWN_OUT_OF_MEMORY_ERROR("Out of memory.");
         }
         mMemoryUsage += bytes;
         return {};
diff --git a/src/dawn_native/opengl/OpenGLFunctions.cpp b/src/dawn_native/opengl/OpenGLFunctions.cpp
index ba5b69a..c54a0ad 100644
--- a/src/dawn_native/opengl/OpenGLFunctions.cpp
+++ b/src/dawn_native/opengl/OpenGLFunctions.cpp
@@ -22,7 +22,7 @@
     MaybeError OpenGLFunctions::Initialize(GetProcAddress getProc) {
         PFNGLGETSTRINGPROC getString = reinterpret_cast<PFNGLGETSTRINGPROC>(getProc("glGetString"));
         if (getString == nullptr) {
-            return DAWN_DEVICE_LOST_ERROR("Couldn't load glGetString");
+            return DAWN_INTERNAL_ERROR("Couldn't load glGetString");
         }
 
         std::string version = reinterpret_cast<const char*>(getString(GL_VERSION));
diff --git a/src/dawn_native/opengl/SwapChainGL.cpp b/src/dawn_native/opengl/SwapChainGL.cpp
index fdedeb5a..40efd0a 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(wgpu::ErrorType::Unknown, error);
+            GetDevice()->HandleError(InternalErrorType::Internal, error);
             return nullptr;
         }
         GLuint nativeTexture = next.texture.u32;
diff --git a/src/dawn_native/vulkan/AdapterVk.cpp b/src/dawn_native/vulkan/AdapterVk.cpp
index 14c5b27..d138cef 100644
--- a/src/dawn_native/vulkan/AdapterVk.cpp
+++ b/src/dawn_native/vulkan/AdapterVk.cpp
@@ -40,7 +40,7 @@
     MaybeError Adapter::Initialize() {
         DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this));
         if (!mDeviceInfo.maintenance1) {
-            return DAWN_DEVICE_LOST_ERROR(
+            return DAWN_INTERNAL_ERROR(
                 "Dawn requires Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 in order to support "
                 "viewport flipY");
         }
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index 8cd2492..aa77951 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -86,7 +86,7 @@
         if (GetInstance()->IsBackendValidationEnabled()) {
             std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR;
             if (!SetEnvironmentVar("VK_LAYER_PATH", vkDataDir.c_str())) {
-                return DAWN_DEVICE_LOST_ERROR("Couldn't set VK_LAYER_PATH");
+                return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH");
             }
         }
 #endif
@@ -94,7 +94,7 @@
         std::string fullSwiftshaderICDPath =
             GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON;
         if (!SetEnvironmentVar("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) {
-            return DAWN_DEVICE_LOST_ERROR("Couldn't set VK_ICD_FILENAMES");
+            return DAWN_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES");
         }
 #endif
 
@@ -112,7 +112,7 @@
         }
 #endif
 
-        return DAWN_DEVICE_LOST_ERROR("Couldn't load Vulkan");
+        return DAWN_INTERNAL_ERROR("Couldn't load Vulkan");
     }
 
     MaybeError Backend::Initialize() {
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index d42719a..e239a64 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -385,7 +385,7 @@
             }
 
             if (universalQueueFamily == -1) {
-                return DAWN_DEVICE_LOST_ERROR("No universal queue family");
+                return DAWN_INTERNAL_ERROR("No universal queue family");
             }
             mQueueFamily = static_cast<uint32_t>(universalQueueFamily);
         }
diff --git a/src/dawn_native/vulkan/StagingBufferVk.cpp b/src/dawn_native/vulkan/StagingBufferVk.cpp
index 4fa3b7b..dfdb978 100644
--- a/src/dawn_native/vulkan/StagingBufferVk.cpp
+++ b/src/dawn_native/vulkan/StagingBufferVk.cpp
@@ -52,7 +52,7 @@
 
         mMappedPointer = mAllocation.GetMappedPointer();
         if (mMappedPointer == nullptr) {
-            return DAWN_DEVICE_LOST_ERROR("Unable to map staging buffer.");
+            return DAWN_INTERNAL_ERROR("Unable to map staging buffer.");
         }
 
         return {};
diff --git a/src/dawn_native/vulkan/SwapChainVk.cpp b/src/dawn_native/vulkan/SwapChainVk.cpp
index e64f5e5..44ec020 100644
--- a/src/dawn_native/vulkan/SwapChainVk.cpp
+++ b/src/dawn_native/vulkan/SwapChainVk.cpp
@@ -43,7 +43,7 @@
         DawnSwapChainError error = im.GetNextTexture(im.userData, &next);
 
         if (error) {
-            GetDevice()->HandleError(wgpu::ErrorType::Unknown, error);
+            GetDevice()->HandleError(InternalErrorType::Internal, error);
             return nullptr;
         }
 
diff --git a/src/dawn_native/vulkan/VulkanError.cpp b/src/dawn_native/vulkan/VulkanError.cpp
index af235f1..9d809ba 100644
--- a/src/dawn_native/vulkan/VulkanError.cpp
+++ b/src/dawn_native/vulkan/VulkanError.cpp
@@ -73,8 +73,14 @@
         if (DAWN_LIKELY(result == VK_SUCCESS)) {
             return {};
         }
+
         std::string message = std::string(context) + " failed with " + VkResultAsString(result);
-        return DAWN_DEVICE_LOST_ERROR(message);
+
+        if (result == VK_ERROR_DEVICE_LOST) {
+            return DAWN_DEVICE_LOST_ERROR(message);
+        } else {
+            return DAWN_INTERNAL_ERROR(message);
+        }
     }
 
     MaybeError CheckVkOOMThenSuccessImpl(VkResult result, const char* context) {
@@ -83,10 +89,14 @@
         }
 
         std::string message = std::string(context) + " failed with " + VkResultAsString(result);
+
         if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY || result == VK_FAKE_DEVICE_OOM_FOR_TESTING) {
             return DAWN_OUT_OF_MEMORY_ERROR(message);
+        } else if (result == VK_ERROR_DEVICE_LOST) {
+            return DAWN_DEVICE_LOST_ERROR(message);
+        } else {
+            return DAWN_INTERNAL_ERROR(message);
         }
-        return DAWN_DEVICE_LOST_ERROR(message);
     }
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/VulkanFunctions.cpp b/src/dawn_native/vulkan/VulkanFunctions.cpp
index 3d9aee0..159b740 100644
--- a/src/dawn_native/vulkan/VulkanFunctions.cpp
+++ b/src/dawn_native/vulkan/VulkanFunctions.cpp
@@ -22,12 +22,12 @@
 #define GET_GLOBAL_PROC(name)                                                          \
     name = reinterpret_cast<decltype(name)>(GetInstanceProcAddr(nullptr, "vk" #name)); \
     if (name == nullptr) {                                                             \
-        return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't get proc vk") + #name);    \
+        return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #name);       \
     }
 
     MaybeError VulkanFunctions::LoadGlobalProcs(const DynamicLib& vulkanLib) {
         if (!vulkanLib.GetProc(&GetInstanceProcAddr, "vkGetInstanceProcAddr")) {
-            return DAWN_DEVICE_LOST_ERROR("Couldn't get vkGetInstanceProcAddr");
+            return DAWN_INTERNAL_ERROR("Couldn't get vkGetInstanceProcAddr");
         }
 
         GET_GLOBAL_PROC(CreateInstance);
@@ -44,7 +44,7 @@
 #define GET_INSTANCE_PROC_BASE(name, procName)                                              \
     name = reinterpret_cast<decltype(name)>(GetInstanceProcAddr(instance, "vk" #procName)); \
     if (name == nullptr) {                                                                  \
-        return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't get proc vk") + #procName);     \
+        return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #procName);        \
     }
 
 #define GET_INSTANCE_PROC(name) GET_INSTANCE_PROC_BASE(name, name)
@@ -147,7 +147,7 @@
 #define GET_DEVICE_PROC(name)                                                       \
     name = reinterpret_cast<decltype(name)>(GetDeviceProcAddr(device, "vk" #name)); \
     if (name == nullptr) {                                                          \
-        return DAWN_DEVICE_LOST_ERROR(std::string("Couldn't get proc vk") + #name); \
+        return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #name);    \
     }
 
     MaybeError VulkanFunctions::LoadDeviceProcs(VkDevice device,
diff --git a/src/dawn_native/vulkan/VulkanInfo.cpp b/src/dawn_native/vulkan/VulkanInfo.cpp
index 70aa8c5..3d2f66c 100644
--- a/src/dawn_native/vulkan/VulkanInfo.cpp
+++ b/src/dawn_native/vulkan/VulkanInfo.cpp
@@ -93,7 +93,7 @@
             // incomplete otherwise. This means that both values represent a success.
             // This is the same for all Enumarte functions
             if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return DAWN_DEVICE_LOST_ERROR("vkEnumerateInstanceLayerProperties");
+                return DAWN_INTERNAL_ERROR("vkEnumerateInstanceLayerProperties");
             }
 
             info.layers.resize(count);
@@ -123,7 +123,7 @@
         // Gather the info about the instance extensions
         {
             if (!EnumerateInstanceExtensions(nullptr, vkFunctions, &info.extensions)) {
-                return DAWN_DEVICE_LOST_ERROR("vkEnumerateInstanceExtensionProperties");
+                return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties");
             }
 
             for (const auto& extension : info.extensions) {
@@ -169,7 +169,7 @@
             std::vector<VkExtensionProperties> layer_extensions;
             if (!EnumerateInstanceExtensions(kLayerNameFuchsiaImagePipeSwapchain, vkFunctions,
                                              &layer_extensions)) {
-                return DAWN_DEVICE_LOST_ERROR("vkEnumerateInstanceExtensionProperties");
+                return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties");
             }
 
             for (const auto& extension : layer_extensions) {
@@ -207,7 +207,7 @@
         VkResult result =
             VkResult::WrapUnsafe(vkFunctions.EnumeratePhysicalDevices(instance, &count, nullptr));
         if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-            return DAWN_DEVICE_LOST_ERROR("vkEnumeratePhysicalDevices");
+            return DAWN_INTERNAL_ERROR("vkEnumeratePhysicalDevices");
         }
 
         std::vector<VkPhysicalDevice> physicalDevices(count);
@@ -254,7 +254,7 @@
             VkResult result = VkResult::WrapUnsafe(
                 vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr));
             if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceLayerProperties");
+                return DAWN_INTERNAL_ERROR("vkEnumerateDeviceLayerProperties");
             }
 
             info.layers.resize(count);
@@ -269,7 +269,7 @@
             VkResult result = VkResult::WrapUnsafe(vkFunctions.EnumerateDeviceExtensionProperties(
                 physicalDevice, nullptr, &count, nullptr));
             if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return DAWN_DEVICE_LOST_ERROR("vkEnumerateDeviceExtensionProperties");
+                return DAWN_INTERNAL_ERROR("vkEnumerateDeviceExtensionProperties");
             }
 
             info.extensions.resize(count);
@@ -357,7 +357,7 @@
             VkResult result = VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(
                 physicalDevice, surface, &count, nullptr));
             if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR");
+                return DAWN_INTERNAL_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR");
             }
 
             info.formats.resize(count);
@@ -373,7 +373,7 @@
                 VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR(
                     physicalDevice, surface, &count, nullptr));
             if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return DAWN_DEVICE_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR");
+                return DAWN_INTERNAL_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR");
             }
 
             info.presentModes.resize(count);