Improve errors in BindGroupLayout, BindingInfo
Updates all validation messages in BindGroupLayout.cpp and
BindingInfo.cpp to give them better contextual information.
Bug: dawn:563
Change-Id: I7166dce65c93d7c8ac4dd72555fff34c9202e041
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/66841
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index f047692..c0097cc 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -37,9 +37,9 @@
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(storageTextureFormat));
ASSERT(format != nullptr);
- if (!format->supportsStorageUsage) {
- return DAWN_VALIDATION_ERROR("Texture format does not support storage textures");
- }
+ DAWN_INVALID_IF(!format->supportsStorageUsage,
+ "Texture format (%s) does not support storage textures.",
+ storageTextureFormat);
return {};
}
@@ -48,8 +48,8 @@
switch (dimension) {
case wgpu::TextureViewDimension::Cube:
case wgpu::TextureViewDimension::CubeArray:
- return DAWN_VALIDATION_ERROR(
- "Cube map and cube map texture views cannot be used as storage textures");
+ return DAWN_FORMAT_VALIDATION_ERROR(
+ "%s texture views cannot be used as storage textures.", dimension);
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::e2D:
@@ -62,40 +62,25 @@
}
UNREACHABLE();
}
- } // anonymous namespace
- MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
- const BindGroupLayoutDescriptor* descriptor,
- bool allowInternalBinding) {
- if (descriptor->nextInChain != nullptr) {
- return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
- }
-
- std::set<BindingNumber> bindingsSet;
- BindingCounts bindingCounts = {};
- for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
- const BindGroupLayoutEntry& entry = descriptor->entries[i];
- BindingNumber bindingNumber = BindingNumber(entry.binding);
-
- if (bindingsSet.count(bindingNumber) != 0) {
- return DAWN_VALIDATION_ERROR("some binding index was specified more than once");
- }
-
+ MaybeError ValidateBindGroupLayoutEntry(DeviceBase* device,
+ const BindGroupLayoutEntry& entry,
+ bool allowInternalBinding) {
DAWN_TRY(ValidateShaderStage(entry.visibility));
int bindingMemberCount = 0;
+ BindingInfoType bindingType;
wgpu::ShaderStage allowedStages = kAllStages;
if (entry.buffer.type != wgpu::BufferBindingType::Undefined) {
bindingMemberCount++;
+ bindingType = BindingInfoType::Buffer;
const BufferBindingLayout& buffer = entry.buffer;
// The kInternalStorageBufferBinding is used internally and not a value
// in wgpu::BufferBindingType.
if (buffer.type == kInternalStorageBufferBinding) {
- if (!allowInternalBinding) {
- return DAWN_VALIDATION_ERROR("Internal binding types are disallowed");
- }
+ DAWN_INVALID_IF(!allowInternalBinding, "Internal binding types are disallowed");
} else {
DAWN_TRY(ValidateBufferBindingType(buffer.type));
}
@@ -107,22 +92,23 @@
// Dynamic storage buffers aren't bounds checked properly in D3D12. Disallow them as
// unsafe until the bounds checks are implemented.
- if (device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs) &&
- buffer.hasDynamicOffset &&
- (buffer.type == wgpu::BufferBindingType::Storage ||
- buffer.type == kInternalStorageBufferBinding ||
- buffer.type == wgpu::BufferBindingType::ReadOnlyStorage)) {
- return DAWN_VALIDATION_ERROR(
- "Dynamic storage buffers are disallowed because they aren't secure yet. "
- "See https://crbug.com/dawn/429");
- }
+ DAWN_INVALID_IF(
+ device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs) &&
+ buffer.hasDynamicOffset &&
+ (buffer.type == wgpu::BufferBindingType::Storage ||
+ buffer.type == kInternalStorageBufferBinding ||
+ buffer.type == wgpu::BufferBindingType::ReadOnlyStorage),
+ "Dynamic storage buffers are disallowed because they aren't secure yet. "
+ "See https://crbug.com/dawn/429");
}
if (entry.sampler.type != wgpu::SamplerBindingType::Undefined) {
bindingMemberCount++;
+ bindingType = BindingInfoType::Sampler;
DAWN_TRY(ValidateSamplerBindingType(entry.sampler.type));
}
if (entry.texture.sampleType != wgpu::TextureSampleType::Undefined) {
bindingMemberCount++;
+ bindingType = BindingInfoType::Texture;
const TextureBindingLayout& texture = entry.texture;
DAWN_TRY(ValidateTextureSampleType(texture.sampleType));
@@ -133,12 +119,14 @@
viewDimension = texture.viewDimension;
}
- if (texture.multisampled && viewDimension != wgpu::TextureViewDimension::e2D) {
- return DAWN_VALIDATION_ERROR("Multisampled texture bindings must be 2D.");
- }
+ DAWN_INVALID_IF(
+ texture.multisampled && viewDimension != wgpu::TextureViewDimension::e2D,
+ "View dimension (%s) for a multisampled texture bindings was not %s.",
+ viewDimension, wgpu::TextureViewDimension::e2D);
}
if (entry.storageTexture.access != wgpu::StorageTextureAccess::Undefined) {
bindingMemberCount++;
+ bindingType = BindingInfoType::StorageTexture;
const StorageTextureBindingLayout& storageTexture = entry.storageTexture;
DAWN_TRY(ValidateStorageTextureAccess(storageTexture.access));
DAWN_TRY(ValidateStorageTextureFormat(device, storageTexture.format));
@@ -158,24 +146,48 @@
FindInChain(entry.nextInChain, &externalTextureBindingLayout);
if (externalTextureBindingLayout != nullptr) {
bindingMemberCount++;
+ bindingType = BindingInfoType::ExternalTexture;
}
- if (bindingMemberCount != 1) {
- return DAWN_VALIDATION_ERROR(
- "Exactly one of buffer, sampler, texture, storageTexture, or externalTexture "
- "must be set for each BindGroupLayoutEntry");
- }
+ DAWN_INVALID_IF(bindingMemberCount != 1,
+ "BindGroupLayoutEntry had more than one of buffer, sampler, texture, "
+ "storageTexture, or externalTexture set");
- if (!IsSubset(entry.visibility, allowedStages)) {
- return DAWN_VALIDATION_ERROR("Binding type cannot be used with this visibility.");
- }
+ DAWN_INVALID_IF(
+ !IsSubset(entry.visibility, allowedStages),
+ "%s bindings cannot be used with a visibility of %s. Only %s are allowed.",
+ bindingType, entry.visibility, allowedStages);
+
+ return {};
+ }
+
+ } // anonymous namespace
+
+ MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device,
+ const BindGroupLayoutDescriptor* descriptor,
+ bool allowInternalBinding) {
+ DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr");
+
+ std::set<BindingNumber> bindingsSet;
+ BindingCounts bindingCounts = {};
+
+ for (uint32_t i = 0; i < descriptor->entryCount; ++i) {
+ const BindGroupLayoutEntry& entry = descriptor->entries[i];
+ BindingNumber bindingNumber = BindingNumber(entry.binding);
+
+ DAWN_INVALID_IF(bindingsSet.count(bindingNumber) != 0,
+ "On entries[%u]: binding index (%u) was specified by a previous entry.",
+ i, entry.binding);
+
+ DAWN_TRY_CONTEXT(ValidateBindGroupLayoutEntry(device, entry, allowInternalBinding),
+ "validating entries[%u]", i);
IncrementBindingCounts(&bindingCounts, entry);
bindingsSet.insert(bindingNumber);
}
- DAWN_TRY(ValidateBindingCounts(bindingCounts));
+ DAWN_TRY_CONTEXT(ValidateBindingCounts(bindingCounts), "validating binding counts");
return {};
}
diff --git a/src/dawn_native/BindingInfo.cpp b/src/dawn_native/BindingInfo.cpp
index 4b6516f..aba5d0d 100644
--- a/src/dawn_native/BindingInfo.cpp
+++ b/src/dawn_native/BindingInfo.cpp
@@ -18,6 +18,32 @@
namespace dawn_native {
+ absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
+ BindingInfoType value,
+ const absl::FormatConversionSpec& spec,
+ absl::FormatSink* s) {
+ switch (value) {
+ case BindingInfoType::Buffer:
+ s->Append("Buffer");
+ break;
+ case BindingInfoType::Sampler:
+ s->Append("Sampler");
+ break;
+ case BindingInfoType::Texture:
+ s->Append("Texture");
+ break;
+ case BindingInfoType::StorageTexture:
+ s->Append("StorageTexture");
+ break;
+ case BindingInfoType::ExternalTexture:
+ s->Append("ExternalTexture");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return {true};
+ }
+
void IncrementBindingCounts(BindingCounts* bindingCounts, const BindGroupLayoutEntry& entry) {
bindingCounts->totalCount += 1;
@@ -96,84 +122,97 @@
}
MaybeError ValidateBindingCounts(const BindingCounts& bindingCounts) {
- if (bindingCounts.dynamicUniformBufferCount > kMaxDynamicUniformBuffersPerPipelineLayout) {
- return DAWN_VALIDATION_ERROR(
- "The number of dynamic uniform buffers exceeds the maximum per-pipeline-layout "
- "limit");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.dynamicUniformBufferCount > kMaxDynamicUniformBuffersPerPipelineLayout,
+ "The number of dynamic uniform buffers (%u) exceeds the maximum per-pipeline-layout "
+ "limit (%u).",
+ bindingCounts.dynamicUniformBufferCount, kMaxDynamicUniformBuffersPerPipelineLayout);
- if (bindingCounts.dynamicStorageBufferCount > kMaxDynamicStorageBuffersPerPipelineLayout) {
- return DAWN_VALIDATION_ERROR(
- "The number of dynamic storage buffers exceeds the maximum per-pipeline-layout "
- "limit");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.dynamicStorageBufferCount > kMaxDynamicStorageBuffersPerPipelineLayout,
+ "The number of dynamic storage buffers (%u) exceeds the maximum per-pipeline-layout "
+ "limit (%u).",
+ bindingCounts.dynamicStorageBufferCount, kMaxDynamicStorageBuffersPerPipelineLayout);
for (SingleShaderStage stage : IterateStages(kAllStages)) {
- if (bindingCounts.perStage[stage].sampledTextureCount >
- kMaxSampledTexturesPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The number of sampled textures exceeds the maximum "
- "per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].sampledTextureCount >
+ kMaxSampledTexturesPerShaderStage,
+ "The number of sampled textures (%u) in the %s stage exceeds the maximum "
+ "per-stage limit (%u).",
+ bindingCounts.perStage[stage].sampledTextureCount, stage,
+ kMaxSampledTexturesPerShaderStage);
// The per-stage number of external textures is bound by the maximum sampled textures
// per stage.
- if (bindingCounts.perStage[stage].externalTextureCount >
- kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture) {
- return DAWN_VALIDATION_ERROR(
- "The number of external textures exceeds the maximum "
- "per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].externalTextureCount >
+ kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
+ "The number of external textures (%u) in the %s stage exceeds the maximum "
+ "per-stage limit (%u).",
+ bindingCounts.perStage[stage].externalTextureCount, stage,
+ kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture);
- if (bindingCounts.perStage[stage].sampledTextureCount +
- (bindingCounts.perStage[stage].externalTextureCount *
- kSampledTexturesPerExternalTexture) >
- kMaxSampledTexturesPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The combination of sampled textures and external textures exceeds the maximum "
- "per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].sampledTextureCount +
+ (bindingCounts.perStage[stage].externalTextureCount *
+ kSampledTexturesPerExternalTexture) >
+ kMaxSampledTexturesPerShaderStage,
+ "The combination of sampled textures (%u) and external textures (%u) in the %s "
+ "stage exceeds the maximum per-stage limit (%u).",
+ bindingCounts.perStage[stage].sampledTextureCount,
+ bindingCounts.perStage[stage].externalTextureCount, stage,
+ kMaxSampledTexturesPerShaderStage);
- if (bindingCounts.perStage[stage].samplerCount > kMaxSamplersPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The number of samplers exceeds the maximum per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].samplerCount > kMaxSamplersPerShaderStage,
+ "The number of samplers (%u) in the %s stage exceeds the maximum per-stage limit "
+ "(%u).",
+ bindingCounts.perStage[stage].samplerCount, stage, kMaxSamplersPerShaderStage);
- if (bindingCounts.perStage[stage].samplerCount +
- (bindingCounts.perStage[stage].externalTextureCount *
- kSamplersPerExternalTexture) >
- kMaxSamplersPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The combination of samplers and external textures exceeds the maximum "
- "per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].samplerCount +
+ (bindingCounts.perStage[stage].externalTextureCount *
+ kSamplersPerExternalTexture) >
+ kMaxSamplersPerShaderStage,
+ "The combination of samplers (%u) and external textures (%u) in the %s stage "
+ "exceeds the maximum per-stage limit (%u).",
+ bindingCounts.perStage[stage].samplerCount,
+ bindingCounts.perStage[stage].externalTextureCount, stage,
+ kMaxSamplersPerShaderStage);
- if (bindingCounts.perStage[stage].storageBufferCount >
- kMaxStorageBuffersPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The number of storage buffers exceeds the maximum per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].storageBufferCount > kMaxStorageBuffersPerShaderStage,
+ "The number of storage buffers (%u) in the %s stage exceeds the maximum per-stage "
+ "limit (%u).",
+ bindingCounts.perStage[stage].storageBufferCount, stage,
+ kMaxStorageBuffersPerShaderStage);
- if (bindingCounts.perStage[stage].storageTextureCount >
- kMaxStorageTexturesPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The number of storage textures exceeds the maximum per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].storageTextureCount >
+ kMaxStorageTexturesPerShaderStage,
+ "The number of storage textures (%u) in the %s stage exceeds the maximum per-stage "
+ "limit (%u).",
+ bindingCounts.perStage[stage].storageTextureCount, stage,
+ kMaxStorageTexturesPerShaderStage);
- if (bindingCounts.perStage[stage].uniformBufferCount >
- kMaxUniformBuffersPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The number of uniform buffers exceeds the maximum per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].uniformBufferCount > kMaxUniformBuffersPerShaderStage,
+ "The number of uniform buffers (%u) in the %s stage exceeds the maximum per-stage "
+ "limit (%u).",
+ bindingCounts.perStage[stage].uniformBufferCount, stage,
+ kMaxUniformBuffersPerShaderStage);
- if (bindingCounts.perStage[stage].uniformBufferCount +
- (bindingCounts.perStage[stage].externalTextureCount *
- kUniformsPerExternalTexture) >
- kMaxUniformBuffersPerShaderStage) {
- return DAWN_VALIDATION_ERROR(
- "The combination of uniform buffers and external textures exceeds the maximum "
- "per-stage limit.");
- }
+ DAWN_INVALID_IF(
+ bindingCounts.perStage[stage].uniformBufferCount +
+ (bindingCounts.perStage[stage].externalTextureCount *
+ kUniformsPerExternalTexture) >
+ kMaxUniformBuffersPerShaderStage,
+ "The combination of uniform buffers (%u) and external textures (%u) in the %s "
+ "stage exceeds the maximum per-stage limit (%u).",
+ bindingCounts.perStage[stage].uniformBufferCount,
+ bindingCounts.perStage[stage].externalTextureCount, stage,
+ kMaxUniformBuffersPerShaderStage);
}
return {};
diff --git a/src/dawn_native/BindingInfo.h b/src/dawn_native/BindingInfo.h
index 0d6cfc0..6662554 100644
--- a/src/dawn_native/BindingInfo.h
+++ b/src/dawn_native/BindingInfo.h
@@ -50,6 +50,11 @@
enum class BindingInfoType { Buffer, Sampler, Texture, StorageTexture, ExternalTexture };
+ absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
+ BindingInfoType value,
+ const absl::FormatConversionSpec& spec,
+ absl::FormatSink* s);
+
struct BindingInfo {
BindingNumber binding;
wgpu::ShaderStage visibility;
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 71e92e2..0c948de 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -1179,7 +1179,9 @@
bool allowInternalBinding) {
DAWN_TRY(ValidateIsAlive());
if (IsValidationEnabled()) {
- DAWN_TRY(ValidateBindGroupLayoutDescriptor(this, descriptor, allowInternalBinding));
+ DAWN_TRY_CONTEXT(
+ ValidateBindGroupLayoutDescriptor(this, descriptor, allowInternalBinding),
+ "validating %s", descriptor);
}
return GetOrCreateBindGroupLayout(descriptor);
}
diff --git a/src/dawn_native/PerStage.cpp b/src/dawn_native/PerStage.cpp
index 198d99d..469fd9f 100644
--- a/src/dawn_native/PerStage.cpp
+++ b/src/dawn_native/PerStage.cpp
@@ -16,6 +16,26 @@
namespace dawn_native {
+ absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
+ SingleShaderStage value,
+ const absl::FormatConversionSpec& spec,
+ absl::FormatSink* s) {
+ switch (value) {
+ case SingleShaderStage::Compute:
+ s->Append("Compute");
+ break;
+ case SingleShaderStage::Vertex:
+ s->Append("Vertex");
+ break;
+ case SingleShaderStage::Fragment:
+ s->Append("Fragment");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return {true};
+ }
+
BitSetIterator<kNumStages, SingleShaderStage> IterateStages(wgpu::ShaderStage stages) {
std::bitset<kNumStages> bits(static_cast<uint32_t>(stages));
return BitSetIterator<kNumStages, SingleShaderStage>(bits);
diff --git a/src/dawn_native/PerStage.h b/src/dawn_native/PerStage.h
index be9f4c4..a67a08a 100644
--- a/src/dawn_native/PerStage.h
+++ b/src/dawn_native/PerStage.h
@@ -18,6 +18,7 @@
#include "common/Assert.h"
#include "common/BitSetIterator.h"
#include "common/Constants.h"
+#include "dawn_native/Error.h"
#include "dawn_native/dawn_platform.h"
@@ -27,6 +28,11 @@
enum class SingleShaderStage { Vertex, Fragment, Compute };
+ absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
+ SingleShaderStage value,
+ const absl::FormatConversionSpec& spec,
+ absl::FormatSink* s);
+
static_assert(static_cast<uint32_t>(SingleShaderStage::Vertex) < kNumStages, "");
static_assert(static_cast<uint32_t>(SingleShaderStage::Fragment) < kNumStages, "");
static_assert(static_cast<uint32_t>(SingleShaderStage::Compute) < kNumStages, "");
diff --git a/src/dawn_native/Pipeline.cpp b/src/dawn_native/Pipeline.cpp
index a1f2453..4fafe8b 100644
--- a/src/dawn_native/Pipeline.cpp
+++ b/src/dawn_native/Pipeline.cpp
@@ -22,26 +22,6 @@
#include "dawn_native/ShaderModule.h"
namespace dawn_native {
- absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
- SingleShaderStage value,
- const absl::FormatConversionSpec& spec,
- absl::FormatSink* s) {
- switch (value) {
- case SingleShaderStage::Compute:
- s->Append("Compute");
- break;
- case SingleShaderStage::Vertex:
- s->Append("Vertex");
- break;
- case SingleShaderStage::Fragment:
- s->Append("Fragment");
- break;
- default:
- UNREACHABLE();
- }
- return {true};
- }
-
MaybeError ValidateProgrammableStage(DeviceBase* device,
const ShaderModuleBase* module,
const std::string& entryPoint,
diff --git a/src/dawn_native/PipelineLayout.cpp b/src/dawn_native/PipelineLayout.cpp
index 2f96edd..9b1b707 100644
--- a/src/dawn_native/PipelineLayout.cpp
+++ b/src/dawn_native/PipelineLayout.cpp
@@ -230,7 +230,8 @@
desc.entryCount = entryVec.size();
if (device->IsValidationEnabled()) {
- DAWN_TRY(ValidateBindGroupLayoutDescriptor(device, &desc));
+ DAWN_TRY_CONTEXT(ValidateBindGroupLayoutDescriptor(device, &desc), "validating %s",
+ &desc);
}
return device->GetOrCreateBindGroupLayout(&desc, pipelineCompatibilityToken);
};