mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-07 12:45:57 +00:00
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:
parent
7c73488b37
commit
ea5d768f94
@ -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...`
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {};
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user