diff --git a/src/backend/CommandBuffer.cpp b/src/backend/CommandBuffer.cpp index 88bdd73882..9bb6b40e88 100644 --- a/src/backend/CommandBuffer.cpp +++ b/src/backend/CommandBuffer.cpp @@ -124,6 +124,8 @@ namespace backend { } // namespace + // CommandBuffer + CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder) : mDevice(builder->mDevice), mBuffersTransitioned(std::move(builder->mState->mBuffersTransitioned)), @@ -150,6 +152,8 @@ namespace backend { return mDevice; } + // CommandBufferBuilder + CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device), mState(std::make_unique(this)) { } @@ -161,23 +165,43 @@ namespace backend { } } + CommandIterator CommandBufferBuilder::AcquireCommands() { + ASSERT(!mWereCommandsAcquired); + mWereCommandsAcquired = true; + return std::move(mIterator); + } + + CommandBufferBase* CommandBufferBuilder::GetResultImpl() { + MoveToIterator(); + return mDevice->CreateCommandBuffer(this); + } + + void CommandBufferBuilder::MoveToIterator() { + if (!mWasMovedToIterator) { + mIterator = std::move(mAllocator); + mWasMovedToIterator = true; + } + } + + // Implementation of the command buffer validation that can be precomputed before submit + bool CommandBufferBuilder::ValidateGetResult() { MoveToIterator(); + mIterator.Reset(); Command type; while (mIterator.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mIterator.NextCommand(); - if (!mState->BeginComputePass()) { + if (!ValidateComputePass()) { return false; } } break; case Command::BeginRenderPass: { BeginRenderPassCmd* cmd = mIterator.NextCommand(); - RenderPassDescriptorBase* info = cmd->info.Get(); - if (!mState->BeginRenderPass(info)) { + if (!ValidateRenderPass(cmd->info.Get())) { return false; } } break; @@ -186,7 +210,6 @@ namespace backend { CopyBufferToBufferCmd* copy = mIterator.NextCommand(); if (!ValidateCopySizeFitsInBuffer(this, copy->source, copy->size) || !ValidateCopySizeFitsInBuffer(this, copy->destination, copy->size) || - !mState->ValidateCanCopy() || !mState->ValidateCanUseBufferAs(copy->source.buffer.Get(), nxt::BufferUsageBit::TransferSrc) || !mState->ValidateCanUseBufferAs(copy->destination.buffer.Get(), @@ -206,7 +229,6 @@ namespace backend { !ValidateCopySizeFitsInBuffer(this, copy->source, bufferCopySize) || !ValidateTexelBufferOffset(this, copy->destination.texture.Get(), copy->source) || - !mState->ValidateCanCopy() || !mState->ValidateCanUseBufferAs(copy->source.buffer.Get(), nxt::BufferUsageBit::TransferSrc) || !mState->ValidateCanUseTextureAs(copy->destination.texture.Get(), @@ -226,7 +248,6 @@ namespace backend { !ValidateCopySizeFitsInBuffer(this, copy->destination, bufferCopySize) || !ValidateTexelBufferOffset(this, copy->source.texture.Get(), copy->destination) || - !mState->ValidateCanCopy() || !mState->ValidateCanUseTextureAs(copy->source.texture.Get(), nxt::TextureUsageBit::TransferSrc) || !mState->ValidateCanUseBufferAs(copy->destination.buffer.Get(), @@ -235,6 +256,42 @@ namespace backend { } } break; + case Command::TransitionBufferUsage: { + TransitionBufferUsageCmd* cmd = + mIterator.NextCommand(); + if (!mState->TransitionBufferUsage(cmd->buffer.Get(), cmd->usage)) { + return false; + } + } break; + + case Command::TransitionTextureUsage: { + TransitionTextureUsageCmd* cmd = + mIterator.NextCommand(); + if (!mState->TransitionTextureUsage(cmd->texture.Get(), cmd->usage)) { + return false; + } + + } break; + + default: + HandleError("Command disallowed outside of a pass"); + return false; + } + } + + return true; + } + + bool CommandBufferBuilder::ValidateComputePass() { + Command type; + while (mIterator.NextCommandId(&type)) { + switch (type) { + case Command::EndComputePass: { + mIterator.NextCommand(); + mState->EndPass(); + return true; + } break; + case Command::Dispatch: { mIterator.NextCommand(); if (!mState->ValidateCanDispatch()) { @@ -242,6 +299,58 @@ namespace backend { } } break; + case Command::SetComputePipeline: { + SetComputePipelineCmd* cmd = mIterator.NextCommand(); + ComputePipelineBase* pipeline = cmd->pipeline.Get(); + if (!mState->SetComputePipeline(pipeline)) { + return false; + } + } break; + + case Command::SetPushConstants: { + SetPushConstantsCmd* cmd = mIterator.NextCommand(); + mIterator.NextData(cmd->count); + // Validation of count and offset has already been done when the command was + // recorded because it impacts the size of an allocation in the + // CommandAllocator. + if (cmd->stages & ~nxt::ShaderStageBit::Compute) { + HandleError( + "SetPushConstants stage must be compute or 0 in compute passes"); + return false; + } + } break; + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = mIterator.NextCommand(); + if (!mState->SetBindGroup(cmd->index, cmd->group.Get())) { + return false; + } + } break; + + default: + HandleError("Command disallowed inside a compute pass"); + return false; + } + } + + HandleError("Unfinished compute pass"); + return false; + } + + bool CommandBufferBuilder::ValidateRenderPass(RenderPassDescriptorBase* renderPass) { + if (!mState->BeginRenderPass(renderPass)) { + return false; + } + + Command type; + while (mIterator.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mIterator.NextCommand(); + mState->EndPass(); + return true; + } break; + case Command::DrawArrays: { mIterator.NextCommand(); if (!mState->ValidateCanDrawArrays()) { @@ -256,31 +365,15 @@ namespace backend { } } break; - case Command::EndComputePass: { - mIterator.NextCommand(); - if (!mState->EndComputePass()) { - return false; - } - } break; - - case Command::EndRenderPass: { - mIterator.NextCommand(); - if (!mState->EndRenderPass()) { - return false; - } - } break; - - case Command::SetComputePipeline: { - SetComputePipelineCmd* cmd = mIterator.NextCommand(); - ComputePipelineBase* pipeline = cmd->pipeline.Get(); - if (!mState->SetComputePipeline(pipeline)) { - return false; - } - } break; - case Command::SetRenderPipeline: { SetRenderPipelineCmd* cmd = mIterator.NextCommand(); RenderPipelineBase* pipeline = cmd->pipeline.Get(); + + if (!pipeline->IsCompatibleWith(renderPass)) { + HandleError("Pipeline is incompatible with this render pass"); + return false; + } + if (!mState->SetRenderPipeline(pipeline)) { return false; } @@ -292,33 +385,25 @@ namespace backend { // Validation of count and offset has already been done when the command was // recorded because it impacts the size of an allocation in the // CommandAllocator. - if (!mState->ValidateSetPushConstants(cmd->stages)) { + if (cmd->stages & + ~(nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment)) { + HandleError( + "SetPushConstants stage must be a subset of (vertex|fragment) in " + "render passes"); return false; } } break; case Command::SetStencilReference: { mIterator.NextCommand(); - if (!mState->HaveRenderPass()) { - HandleError("Can't set stencil reference without an active render pass"); - return false; - } } break; case Command::SetBlendColor: { mIterator.NextCommand(); - if (!mState->HaveRenderPass()) { - HandleError("Can't set blend color without an active render pass"); - return false; - } } break; case Command::SetScissorRect: { mIterator.NextCommand(); - if (!mState->HaveRenderPass()) { - HandleError("Can't set scissor rect without an active render pass"); - return false; - } } break; case Command::SetBindGroup: { @@ -345,42 +430,17 @@ namespace backend { } } break; - case Command::TransitionBufferUsage: { - TransitionBufferUsageCmd* cmd = - mIterator.NextCommand(); - if (!mState->TransitionBufferUsage(cmd->buffer.Get(), cmd->usage)) { - return false; - } - } break; - - case Command::TransitionTextureUsage: { - TransitionTextureUsageCmd* cmd = - mIterator.NextCommand(); - if (!mState->TransitionTextureUsage(cmd->texture.Get(), cmd->usage)) { - return false; - } - - } break; + default: + HandleError("Command disallowed inside a render pass"); + return false; } } - if (!mState->ValidateEndCommandBuffer()) { - return false; - } - - return true; + HandleError("Unfinished render pass"); + return false; } - CommandIterator CommandBufferBuilder::AcquireCommands() { - ASSERT(!mWereCommandsAcquired); - mWereCommandsAcquired = true; - return std::move(mIterator); - } - - CommandBufferBase* CommandBufferBuilder::GetResultImpl() { - MoveToIterator(); - return mDevice->CreateCommandBuffer(this); - } + // Implementation of the API's command recording methods void CommandBufferBuilder::BeginComputePass() { mAllocator.Allocate(Command::BeginComputePass); @@ -630,11 +690,4 @@ namespace backend { cmd->usage = usage; } - void CommandBufferBuilder::MoveToIterator() { - if (!mWasMovedToIterator) { - mIterator = std::move(mAllocator); - mWasMovedToIterator = true; - } - } - } // namespace backend diff --git a/src/backend/CommandBuffer.h b/src/backend/CommandBuffer.h index 23f17e289b..cf4fec59f6 100644 --- a/src/backend/CommandBuffer.h +++ b/src/backend/CommandBuffer.h @@ -136,6 +136,9 @@ namespace backend { CommandBufferBase* GetResultImpl() override; void MoveToIterator(); + bool ValidateComputePass(); + bool ValidateRenderPass(RenderPassDescriptorBase* renderPass); + std::unique_ptr mState; CommandAllocator mAllocator; CommandIterator mIterator; diff --git a/src/backend/CommandBufferStateTracker.cpp b/src/backend/CommandBufferStateTracker.cpp index 6003a22b11..7ae73aa68f 100644 --- a/src/backend/CommandBufferStateTracker.cpp +++ b/src/backend/CommandBufferStateTracker.cpp @@ -32,18 +32,6 @@ namespace backend { : mBuilder(mBuilder) { } - bool CommandBufferStateTracker::HaveRenderPass() const { - return mCurrentRenderPass != nullptr; - } - - bool CommandBufferStateTracker::ValidateCanCopy() const { - if (mCurrentRenderPass) { - mBuilder->HandleError("Copy cannot occur during a render pass"); - return false; - } - return true; - } - bool CommandBufferStateTracker::ValidateCanUseBufferAs(BufferBase* buffer, nxt::BufferUsageBit usage) const { if (!BufferHasGuaranteedUsageBit(buffer, usage)) { @@ -64,14 +52,13 @@ namespace backend { bool CommandBufferStateTracker::ValidateCanDispatch() { constexpr ValidationAspects requiredAspects = - 1 << VALIDATION_ASPECT_COMPUTE_PIPELINE | // implicitly requires COMPUTE_PASS - 1 << VALIDATION_ASPECT_BIND_GROUPS; + 1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS; if ((requiredAspects & ~mAspects).none()) { // Fast return-true path if everything is good return true; } - if (!mAspects[VALIDATION_ASPECT_COMPUTE_PIPELINE]) { + if (!mAspects[VALIDATION_ASPECT_PIPELINE]) { mBuilder->HandleError("No active compute pipeline"); return false; } @@ -84,10 +71,9 @@ namespace backend { } bool CommandBufferStateTracker::ValidateCanDrawArrays() { - // TODO(kainino@chromium.org): Check for a current render pass - constexpr ValidationAspects requiredAspects = - 1 << VALIDATION_ASPECT_RENDER_PIPELINE | // implicitly requires RENDER_PASS - 1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS; + constexpr ValidationAspects requiredAspects = 1 << VALIDATION_ASPECT_PIPELINE | + 1 << VALIDATION_ASPECT_BIND_GROUPS | + 1 << VALIDATION_ASPECT_VERTEX_BUFFERS; if ((requiredAspects & ~mAspects).none()) { // Fast return-true path if everything is good return true; @@ -97,9 +83,8 @@ namespace backend { } bool CommandBufferStateTracker::ValidateCanDrawElements() { - // TODO(kainino@chromium.org): Check for a current render pass constexpr ValidationAspects requiredAspects = - 1 << VALIDATION_ASPECT_RENDER_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; if ((requiredAspects & ~mAspects).none()) { // Fast return-true path if everything is good @@ -113,72 +98,7 @@ namespace backend { return RevalidateCanDraw(); } - bool CommandBufferStateTracker::ValidateEndCommandBuffer() const { - if (mCurrentRenderPass != nullptr) { - mBuilder->HandleError("Can't end command buffer with an active render pass"); - return false; - } - if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) { - mBuilder->HandleError("Can't end command buffer with an active compute pass"); - return false; - } - return true; - } - - bool CommandBufferStateTracker::ValidateSetPushConstants(nxt::ShaderStageBit stages) { - if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) { - if (stages & ~nxt::ShaderStageBit::Compute) { - mBuilder->HandleError( - "SetPushConstants stage must be compute or 0 in compute passes"); - return false; - } - } else if (mAspects[VALIDATION_ASPECT_RENDER_PASS]) { - if (stages & ~(nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment)) { - mBuilder->HandleError( - "SetPushConstants stage must be a subset if (vertex|fragment) in render " - "passes"); - return false; - } - } else { - mBuilder->HandleError( - "PushConstants must be set in either compute passes or render passes"); - return false; - } - return true; - } - - bool CommandBufferStateTracker::BeginComputePass() { - if (mCurrentRenderPass != nullptr) { - mBuilder->HandleError("Cannot begin a compute pass while a render pass is active"); - return false; - } - mAspects.set(VALIDATION_ASPECT_COMPUTE_PASS); - return true; - } - - bool CommandBufferStateTracker::EndComputePass() { - if (!mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) { - mBuilder->HandleError("Can't end a compute pass without beginning one"); - return false; - } - mAspects.reset(VALIDATION_ASPECT_COMPUTE_PASS); - UnsetPipeline(); - return true; - } - bool CommandBufferStateTracker::BeginRenderPass(RenderPassDescriptorBase* info) { - if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) { - mBuilder->HandleError("Cannot begin a render pass while a compute pass is active"); - return false; - } - if (mCurrentRenderPass != nullptr) { - mBuilder->HandleError("A render pass is already active"); - return false; - } - - mCurrentRenderPass = info; - mAspects.set(VALIDATION_ASPECT_RENDER_PASS); - for (uint32_t i : IterateBitSet(info->GetColorAttachmentMask())) { TextureBase* texture = info->GetColorAttachment(i).view->GetTexture(); if (!EnsureTextureUsage(texture, nxt::TextureUsageBit::OutputAttachment)) { @@ -200,50 +120,21 @@ namespace backend { return true; } - bool CommandBufferStateTracker::EndRenderPass() { - if (mCurrentRenderPass == nullptr) { - mBuilder->HandleError("No render pass is currently active"); - return false; - } - + void CommandBufferStateTracker::EndPass() { // Everything in mTexturesAttached should be for the current render pass. mTexturesAttached.clear(); mInputsSet.reset(); - UnsetPipeline(); - - mAspects.reset(VALIDATION_ASPECT_RENDER_PASS); - mCurrentRenderPass = nullptr; - - return true; + mAspects = 0; + mBindgroups.fill(nullptr); } bool CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) { - if (!mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) { - mBuilder->HandleError("A compute pass must be active when a compute pipeline is set"); - return false; - } - if (mCurrentRenderPass) { - mBuilder->HandleError("Can't use a compute pipeline while a render pass is active"); - return false; - } - - mAspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE); SetPipelineCommon(pipeline); return true; } bool CommandBufferStateTracker::SetRenderPipeline(RenderPipelineBase* pipeline) { - if (!mAspects[VALIDATION_ASPECT_RENDER_PASS]) { - mBuilder->HandleError("A render pass must be active when a render pipeline is set"); - return false; - } - if (!pipeline->IsCompatibleWith(mCurrentRenderPass)) { - mBuilder->HandleError("Pipeline is incompatible with this render pass"); - return false; - } - - mAspects.set(VALIDATION_ASPECT_RENDER_PIPELINE); mLastRenderPipeline = pipeline; SetPipelineCommon(pipeline); return true; @@ -419,9 +310,7 @@ namespace backend { } bool CommandBufferStateTracker::HavePipeline() const { - constexpr ValidationAspects pipelineAspects = - 1 << VALIDATION_ASPECT_COMPUTE_PIPELINE | 1 << VALIDATION_ASPECT_RENDER_PIPELINE; - return (mAspects & pipelineAspects).any(); + return mAspects[VALIDATION_ASPECT_PIPELINE]; } bool CommandBufferStateTracker::ValidateBindGroupUsages(BindGroupBase* group) const { @@ -472,7 +361,7 @@ namespace backend { } bool CommandBufferStateTracker::RevalidateCanDraw() { - if (!mAspects[VALIDATION_ASPECT_RENDER_PIPELINE]) { + if (!mAspects[VALIDATION_ASPECT_PIPELINE]) { mBuilder->HandleError("No active render pipeline"); return false; } @@ -491,6 +380,8 @@ namespace backend { void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) { PipelineLayoutBase* layout = pipeline->GetLayout(); + mAspects.set(VALIDATION_ASPECT_PIPELINE); + mAspects.reset(VALIDATION_ASPECT_BIND_GROUPS); mAspects.reset(VALIDATION_ASPECT_VERTEX_BUFFERS); // Reset bindgroups but mark unused bindgroups as valid @@ -504,12 +395,4 @@ namespace backend { mLastPipeline = pipeline; } - void CommandBufferStateTracker::UnsetPipeline() { - constexpr ValidationAspects pipelineDependentAspects = - 1 << VALIDATION_ASPECT_RENDER_PIPELINE | 1 << VALIDATION_ASPECT_COMPUTE_PIPELINE | - 1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS | - 1 << VALIDATION_ASPECT_INDEX_BUFFER; - mAspects &= ~pipelineDependentAspects; - mBindgroups.fill(nullptr); - } } // namespace backend diff --git a/src/backend/CommandBufferStateTracker.h b/src/backend/CommandBufferStateTracker.h index 85d2901621..3f784f4dea 100644 --- a/src/backend/CommandBufferStateTracker.h +++ b/src/backend/CommandBufferStateTracker.h @@ -29,21 +29,16 @@ namespace backend { explicit CommandBufferStateTracker(CommandBufferBuilder* builder); // Non-state-modifying validation functions - bool HaveRenderPass() const; bool ValidateCanCopy() const; bool ValidateCanUseBufferAs(BufferBase* buffer, nxt::BufferUsageBit usage) const; bool ValidateCanUseTextureAs(TextureBase* texture, nxt::TextureUsageBit usage) const; bool ValidateCanDispatch(); bool ValidateCanDrawArrays(); bool ValidateCanDrawElements(); - bool ValidateEndCommandBuffer() const; - bool ValidateSetPushConstants(nxt::ShaderStageBit stages); // State-modifying methods - bool BeginComputePass(); - bool EndComputePass(); + void EndPass(); bool BeginRenderPass(RenderPassDescriptorBase* info); - bool EndRenderPass(); bool SetComputePipeline(ComputePipelineBase* pipeline); bool SetRenderPipeline(RenderPipelineBase* pipeline); bool SetBindGroup(uint32_t index, BindGroupBase* bindgroup); @@ -58,17 +53,13 @@ namespace backend { // command buffer. std::set mBuffersTransitioned; std::set mTexturesTransitioned; - std::set mTexturesAttached; private: enum ValidationAspect { - VALIDATION_ASPECT_RENDER_PIPELINE, - VALIDATION_ASPECT_COMPUTE_PIPELINE, + VALIDATION_ASPECT_PIPELINE, VALIDATION_ASPECT_BIND_GROUPS, VALIDATION_ASPECT_VERTEX_BUFFERS, VALIDATION_ASPECT_INDEX_BUFFER, - VALIDATION_ASPECT_RENDER_PASS, - VALIDATION_ASPECT_COMPUTE_PASS, VALIDATION_ASPECT_COUNT }; @@ -91,7 +82,6 @@ namespace backend { bool RevalidateCanDraw(); void SetPipelineCommon(PipelineBase* pipeline); - void UnsetPipeline(); CommandBufferBuilder* mBuilder; @@ -105,8 +95,7 @@ namespace backend { std::map mMostRecentBufferUsages; std::map mMostRecentTextureUsages; - - RenderPassDescriptorBase* mCurrentRenderPass = nullptr; + std::set mTexturesAttached; }; } // namespace backend