Improve validation errors, Part 2

Updated validation messages in:
 - CommandBufferStateTracker
 - RenderBundleEncoder
 - RenderEncoderBase
 - RenderPassEncoder

Bug: dawn:563
Change-Id: I0e8f133333bf759921e5db1b1908ee2dc882c504
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64982
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Brandon Jones 2021-09-28 21:35:43 +00:00 committed by Dawn LUCI CQ
parent 7c73488b37
commit ea5d768f94
6 changed files with 204 additions and 177 deletions

View File

@ -1,6 +1,6 @@
# Dawn Errors # Dawn Errors
Dawn produces errors for several reasons. The most common is validation errors, indicatin that a Dawn produces errors for several reasons. The most common is validation errors, indicating that a
given descriptor, configuration, state, or action is not valid according to the WebGPU spec. Errors given descriptor, configuration, state, or action is not valid according to the WebGPU spec. Errors
can also be produced during exceptional circumstances such as the system running out of GPU memory can also be produced during exceptional circumstances such as the system running out of GPU memory
or the device being lost. or the device being lost.
@ -44,6 +44,10 @@ Errors returned from `DAWN_INVALID_IF()` or `DAWN_VALIDATION_ERROR()` should fol
* Example: `Command encoding has already finished.` * Example: `Command encoding has already finished.`
* Instead of: `encoder finished` * Instead of: `encoder finished`
**Error messages should be in the present tense.**
* Example: `Buffer is not large enough...`
* Instead of: `Buffer was not large enough...`
**When possible any values mentioned should be immediately followed in parentheses by the given value.** **When possible any values mentioned should be immediately followed in parentheses by the given value.**
* Example: `("Array stride (%u) is not...", stride)` * Example: `("Array stride (%u) is not...", stride)`
* Output: `Array stride (16) is not...` * Output: `Array stride (16) is not...`

View File

@ -22,6 +22,10 @@
#include "dawn_native/PipelineLayout.h" #include "dawn_native/PipelineLayout.h"
#include "dawn_native/RenderPipeline.h" #include "dawn_native/RenderPipeline.h"
// TODO(dawn:563): None of the error messages in this file include the buffer objects they are
// validating against. It would be nice to improve that, but difficult to do without incurring
// additional tracking costs.
namespace dawn_native { namespace dawn_native {
namespace { namespace {
@ -88,17 +92,25 @@ namespace dawn_native {
mLastRenderPipeline->GetVertexBuffer(usedSlotVertex); mLastRenderPipeline->GetVertexBuffer(usedSlotVertex);
uint64_t arrayStride = vertexBuffer.arrayStride; uint64_t arrayStride = vertexBuffer.arrayStride;
uint64_t bufferSize = mVertexBufferSizes[usedSlotVertex]; uint64_t bufferSize = mVertexBufferSizes[usedSlotVertex];
if (arrayStride == 0) { if (arrayStride == 0) {
if (vertexBuffer.usedBytesInStride > bufferSize) { DAWN_INVALID_IF(vertexBuffer.usedBytesInStride > bufferSize,
return DAWN_VALIDATION_ERROR("Vertex buffer out of bound"); "Bound vertex buffer size (%u) at slot %u with an arrayStride of 0 "
} "is smaller than the required size for all attributes (%u)",
bufferSize, static_cast<uint8_t>(usedSlotVertex),
vertexBuffer.usedBytesInStride);
} else { } else {
uint64_t requiredSize =
(static_cast<uint64_t>(firstVertex) + vertexCount) * arrayStride;
// firstVertex and vertexCount are in uint32_t, and arrayStride must not // firstVertex and vertexCount are in uint32_t, and arrayStride must not
// be larger than kMaxVertexBufferArrayStride, which is currently 2048. So by // be larger than kMaxVertexBufferArrayStride, which is currently 2048. So by
// doing checks in uint64_t we avoid overflows. // doing checks in uint64_t we avoid overflows.
if ((static_cast<uint64_t>(firstVertex) + vertexCount) * arrayStride > bufferSize) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Vertex buffer out of bound"); requiredSize > bufferSize,
} "Vertex range (first: %u, count: %u) requires a larger buffer (%u) than the "
"bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
firstVertex, vertexCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotVertex), arrayStride);
} }
} }
@ -118,17 +130,23 @@ namespace dawn_native {
uint64_t arrayStride = vertexBuffer.arrayStride; uint64_t arrayStride = vertexBuffer.arrayStride;
uint64_t bufferSize = mVertexBufferSizes[usedSlotInstance]; uint64_t bufferSize = mVertexBufferSizes[usedSlotInstance];
if (arrayStride == 0) { if (arrayStride == 0) {
if (vertexBuffer.usedBytesInStride > bufferSize) { DAWN_INVALID_IF(vertexBuffer.usedBytesInStride > bufferSize,
return DAWN_VALIDATION_ERROR("Vertex buffer out of bound"); "Bound vertex buffer size (%u) at slot %u with an arrayStride of 0 "
} "is smaller than the required size for all attributes (%u)",
bufferSize, static_cast<uint8_t>(usedSlotInstance),
vertexBuffer.usedBytesInStride);
} else { } else {
uint64_t requiredSize =
(static_cast<uint64_t>(firstInstance) + instanceCount) * arrayStride;
// firstInstance and instanceCount are in uint32_t, and arrayStride must // firstInstance and instanceCount are in uint32_t, and arrayStride must
// not be larger than kMaxVertexBufferArrayStride, which is currently 2048. // not be larger than kMaxVertexBufferArrayStride, which is currently 2048.
// So by doing checks in uint64_t we avoid overflows. // So by doing checks in uint64_t we avoid overflows.
if ((static_cast<uint64_t>(firstInstance) + instanceCount) * arrayStride > DAWN_INVALID_IF(
bufferSize) { requiredSize > bufferSize,
return DAWN_VALIDATION_ERROR("Vertex buffer out of bound"); "Instance range (first: %u, count: %u) requires a larger buffer (%u) than the "
} "bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
firstInstance, instanceCount, requiredSize, bufferSize,
static_cast<uint8_t>(usedSlotInstance), arrayStride);
} }
} }
@ -141,11 +159,12 @@ namespace dawn_native {
// firstIndex and indexCount are in uint32_t, while IndexFormatSize is 2 (for // firstIndex and indexCount are in uint32_t, while IndexFormatSize is 2 (for
// wgpu::IndexFormat::Uint16) or 4 (for wgpu::IndexFormat::Uint32), so by doing checks in // wgpu::IndexFormat::Uint16) or 4 (for wgpu::IndexFormat::Uint32), so by doing checks in
// uint64_t we avoid overflows. // uint64_t we avoid overflows.
if ((static_cast<uint64_t>(firstIndex) + indexCount) * IndexFormatSize(mIndexFormat) > DAWN_INVALID_IF(
mIndexBufferSize) { (static_cast<uint64_t>(firstIndex) + indexCount) * IndexFormatSize(mIndexFormat) >
// Index range is out of bounds mIndexBufferSize,
return DAWN_VALIDATION_ERROR("Index buffer out of bound"); "Index range (first: %u, count: %u, format: %s) does not fit in index buffer size "
} "(%u).",
firstIndex, indexCount, mIndexFormat, mIndexBufferSize);
return {}; return {};
} }
@ -212,15 +231,15 @@ namespace dawn_native {
return {}; return {};
} }
if (aspects[VALIDATION_ASPECT_INDEX_BUFFER]) { if (DAWN_UNLIKELY(aspects[VALIDATION_ASPECT_INDEX_BUFFER])) {
DAWN_INVALID_IF(!mIndexBufferSet, "Index buffer was not set.");
wgpu::IndexFormat pipelineIndexFormat = mLastRenderPipeline->GetStripIndexFormat(); wgpu::IndexFormat pipelineIndexFormat = mLastRenderPipeline->GetStripIndexFormat();
if (!mIndexBufferSet) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Missing index buffer"); IsStripPrimitiveTopology(mLastRenderPipeline->GetPrimitiveTopology()) &&
} else if (IsStripPrimitiveTopology(mLastRenderPipeline->GetPrimitiveTopology()) && mIndexFormat != pipelineIndexFormat,
mIndexFormat != pipelineIndexFormat) { "Strip index format (%s) of %s does not match index buffer format (%s).",
return DAWN_VALIDATION_ERROR( pipelineIndexFormat, mLastRenderPipeline, mIndexFormat);
"Pipeline strip index format does not match index buffer format");
}
// The chunk of code above should be similar to the one in |RecomputeLazyAspects|. // The chunk of code above should be similar to the one in |RecomputeLazyAspects|.
// It returns the first invalid state found. We shouldn't be able to reach this line // It returns the first invalid state found. We shouldn't be able to reach this line
@ -230,25 +249,27 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Index buffer invalid"); return DAWN_VALIDATION_ERROR("Index buffer invalid");
} }
if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) { // TODO(dawn:563): Indicate which slots were not set.
return DAWN_VALIDATION_ERROR("Missing vertex buffer"); DAWN_INVALID_IF(aspects[VALIDATION_ASPECT_VERTEX_BUFFERS],
} "Vertex buffer slots required by %s were not set.", mLastRenderPipeline);
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) { if (DAWN_UNLIKELY(aspects[VALIDATION_ASPECT_BIND_GROUPS])) {
for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) { for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
if (mBindgroups[i] == nullptr) { DAWN_INVALID_IF(mBindgroups[i] == nullptr, "No bind group set at index %u.",
return DAWN_VALIDATION_ERROR("Missing bind group " + static_cast<uint32_t>(i));
std::to_string(static_cast<uint32_t>(i)));
} else if (mLastPipelineLayout->GetBindGroupLayout(i) != DAWN_INVALID_IF(
mBindgroups[i]->GetLayout()) { mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout(),
return DAWN_VALIDATION_ERROR( "Bind group layout %s of pipeline layout %s does not match layout %s of bind "
"Pipeline and bind group layout doesn't match for bind group " + "group %s at index %u.",
std::to_string(static_cast<uint32_t>(i))); mLastPipelineLayout->GetBindGroupLayout(i), mLastPipelineLayout,
} else if (!BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(), mBindgroups[i]->GetLayout(), mBindgroups[i], static_cast<uint32_t>(i));
(*mMinBufferSizes)[i])) {
return DAWN_VALIDATION_ERROR("Binding sizes too small for bind group " + // TODO(dawn:563): Report the binding sizes and which ones are failing.
std::to_string(static_cast<uint32_t>(i))); DAWN_INVALID_IF(!BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(),
} (*mMinBufferSizes)[i]),
"Binding sizes are too small for bind group %s at index %u",
mBindgroups[i], static_cast<uint32_t>(i));
} }
// The chunk of code above should be similar to the one in |RecomputeLazyAspects|. // The chunk of code above should be similar to the one in |RecomputeLazyAspects|.
@ -259,9 +280,7 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Bind groups invalid"); return DAWN_VALIDATION_ERROR("Bind groups invalid");
} }
if (aspects[VALIDATION_ASPECT_PIPELINE]) { DAWN_INVALID_IF(aspects[VALIDATION_ASPECT_PIPELINE], "No pipeline set.");
return DAWN_VALIDATION_ERROR("Missing pipeline");
}
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -405,6 +405,7 @@ namespace dawn_native {
std::ostringstream ss; std::ostringstream ss;
ss << error->GetMessage(); ss << error->GetMessage();
// TODO(dawn:563): Print debug groups when avaialble.
const std::vector<std::string>& contexts = error->GetContexts(); const std::vector<std::string>& contexts = error->GetContexts();
if (!contexts.empty()) { if (!contexts.empty()) {
for (auto context : contexts) { for (auto context : contexts) {

View File

@ -31,10 +31,8 @@ namespace dawn_native {
DAWN_TRY(ValidateTextureFormat(textureFormat)); DAWN_TRY(ValidateTextureFormat(textureFormat));
const Format* format = nullptr; const Format* format = nullptr;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat)); DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat));
if (!format->IsColor() || !format->isRenderable) { DAWN_INVALID_IF(!format->IsColor() || !format->isRenderable,
return DAWN_VALIDATION_ERROR( "Texture format %s is not color renderable.", textureFormat);
"The color attachment texture format is not color renderable");
}
return {}; return {};
} }
@ -43,36 +41,35 @@ namespace dawn_native {
DAWN_TRY(ValidateTextureFormat(textureFormat)); DAWN_TRY(ValidateTextureFormat(textureFormat));
const Format* format = nullptr; const Format* format = nullptr;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat)); DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat));
if (!format->HasDepthOrStencil() || !format->isRenderable) { DAWN_INVALID_IF(!format->HasDepthOrStencil() || !format->isRenderable,
return DAWN_VALIDATION_ERROR( "Texture format %s is not depth/stencil renderable.", textureFormat);
"The depth stencil attachment texture format is not a renderable depth/stencil "
"format");
}
return {}; return {};
} }
MaybeError ValidateRenderBundleEncoderDescriptor( MaybeError ValidateRenderBundleEncoderDescriptor(
const DeviceBase* device, const DeviceBase* device,
const RenderBundleEncoderDescriptor* descriptor) { const RenderBundleEncoderDescriptor* descriptor) {
if (!IsValidSampleCount(descriptor->sampleCount)) { DAWN_INVALID_IF(!IsValidSampleCount(descriptor->sampleCount),
return DAWN_VALIDATION_ERROR("Sample count is not supported"); "Sample count (%u) is not supported.", descriptor->sampleCount);
}
if (descriptor->colorFormatsCount > kMaxColorAttachments) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Color formats count exceeds maximum"); descriptor->colorFormatsCount > kMaxColorAttachments,
} "Color formats count (%u) exceeds maximum number of color attachements (%u).",
descriptor->colorFormatsCount, kMaxColorAttachments);
if (descriptor->colorFormatsCount == 0 && DAWN_INVALID_IF(descriptor->colorFormatsCount == 0 &&
descriptor->depthStencilFormat == wgpu::TextureFormat::Undefined) { descriptor->depthStencilFormat == wgpu::TextureFormat::Undefined,
return DAWN_VALIDATION_ERROR("Should have at least one attachment format"); "No color or depth/stencil attachment formats specified.");
}
for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) { for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) {
DAWN_TRY(ValidateColorAttachmentFormat(device, descriptor->colorFormats[i])); DAWN_TRY_CONTEXT(ValidateColorAttachmentFormat(device, descriptor->colorFormats[i]),
"validating colorFormats[%u]", i);
} }
if (descriptor->depthStencilFormat != wgpu::TextureFormat::Undefined) { if (descriptor->depthStencilFormat != wgpu::TextureFormat::Undefined) {
DAWN_TRY(ValidateDepthStencilAttachmentFormat(device, descriptor->depthStencilFormat)); DAWN_TRY_CONTEXT(
ValidateDepthStencilAttachmentFormat(device, descriptor->depthStencilFormat),
"validating depthStencilFormat");
} }
return {}; return {};

View File

@ -65,9 +65,8 @@ namespace dawn_native {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(mCommandBufferState.ValidateCanDraw()); DAWN_TRY(mCommandBufferState.ValidateCanDraw());
if (mDisableBaseInstance && firstInstance != 0) { DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
return DAWN_VALIDATION_ERROR("Non-zero first instance not supported"); "First instance (%u) must be zero.", firstInstance);
}
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(vertexCount, DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(vertexCount,
firstVertex)); firstVertex));
@ -94,12 +93,11 @@ namespace dawn_native {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed()); DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
if (mDisableBaseInstance && firstInstance != 0) { DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
return DAWN_VALIDATION_ERROR("Non-zero first instance not supported"); "First instance (%u) must be zero.", firstInstance);
}
if (mDisableBaseVertex && baseVertex != 0) { DAWN_INVALID_IF(mDisableBaseVertex && baseVertex != 0,
return DAWN_VALIDATION_ERROR("Non-zero base vertex not supported"); "Base vertex (%u) must be zero.", baseVertex);
}
DAWN_TRY(mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex)); DAWN_TRY(mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex));
@ -129,14 +127,14 @@ namespace dawn_native {
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect)); DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_TRY(mCommandBufferState.ValidateCanDraw()); DAWN_TRY(mCommandBufferState.ValidateCanDraw());
if (indirectOffset % 4 != 0) { DAWN_INVALID_IF(indirectOffset % 4 != 0,
return DAWN_VALIDATION_ERROR("Indirect offset must be a multiple of 4"); "Indirect offset (%u) is not a multiple of 4.", indirectOffset);
}
if (indirectOffset >= indirectBuffer->GetSize() || DAWN_INVALID_IF(
kDrawIndirectSize > indirectBuffer->GetSize() - indirectOffset) { indirectOffset >= indirectBuffer->GetSize() ||
return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); kDrawIndirectSize > indirectBuffer->GetSize() - indirectOffset,
} "Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
indirectOffset, indirectBuffer, indirectBuffer->GetSize());
} }
DrawIndirectCmd* cmd = allocator->Allocate<DrawIndirectCmd>(Command::DrawIndirect); DrawIndirectCmd* cmd = allocator->Allocate<DrawIndirectCmd>(Command::DrawIndirect);
@ -157,14 +155,14 @@ namespace dawn_native {
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect)); DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed()); DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
if (indirectOffset % 4 != 0) { DAWN_INVALID_IF(indirectOffset % 4 != 0,
return DAWN_VALIDATION_ERROR("Indirect offset must be a multiple of 4"); "Indirect offset (%u) is not a multiple of 4.", indirectOffset);
}
if ((indirectOffset >= indirectBuffer->GetSize() || DAWN_INVALID_IF(
kDrawIndexedIndirectSize > indirectBuffer->GetSize() - indirectOffset)) { (indirectOffset >= indirectBuffer->GetSize() ||
return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); kDrawIndexedIndirectSize > indirectBuffer->GetSize() - indirectOffset),
} "Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
indirectOffset, indirectBuffer, indirectBuffer->GetSize());
} }
DrawIndexedIndirectCmd* cmd = DrawIndexedIndirectCmd* cmd =
@ -189,11 +187,11 @@ namespace dawn_native {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(pipeline)); DAWN_TRY(GetDevice()->ValidateObject(pipeline));
if (pipeline->GetAttachmentState() != mAttachmentState.Get()) { // TODO(dawn:563): More detail about why the states are incompatible would be nice.
return DAWN_VALIDATION_ERROR( DAWN_INVALID_IF(
"Pipeline attachment state is not compatible with render encoder " pipeline->GetAttachmentState() != mAttachmentState.Get(),
"attachment state"); "Attachment state of %s is not compatible with the attachment state of %s",
} pipeline, this);
} }
mCommandBufferState.SetRenderPipeline(pipeline); mCommandBufferState.SetRenderPipeline(pipeline);
@ -215,20 +213,19 @@ namespace dawn_native {
DAWN_TRY(GetDevice()->ValidateObject(buffer)); DAWN_TRY(GetDevice()->ValidateObject(buffer));
DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Index)); DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Index));
DAWN_TRY(ValidateIndexFormat(format)); DAWN_INVALID_IF(format == wgpu::IndexFormat::Undefined,
if (format == wgpu::IndexFormat::Undefined) { "Index format must be specified");
return DAWN_VALIDATION_ERROR("Index format must be specified");
}
if (offset % uint64_t(IndexFormatSize(format)) != 0) { DAWN_INVALID_IF(offset % uint64_t(IndexFormatSize(format)) != 0,
return DAWN_VALIDATION_ERROR( "Index buffer offset (%u) is not a multiple of the size (%u)"
"Offset must be a multiple of the index format size"); "of %s.",
} offset, IndexFormatSize(format), format);
uint64_t bufferSize = buffer->GetSize(); uint64_t bufferSize = buffer->GetSize();
if (offset > bufferSize) { DAWN_INVALID_IF(offset > bufferSize,
return DAWN_VALIDATION_ERROR("Offset larger than the buffer size"); "Index buffer offset (%u) is larger than the size (%u) of %s.",
} offset, bufferSize, buffer);
uint64_t remainingSize = bufferSize - offset; uint64_t remainingSize = bufferSize - offset;
// Temporarily treat 0 as undefined for size, and give a warning // Temporarily treat 0 as undefined for size, and give a warning
@ -244,9 +241,11 @@ namespace dawn_native {
if (size == wgpu::kWholeSize) { if (size == wgpu::kWholeSize) {
size = remainingSize; size = remainingSize;
} else { } else {
if (size > remainingSize) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Size + offset larger than the buffer size"); size > remainingSize,
} "Index buffer range (offset: %u, size: %u) doesn't fit in the size (%u) of "
"%s.",
offset, size, bufferSize, buffer);
} }
} else { } else {
if (size == wgpu::kWholeSize) { if (size == wgpu::kWholeSize) {
@ -279,18 +278,18 @@ namespace dawn_native {
DAWN_TRY(GetDevice()->ValidateObject(buffer)); DAWN_TRY(GetDevice()->ValidateObject(buffer));
DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Vertex)); DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Vertex));
if (slot >= kMaxVertexBuffers) { DAWN_INVALID_IF(slot >= kMaxVertexBuffers,
return DAWN_VALIDATION_ERROR("Vertex buffer slot out of bounds"); "Vertex buffer slot (%u) is larger the maximum (%u)", slot,
} kMaxVertexBuffers - 1);
if (offset % 4 != 0) { DAWN_INVALID_IF(offset % 4 != 0, "Vertex buffer offset (%u) is not a multiple of 4",
return DAWN_VALIDATION_ERROR("Offset must be a multiple of 4"); offset);
}
uint64_t bufferSize = buffer->GetSize(); uint64_t bufferSize = buffer->GetSize();
if (offset > bufferSize) { DAWN_INVALID_IF(offset > bufferSize,
return DAWN_VALIDATION_ERROR("Offset larger than the buffer size"); "Vertex buffer offset (%u) is larger than the size (%u) of %s.",
} offset, bufferSize, buffer);
uint64_t remainingSize = bufferSize - offset; uint64_t remainingSize = bufferSize - offset;
// Temporarily treat 0 as undefined for size, and give a warning // Temporarily treat 0 as undefined for size, and give a warning
@ -306,9 +305,11 @@ namespace dawn_native {
if (size == wgpu::kWholeSize) { if (size == wgpu::kWholeSize) {
size = remainingSize; size = remainingSize;
} else { } else {
if (size > remainingSize) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Size + offset larger than the buffer size"); size > remainingSize,
} "Vertex buffer range (offset: %u, size: %u) doesn't fit in the size (%u) "
"of %s.",
offset, size, bufferSize, buffer);
} }
} else { } else {
if (size == wgpu::kWholeSize) { if (size == wgpu::kWholeSize) {

View File

@ -36,10 +36,9 @@ namespace dawn_native {
uint32_t queryIndex, uint32_t queryIndex,
const QueryAvailabilityMap& queryAvailabilityMap) { const QueryAvailabilityMap& queryAvailabilityMap) {
auto it = queryAvailabilityMap.find(querySet); auto it = queryAvailabilityMap.find(querySet);
if (it != queryAvailabilityMap.end() && it->second[queryIndex]) { DAWN_INVALID_IF(it != queryAvailabilityMap.end() && it->second[queryIndex],
return DAWN_VALIDATION_ERROR( "Query index %u of %s is written to twice in a render pass.",
"The same query cannot be written twice in same render pass."); queryIndex, querySet);
}
return {}; return {};
} }
@ -97,10 +96,11 @@ namespace dawn_native {
if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(ValidateProgrammableEncoderEnd()); DAWN_TRY(ValidateProgrammableEncoderEnd());
if (mOcclusionQueryActive) {
return DAWN_VALIDATION_ERROR( DAWN_INVALID_IF(
"The occlusion query must be ended before endPass."); mOcclusionQueryActive,
} "Render pass %s ended with incomplete occlusion query index %u of %s.",
this, mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
} }
allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass); allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
@ -140,25 +140,30 @@ namespace dawn_native {
float maxDepth) { float maxDepth) {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
if ((isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) || DAWN_INVALID_IF((isnan(x) || isnan(y) || isnan(width) || isnan(height) ||
isnan(maxDepth))) { isnan(minDepth) || isnan(maxDepth)),
return DAWN_VALIDATION_ERROR("NaN is not allowed."); "A parameter of the viewport (x: %f, y: %f, width: %f, height: %f, "
} "minDepth: %f, maxDepth: %f) is NaN.",
x, y, width, height, minDepth, maxDepth);
if (x < 0 || y < 0 || width < 0 || height < 0) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("X, Y, width and height must be non-negative."); x < 0 || y < 0 || width < 0 || height < 0,
} "Viewport bounds (x: %f, y: %f, width: %f, height: %f) contains a negative "
"value.",
x, y, width, height);
if (x + width > mRenderTargetWidth || y + height > mRenderTargetHeight) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( x + width > mRenderTargetWidth || y + height > mRenderTargetHeight,
"The viewport must be contained in the render targets"); "Viewport bounds (x: %f, y: %f, width: %f, height: %f) are not contained in "
} "the render target dimensions (%u x %u).",
x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
// Check for depths being in [0, 1] and min <= max in 3 checks instead of 5. // Check for depths being in [0, 1] and min <= max in 3 checks instead of 5.
if (minDepth < 0 || minDepth > maxDepth || maxDepth > 1) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( minDepth < 0 || minDepth > maxDepth || maxDepth > 1,
"minDepth and maxDepth must be in [0, 1] and minDepth <= maxDepth."); "Viewport minDepth (%f) and maxDepth (%f) are not in [0, 1] or minDepth was "
} "greater than maxDepth.",
minDepth, maxDepth);
} }
SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport); SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport);
@ -179,11 +184,12 @@ namespace dawn_native {
uint32_t height) { uint32_t height) {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
if (width > mRenderTargetWidth || height > mRenderTargetHeight || DAWN_INVALID_IF(
x > mRenderTargetWidth - width || y > mRenderTargetHeight - height) { width > mRenderTargetWidth || height > mRenderTargetHeight ||
return DAWN_VALIDATION_ERROR( x > mRenderTargetWidth - width || y > mRenderTargetHeight - height,
"The scissor rect must be contained in the render targets"); "Scissor rect (x: %u, y: %u, width: %u, height: %u) is not contained in "
} "the render target dimensions (%u x %u).",
x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
} }
SetScissorRectCmd* cmd = SetScissorRectCmd* cmd =
@ -204,11 +210,12 @@ namespace dawn_native {
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i])); DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i]));
if (GetAttachmentState() != renderBundles[i]->GetAttachmentState()) { // TODO(dawn:563): Give more detail about why the states are incompatible.
return DAWN_VALIDATION_ERROR( DAWN_INVALID_IF(
"Render bundle attachment state is not compatible with render pass " GetAttachmentState() != renderBundles[i]->GetAttachmentState(),
"attachment state"); "Attachment state of renderBundles[%i] (%s) is not compatible with "
} "attachment state of %s.",
i, renderBundles[i], this);
} }
} }
@ -244,25 +251,25 @@ namespace dawn_native {
void RenderPassEncoder::APIBeginOcclusionQuery(uint32_t queryIndex) { void RenderPassEncoder::APIBeginOcclusionQuery(uint32_t queryIndex) {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
if (mOcclusionQuerySet.Get() == nullptr) { DAWN_INVALID_IF(mOcclusionQuerySet.Get() == nullptr,
return DAWN_VALIDATION_ERROR( "The occlusionQuerySet in RenderPassDescriptor is not set.");
"The occlusionQuerySet in RenderPassDescriptor must be set.");
}
// The type of querySet has been validated by ValidateRenderPassDescriptor // The type of querySet has been validated by ValidateRenderPassDescriptor
if (queryIndex >= mOcclusionQuerySet->GetQueryCount()) { DAWN_INVALID_IF(queryIndex >= mOcclusionQuerySet->GetQueryCount(),
return DAWN_VALIDATION_ERROR( "Query index (%u) exceeds the number of queries (%u) in %s.",
"Query index exceeds the number of queries in query set."); queryIndex, mOcclusionQuerySet->GetQueryCount(),
} mOcclusionQuerySet.Get());
if (mOcclusionQueryActive) { DAWN_INVALID_IF(mOcclusionQueryActive,
return DAWN_VALIDATION_ERROR( "An occlusion query (%u) in %s is already active.",
"Only a single occlusion query can be begun at a time."); mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
}
DAWN_TRY(ValidateQueryIndexOverwrite(mOcclusionQuerySet.Get(), queryIndex, DAWN_TRY_CONTEXT(
mUsageTracker.GetQueryAvailabilityMap())); ValidateQueryIndexOverwrite(mOcclusionQuerySet.Get(), queryIndex,
mUsageTracker.GetQueryAvailabilityMap()),
"validating the occlusion query index (%u) in %s", queryIndex,
mOcclusionQuerySet.Get());
} }
// Record the current query index for endOcclusionQuery. // Record the current query index for endOcclusionQuery.
@ -281,11 +288,7 @@ namespace dawn_native {
void RenderPassEncoder::APIEndOcclusionQuery() { void RenderPassEncoder::APIEndOcclusionQuery() {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
if (!mOcclusionQueryActive) { DAWN_INVALID_IF(!mOcclusionQueryActive, "No occlusion queries are active.");
return DAWN_VALIDATION_ERROR(
"EndOcclusionQuery cannot be called without corresponding "
"BeginOcclusionQuery.");
}
} }
TrackQueryAvailability(mOcclusionQuerySet.Get(), mCurrentOcclusionQueryIndex); TrackQueryAvailability(mOcclusionQuerySet.Get(), mCurrentOcclusionQueryIndex);
@ -306,8 +309,10 @@ namespace dawn_native {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet)); DAWN_TRY(GetDevice()->ValidateObject(querySet));
DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
DAWN_TRY(ValidateQueryIndexOverwrite(querySet, queryIndex, DAWN_TRY_CONTEXT(ValidateQueryIndexOverwrite(
mUsageTracker.GetQueryAvailabilityMap())); querySet, queryIndex, mUsageTracker.GetQueryAvailabilityMap()),
"validating the timestamp query index (%u) of %s", queryIndex,
querySet);
} }
TrackQueryAvailability(querySet, queryIndex); TrackQueryAvailability(querySet, queryIndex);