From fde94905fec00fa52ca2280d42e023c39600433e Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Wed, 24 Jul 2019 18:15:24 +0000 Subject: [PATCH] Factor EncodingContext out of CommandEncoderBase. This patch factors the CommandAllocator, CommandIterator, and error handling out of CommandEncoderBase so it can later be used by the RenderBundleEncoder. Bug: dawn:154 Change-Id: Ia4f8c3ce7f432f0887b619bd8090aa9bec7330fc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9181 Reviewed-by: Corentin Wallez Reviewed-by: Kai Ninomiya Commit-Queue: Austin Eng --- BUILD.gn | 2 + src/dawn_native/CommandEncoder.cpp | 489 ++++++++---------- src/dawn_native/CommandEncoder.h | 33 +- src/dawn_native/ComputePassEncoder.cpp | 84 +-- src/dawn_native/ComputePassEncoder.h | 15 +- src/dawn_native/EncodingContext.cpp | 101 ++++ src/dawn_native/EncodingContext.h | 101 ++++ src/dawn_native/ProgrammablePassEncoder.cpp | 131 +++-- src/dawn_native/ProgrammablePassEncoder.h | 14 +- src/dawn_native/RenderEncoderBase.cpp | 159 +++--- src/dawn_native/RenderEncoderBase.h | 8 +- src/dawn_native/RenderPassEncoder.cpp | 124 ++--- src/dawn_native/RenderPassEncoder.h | 15 +- .../CommandBufferValidationTests.cpp | 12 +- 14 files changed, 691 insertions(+), 597 deletions(-) create mode 100644 src/dawn_native/EncodingContext.cpp create mode 100644 src/dawn_native/EncodingContext.h diff --git a/BUILD.gn b/BUILD.gn index 42396ce923..513c74e3f3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -130,6 +130,8 @@ source_set("libdawn_native_sources") { "src/dawn_native/Device.h", "src/dawn_native/DynamicUploader.cpp", "src/dawn_native/DynamicUploader.h", + "src/dawn_native/EncodingContext.cpp", + "src/dawn_native/EncodingContext.h", "src/dawn_native/Error.cpp", "src/dawn_native/Error.h", "src/dawn_native/ErrorData.cpp", diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 93c0fe5abf..c5c351a366 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -621,28 +621,8 @@ namespace dawn_native { } // namespace - enum class CommandEncoderBase::EncodingState : uint8_t { - TopLevel, - ComputePass, - RenderPass, - Finished - }; - CommandEncoderBase::CommandEncoderBase(DeviceBase* device, const CommandEncoderDescriptor*) - : ObjectBase(device), mEncodingState(EncodingState::TopLevel) { - } - - CommandEncoderBase::~CommandEncoderBase() { - if (!mWereCommandsAcquired) { - MoveToIterator(); - FreeCommands(&mIterator); - } - } - - CommandIterator CommandEncoderBase::AcquireCommands() { - ASSERT(!mWereCommandsAcquired); - mWereCommandsAcquired = true; - return std::move(mIterator); + : ObjectBase(device), mEncodingContext(device, this) { } CommandBufferResourceUsage CommandEncoderBase::AcquireResourceUsages() { @@ -651,11 +631,8 @@ namespace dawn_native { return std::move(mResourceUsages); } - void CommandEncoderBase::MoveToIterator() { - if (!mWasMovedToIterator) { - mIterator = std::move(mAllocator); - mWasMovedToIterator = true; - } + CommandIterator CommandEncoderBase::AcquireCommands() { + return mEncodingContext.AcquireCommands(); } // Implementation of the API's command recording methods @@ -663,76 +640,91 @@ namespace dawn_native { ComputePassEncoderBase* CommandEncoderBase::BeginComputePass( const ComputePassDescriptor* descriptor) { DeviceBase* device = GetDevice(); - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return ComputePassEncoderBase::MakeError(device, this); + + bool success = + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(ValidateComputePassDescriptor(device, descriptor)); + + allocator->Allocate(Command::BeginComputePass); + + return {}; + }); + + if (success) { + ComputePassEncoderBase* passEncoder = + new ComputePassEncoderBase(device, this, &mEncodingContext); + mEncodingContext.EnterPass(passEncoder); + return passEncoder; } - if (ConsumedError(ValidateComputePassDescriptor(device, descriptor))) { - return ComputePassEncoderBase::MakeError(device, this); - } - - mAllocator.Allocate(Command::BeginComputePass); - - mEncodingState = EncodingState::ComputePass; - return new ComputePassEncoderBase(device, this, &mAllocator); + return ComputePassEncoderBase::MakeError(device, this, &mEncodingContext); } RenderPassEncoderBase* CommandEncoderBase::BeginRenderPass( const RenderPassDescriptor* descriptor) { DeviceBase* device = GetDevice(); - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return RenderPassEncoderBase::MakeError(device, this); + bool success = + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + uint32_t width = 0; + uint32_t height = 0; + uint32_t sampleCount = 0; + + DAWN_TRY(ValidateRenderPassDescriptor(device, descriptor, &width, &height, + &sampleCount)); + + ASSERT(width > 0 && height > 0 && sampleCount > 0); + + BeginRenderPassCmd* cmd = + allocator->Allocate(Command::BeginRenderPass); + + for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) { + if (descriptor->colorAttachments[i] != nullptr) { + cmd->colorAttachmentsSet.set(i); + cmd->colorAttachments[i].view = descriptor->colorAttachments[i]->attachment; + cmd->colorAttachments[i].resolveTarget = + descriptor->colorAttachments[i]->resolveTarget; + cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i]->loadOp; + cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i]->storeOp; + cmd->colorAttachments[i].clearColor = + descriptor->colorAttachments[i]->clearColor; + } + } + + cmd->hasDepthStencilAttachment = descriptor->depthStencilAttachment != nullptr; + if (cmd->hasDepthStencilAttachment) { + cmd->hasDepthStencilAttachment = true; + cmd->depthStencilAttachment.view = + descriptor->depthStencilAttachment->attachment; + cmd->depthStencilAttachment.clearDepth = + descriptor->depthStencilAttachment->clearDepth; + cmd->depthStencilAttachment.clearStencil = + descriptor->depthStencilAttachment->clearStencil; + cmd->depthStencilAttachment.depthLoadOp = + descriptor->depthStencilAttachment->depthLoadOp; + cmd->depthStencilAttachment.depthStoreOp = + descriptor->depthStencilAttachment->depthStoreOp; + cmd->depthStencilAttachment.stencilLoadOp = + descriptor->depthStencilAttachment->stencilLoadOp; + cmd->depthStencilAttachment.stencilStoreOp = + descriptor->depthStencilAttachment->stencilStoreOp; + } + + cmd->width = width; + cmd->height = height; + cmd->sampleCount = sampleCount; + + return {}; + }); + + if (success) { + RenderPassEncoderBase* passEncoder = + new RenderPassEncoderBase(device, this, &mEncodingContext); + mEncodingContext.EnterPass(passEncoder); + return passEncoder; } - uint32_t width = 0; - uint32_t height = 0; - uint32_t sampleCount = 0; - if (ConsumedError( - ValidateRenderPassDescriptor(device, descriptor, &width, &height, &sampleCount))) { - return RenderPassEncoderBase::MakeError(device, this); - } - - ASSERT(width > 0 && height > 0 && sampleCount > 0); - - mEncodingState = EncodingState::RenderPass; - - BeginRenderPassCmd* cmd = mAllocator.Allocate(Command::BeginRenderPass); - - for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) { - if (descriptor->colorAttachments[i] != nullptr) { - cmd->colorAttachmentsSet.set(i); - cmd->colorAttachments[i].view = descriptor->colorAttachments[i]->attachment; - cmd->colorAttachments[i].resolveTarget = - descriptor->colorAttachments[i]->resolveTarget; - cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i]->loadOp; - cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i]->storeOp; - cmd->colorAttachments[i].clearColor = descriptor->colorAttachments[i]->clearColor; - } - } - - cmd->hasDepthStencilAttachment = descriptor->depthStencilAttachment != nullptr; - if (cmd->hasDepthStencilAttachment) { - cmd->hasDepthStencilAttachment = true; - cmd->depthStencilAttachment.view = descriptor->depthStencilAttachment->attachment; - cmd->depthStencilAttachment.clearDepth = descriptor->depthStencilAttachment->clearDepth; - cmd->depthStencilAttachment.clearStencil = - descriptor->depthStencilAttachment->clearStencil; - cmd->depthStencilAttachment.depthLoadOp = - descriptor->depthStencilAttachment->depthLoadOp; - cmd->depthStencilAttachment.depthStoreOp = - descriptor->depthStencilAttachment->depthStoreOp; - cmd->depthStencilAttachment.stencilLoadOp = - descriptor->depthStencilAttachment->stencilLoadOp; - cmd->depthStencilAttachment.stencilStoreOp = - descriptor->depthStencilAttachment->stencilStoreOp; - } - - cmd->width = width; - cmd->height = height; - cmd->sampleCount = sampleCount; - - return new RenderPassEncoderBase(device, this, &mAllocator); + return RenderPassEncoderBase::MakeError(device, this, &mEncodingContext); } void CommandEncoderBase::CopyBufferToBuffer(BufferBase* source, @@ -740,208 +732,147 @@ namespace dawn_native { BufferBase* destination, uint64_t destinationOffset, uint64_t size) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(source)); + DAWN_TRY(GetDevice()->ValidateObject(destination)); - if (ConsumedError(GetDevice()->ValidateObject(source))) { - return; - } + CopyBufferToBufferCmd* copy = + allocator->Allocate(Command::CopyBufferToBuffer); + copy->source = source; + copy->sourceOffset = sourceOffset; + copy->destination = destination; + copy->destinationOffset = destinationOffset; + copy->size = size; - if (ConsumedError(GetDevice()->ValidateObject(destination))) { - return; - } - - CopyBufferToBufferCmd* copy = - mAllocator.Allocate(Command::CopyBufferToBuffer); - copy->source = source; - copy->sourceOffset = sourceOffset; - copy->destination = destination; - copy->destinationOffset = destinationOffset; - copy->size = size; + return {}; + }); } void CommandEncoderBase::CopyBufferToTexture(const BufferCopyView* source, const TextureCopyView* destination, const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(source->buffer)); + DAWN_TRY(GetDevice()->ValidateObject(destination->texture)); - if (ConsumedError(GetDevice()->ValidateObject(source->buffer))) { - return; - } + CopyBufferToTextureCmd* copy = + allocator->Allocate(Command::CopyBufferToTexture); + copy->source.buffer = source->buffer; + copy->source.offset = source->offset; + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->copySize = *copySize; + copy->destination.mipLevel = destination->mipLevel; + copy->destination.arrayLayer = destination->arrayLayer; + if (source->rowPitch == 0) { + copy->source.rowPitch = + ComputeDefaultRowPitch(destination->texture->GetFormat(), copySize->width); + } else { + copy->source.rowPitch = source->rowPitch; + } + if (source->imageHeight == 0) { + copy->source.imageHeight = copySize->height; + } else { + copy->source.imageHeight = source->imageHeight; + } - if (ConsumedError(GetDevice()->ValidateObject(destination->texture))) { - return; - } - - CopyBufferToTextureCmd* copy = - mAllocator.Allocate(Command::CopyBufferToTexture); - copy->source.buffer = source->buffer; - copy->source.offset = source->offset; - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->copySize = *copySize; - copy->destination.mipLevel = destination->mipLevel; - copy->destination.arrayLayer = destination->arrayLayer; - if (source->rowPitch == 0) { - copy->source.rowPitch = - ComputeDefaultRowPitch(destination->texture->GetFormat(), copySize->width); - } else { - copy->source.rowPitch = source->rowPitch; - } - if (source->imageHeight == 0) { - copy->source.imageHeight = copySize->height; - } else { - copy->source.imageHeight = source->imageHeight; - } + return {}; + }); } void CommandEncoderBase::CopyTextureToBuffer(const TextureCopyView* source, const BufferCopyView* destination, const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(source->texture)); + DAWN_TRY(GetDevice()->ValidateObject(destination->buffer)); - if (ConsumedError(GetDevice()->ValidateObject(source->texture))) { - return; - } + CopyTextureToBufferCmd* copy = + allocator->Allocate(Command::CopyTextureToBuffer); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->copySize = *copySize; + copy->source.mipLevel = source->mipLevel; + copy->source.arrayLayer = source->arrayLayer; + copy->destination.buffer = destination->buffer; + copy->destination.offset = destination->offset; + if (destination->rowPitch == 0) { + copy->destination.rowPitch = + ComputeDefaultRowPitch(source->texture->GetFormat(), copySize->width); + } else { + copy->destination.rowPitch = destination->rowPitch; + } + if (destination->imageHeight == 0) { + copy->destination.imageHeight = copySize->height; + } else { + copy->destination.imageHeight = destination->imageHeight; + } - if (ConsumedError(GetDevice()->ValidateObject(destination->buffer))) { - return; - } - - CopyTextureToBufferCmd* copy = - mAllocator.Allocate(Command::CopyTextureToBuffer); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->copySize = *copySize; - copy->source.mipLevel = source->mipLevel; - copy->source.arrayLayer = source->arrayLayer; - copy->destination.buffer = destination->buffer; - copy->destination.offset = destination->offset; - if (destination->rowPitch == 0) { - copy->destination.rowPitch = - ComputeDefaultRowPitch(source->texture->GetFormat(), copySize->width); - } else { - copy->destination.rowPitch = destination->rowPitch; - } - if (destination->imageHeight == 0) { - copy->destination.imageHeight = copySize->height; - } else { - copy->destination.imageHeight = destination->imageHeight; - } + return {}; + }); } void CommandEncoderBase::CopyTextureToTexture(const TextureCopyView* source, const TextureCopyView* destination, const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(source->texture)); + DAWN_TRY(GetDevice()->ValidateObject(destination->texture)); - if (ConsumedError(GetDevice()->ValidateObject(source->texture))) { - return; - } + CopyTextureToTextureCmd* copy = + allocator->Allocate(Command::CopyTextureToTexture); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->source.mipLevel = source->mipLevel; + copy->source.arrayLayer = source->arrayLayer; + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->destination.mipLevel = destination->mipLevel; + copy->destination.arrayLayer = destination->arrayLayer; + copy->copySize = *copySize; - if (ConsumedError(GetDevice()->ValidateObject(destination->texture))) { - return; - } - - CopyTextureToTextureCmd* copy = - mAllocator.Allocate(Command::CopyTextureToTexture); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->source.mipLevel = source->mipLevel; - copy->source.arrayLayer = source->arrayLayer; - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->destination.mipLevel = destination->mipLevel; - copy->destination.arrayLayer = destination->arrayLayer; - copy->copySize = *copySize; + return {}; + }); } CommandBufferBase* CommandEncoderBase::Finish(const CommandBufferDescriptor* descriptor) { if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) { // Even if finish validation fails, it is now invalid to call any encoding commands on // this object, so we set its state to finished. - mEncodingState = EncodingState::Finished; return CommandBufferBase::MakeError(GetDevice()); } ASSERT(!IsError()); - mEncodingState = EncodingState::Finished; - - MoveToIterator(); return GetDevice()->CreateCommandBuffer(this, descriptor); } - // Implementation of functions to interact with sub-encoders - - void CommandEncoderBase::HandleError(const char* message) { - if (mEncodingState != EncodingState::Finished) { - if (!mGotError) { - mGotError = true; - mErrorMessage = message; - } - } else { - GetDevice()->HandleError(message); - } - } - - void CommandEncoderBase::ConsumeError(ErrorData* error) { - HandleError(error->GetMessage().c_str()); - delete error; - } - - void CommandEncoderBase::PassEnded() { - // This function may still be called when the command encoder is finished, just do nothing. - if (mEncodingState == EncodingState::Finished) { - return; - } - - if (mEncodingState == EncodingState::ComputePass) { - mAllocator.Allocate(Command::EndComputePass); - } else { - ASSERT(mEncodingState == EncodingState::RenderPass); - mAllocator.Allocate(Command::EndRenderPass); - } - mEncodingState = EncodingState::TopLevel; - } - // Implementation of the command buffer validation that can be precomputed before submit MaybeError CommandEncoderBase::ValidateFinish(const CommandBufferDescriptor*) { DAWN_TRY(GetDevice()->ValidateObject(this)); - if (mGotError) { - return DAWN_VALIDATION_ERROR(mErrorMessage); - } + // Even if Finish() validation fails, calling it will mutate the internal state of the + // encoding context. Subsequent calls to encode commands will generate errors. + DAWN_TRY(mEncodingContext.Finish()); - if (mEncodingState != EncodingState::TopLevel) { - return DAWN_VALIDATION_ERROR("Command buffer recording ended mid-pass"); - } - - MoveToIterator(); - mIterator.Reset(); + CommandIterator* commands = mEncodingContext.GetIterator(); + commands->Reset(); Command type; - while (mIterator.NextCommandId(&type)) { + while (commands->NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { - mIterator.NextCommand(); - DAWN_TRY(ValidateComputePass()); + commands->NextCommand(); + DAWN_TRY(ValidateComputePass(commands)); } break; case Command::BeginRenderPass: { - BeginRenderPassCmd* cmd = mIterator.NextCommand(); - DAWN_TRY(ValidateRenderPass(cmd)); + BeginRenderPassCmd* cmd = commands->NextCommand(); + DAWN_TRY(ValidateRenderPass(commands, cmd)); } break; case Command::CopyBufferToBuffer: { - CopyBufferToBufferCmd* copy = mIterator.NextCommand(); + CopyBufferToBufferCmd* copy = commands->NextCommand(); DAWN_TRY( ValidateCopySizeFitsInBuffer(copy->source, copy->sourceOffset, copy->size)); @@ -959,7 +890,7 @@ namespace dawn_native { } break; case Command::CopyBufferToTexture: { - CopyBufferToTextureCmd* copy = mIterator.NextCommand(); + CopyBufferToTextureCmd* copy = commands->NextCommand(); DAWN_TRY( ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get())); @@ -994,7 +925,7 @@ namespace dawn_native { } break; case Command::CopyTextureToBuffer: { - CopyTextureToBufferCmd* copy = mIterator.NextCommand(); + CopyTextureToBufferCmd* copy = commands->NextCommand(); DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get())); @@ -1030,7 +961,7 @@ namespace dawn_native { case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(ValidateTextureToTextureCopyRestrictions( copy->source, copy->destination, copy->copySize)); @@ -1064,15 +995,15 @@ namespace dawn_native { return {}; } - MaybeError CommandEncoderBase::ValidateComputePass() { + MaybeError CommandEncoderBase::ValidateComputePass(CommandIterator* commands) { PassResourceUsageTracker usageTracker; CommandBufferStateTracker persistentState; Command type; - while (mIterator.NextCommandId(&type)) { + while (commands->NextCommandId(&type)) { switch (type) { case Command::EndComputePass: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize)); @@ -1082,43 +1013,43 @@ namespace dawn_native { } break; case Command::Dispatch: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDispatch()); } break; case Command::DispatchIndirect: { - DispatchIndirectCmd* cmd = mIterator.NextCommand(); + DispatchIndirectCmd* cmd = commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDispatch()); usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), dawn::BufferUsageBit::Indirect); } break; case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); } break; case Command::PopDebugGroup: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize)); } break; case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize)); } break; case Command::SetComputePipeline: { - SetComputePipelineCmd* cmd = mIterator.NextCommand(); + SetComputePipelineCmd* cmd = commands->NextCommand(); ComputePipelineBase* pipeline = cmd->pipeline.Get(); persistentState.SetComputePipeline(pipeline); } break; case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mIterator.NextCommand(); + SetBindGroupCmd* cmd = commands->NextCommand(); if (cmd->dynamicOffsetCount > 0) { - mIterator.NextData(cmd->dynamicOffsetCount); + commands->NextData(cmd->dynamicOffsetCount); } TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker); @@ -1134,7 +1065,8 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Unfinished compute pass"); } - MaybeError CommandEncoderBase::ValidateRenderPass(BeginRenderPassCmd* renderPass) { + MaybeError CommandEncoderBase::ValidateRenderPass(CommandIterator* commands, + BeginRenderPassCmd* renderPass) { PassResourceUsageTracker usageTracker; CommandBufferStateTracker persistentState; @@ -1157,10 +1089,10 @@ namespace dawn_native { } Command type; - while (mIterator.NextCommandId(&type)) { + while (commands->NextCommandId(&type)) { switch (type) { case Command::EndRenderPass: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize)); @@ -1170,47 +1102,47 @@ namespace dawn_native { } break; case Command::Draw: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDraw()); } break; case Command::DrawIndexed: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDrawIndexed()); } break; case Command::DrawIndirect: { - DrawIndirectCmd* cmd = mIterator.NextCommand(); + DrawIndirectCmd* cmd = commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDraw()); usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), dawn::BufferUsageBit::Indirect); } break; case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* cmd = mIterator.NextCommand(); + DrawIndexedIndirectCmd* cmd = commands->NextCommand(); DAWN_TRY(persistentState.ValidateCanDrawIndexed()); usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), dawn::BufferUsageBit::Indirect); } break; case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); } break; case Command::PopDebugGroup: { - mIterator.NextCommand(); + commands->NextCommand(); DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize)); } break; case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize)); } break; case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mIterator.NextCommand(); + SetRenderPipelineCmd* cmd = commands->NextCommand(); RenderPipelineBase* pipeline = cmd->pipeline.Get(); DAWN_TRY(pipeline->ValidateCompatibleWith(renderPass)); @@ -1218,25 +1150,25 @@ namespace dawn_native { } break; case Command::SetStencilReference: { - mIterator.NextCommand(); + commands->NextCommand(); } break; case Command::SetBlendColor: { - mIterator.NextCommand(); + commands->NextCommand(); } break; case Command::SetViewport: { - mIterator.NextCommand(); + commands->NextCommand(); } break; case Command::SetScissorRect: { - mIterator.NextCommand(); + commands->NextCommand(); } break; case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mIterator.NextCommand(); + SetBindGroupCmd* cmd = commands->NextCommand(); if (cmd->dynamicOffsetCount > 0) { - mIterator.NextData(cmd->dynamicOffsetCount); + commands->NextData(cmd->dynamicOffsetCount); } TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker); @@ -1244,16 +1176,16 @@ namespace dawn_native { } break; case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mIterator.NextCommand(); + SetIndexBufferCmd* cmd = commands->NextCommand(); usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index); persistentState.SetIndexBuffer(); } break; case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mIterator.NextCommand(); - auto buffers = mIterator.NextData>(cmd->count); - mIterator.NextData(cmd->count); + SetVertexBuffersCmd* cmd = commands->NextCommand(); + auto buffers = commands->NextData>(cmd->count); + commands->NextData(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { usageTracker.BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex); @@ -1270,11 +1202,4 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Unfinished render pass"); } - MaybeError CommandEncoderBase::ValidateCanRecordTopLevelCommands() const { - if (mEncodingState != EncodingState::TopLevel) { - return DAWN_VALIDATION_ERROR("Command cannot be recorded inside a pass"); - } - return {}; - } - } // namespace dawn_native diff --git a/src/dawn_native/CommandEncoder.h b/src/dawn_native/CommandEncoder.h index 2faaa3fd84..3a59e8e188 100644 --- a/src/dawn_native/CommandEncoder.h +++ b/src/dawn_native/CommandEncoder.h @@ -17,7 +17,7 @@ #include "dawn_native/dawn_platform.h" -#include "dawn_native/CommandAllocator.h" +#include "dawn_native/EncodingContext.h" #include "dawn_native/Error.h" #include "dawn_native/ObjectBase.h" #include "dawn_native/PassResourceUsage.h" @@ -31,7 +31,6 @@ namespace dawn_native { class CommandEncoderBase : public ObjectBase { public: CommandEncoderBase(DeviceBase* device, const CommandEncoderDescriptor* descriptor); - ~CommandEncoderBase(); CommandIterator AcquireCommands(); CommandBufferResourceUsage AcquireResourceUsages(); @@ -55,41 +54,17 @@ namespace dawn_native { const Extent3D* copySize); CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor); - // Functions to interact with the encoders - void HandleError(const char* message); - void ConsumeError(ErrorData* error); - bool ConsumedError(MaybeError maybeError) { - if (DAWN_UNLIKELY(maybeError.IsError())) { - ConsumeError(maybeError.AcquireError()); - return true; - } - return false; - } - - void PassEnded(); - private: MaybeError ValidateFinish(const CommandBufferDescriptor* descriptor); - MaybeError ValidateComputePass(); - MaybeError ValidateRenderPass(BeginRenderPassCmd* renderPass); - MaybeError ValidateCanRecordTopLevelCommands() const; + MaybeError ValidateComputePass(CommandIterator* commands); + MaybeError ValidateRenderPass(CommandIterator* commands, BeginRenderPassCmd* renderPass); - enum class EncodingState : uint8_t; - EncodingState mEncodingState; - - void MoveToIterator(); - CommandAllocator mAllocator; - CommandIterator mIterator; - bool mWasMovedToIterator = false; - bool mWereCommandsAcquired = false; + EncodingContext mEncodingContext; bool mWereResourceUsagesAcquired = false; CommandBufferResourceUsage mResourceUsages; unsigned int mDebugGroupStackSize = 0; - - bool mGotError = false; - std::string mErrorMessage; }; } // namespace dawn_native diff --git a/src/dawn_native/ComputePassEncoder.cpp b/src/dawn_native/ComputePassEncoder.cpp index e03924157a..2a6eb42d35 100644 --- a/src/dawn_native/ComputePassEncoder.cpp +++ b/src/dawn_native/ComputePassEncoder.cpp @@ -23,70 +23,76 @@ namespace dawn_native { ComputePassEncoderBase::ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ProgrammablePassEncoder(device, topLevelEncoder, allocator) { + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext) + : ProgrammablePassEncoder(device, encodingContext), mCommandEncoder(commandEncoder) { } ComputePassEncoderBase::ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext, ErrorTag errorTag) - : ProgrammablePassEncoder(device, topLevelEncoder, errorTag) { + : ProgrammablePassEncoder(device, encodingContext, errorTag), + mCommandEncoder(commandEncoder) { } ComputePassEncoderBase* ComputePassEncoderBase::MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder) { - return new ComputePassEncoderBase(device, topLevelEncoder, ObjectBase::kError); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext) { + return new ComputePassEncoderBase(device, commandEncoder, encodingContext, + ObjectBase::kError); } void ComputePassEncoderBase::EndPass() { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::EndComputePass); - mTopLevelEncoder->PassEnded(); - mAllocator = nullptr; + return {}; + })) { + mEncodingContext->ExitPass(this); + } } void ComputePassEncoderBase::Dispatch(uint32_t x, uint32_t y, uint32_t z) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DispatchCmd* dispatch = allocator->Allocate(Command::Dispatch); + dispatch->x = x; + dispatch->y = y; + dispatch->z = z; - DispatchCmd* dispatch = mAllocator->Allocate(Command::Dispatch); - dispatch->x = x; - dispatch->y = y; - dispatch->z = z; + return {}; + }); } void ComputePassEncoderBase::DispatchIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDispatchIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + if (indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDispatchIndirectSize > indirectBuffer->GetSize()) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } - DispatchIndirectCmd* dispatch = - mAllocator->Allocate(Command::DispatchIndirect); - dispatch->indirectBuffer = indirectBuffer; - dispatch->indirectOffset = indirectOffset; + DispatchIndirectCmd* dispatch = + allocator->Allocate(Command::DispatchIndirect); + dispatch->indirectBuffer = indirectBuffer; + dispatch->indirectOffset = indirectOffset; + + return {}; + }); } void ComputePassEncoderBase::SetPipeline(ComputePipelineBase* pipeline) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(pipeline)); - SetComputePipelineCmd* cmd = - mAllocator->Allocate(Command::SetComputePipeline); - cmd->pipeline = pipeline; + SetComputePipelineCmd* cmd = + allocator->Allocate(Command::SetComputePipeline); + cmd->pipeline = pipeline; + + return {}; + }); } } // namespace dawn_native diff --git a/src/dawn_native/ComputePassEncoder.h b/src/dawn_native/ComputePassEncoder.h index 3e645bba8a..8e4c2f6836 100644 --- a/src/dawn_native/ComputePassEncoder.h +++ b/src/dawn_native/ComputePassEncoder.h @@ -27,11 +27,12 @@ namespace dawn_native { class ComputePassEncoderBase : public ProgrammablePassEncoder { public: ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext); static ComputePassEncoderBase* MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext); void EndPass(); @@ -41,8 +42,14 @@ namespace dawn_native { protected: ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext, ErrorTag errorTag); + + private: + // For render and compute passes, the encoding context is borrowed from the command encoder. + // Keep a reference to the encoder to make sure the context isn't freed. + Ref mCommandEncoder; }; } // namespace dawn_native diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp new file mode 100644 index 0000000000..d2c8a751a9 --- /dev/null +++ b/src/dawn_native/EncodingContext.cpp @@ -0,0 +1,101 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/EncodingContext.h" + +#include "common/Assert.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/ErrorData.h" + +namespace dawn_native { + + EncodingContext::EncodingContext(DeviceBase* device, const ObjectBase* initialEncoder) + : mDevice(device), mTopLevelEncoder(initialEncoder), mCurrentEncoder(initialEncoder) { + } + + EncodingContext::~EncodingContext() { + if (!mWereCommandsAcquired) { + FreeCommands(GetIterator()); + } + } + + CommandIterator EncodingContext::AcquireCommands() { + ASSERT(!mWereCommandsAcquired); + mWereCommandsAcquired = true; + return std::move(mIterator); + } + + CommandIterator* EncodingContext::GetIterator() { + if (!mWasMovedToIterator) { + mIterator = std::move(mAllocator); + mWasMovedToIterator = true; + } + return &mIterator; + } + + void EncodingContext::HandleError(const char* message) { + if (!IsFinished()) { + // If the encoding context is not finished, errors are deferred until + // Finish() is called. + if (!mGotError) { + mGotError = true; + mErrorMessage = message; + } + } else { + mDevice->HandleError(message); + } + } + + void EncodingContext::EnterPass(const ObjectBase* passEncoder) { + // Assert we're at the top level. + ASSERT(mCurrentEncoder == mTopLevelEncoder); + ASSERT(passEncoder != nullptr); + + mCurrentEncoder = passEncoder; + } + + void EncodingContext::ExitPass(const ObjectBase* passEncoder) { + // Assert we're not at the top level. + ASSERT(mCurrentEncoder != mTopLevelEncoder); + // Assert the pass encoder is current. + ASSERT(mCurrentEncoder == passEncoder); + + mCurrentEncoder = mTopLevelEncoder; + } + + MaybeError EncodingContext::Finish() { + const void* currentEncoder = mCurrentEncoder; + const void* topLevelEncoder = mTopLevelEncoder; + + // Even if finish validation fails, it is now invalid to call any encoding commands, + // so we clear the encoders. Note: mTopLevelEncoder == nullptr is used as a flag for + // if Finish() has been called. + mCurrentEncoder = nullptr; + mTopLevelEncoder = nullptr; + + if (mGotError) { + return DAWN_VALIDATION_ERROR(mErrorMessage); + } + if (currentEncoder != topLevelEncoder) { + return DAWN_VALIDATION_ERROR("Command buffer recording ended mid-pass"); + } + return {}; + } + + bool EncodingContext::IsFinished() const { + return mTopLevelEncoder == nullptr; + } + +} // namespace dawn_native diff --git a/src/dawn_native/EncodingContext.h b/src/dawn_native/EncodingContext.h new file mode 100644 index 0000000000..a810f501ef --- /dev/null +++ b/src/dawn_native/EncodingContext.h @@ -0,0 +1,101 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ENCODINGCONTEXT_H_ +#define DAWNNATIVE_ENCODINGCONTEXT_H_ + +#include "dawn_native/CommandAllocator.h" +#include "dawn_native/Error.h" +#include "dawn_native/ErrorData.h" + +#include + +namespace dawn_native { + + class ObjectBase; + class DeviceBase; + + // Base class for allocating/iterating commands. + // It performs error tracking as well as encoding state for render/compute passes. + class EncodingContext { + public: + EncodingContext(DeviceBase* device, const ObjectBase* initialEncoder); + ~EncodingContext(); + + CommandIterator AcquireCommands(); + CommandIterator* GetIterator(); + + // Functions to handle encoder errors + void HandleError(const char* message); + + inline void ConsumeError(ErrorData* error) { + HandleError(error->GetMessage().c_str()); + delete error; + } + + inline bool ConsumedError(MaybeError maybeError) { + if (DAWN_UNLIKELY(maybeError.IsError())) { + ConsumeError(maybeError.AcquireError()); + return true; + } + return false; + } + + template + inline bool TryEncode(const void* encoder, EncodeFunction&& encodeFunction) { + if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) { + if (mCurrentEncoder != mTopLevelEncoder) { + // The top level encoder was used when a pass encoder was current. + HandleError("Command cannot be recorded inside a pass"); + } else { + HandleError("Recording in an error or already ended pass encoder"); + } + return false; + } + ASSERT(!mWasMovedToIterator); + return !ConsumedError(encodeFunction(&mAllocator)); + } + + // Functions to set current encoder state + void EnterPass(const ObjectBase* passEncoder); + void ExitPass(const ObjectBase* passEncoder); + MaybeError Finish(); + + private: + bool IsFinished() const; + + DeviceBase* mDevice; + + // There can only be two levels of encoders. Top-level and render/compute pass. + // The top level encoder is the encoder the EncodingContext is created with. + // It doubles as flag to check if encoding has been Finished. + const ObjectBase* mTopLevelEncoder; + // The current encoder must be the same as the encoder provided to TryEncode, + // otherwise an error is produced. It may be nullptr if the EncodingContext is an error. + // The current encoder changes with Enter/ExitPass which should be called by + // CommandEncoder::Begin/EndPass. + const ObjectBase* mCurrentEncoder; + + CommandAllocator mAllocator; + CommandIterator mIterator; + bool mWasMovedToIterator = false; + bool mWereCommandsAcquired = false; + + bool mGotError = false; + std::string mErrorMessage; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_ENCODINGCONTEXT_H_ diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp index 4ba5b1142c..4210a13d72 100644 --- a/src/dawn_native/ProgrammablePassEncoder.cpp +++ b/src/dawn_native/ProgrammablePassEncoder.cpp @@ -26,109 +26,98 @@ namespace dawn_native { ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ObjectBase(device), mTopLevelEncoder(topLevelEncoder), mAllocator(allocator) { - DAWN_ASSERT(allocator != nullptr); + EncodingContext* encodingContext) + : ObjectBase(device), mEncodingContext(encodingContext) { } ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + EncodingContext* encodingContext, ErrorTag errorTag) - : ObjectBase(device, errorTag), mTopLevelEncoder(topLevelEncoder), mAllocator(nullptr) { + : ObjectBase(device, errorTag), mEncodingContext(encodingContext) { } void ProgrammablePassEncoder::InsertDebugMarker(const char* groupLabel) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + InsertDebugMarkerCmd* cmd = + allocator->Allocate(Command::InsertDebugMarker); + cmd->length = strlen(groupLabel); - InsertDebugMarkerCmd* cmd = - mAllocator->Allocate(Command::InsertDebugMarker); - cmd->length = strlen(groupLabel); + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - char* label = mAllocator->AllocateData(cmd->length + 1); - memcpy(label, groupLabel, cmd->length + 1); + return {}; + }); } void ProgrammablePassEncoder::PopDebugGroup() { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::PopDebugGroup); - mAllocator->Allocate(Command::PopDebugGroup); + return {}; + }); } void ProgrammablePassEncoder::PushDebugGroup(const char* groupLabel) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + PushDebugGroupCmd* cmd = + allocator->Allocate(Command::PushDebugGroup); + cmd->length = strlen(groupLabel); - PushDebugGroupCmd* cmd = mAllocator->Allocate(Command::PushDebugGroup); - cmd->length = strlen(groupLabel); + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - char* label = mAllocator->AllocateData(cmd->length + 1); - memcpy(label, groupLabel, cmd->length + 1); + return {}; + }); } void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndex, BindGroupBase* group, uint32_t dynamicOffsetCount, const uint64_t* dynamicOffsets) { - const BindGroupLayoutBase* layout = group->GetLayout(); + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + const BindGroupLayoutBase* layout = group->GetLayout(); - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(group))) { - return; - } + DAWN_TRY(GetDevice()->ValidateObject(group)); - if (groupIndex >= kMaxBindGroups) { - mTopLevelEncoder->HandleError("Setting bind group over the max"); - return; - } - - // Dynamic offsets count must match the number required by the layout perfectly. - if (layout->GetDynamicBufferCount() != dynamicOffsetCount) { - mTopLevelEncoder->HandleError("dynamicOffset count mismatch"); - } - - for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { - if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { - mTopLevelEncoder->HandleError("Dynamic Buffer Offset need to be aligned"); - return; + if (groupIndex >= kMaxBindGroups) { + return DAWN_VALIDATION_ERROR("Setting bind group over the max"); } - BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); - - // During BindGroup creation, validation ensures binding offset + binding size <= buffer - // size. - DAWN_ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); - DAWN_ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= - bufferBinding.offset); - - if (dynamicOffsets[i] > - bufferBinding.buffer->GetSize() - bufferBinding.offset - bufferBinding.size) { - mTopLevelEncoder->HandleError("dynamic offset out of bounds"); - return; + // Dynamic offsets count must match the number required by the layout perfectly. + if (layout->GetDynamicBufferCount() != dynamicOffsetCount) { + return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch"); } - } - SetBindGroupCmd* cmd = mAllocator->Allocate(Command::SetBindGroup); - cmd->index = groupIndex; - cmd->group = group; - cmd->dynamicOffsetCount = dynamicOffsetCount; - if (dynamicOffsetCount > 0) { - uint64_t* offsets = mAllocator->AllocateData(cmd->dynamicOffsetCount); - memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint64_t)); - } - } + for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { + if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { + return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned"); + } - MaybeError ProgrammablePassEncoder::ValidateCanRecordCommands() const { - if (mAllocator == nullptr) { - return DAWN_VALIDATION_ERROR("Recording in an error or already ended pass encoder"); - } + BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); - return nullptr; + // During BindGroup creation, validation ensures binding offset + binding size <= + // buffer size. + DAWN_ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); + DAWN_ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= + bufferBinding.offset); + + if ((dynamicOffsets[i] > + bufferBinding.buffer->GetSize() - bufferBinding.offset - bufferBinding.size)) { + return DAWN_VALIDATION_ERROR("dynamic offset out of bounds"); + } + } + + SetBindGroupCmd* cmd = allocator->Allocate(Command::SetBindGroup); + cmd->index = groupIndex; + cmd->group = group; + cmd->dynamicOffsetCount = dynamicOffsetCount; + if (dynamicOffsetCount > 0) { + uint64_t* offsets = allocator->AllocateData(cmd->dynamicOffsetCount); + memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint64_t)); + } + + return {}; + }); } } // namespace dawn_native diff --git a/src/dawn_native/ProgrammablePassEncoder.h b/src/dawn_native/ProgrammablePassEncoder.h index e64592cef2..fc8f11a952 100644 --- a/src/dawn_native/ProgrammablePassEncoder.h +++ b/src/dawn_native/ProgrammablePassEncoder.h @@ -29,9 +29,7 @@ namespace dawn_native { // Base class for shared functionality between ComputePassEncoder and RenderPassEncoder. class ProgrammablePassEncoder : public ObjectBase { public: - ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); + ProgrammablePassEncoder(DeviceBase* device, EncodingContext* encodingContext); void InsertDebugMarker(const char* groupLabel); void PopDebugGroup(); @@ -45,16 +43,10 @@ namespace dawn_native { protected: // Construct an "error" programmable pass encoder. ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + EncodingContext* encodingContext, ErrorTag errorTag); - MaybeError ValidateCanRecordCommands() const; - - // The allocator is borrowed from the top level encoder. Keep a reference to the encoder - // to make sure the allocator isn't freed. - Ref mTopLevelEncoder = nullptr; - // mAllocator is cleared at the end of the pass so it acts as a tag that EndPass was called - CommandAllocator* mAllocator = nullptr; + EncodingContext* mEncodingContext = nullptr; }; } // namespace dawn_native diff --git a/src/dawn_native/RenderEncoderBase.cpp b/src/dawn_native/RenderEncoderBase.cpp index 198428a557..ca80aefc75 100644 --- a/src/dawn_native/RenderEncoderBase.cpp +++ b/src/dawn_native/RenderEncoderBase.cpp @@ -26,31 +26,29 @@ namespace dawn_native { - RenderEncoderBase::RenderEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ProgrammablePassEncoder(device, topLevelEncoder, allocator) { + RenderEncoderBase::RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext) + : ProgrammablePassEncoder(device, encodingContext) { } RenderEncoderBase::RenderEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + EncodingContext* encodingContext, ErrorTag errorTag) - : ProgrammablePassEncoder(device, topLevelEncoder, errorTag) { + : ProgrammablePassEncoder(device, encodingContext, errorTag) { } void RenderEncoderBase::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DrawCmd* draw = allocator->Allocate(Command::Draw); + draw->vertexCount = vertexCount; + draw->instanceCount = instanceCount; + draw->firstVertex = firstVertex; + draw->firstInstance = firstInstance; - DrawCmd* draw = mAllocator->Allocate(Command::Draw); - draw->vertexCount = vertexCount; - draw->instanceCount = instanceCount; - draw->firstVertex = firstVertex; - draw->firstInstance = firstInstance; + return {}; + }); } void RenderEncoderBase::DrawIndexed(uint32_t indexCount, @@ -58,102 +56,103 @@ namespace dawn_native { uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DrawIndexedCmd* draw = allocator->Allocate(Command::DrawIndexed); + draw->indexCount = indexCount; + draw->instanceCount = instanceCount; + draw->firstIndex = firstIndex; + draw->baseVertex = baseVertex; + draw->firstInstance = firstInstance; - DrawIndexedCmd* draw = mAllocator->Allocate(Command::DrawIndexed); - draw->indexCount = indexCount; - draw->instanceCount = instanceCount; - draw->firstIndex = firstIndex; - draw->baseVertex = baseVertex; - draw->firstInstance = firstInstance; + return {}; + }); } void RenderEncoderBase::DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDrawIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + if (indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDrawIndirectSize > indirectBuffer->GetSize()) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } - DrawIndirectCmd* cmd = mAllocator->Allocate(Command::DrawIndirect); - cmd->indirectBuffer = indirectBuffer; - cmd->indirectOffset = indirectOffset; + DrawIndirectCmd* cmd = allocator->Allocate(Command::DrawIndirect); + cmd->indirectBuffer = indirectBuffer; + cmd->indirectOffset = indirectOffset; + + return {}; + }); } void RenderEncoderBase::DrawIndexedIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDrawIndexedIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + if ((indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDrawIndexedIndirectSize > indirectBuffer->GetSize())) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } - DrawIndexedIndirectCmd* cmd = - mAllocator->Allocate(Command::DrawIndexedIndirect); - cmd->indirectBuffer = indirectBuffer; - cmd->indirectOffset = indirectOffset; + DrawIndexedIndirectCmd* cmd = + allocator->Allocate(Command::DrawIndexedIndirect); + cmd->indirectBuffer = indirectBuffer; + cmd->indirectOffset = indirectOffset; + + return {}; + }); } void RenderEncoderBase::SetPipeline(RenderPipelineBase* pipeline) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(pipeline)); - SetRenderPipelineCmd* cmd = - mAllocator->Allocate(Command::SetRenderPipeline); - cmd->pipeline = pipeline; + SetRenderPipelineCmd* cmd = + allocator->Allocate(Command::SetRenderPipeline); + cmd->pipeline = pipeline; + + return {}; + }); } void RenderEncoderBase::SetIndexBuffer(BufferBase* buffer, uint64_t offset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(buffer))) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(buffer)); - SetIndexBufferCmd* cmd = mAllocator->Allocate(Command::SetIndexBuffer); - cmd->buffer = buffer; - cmd->offset = offset; + SetIndexBufferCmd* cmd = + allocator->Allocate(Command::SetIndexBuffer); + cmd->buffer = buffer; + cmd->offset = offset; + + return {}; + }); } void RenderEncoderBase::SetVertexBuffers(uint32_t startSlot, uint32_t count, BufferBase* const* buffers, uint64_t const* offsets) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } - - for (size_t i = 0; i < count; ++i) { - if (mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(buffers[i]))) { - return; + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + for (size_t i = 0; i < count; ++i) { + DAWN_TRY(GetDevice()->ValidateObject(buffers[i])); } - } - SetVertexBuffersCmd* cmd = - mAllocator->Allocate(Command::SetVertexBuffers); - cmd->startSlot = startSlot; - cmd->count = count; + SetVertexBuffersCmd* cmd = + allocator->Allocate(Command::SetVertexBuffers); + cmd->startSlot = startSlot; + cmd->count = count; - Ref* cmdBuffers = mAllocator->AllocateData>(count); - for (size_t i = 0; i < count; ++i) { - cmdBuffers[i] = buffers[i]; - } + Ref* cmdBuffers = allocator->AllocateData>(count); + for (size_t i = 0; i < count; ++i) { + cmdBuffers[i] = buffers[i]; + } - uint64_t* cmdOffsets = mAllocator->AllocateData(count); - memcpy(cmdOffsets, offsets, count * sizeof(uint64_t)); + uint64_t* cmdOffsets = allocator->AllocateData(count); + memcpy(cmdOffsets, offsets, count * sizeof(uint64_t)); + + return {}; + }); } } // namespace dawn_native diff --git a/src/dawn_native/RenderEncoderBase.h b/src/dawn_native/RenderEncoderBase.h index adeead3f2f..19061bc812 100644 --- a/src/dawn_native/RenderEncoderBase.h +++ b/src/dawn_native/RenderEncoderBase.h @@ -22,9 +22,7 @@ namespace dawn_native { class RenderEncoderBase : public ProgrammablePassEncoder { public: - RenderEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); + RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext); void Draw(uint32_t vertexCount, uint32_t instanceCount, @@ -57,9 +55,7 @@ namespace dawn_native { protected: // Construct an "error" render encoder base. - RenderEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag); + RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext, ErrorTag errorTag); }; } // namespace dawn_native diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp index 07b1be83c9..54d5db5f54 100644 --- a/src/dawn_native/RenderPassEncoder.cpp +++ b/src/dawn_native/RenderPassEncoder.cpp @@ -27,48 +27,52 @@ namespace dawn_native { RenderPassEncoderBase::RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : RenderEncoderBase(device, topLevelEncoder, allocator) { + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext) + : RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) { } RenderPassEncoderBase::RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext, ErrorTag errorTag) - : RenderEncoderBase(device, topLevelEncoder, errorTag) { + : RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) { } RenderPassEncoderBase* RenderPassEncoderBase::MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder) { - return new RenderPassEncoderBase(device, topLevelEncoder, ObjectBase::kError); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext) { + return new RenderPassEncoderBase(device, commandEncoder, encodingContext, + ObjectBase::kError); } void RenderPassEncoderBase::EndPass() { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::EndRenderPass); - mTopLevelEncoder->PassEnded(); - mAllocator = nullptr; + return {}; + })) { + mEncodingContext->ExitPass(this); + } } void RenderPassEncoderBase::SetStencilReference(uint32_t reference) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + SetStencilReferenceCmd* cmd = + allocator->Allocate(Command::SetStencilReference); + cmd->reference = reference; - SetStencilReferenceCmd* cmd = - mAllocator->Allocate(Command::SetStencilReference); - cmd->reference = reference; + return {}; + }); } void RenderPassEncoderBase::SetBlendColor(const Color* color) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + SetBlendColorCmd* cmd = allocator->Allocate(Command::SetBlendColor); + cmd->color = *color; - SetBlendColorCmd* cmd = mAllocator->Allocate(Command::SetBlendColor); - cmd->color = *color; + return {}; + }); } void RenderPassEncoderBase::SetViewport(float x, @@ -77,55 +81,53 @@ namespace dawn_native { float height, float minDepth, float maxDepth) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if ((isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) || + isnan(maxDepth))) { + return DAWN_VALIDATION_ERROR("NaN is not allowed."); + } - if (isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) || - isnan(maxDepth)) { - mTopLevelEncoder->HandleError("NaN is not allowed."); - return; - } + // TODO(yunchao.he@intel.com): there are more restrictions for x, y, width and height in + // Vulkan, and height can be a negative value in Vulkan 1.1. Revisit this part later + // (say, for WebGPU v1). + if (width <= 0 || height <= 0) { + return DAWN_VALIDATION_ERROR("Width and height must be greater than 0."); + } - // TODO(yunchao.he@intel.com): there are more restrictions for x, y, width and height in - // Vulkan, and height can be a negative value in Vulkan 1.1. Revisit this part later (say, - // for WebGPU v1). - if (width <= 0 || height <= 0) { - mTopLevelEncoder->HandleError("Width and height must be greater than 0."); - return; - } + if (minDepth < 0 || minDepth > 1 || maxDepth < 0 || maxDepth > 1) { + return DAWN_VALIDATION_ERROR("minDepth and maxDepth must be in [0, 1]."); + } - if (minDepth < 0 || minDepth > 1 || maxDepth < 0 || maxDepth > 1) { - mTopLevelEncoder->HandleError("minDepth and maxDepth must be in [0, 1]."); - return; - } + SetViewportCmd* cmd = allocator->Allocate(Command::SetViewport); + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + cmd->minDepth = minDepth; + cmd->maxDepth = maxDepth; - SetViewportCmd* cmd = mAllocator->Allocate(Command::SetViewport); - cmd->x = x; - cmd->y = y; - cmd->width = width; - cmd->height = height; - cmd->minDepth = minDepth; - cmd->maxDepth = maxDepth; + return {}; + }); } void RenderPassEncoderBase::SetScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } - if (width == 0 || height == 0) { - mTopLevelEncoder->HandleError("Width and height must be greater than 0."); - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (width == 0 || height == 0) { + return DAWN_VALIDATION_ERROR("Width and height must be greater than 0."); + } - SetScissorRectCmd* cmd = mAllocator->Allocate(Command::SetScissorRect); - cmd->x = x; - cmd->y = y; - cmd->width = width; - cmd->height = height; + SetScissorRectCmd* cmd = + allocator->Allocate(Command::SetScissorRect); + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + + return {}; + }); } } // namespace dawn_native diff --git a/src/dawn_native/RenderPassEncoder.h b/src/dawn_native/RenderPassEncoder.h index ed947e5e5e..b9610793dc 100644 --- a/src/dawn_native/RenderPassEncoder.h +++ b/src/dawn_native/RenderPassEncoder.h @@ -27,11 +27,12 @@ namespace dawn_native { class RenderPassEncoderBase : public RenderEncoderBase { public: RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext); static RenderPassEncoderBase* MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder); + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext); void EndPass(); @@ -47,8 +48,14 @@ namespace dawn_native { protected: RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, + CommandEncoderBase* commandEncoder, + EncodingContext* encodingContext, ErrorTag errorTag); + + private: + // For render and compute passes, the encoding context is borrowed from the command encoder. + // Keep a reference to the encoder to make sure the context isn't freed. + Ref mCommandEncoder; }; } // namespace dawn_native diff --git a/src/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/tests/unittests/validation/CommandBufferValidationTests.cpp index 9fe2863023..b8415bd676 100644 --- a/src/tests/unittests/validation/CommandBufferValidationTests.cpp +++ b/src/tests/unittests/validation/CommandBufferValidationTests.cpp @@ -49,9 +49,7 @@ TEST_F(CommandBufferValidationTest, EndedMidRenderPass) { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); ASSERT_DEVICE_ERROR(encoder.Finish()); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. - pass.EndPass(); + ASSERT_DEVICE_ERROR(pass.EndPass()); } } @@ -78,9 +76,7 @@ TEST_F(CommandBufferValidationTest, EndedMidComputePass) { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::ComputePassEncoder pass = encoder.BeginComputePass(); ASSERT_DEVICE_ERROR(encoder.Finish()); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. - pass.EndPass(); + ASSERT_DEVICE_ERROR(pass.EndPass()); } } @@ -101,8 +97,6 @@ TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); pass.EndPass(); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -123,8 +117,6 @@ TEST_F(CommandBufferValidationTest, ComputePassEndedTwice) { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::ComputePassEncoder pass = encoder.BeginComputePass(); pass.EndPass(); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); }