Make CommandBufferValidation use Error.h

This commit is contained in:
Corentin Wallez 2018-07-16 18:20:09 +02:00 committed by Corentin Wallez
parent 52f2383bb8
commit 021c9504d0
7 changed files with 152 additions and 203 deletions

View File

@ -123,11 +123,24 @@ namespace {{namespace}} {
//* can be skipped in the NonValidatingEntryPoints. //* can be skipped in the NonValidatingEntryPoints.
{% if suffix in methodsWithExtraValidation %} {% if suffix in methodsWithExtraValidation %}
if (valid) { if (valid) {
valid = self->Validate{{method.name.CamelCase()}}( MaybeError error = self->Validate{{method.name.CamelCase()}}(
{%- for arg in method.arguments -%} {%- for arg in method.arguments -%}
{% if not loop.first %}, {% endif %}{{as_varName(arg.name)}} {% if not loop.first %}, {% endif %}{{as_varName(arg.name)}}
{%- endfor -%} {%- endfor -%}
); );
//* Builders want to handle error themselves, unpack the error and make
//* the builder handle it.
{% if type.is_builder %}
if (error.IsError()) {
ErrorData* errorData = error.AcquireError();
self->HandleError(errorData->GetMessage().c_str());
delete errorData;
valid = false;
}
{% else %}
//* Non-builder errors are handled by the device
valid = !self->GetDevice()->ConsumedError(std::move(error));
{% endif %}
} }
{% endif %} {% endif %}

View File

@ -32,12 +32,10 @@ namespace backend {
namespace { namespace {
bool ValidateCopyLocationFitsInTexture(CommandBufferBuilder* builder, MaybeError ValidateCopyLocationFitsInTexture(const TextureCopyLocation& location) {
const TextureCopyLocation& location) {
const TextureBase* texture = location.texture.Get(); const TextureBase* texture = location.texture.Get();
if (location.level >= texture->GetNumMipLevels()) { if (location.level >= texture->GetNumMipLevels()) {
builder->HandleError("Copy mip-level out of range"); NXT_RETURN_ERROR("Copy mip-level out of range");
return false;
} }
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
@ -47,18 +45,16 @@ namespace backend {
(static_cast<uint64_t>(texture->GetWidth()) >> level) || (static_cast<uint64_t>(texture->GetWidth()) >> level) ||
uint64_t(location.y) + uint64_t(location.height) > uint64_t(location.y) + uint64_t(location.height) >
(static_cast<uint64_t>(texture->GetHeight()) >> level)) { (static_cast<uint64_t>(texture->GetHeight()) >> level)) {
builder->HandleError("Copy would touch outside of the texture"); NXT_RETURN_ERROR("Copy would touch outside of the texture");
return false;
} }
// TODO(cwallez@chromium.org): Check the depth bound differently for 2D arrays and 3D // TODO(cwallez@chromium.org): Check the depth bound differently for 2D arrays and 3D
// textures // textures
if (location.z != 0 || location.depth != 1) { if (location.z != 0 || location.depth != 1) {
builder->HandleError("No support for z != 0 and depth != 1 for now"); NXT_RETURN_ERROR("No support for z != 0 and depth != 1 for now");
return false;
} }
return true; return {};
} }
bool FitsInBuffer(const BufferBase* buffer, uint32_t offset, uint32_t size) { bool FitsInBuffer(const BufferBase* buffer, uint32_t offset, uint32_t size) {
@ -66,38 +62,33 @@ namespace backend {
return offset <= bufferSize && (size <= (bufferSize - offset)); return offset <= bufferSize && (size <= (bufferSize - offset));
} }
bool ValidateCopySizeFitsInBuffer(CommandBufferBuilder* builder, MaybeError ValidateCopySizeFitsInBuffer(const BufferCopyLocation& location,
const BufferCopyLocation& location,
uint32_t dataSize) { uint32_t dataSize) {
if (!FitsInBuffer(location.buffer.Get(), location.offset, dataSize)) { if (!FitsInBuffer(location.buffer.Get(), location.offset, dataSize)) {
builder->HandleError("Copy would overflow the buffer"); NXT_RETURN_ERROR("Copy would overflow the buffer");
return false;
} }
return true; return {};
} }
bool ValidateTexelBufferOffset(CommandBufferBuilder* builder, MaybeError ValidateTexelBufferOffset(TextureBase* texture,
TextureBase* texture,
const BufferCopyLocation& location) { const BufferCopyLocation& location) {
uint32_t texelSize = uint32_t texelSize =
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())); static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat()));
if (location.offset % texelSize != 0) { if (location.offset % texelSize != 0) {
builder->HandleError("Buffer offset must be a multiple of the texel size"); NXT_RETURN_ERROR("Buffer offset must be a multiple of the texel size");
return false;
} }
return true; return {};
} }
bool ComputeTextureCopyBufferSize(CommandBufferBuilder*, MaybeError ComputeTextureCopyBufferSize(const TextureCopyLocation& location,
const TextureCopyLocation& location,
uint32_t rowPitch, uint32_t rowPitch,
uint32_t* bufferSize) { uint32_t* bufferSize) {
// TODO(cwallez@chromium.org): check for overflows // TODO(cwallez@chromium.org): check for overflows
*bufferSize = (rowPitch * (location.height - 1) + location.width) * location.depth; *bufferSize = (rowPitch * (location.height - 1) + location.width) * location.depth;
return true; return {};
} }
uint32_t ComputeDefaultRowPitch(TextureBase* texture, uint32_t width) { uint32_t ComputeDefaultRowPitch(TextureBase* texture, uint32_t width) {
@ -105,45 +96,35 @@ namespace backend {
return texelSize * width; return texelSize * width;
} }
bool ValidateRowPitch(CommandBufferBuilder* builder, MaybeError ValidateRowPitch(const TextureCopyLocation& location, uint32_t rowPitch) {
const TextureCopyLocation& location,
uint32_t rowPitch) {
if (rowPitch % kTextureRowPitchAlignment != 0) { if (rowPitch % kTextureRowPitchAlignment != 0) {
builder->HandleError("Row pitch must be a multiple of 256"); NXT_RETURN_ERROR("Row pitch must be a multiple of 256");
return false;
} }
uint32_t texelSize = TextureFormatPixelSize(location.texture.Get()->GetFormat()); uint32_t texelSize = TextureFormatPixelSize(location.texture.Get()->GetFormat());
if (rowPitch < location.width * texelSize) { if (rowPitch < location.width * texelSize) {
builder->HandleError("Row pitch must not be less than the number of bytes per row"); NXT_RETURN_ERROR("Row pitch must not be less than the number of bytes per row");
return false;
} }
return true; return {};
} }
bool ValidateCanUseAs(CommandBufferBuilder* builder, MaybeError ValidateCanUseAs(BufferBase* buffer, nxt::BufferUsageBit usage) {
BufferBase* buffer,
nxt::BufferUsageBit usage) {
ASSERT(HasZeroOrOneBits(usage)); ASSERT(HasZeroOrOneBits(usage));
if (!(buffer->GetAllowedUsage() & usage)) { if (!(buffer->GetAllowedUsage() & usage)) {
builder->HandleError("buffer doesn't have the required usage."); NXT_RETURN_ERROR("buffer doesn't have the required usage.");
return false;
} }
return true; return {};
} }
bool ValidateCanUseAs(CommandBufferBuilder* builder, MaybeError ValidateCanUseAs(TextureBase* texture, nxt::TextureUsageBit usage) {
TextureBase* texture,
nxt::TextureUsageBit usage) {
ASSERT(HasZeroOrOneBits(usage)); ASSERT(HasZeroOrOneBits(usage));
if (!(texture->GetAllowedUsage() & usage)) { if (!(texture->GetAllowedUsage() & usage)) {
builder->HandleError("texture doesn't have the required usage."); NXT_RETURN_ERROR("texture doesn't have the required usage.");
return false;
} }
return true; return {};
} }
enum class PassType { enum class PassType {
@ -184,10 +165,10 @@ namespace backend {
} }
// Performs the per-pass usage validation checks // Performs the per-pass usage validation checks
bool AreUsagesValid(PassType pass) const { MaybeError ValidateUsages(PassType pass) const {
// Storage resources cannot be used twice in the same compute pass // Storage resources cannot be used twice in the same compute pass
if (pass == PassType::Compute && mStorageUsedMultipleTimes) { if (pass == PassType::Compute && mStorageUsedMultipleTimes) {
return false; NXT_RETURN_ERROR("Storage resource used multiple times in compute pass");
} }
// Buffers can only be used as single-write or multiple read. // Buffers can only be used as single-write or multiple read.
@ -196,14 +177,15 @@ namespace backend {
nxt::BufferUsageBit usage = it.second; nxt::BufferUsageBit usage = it.second;
if (usage & ~buffer->GetAllowedUsage()) { if (usage & ~buffer->GetAllowedUsage()) {
return false; NXT_RETURN_ERROR("Buffer missing usage for the pass");
} }
bool readOnly = (usage & kReadOnlyBufferUsages) == usage; bool readOnly = (usage & kReadOnlyBufferUsages) == usage;
bool singleUse = nxt::HasZeroOrOneBits(usage); bool singleUse = nxt::HasZeroOrOneBits(usage);
if (!readOnly && !singleUse) { if (!readOnly && !singleUse) {
return false; NXT_RETURN_ERROR(
"Buffer used as writeable usage and another usage in pass");
} }
} }
@ -214,17 +196,17 @@ namespace backend {
nxt::TextureUsageBit usage = it.second; nxt::TextureUsageBit usage = it.second;
if (usage & ~texture->GetAllowedUsage()) { if (usage & ~texture->GetAllowedUsage()) {
return false; NXT_RETURN_ERROR("Texture missing usage for the pass");
} }
// For textures the only read-only usage in a pass is Sampled, so checking the // For textures the only read-only usage in a pass is Sampled, so checking the
// usage constraint simplifies to checking a single usage bit is set. // usage constraint simplifies to checking a single usage bit is set.
if (!nxt::HasZeroOrOneBits(it.second)) { if (!nxt::HasZeroOrOneBits(it.second)) {
return false; NXT_RETURN_ERROR("Texture used with more than one usage in pass");
} }
} }
return true; return {};
} }
// Returns the per-pass usage for use by backends for APIs with explicit barriers. // Returns the per-pass usage for use by backends for APIs with explicit barriers.
@ -297,7 +279,7 @@ namespace backend {
// CommandBufferBuilder // CommandBufferBuilder
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device)
: Builder(device), mState(std::make_unique<CommandBufferStateTracker>(this)) { : Builder(device), mState(std::make_unique<CommandBufferStateTracker>()) {
} }
CommandBufferBuilder::~CommandBufferBuilder() { CommandBufferBuilder::~CommandBufferBuilder() {
@ -333,7 +315,7 @@ namespace backend {
// Implementation of the command buffer validation that can be precomputed before submit // Implementation of the command buffer validation that can be precomputed before submit
bool CommandBufferBuilder::ValidateGetResult() { MaybeError CommandBufferBuilder::ValidateGetResult() {
MoveToIterator(); MoveToIterator();
mIterator.Reset(); mIterator.Reset();
@ -342,78 +324,73 @@ namespace backend {
switch (type) { switch (type) {
case Command::BeginComputePass: { case Command::BeginComputePass: {
mIterator.NextCommand<BeginComputePassCmd>(); mIterator.NextCommand<BeginComputePassCmd>();
if (!ValidateComputePass()) { NXT_TRY(ValidateComputePass());
return false;
}
} break; } break;
case Command::BeginRenderPass: { case Command::BeginRenderPass: {
BeginRenderPassCmd* cmd = mIterator.NextCommand<BeginRenderPassCmd>(); BeginRenderPassCmd* cmd = mIterator.NextCommand<BeginRenderPassCmd>();
if (!ValidateRenderPass(cmd->info.Get())) { NXT_TRY(ValidateRenderPass(cmd->info.Get()));
return false;
}
} break; } break;
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mIterator.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = mIterator.NextCommand<CopyBufferToBufferCmd>();
if (!ValidateCopySizeFitsInBuffer(this, copy->source, copy->size) ||
!ValidateCopySizeFitsInBuffer(this, copy->destination, copy->size) || NXT_TRY(ValidateCopySizeFitsInBuffer(copy->source, copy->size));
!ValidateCanUseAs(this, copy->source.buffer.Get(), NXT_TRY(ValidateCopySizeFitsInBuffer(copy->destination, copy->size));
nxt::BufferUsageBit::TransferSrc) ||
!ValidateCanUseAs(this, copy->destination.buffer.Get(), NXT_TRY(ValidateCanUseAs(copy->source.buffer.Get(),
nxt::BufferUsageBit::TransferDst)) { nxt::BufferUsageBit::TransferSrc));
return false; NXT_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
} nxt::BufferUsageBit::TransferDst));
} break; } break;
case Command::CopyBufferToTexture: { case Command::CopyBufferToTexture: {
CopyBufferToTextureCmd* copy = mIterator.NextCommand<CopyBufferToTextureCmd>(); CopyBufferToTextureCmd* copy = mIterator.NextCommand<CopyBufferToTextureCmd>();
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
if (!ValidateRowPitch(this, copy->destination, copy->rowPitch) || NXT_TRY(ValidateRowPitch(copy->destination, copy->rowPitch));
!ComputeTextureCopyBufferSize(this, copy->destination, copy->rowPitch, NXT_TRY(ComputeTextureCopyBufferSize(copy->destination, copy->rowPitch,
&bufferCopySize) || &bufferCopySize));
!ValidateCopyLocationFitsInTexture(this, copy->destination) ||
!ValidateCopySizeFitsInBuffer(this, copy->source, bufferCopySize) || NXT_TRY(ValidateCopyLocationFitsInTexture(copy->destination));
!ValidateTexelBufferOffset(this, copy->destination.texture.Get(), NXT_TRY(ValidateCopySizeFitsInBuffer(copy->source, bufferCopySize));
copy->source) || NXT_TRY(
!ValidateCanUseAs(this, copy->source.buffer.Get(), ValidateTexelBufferOffset(copy->destination.texture.Get(), copy->source));
nxt::BufferUsageBit::TransferSrc) ||
!ValidateCanUseAs(this, copy->destination.texture.Get(), NXT_TRY(ValidateCanUseAs(copy->source.buffer.Get(),
nxt::TextureUsageBit::TransferDst)) { nxt::BufferUsageBit::TransferSrc));
return false; NXT_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
} nxt::TextureUsageBit::TransferDst));
} break; } break;
case Command::CopyTextureToBuffer: { case Command::CopyTextureToBuffer: {
CopyTextureToBufferCmd* copy = mIterator.NextCommand<CopyTextureToBufferCmd>(); CopyTextureToBufferCmd* copy = mIterator.NextCommand<CopyTextureToBufferCmd>();
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
if (!ValidateRowPitch(this, copy->source, copy->rowPitch) || NXT_TRY(ValidateRowPitch(copy->source, copy->rowPitch));
!ComputeTextureCopyBufferSize(this, copy->source, copy->rowPitch, NXT_TRY(ComputeTextureCopyBufferSize(copy->source, copy->rowPitch,
&bufferCopySize) || &bufferCopySize));
!ValidateCopyLocationFitsInTexture(this, copy->source) ||
!ValidateCopySizeFitsInBuffer(this, copy->destination, bufferCopySize) || NXT_TRY(ValidateCopyLocationFitsInTexture(copy->source));
!ValidateTexelBufferOffset(this, copy->source.texture.Get(), NXT_TRY(ValidateCopySizeFitsInBuffer(copy->destination, bufferCopySize));
copy->destination) || NXT_TRY(
!ValidateCanUseAs(this, copy->source.texture.Get(), ValidateTexelBufferOffset(copy->source.texture.Get(), copy->destination));
nxt::TextureUsageBit::TransferSrc) ||
!ValidateCanUseAs(this, copy->destination.buffer.Get(), NXT_TRY(ValidateCanUseAs(copy->source.texture.Get(),
nxt::BufferUsageBit::TransferDst)) { nxt::TextureUsageBit::TransferSrc));
return false; NXT_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
} nxt::BufferUsageBit::TransferDst));
} break; } break;
default: default:
HandleError("Command disallowed outside of a pass"); NXT_RETURN_ERROR("Command disallowed outside of a pass");
return false;
} }
} }
return true; return {};
} }
bool CommandBufferBuilder::ValidateComputePass() { MaybeError CommandBufferBuilder::ValidateComputePass() {
PassResourceUsageTracker usageTracker; PassResourceUsageTracker usageTracker;
Command type; Command type;
@ -422,28 +399,22 @@ namespace backend {
case Command::EndComputePass: { case Command::EndComputePass: {
mIterator.NextCommand<EndComputePassCmd>(); mIterator.NextCommand<EndComputePassCmd>();
if (!usageTracker.AreUsagesValid(PassType::Compute)) { NXT_TRY(usageTracker.ValidateUsages(PassType::Compute));
return false;
}
mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage()); mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
mState->EndPass(); mState->EndPass();
return true; return {};
} break; } break;
case Command::Dispatch: { case Command::Dispatch: {
mIterator.NextCommand<DispatchCmd>(); mIterator.NextCommand<DispatchCmd>();
if (!mState->ValidateCanDispatch()) { NXT_TRY(mState->ValidateCanDispatch());
return false;
}
} break; } break;
case Command::SetComputePipeline: { case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mIterator.NextCommand<SetComputePipelineCmd>(); SetComputePipelineCmd* cmd = mIterator.NextCommand<SetComputePipelineCmd>();
ComputePipelineBase* pipeline = cmd->pipeline.Get(); ComputePipelineBase* pipeline = cmd->pipeline.Get();
if (!mState->SetComputePipeline(pipeline)) { mState->SetComputePipeline(pipeline);
return false;
}
} break; } break;
case Command::SetPushConstants: { case Command::SetPushConstants: {
@ -453,9 +424,8 @@ namespace backend {
// recorded because it impacts the size of an allocation in the // recorded because it impacts the size of an allocation in the
// CommandAllocator. // CommandAllocator.
if (cmd->stages & ~nxt::ShaderStageBit::Compute) { if (cmd->stages & ~nxt::ShaderStageBit::Compute) {
HandleError( NXT_RETURN_ERROR(
"SetPushConstants stage must be compute or 0 in compute passes"); "SetPushConstants stage must be compute or 0 in compute passes");
return false;
} }
} break; } break;
@ -467,16 +437,14 @@ namespace backend {
} break; } break;
default: default:
HandleError("Command disallowed inside a compute pass"); NXT_RETURN_ERROR("Command disallowed inside a compute pass");
return false;
} }
} }
HandleError("Unfinished compute pass"); NXT_RETURN_ERROR("Unfinished compute pass");
return false;
} }
bool CommandBufferBuilder::ValidateRenderPass(RenderPassDescriptorBase* renderPass) { MaybeError CommandBufferBuilder::ValidateRenderPass(RenderPassDescriptorBase* renderPass) {
PassResourceUsageTracker usageTracker; PassResourceUsageTracker usageTracker;
// Track usage of the render pass attachments // Track usage of the render pass attachments
@ -496,27 +464,21 @@ namespace backend {
case Command::EndRenderPass: { case Command::EndRenderPass: {
mIterator.NextCommand<EndRenderPassCmd>(); mIterator.NextCommand<EndRenderPassCmd>();
if (!usageTracker.AreUsagesValid(PassType::Render)) { NXT_TRY(usageTracker.ValidateUsages(PassType::Render));
return false;
}
mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage()); mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
mState->EndPass(); mState->EndPass();
return true; return {};
} break; } break;
case Command::DrawArrays: { case Command::DrawArrays: {
mIterator.NextCommand<DrawArraysCmd>(); mIterator.NextCommand<DrawArraysCmd>();
if (!mState->ValidateCanDrawArrays()) { NXT_TRY(mState->ValidateCanDrawArrays());
return false;
}
} break; } break;
case Command::DrawElements: { case Command::DrawElements: {
mIterator.NextCommand<DrawElementsCmd>(); mIterator.NextCommand<DrawElementsCmd>();
if (!mState->ValidateCanDrawElements()) { NXT_TRY(mState->ValidateCanDrawElements());
return false;
}
} break; } break;
case Command::SetRenderPipeline: { case Command::SetRenderPipeline: {
@ -524,13 +486,10 @@ namespace backend {
RenderPipelineBase* pipeline = cmd->pipeline.Get(); RenderPipelineBase* pipeline = cmd->pipeline.Get();
if (!pipeline->IsCompatibleWith(renderPass)) { if (!pipeline->IsCompatibleWith(renderPass)) {
HandleError("Pipeline is incompatible with this render pass"); NXT_RETURN_ERROR("Pipeline is incompatible with this render pass");
return false;
} }
if (!mState->SetRenderPipeline(pipeline)) { mState->SetRenderPipeline(pipeline);
return false;
}
} break; } break;
case Command::SetPushConstants: { case Command::SetPushConstants: {
@ -541,10 +500,9 @@ namespace backend {
// CommandAllocator. // CommandAllocator.
if (cmd->stages & if (cmd->stages &
~(nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment)) { ~(nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment)) {
HandleError( NXT_RETURN_ERROR(
"SetPushConstants stage must be a subset of (vertex|fragment) in " "SetPushConstants stage must be a subset of (vertex|fragment) in "
"render passes"); "render passes");
return false;
} }
} break; } break;
@ -571,9 +529,7 @@ namespace backend {
SetIndexBufferCmd* cmd = mIterator.NextCommand<SetIndexBufferCmd>(); SetIndexBufferCmd* cmd = mIterator.NextCommand<SetIndexBufferCmd>();
usageTracker.BufferUsedAs(cmd->buffer.Get(), nxt::BufferUsageBit::Index); usageTracker.BufferUsedAs(cmd->buffer.Get(), nxt::BufferUsageBit::Index);
if (!mState->SetIndexBuffer()) { NXT_TRY(mState->SetIndexBuffer());
return false;
}
} break; } break;
case Command::SetVertexBuffers: { case Command::SetVertexBuffers: {
@ -583,18 +539,16 @@ namespace backend {
for (uint32_t i = 0; i < cmd->count; ++i) { for (uint32_t i = 0; i < cmd->count; ++i) {
usageTracker.BufferUsedAs(buffers[i].Get(), nxt::BufferUsageBit::Vertex); usageTracker.BufferUsedAs(buffers[i].Get(), nxt::BufferUsageBit::Vertex);
mState->SetVertexBuffer(cmd->startSlot + i); NXT_TRY(mState->SetVertexBuffer(cmd->startSlot + i));
} }
} break; } break;
default: default:
HandleError("Command disallowed inside a render pass"); NXT_RETURN_ERROR("Command disallowed inside a render pass");
return false;
} }
} }
HandleError("Unfinished render pass"); NXT_RETURN_ERROR("Unfinished render pass");
return false;
} }
// Implementation of the API's command recording methods // Implementation of the API's command recording methods

View File

@ -19,6 +19,7 @@
#include "backend/Builder.h" #include "backend/Builder.h"
#include "backend/CommandAllocator.h" #include "backend/CommandAllocator.h"
#include "backend/Error.h"
#include "backend/PassResourceUsage.h" #include "backend/PassResourceUsage.h"
#include "backend/RefCounted.h" #include "backend/RefCounted.h"
@ -54,7 +55,7 @@ namespace backend {
CommandBufferBuilder(DeviceBase* device); CommandBufferBuilder(DeviceBase* device);
~CommandBufferBuilder(); ~CommandBufferBuilder();
bool ValidateGetResult(); MaybeError ValidateGetResult();
CommandIterator AcquireCommands(); CommandIterator AcquireCommands();
std::vector<PassResourceUsage> AcquirePassResourceUsage(); std::vector<PassResourceUsage> AcquirePassResourceUsage();
@ -134,8 +135,8 @@ namespace backend {
CommandBufferBase* GetResultImpl() override; CommandBufferBase* GetResultImpl() override;
void MoveToIterator(); void MoveToIterator();
bool ValidateComputePass(); MaybeError ValidateComputePass();
bool ValidateRenderPass(RenderPassDescriptorBase* renderPass); MaybeError ValidateRenderPass(RenderPassDescriptorBase* renderPass);
std::unique_ptr<CommandBufferStateTracker> mState; std::unique_ptr<CommandBufferStateTracker> mState;
CommandAllocator mAllocator; CommandAllocator mAllocator;

View File

@ -29,54 +29,47 @@
namespace backend { namespace backend {
CommandBufferStateTracker::CommandBufferStateTracker(CommandBufferBuilder* mBuilder) MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
: mBuilder(mBuilder) {
}
bool CommandBufferStateTracker::ValidateCanDispatch() {
constexpr ValidationAspects requiredAspects = constexpr ValidationAspects requiredAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS; 1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
if ((requiredAspects & ~mAspects).none()) { if ((requiredAspects & ~mAspects).none()) {
// Fast return-true path if everything is good // Fast return-true path if everything is good
return true; return {};
} }
if (!mAspects[VALIDATION_ASPECT_PIPELINE]) { if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
mBuilder->HandleError("No active compute pipeline"); NXT_RETURN_ERROR("No active compute pipeline");
return false;
} }
// Compute the lazily computed mAspects // Compute the lazily computed mAspects
if (!RecomputeHaveAspectBindGroups()) { if (!RecomputeHaveAspectBindGroups()) {
mBuilder->HandleError("Bind group state not valid"); NXT_RETURN_ERROR("Bind group state not valid");
return false;
} }
return true; return {};
} }
bool CommandBufferStateTracker::ValidateCanDrawArrays() { MaybeError CommandBufferStateTracker::ValidateCanDrawArrays() {
constexpr ValidationAspects requiredAspects = 1 << VALIDATION_ASPECT_PIPELINE | constexpr ValidationAspects requiredAspects = 1 << VALIDATION_ASPECT_PIPELINE |
1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_BIND_GROUPS |
1 << VALIDATION_ASPECT_VERTEX_BUFFERS; 1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
if ((requiredAspects & ~mAspects).none()) { if ((requiredAspects & ~mAspects).none()) {
// Fast return-true path if everything is good // Fast return-true path if everything is good
return true; return {};
} }
return RevalidateCanDraw(); return RevalidateCanDraw();
} }
bool CommandBufferStateTracker::ValidateCanDrawElements() { MaybeError CommandBufferStateTracker::ValidateCanDrawElements() {
constexpr ValidationAspects requiredAspects = constexpr ValidationAspects requiredAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER; 1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
if ((requiredAspects & ~mAspects).none()) { if ((requiredAspects & ~mAspects).none()) {
// Fast return-true path if everything is good // Fast return-true path if everything is good
return true; return {};
} }
if (!mAspects[VALIDATION_ASPECT_INDEX_BUFFER]) { if (!mAspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
mBuilder->HandleError("Cannot DrawElements without index buffer set"); NXT_RETURN_ERROR("Cannot DrawElements without index buffer set");
return false;
} }
return RevalidateCanDraw(); return RevalidateCanDraw();
} }
@ -87,15 +80,13 @@ namespace backend {
mBindgroups.fill(nullptr); mBindgroups.fill(nullptr);
} }
bool CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) { void CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) {
SetPipelineCommon(pipeline); SetPipelineCommon(pipeline);
return true;
} }
bool CommandBufferStateTracker::SetRenderPipeline(RenderPipelineBase* pipeline) { void CommandBufferStateTracker::SetRenderPipeline(RenderPipelineBase* pipeline) {
mLastRenderPipeline = pipeline; mLastRenderPipeline = pipeline;
SetPipelineCommon(pipeline); SetPipelineCommon(pipeline);
return true;
} }
void CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) { void CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) {
@ -103,24 +94,22 @@ namespace backend {
mBindgroups[index] = bindgroup; mBindgroups[index] = bindgroup;
} }
bool CommandBufferStateTracker::SetIndexBuffer() { MaybeError CommandBufferStateTracker::SetIndexBuffer() {
if (!HavePipeline()) { if (!HavePipeline()) {
mBuilder->HandleError("Can't set the index buffer without a pipeline"); NXT_RETURN_ERROR("Can't set the index buffer without a pipeline");
return false;
} }
mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER); mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
return true; return {};
} }
bool CommandBufferStateTracker::SetVertexBuffer(uint32_t index) { MaybeError CommandBufferStateTracker::SetVertexBuffer(uint32_t index) {
if (!HavePipeline()) { if (!HavePipeline()) {
mBuilder->HandleError("Can't set vertex buffers without a pipeline"); NXT_RETURN_ERROR("Can't set vertex buffers without a pipeline");
return false;
} }
mInputsSet.set(index); mInputsSet.set(index);
return true; return {};
} }
bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() { bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() {
@ -161,21 +150,18 @@ namespace backend {
return mAspects[VALIDATION_ASPECT_PIPELINE]; return mAspects[VALIDATION_ASPECT_PIPELINE];
} }
bool CommandBufferStateTracker::RevalidateCanDraw() { MaybeError CommandBufferStateTracker::RevalidateCanDraw() {
if (!mAspects[VALIDATION_ASPECT_PIPELINE]) { if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
mBuilder->HandleError("No active render pipeline"); NXT_RETURN_ERROR("No active render pipeline");
return false;
} }
// Compute the lazily computed mAspects // Compute the lazily computed mAspects
if (!RecomputeHaveAspectBindGroups()) { if (!RecomputeHaveAspectBindGroups()) {
mBuilder->HandleError("Bind group state not valid"); NXT_RETURN_ERROR("Bind group state not valid");
return false;
} }
if (!RecomputeHaveAspectVertexBuffers()) { if (!RecomputeHaveAspectVertexBuffers()) {
mBuilder->HandleError("Some vertex buffers are not set"); NXT_RETURN_ERROR("Some vertex buffers are not set");
return false;
} }
return true; return {};
} }
void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) { void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) {

View File

@ -27,21 +27,19 @@ namespace backend {
class CommandBufferStateTracker { class CommandBufferStateTracker {
public: public:
explicit CommandBufferStateTracker(CommandBufferBuilder* builder);
// Non-state-modifying validation functions // Non-state-modifying validation functions
bool ValidateCanCopy() const; MaybeError ValidateCanCopy() const;
bool ValidateCanDispatch(); MaybeError ValidateCanDispatch();
bool ValidateCanDrawArrays(); MaybeError ValidateCanDrawArrays();
bool ValidateCanDrawElements(); MaybeError ValidateCanDrawElements();
// State-modifying methods // State-modifying methods
void EndPass(); void EndPass();
bool SetComputePipeline(ComputePipelineBase* pipeline); void SetComputePipeline(ComputePipelineBase* pipeline);
bool SetRenderPipeline(RenderPipelineBase* pipeline); void SetRenderPipeline(RenderPipelineBase* pipeline);
void SetBindGroup(uint32_t index, BindGroupBase* bindgroup); void SetBindGroup(uint32_t index, BindGroupBase* bindgroup);
bool SetIndexBuffer(); MaybeError SetIndexBuffer();
bool SetVertexBuffer(uint32_t index); MaybeError SetVertexBuffer(uint32_t index);
private: private:
enum ValidationAspect { enum ValidationAspect {
@ -59,12 +57,10 @@ namespace backend {
bool RecomputeHaveAspectVertexBuffers(); bool RecomputeHaveAspectVertexBuffers();
bool HavePipeline() const; bool HavePipeline() const;
bool RevalidateCanDraw(); MaybeError RevalidateCanDraw();
void SetPipelineCommon(PipelineBase* pipeline); void SetPipelineCommon(PipelineBase* pipeline);
CommandBufferBuilder* mBuilder;
ValidationAspects mAspects; ValidationAspects mAspects;
std::bitset<kMaxBindGroups> mBindgroupsSet; std::bitset<kMaxBindGroups> mBindgroupsSet;

View File

@ -28,9 +28,9 @@ namespace backend {
return mDevice; return mDevice;
} }
bool QueueBase::ValidateSubmitCommand(CommandBufferBase*) { MaybeError QueueBase::ValidateSubmitCommand(CommandBufferBase*) {
// TODO(cwallez@chromium.org): Validate resources referenced by command buffers can be used // TODO(cwallez@chromium.org): Validate resources referenced by command buffers can be used
return true; return {};
} }
} // namespace backend } // namespace backend

View File

@ -16,6 +16,7 @@
#define BACKEND_QUEUE_H_ #define BACKEND_QUEUE_H_
#include "backend/Builder.h" #include "backend/Builder.h"
#include "backend/Error.h"
#include "backend/Forward.h" #include "backend/Forward.h"
#include "backend/RefCounted.h" #include "backend/RefCounted.h"
@ -30,20 +31,18 @@ namespace backend {
DeviceBase* GetDevice(); DeviceBase* GetDevice();
template <typename T> template <typename T>
bool ValidateSubmit(uint32_t numCommands, T* const* commands) { MaybeError ValidateSubmit(uint32_t numCommands, T* const* commands) {
static_assert(std::is_base_of<CommandBufferBase, T>::value, static_assert(std::is_base_of<CommandBufferBase, T>::value,
"invalid command buffer type"); "invalid command buffer type");
for (uint32_t i = 0; i < numCommands; ++i) { for (uint32_t i = 0; i < numCommands; ++i) {
if (!ValidateSubmitCommand(commands[i])) { NXT_TRY(ValidateSubmitCommand(commands[i]));
return false;
} }
} return {};
return true;
} }
private: private:
bool ValidateSubmitCommand(CommandBufferBase* command); MaybeError ValidateSubmitCommand(CommandBufferBase* command);
DeviceBase* mDevice; DeviceBase* mDevice;
}; };