Improve validation errors for Buffer

This required adding a version of device.ConsumedError that takes a
MaybeError and a formatted string context.

Also removes an unused method.

Bug: dawn:563
Change-Id: I7a2139cc47945d1f29bdfe926db3c932bf17c6d7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/65564
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Brandon Jones <bajones@google.com>
diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp
index 5872c68..86ffcb9 100644
--- a/src/dawn_native/Buffer.cpp
+++ b/src/dawn_native/Buffer.cpp
@@ -99,29 +99,28 @@
     }  // anonymous namespace
 
     MaybeError ValidateBufferDescriptor(DeviceBase*, const BufferDescriptor* descriptor) {
-        if (descriptor->nextInChain != nullptr) {
-            return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
-        }
-
+        DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr");
         DAWN_TRY(ValidateBufferUsage(descriptor->usage));
 
         wgpu::BufferUsage usage = descriptor->usage;
 
         const wgpu::BufferUsage kMapWriteAllowedUsages =
             wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
-        if (usage & wgpu::BufferUsage::MapWrite && (usage & kMapWriteAllowedUsages) != usage) {
-            return DAWN_VALIDATION_ERROR("Only CopySrc is allowed with MapWrite");
-        }
+        DAWN_INVALID_IF(
+            usage & wgpu::BufferUsage::MapWrite && !IsSubset(usage, kMapWriteAllowedUsages),
+            "Buffer usages (%s) contains %s but is not a subset of %s.", usage,
+            wgpu::BufferUsage::MapWrite, kMapWriteAllowedUsages);
 
         const wgpu::BufferUsage kMapReadAllowedUsages =
             wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
-        if (usage & wgpu::BufferUsage::MapRead && (usage & kMapReadAllowedUsages) != usage) {
-            return DAWN_VALIDATION_ERROR("Only CopyDst is allowed with MapRead");
-        }
+        DAWN_INVALID_IF(
+            usage & wgpu::BufferUsage::MapRead && !IsSubset(usage, kMapReadAllowedUsages),
+            "Buffer usages (%s) contains %s but is not a subset of %s.", usage,
+            wgpu::BufferUsage::MapRead, kMapReadAllowedUsages);
 
-        if (descriptor->mappedAtCreation && descriptor->size % 4 != 0) {
-            return DAWN_VALIDATION_ERROR("size must be aligned to 4 when mappedAtCreation is true");
-        }
+        DAWN_INVALID_IF(descriptor->mappedAtCreation && descriptor->size % 4 != 0,
+                        "Buffer is mapped at creation but its size (%u) is not a multiple of 4.",
+                        descriptor->size);
 
         return {};
     }
@@ -265,10 +264,10 @@
 
         switch (mState) {
             case BufferState::Destroyed:
-                return DAWN_VALIDATION_ERROR("Destroyed buffer used in a submit");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s used in submit while destroyed.", this);
             case BufferState::Mapped:
             case BufferState::MappedAtCreation:
-                return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s used in submit while mapped.", this);
             case BufferState::Unmapped:
                 return {};
         }
@@ -304,7 +303,9 @@
         }
 
         WGPUBufferMapAsyncStatus status;
-        if (GetDevice()->ConsumedError(ValidateMapAsync(mode, offset, size, &status))) {
+        if (GetDevice()->ConsumedError(ValidateMapAsync(mode, offset, size, &status),
+                                       "calling %s.MapAsync(%s, %u, %u, ...)", this, mode, offset,
+                                       size)) {
             if (callback) {
                 callback(status, userdata);
             }
@@ -360,7 +361,7 @@
             static_cast<ErrorBuffer*>(this)->ClearMappedData();
             mState = BufferState::Destroyed;
         }
-        if (GetDevice()->ConsumedError(ValidateDestroy())) {
+        if (GetDevice()->ConsumedError(ValidateDestroy(), "calling %s.Destroy()", this)) {
             return;
         }
         ASSERT(!IsError());
@@ -411,7 +412,7 @@
             static_cast<ErrorBuffer*>(this)->ClearMappedData();
             mState = BufferState::Unmapped;
         }
-        if (GetDevice()->ConsumedError(ValidateUnmap())) {
+        if (GetDevice()->ConsumedError(ValidateUnmap(), "calling %s.Unmap()", this)) {
             return;
         }
         ASSERT(!IsError());
@@ -439,32 +440,6 @@
         mState = BufferState::Unmapped;
     }
 
-    MaybeError BufferBase::ValidateMap(wgpu::BufferUsage requiredUsage,
-                                       WGPUBufferMapAsyncStatus* status) const {
-        *status = WGPUBufferMapAsyncStatus_DeviceLost;
-        DAWN_TRY(GetDevice()->ValidateIsAlive());
-
-        *status = WGPUBufferMapAsyncStatus_Error;
-        DAWN_TRY(GetDevice()->ValidateObject(this));
-
-        switch (mState) {
-            case BufferState::Mapped:
-            case BufferState::MappedAtCreation:
-                return DAWN_VALIDATION_ERROR("Buffer is already mapped");
-            case BufferState::Destroyed:
-                return DAWN_VALIDATION_ERROR("Buffer is destroyed");
-            case BufferState::Unmapped:
-                break;
-        }
-
-        if (!(mUsage & requiredUsage)) {
-            return DAWN_VALIDATION_ERROR("Buffer needs the correct map usage bit");
-        }
-
-        *status = WGPUBufferMapAsyncStatus_Success;
-        return {};
-    }
-
     MaybeError BufferBase::ValidateMapAsync(wgpu::MapMode mode,
                                             size_t offset,
                                             size_t size,
@@ -475,44 +450,37 @@
         *status = WGPUBufferMapAsyncStatus_Error;
         DAWN_TRY(GetDevice()->ValidateObject(this));
 
-        if (offset % 8 != 0) {
-            return DAWN_VALIDATION_ERROR("offset must be a multiple of 8");
-        }
+        DAWN_INVALID_IF(offset % 8 != 0, "Offset (%u) must be a multiple of 8.", offset);
+        DAWN_INVALID_IF(size % 4 != 0, "Size (%u) must be a multiple of 4.", size);
 
-        if (size % 4 != 0) {
-            return DAWN_VALIDATION_ERROR("size must be a multiple of 4");
-        }
-
-        if (uint64_t(offset) > mSize || uint64_t(size) > mSize - uint64_t(offset)) {
-            return DAWN_VALIDATION_ERROR("size + offset must fit in the buffer");
-        }
+        DAWN_INVALID_IF(uint64_t(offset) > mSize || uint64_t(size) > mSize - uint64_t(offset),
+                        "Mapping range (offset:%u, size: %u) doesn't fit in the size (%u) of %s.",
+                        offset, size, mSize, this);
 
         switch (mState) {
             case BufferState::Mapped:
             case BufferState::MappedAtCreation:
-                return DAWN_VALIDATION_ERROR("Buffer is already mapped");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s is already mapped.", this);
             case BufferState::Destroyed:
-                return DAWN_VALIDATION_ERROR("Buffer is destroyed");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s is destroyed.", this);
             case BufferState::Unmapped:
                 break;
         }
 
         bool isReadMode = mode & wgpu::MapMode::Read;
         bool isWriteMode = mode & wgpu::MapMode::Write;
-        if (!(isReadMode ^ isWriteMode)) {
-            return DAWN_VALIDATION_ERROR("Exactly one of Read or Write mode must be set");
-        }
+        DAWN_INVALID_IF(!(isReadMode ^ isWriteMode), "Map mode (%s) is not one of %s or %s.", mode,
+                        wgpu::MapMode::Write, wgpu::MapMode::Read);
 
         if (mode & wgpu::MapMode::Read) {
-            if (!(mUsage & wgpu::BufferUsage::MapRead)) {
-                return DAWN_VALIDATION_ERROR("The buffer must have the MapRead usage");
-            }
+            DAWN_INVALID_IF(!(mUsage & wgpu::BufferUsage::MapRead),
+                            "The buffer usages (%s) do not contain %s.", mUsage,
+                            wgpu::BufferUsage::MapRead);
         } else {
             ASSERT(mode & wgpu::MapMode::Write);
-
-            if (!(mUsage & wgpu::BufferUsage::MapWrite)) {
-                return DAWN_VALIDATION_ERROR("The buffer must have the MapWrite usage");
-            }
+            DAWN_INVALID_IF(!(mUsage & wgpu::BufferUsage::MapWrite),
+                            "The buffer usages (%s) do not contain %s.", mUsage,
+                            wgpu::BufferUsage::MapWrite);
         }
 
         *status = WGPUBufferMapAsyncStatus_Success;
@@ -569,9 +537,9 @@
                 // even if it did not have a mappable usage.
                 return {};
             case BufferState::Unmapped:
-                return DAWN_VALIDATION_ERROR("Buffer is unmapped");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s is unmapped.", this);
             case BufferState::Destroyed:
-                return DAWN_VALIDATION_ERROR("Buffer is destroyed");
+                return DAWN_FORMAT_VALIDATION_ERROR("%s is destroyed.", this);
         }
         UNREACHABLE();
     }
diff --git a/src/dawn_native/Buffer.h b/src/dawn_native/Buffer.h
index 4192e2d..6ba5755 100644
--- a/src/dawn_native/Buffer.h
+++ b/src/dawn_native/Buffer.h
@@ -105,8 +105,6 @@
         MaybeError CopyFromStagingBuffer();
         void CallMapCallback(MapRequestID mapID, WGPUBufferMapAsyncStatus status);
 
-        MaybeError ValidateMap(wgpu::BufferUsage requiredUsage,
-                               WGPUBufferMapAsyncStatus* status) const;
         MaybeError ValidateMapAsync(wgpu::MapMode mode,
                                     size_t offset,
                                     size_t size,
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 7077d6a..bb2a621 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -1160,7 +1160,8 @@
     ResultOrError<Ref<BufferBase>> DeviceBase::CreateBuffer(const BufferDescriptor* descriptor) {
         DAWN_TRY(ValidateIsAlive());
         if (IsValidationEnabled()) {
-            DAWN_TRY(ValidateBufferDescriptor(this, descriptor));
+            DAWN_TRY_CONTEXT(ValidateBufferDescriptor(this, descriptor), "validating %s",
+                             descriptor);
         }
 
         Ref<BufferBase> buffer;
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 79c0c80..49614c3 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -78,6 +78,23 @@
             return false;
         }
 
+        template <typename... Args>
+        bool ConsumedError(MaybeError maybeError, const char* formatStr, const Args&... args) {
+            if (DAWN_UNLIKELY(maybeError.IsError())) {
+                std::unique_ptr<ErrorData> error = maybeError.AcquireError();
+                if (error->GetType() == InternalErrorType::Validation) {
+                    std::string out;
+                    absl::UntypedFormatSpec format(formatStr);
+                    if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) {
+                        error->AppendContext(std::move(out));
+                    }
+                }
+                ConsumeError(std::move(error));
+                return true;
+            }
+            return false;
+        }
+
         template <typename T, typename... Args>
         bool ConsumedError(ResultOrError<T> resultOrError,
                            T* result,