CommandBuffer: have a state tracker per-pass

Also perform small code simplifications of the CommandBufferStateTracker
now that it only tracks aspects.
This commit is contained in:
Corentin Wallez 2018-07-18 18:23:08 +02:00 committed by Corentin Wallez
parent 1184e46f87
commit 1ea205fb12
4 changed files with 124 additions and 156 deletions

View File

@ -278,8 +278,7 @@ namespace backend {
// CommandBufferBuilder
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device)
: Builder(device), mState(std::make_unique<CommandBufferStateTracker>()) {
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device) {
}
CommandBufferBuilder::~CommandBufferBuilder() {
@ -392,6 +391,7 @@ namespace backend {
MaybeError CommandBufferBuilder::ValidateComputePass() {
PassResourceUsageTracker usageTracker;
CommandBufferStateTracker persistentState;
Command type;
while (mIterator.NextCommandId(&type)) {
@ -401,20 +401,18 @@ namespace backend {
DAWN_TRY(usageTracker.ValidateUsages(PassType::Compute));
mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
mState->EndPass();
return {};
} break;
case Command::Dispatch: {
mIterator.NextCommand<DispatchCmd>();
DAWN_TRY(mState->ValidateCanDispatch());
DAWN_TRY(persistentState.ValidateCanDispatch());
} break;
case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mIterator.NextCommand<SetComputePipelineCmd>();
ComputePipelineBase* pipeline = cmd->pipeline.Get();
mState->SetComputePipeline(pipeline);
persistentState.SetComputePipeline(pipeline);
} break;
case Command::SetPushConstants: {
@ -433,7 +431,7 @@ namespace backend {
SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
mState->SetBindGroup(cmd->index, cmd->group.Get());
persistentState.SetBindGroup(cmd->index, cmd->group.Get());
} break;
default:
@ -446,6 +444,7 @@ namespace backend {
MaybeError CommandBufferBuilder::ValidateRenderPass(RenderPassDescriptorBase* renderPass) {
PassResourceUsageTracker usageTracker;
CommandBufferStateTracker persistentState;
// Track usage of the render pass attachments
for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) {
@ -466,19 +465,17 @@ namespace backend {
DAWN_TRY(usageTracker.ValidateUsages(PassType::Render));
mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
mState->EndPass();
return {};
} break;
case Command::DrawArrays: {
mIterator.NextCommand<DrawArraysCmd>();
DAWN_TRY(mState->ValidateCanDrawArrays());
DAWN_TRY(persistentState.ValidateCanDrawArrays());
} break;
case Command::DrawElements: {
mIterator.NextCommand<DrawElementsCmd>();
DAWN_TRY(mState->ValidateCanDrawElements());
DAWN_TRY(persistentState.ValidateCanDrawElements());
} break;
case Command::SetRenderPipeline: {
@ -489,7 +486,7 @@ namespace backend {
DAWN_RETURN_ERROR("Pipeline is incompatible with this render pass");
}
mState->SetRenderPipeline(pipeline);
persistentState.SetRenderPipeline(pipeline);
} break;
case Command::SetPushConstants: {
@ -522,14 +519,14 @@ namespace backend {
SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
mState->SetBindGroup(cmd->index, cmd->group.Get());
persistentState.SetBindGroup(cmd->index, cmd->group.Get());
} break;
case Command::SetIndexBuffer: {
SetIndexBufferCmd* cmd = mIterator.NextCommand<SetIndexBufferCmd>();
usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index);
DAWN_TRY(mState->SetIndexBuffer());
persistentState.SetIndexBuffer();
} break;
case Command::SetVertexBuffers: {
@ -539,8 +536,8 @@ namespace backend {
for (uint32_t i = 0; i < cmd->count; ++i) {
usageTracker.BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex);
DAWN_TRY(mState->SetVertexBuffer(cmd->startSlot + i));
}
persistentState.SetVertexBuffer(cmd->startSlot, cmd->count);
} break;
default:

View File

@ -31,7 +31,6 @@ namespace backend {
class BindGroupBase;
class BufferBase;
class CommandBufferStateTracker;
class FramebufferBase;
class DeviceBase;
class PipelineBase;
@ -138,7 +137,6 @@ namespace backend {
MaybeError ValidateComputePass();
MaybeError ValidateRenderPass(RenderPassDescriptorBase* renderPass);
std::unique_ptr<CommandBufferStateTracker> mState;
CommandAllocator mAllocator;
CommandIterator mIterator;
bool mWasMovedToIterator = false;

View File

@ -15,69 +15,124 @@
#include "backend/CommandBufferStateTracker.h"
#include "backend/BindGroup.h"
#include "backend/BindGroupLayout.h"
#include "backend/Buffer.h"
#include "backend/ComputePipeline.h"
#include "backend/Forward.h"
#include "backend/InputState.h"
#include "backend/PipelineLayout.h"
#include "backend/RenderPassDescriptor.h"
#include "backend/RenderPipeline.h"
#include "backend/Texture.h"
#include "common/Assert.h"
#include "common/BitSetIterator.h"
namespace backend {
MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
constexpr ValidationAspects requiredAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
if ((requiredAspects & ~mAspects).none()) {
// Fast return-true path if everything is good
return {};
}
enum ValidationAspect {
VALIDATION_ASPECT_PIPELINE,
VALIDATION_ASPECT_BIND_GROUPS,
VALIDATION_ASPECT_VERTEX_BUFFERS,
VALIDATION_ASPECT_INDEX_BUFFER,
if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
DAWN_RETURN_ERROR("No active compute pipeline");
}
// Compute the lazily computed mAspects
if (!RecomputeHaveAspectBindGroups()) {
DAWN_RETURN_ERROR("Bind group state not valid");
}
return {};
VALIDATION_ASPECT_COUNT
};
static_assert(VALIDATION_ASPECT_COUNT == CommandBufferStateTracker::kNumAspects, "");
static constexpr CommandBufferStateTracker::ValidationAspects kDispatchAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
static constexpr CommandBufferStateTracker::ValidationAspects kDrawArraysAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
static constexpr CommandBufferStateTracker::ValidationAspects kDrawElementsAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
static constexpr CommandBufferStateTracker::ValidationAspects kLazyAspects =
1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
return ValidateOperation(kDispatchAspects);
}
MaybeError CommandBufferStateTracker::ValidateCanDrawArrays() {
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 {};
}
return RevalidateCanDraw();
return ValidateOperation(kDrawArraysAspects);
}
MaybeError CommandBufferStateTracker::ValidateCanDrawElements() {
constexpr ValidationAspects requiredAspects =
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
if ((requiredAspects & ~mAspects).none()) {
return ValidateOperation(kDrawElementsAspects);
}
MaybeError CommandBufferStateTracker::ValidateOperation(ValidationAspects requiredAspects) {
// Fast return-true path if everything is good
ValidationAspects missingAspects = requiredAspects & ~mAspects;
if (missingAspects.none()) {
return {};
}
if (!mAspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
DAWN_RETURN_ERROR("Cannot DrawElements without index buffer set");
}
return RevalidateCanDraw();
// Generate an error immediately if a non-lazy aspect is missing as computing lazy aspects
// requires the pipeline to be set.
if ((missingAspects & ~kLazyAspects).any()) {
return GenerateAspectError(missingAspects);
}
void CommandBufferStateTracker::EndPass() {
mInputsSet.reset();
mAspects = 0;
mBindgroups.fill(nullptr);
RecomputeLazyAspects(missingAspects);
missingAspects = requiredAspects & ~mAspects;
if (missingAspects.any()) {
return GenerateAspectError(missingAspects);
}
return {};
}
void CommandBufferStateTracker::RecomputeLazyAspects(ValidationAspects aspects) {
ASSERT(mAspects[VALIDATION_ASPECT_PIPELINE]);
ASSERT((aspects & ~kLazyAspects).none());
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
bool matches = true;
for (uint32_t i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
if (mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout()) {
matches = false;
break;
}
}
if (matches) {
mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
}
}
if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
ASSERT(mLastRenderPipeline != nullptr);
auto requiredInputs = mLastRenderPipeline->GetInputState()->GetInputsSetMask();
if ((mInputsSet & requiredInputs) == requiredInputs) {
mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
}
}
}
MaybeError CommandBufferStateTracker::GenerateAspectError(ValidationAspects aspects) {
ASSERT(aspects.any());
if (aspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
DAWN_RETURN_ERROR("Missing index buffer");
}
if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
DAWN_RETURN_ERROR("Missing vertex buffer");
}
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
DAWN_RETURN_ERROR("Missing bind group");
}
if (aspects[VALIDATION_ASPECT_PIPELINE]) {
DAWN_RETURN_ERROR("Missing pipeline");
}
UNREACHABLE();
}
void CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) {
@ -90,96 +145,26 @@ namespace backend {
}
void CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) {
mBindgroupsSet.set(index);
mBindgroups[index] = bindgroup;
}
MaybeError CommandBufferStateTracker::SetIndexBuffer() {
if (!HavePipeline()) {
DAWN_RETURN_ERROR("Can't set the index buffer without a pipeline");
}
void CommandBufferStateTracker::SetIndexBuffer() {
mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
return {};
}
MaybeError CommandBufferStateTracker::SetVertexBuffer(uint32_t index) {
if (!HavePipeline()) {
DAWN_RETURN_ERROR("Can't set vertex buffers without a pipeline");
void CommandBufferStateTracker::SetVertexBuffer(uint32_t start, uint32_t count) {
for (uint32_t i = 0; i < count; ++i) {
mInputsSet.set(start + i);
}
mInputsSet.set(index);
return {};
}
bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() {
if (mAspects[VALIDATION_ASPECT_BIND_GROUPS]) {
return true;
}
// Assumes we have a pipeline already
if (!mBindgroupsSet.all()) {
return false;
}
for (size_t i = 0; i < mBindgroups.size(); ++i) {
if (auto* bindgroup = mBindgroups[i]) {
// TODO(kainino@chromium.org): bind group compatibility
auto* pipelineBGL = mLastPipeline->GetLayout()->GetBindGroupLayout(i);
if (pipelineBGL && bindgroup->GetLayout() != pipelineBGL) {
return false;
}
}
}
mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
return true;
}
bool CommandBufferStateTracker::RecomputeHaveAspectVertexBuffers() {
if (mAspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
return true;
}
// Assumes we have a pipeline already
auto requiredInputs = mLastRenderPipeline->GetInputState()->GetInputsSetMask();
if ((mInputsSet & requiredInputs) == requiredInputs) {
mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
return true;
}
return false;
}
bool CommandBufferStateTracker::HavePipeline() const {
return mAspects[VALIDATION_ASPECT_PIPELINE];
}
MaybeError CommandBufferStateTracker::RevalidateCanDraw() {
if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
DAWN_RETURN_ERROR("No active render pipeline");
}
// Compute the lazily computed mAspects
if (!RecomputeHaveAspectBindGroups()) {
DAWN_RETURN_ERROR("Bind group state not valid");
}
if (!RecomputeHaveAspectVertexBuffers()) {
DAWN_RETURN_ERROR("Some vertex buffers are not set");
}
return {};
}
void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) {
PipelineLayoutBase* layout = pipeline->GetLayout();
mLastPipelineLayout = 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
mBindgroupsSet = ~layout->GetBindGroupLayoutsMask();
// Only bindgroups that were not the same layout in the last pipeline need to be set again.
if (mLastPipeline) {
mBindgroupsSet |= layout->InheritedGroupsMask(mLastPipeline->GetLayout());
}
mLastPipeline = pipeline;
// Reset lazy aspects so they get recomputed on the next operation.
mAspects &= ~kLazyAspects;
}
} // namespace backend

View File

@ -28,45 +28,33 @@ namespace backend {
class CommandBufferStateTracker {
public:
// Non-state-modifying validation functions
MaybeError ValidateCanCopy() const;
MaybeError ValidateCanDispatch();
MaybeError ValidateCanDrawArrays();
MaybeError ValidateCanDrawElements();
// State-modifying methods
void EndPass();
void SetComputePipeline(ComputePipelineBase* pipeline);
void SetRenderPipeline(RenderPipelineBase* pipeline);
void SetBindGroup(uint32_t index, BindGroupBase* bindgroup);
MaybeError SetIndexBuffer();
MaybeError SetVertexBuffer(uint32_t index);
void SetIndexBuffer();
void SetVertexBuffer(uint32_t start, uint32_t count);
static constexpr size_t kNumAspects = 4;
using ValidationAspects = std::bitset<kNumAspects>;
private:
enum ValidationAspect {
VALIDATION_ASPECT_PIPELINE,
VALIDATION_ASPECT_BIND_GROUPS,
VALIDATION_ASPECT_VERTEX_BUFFERS,
VALIDATION_ASPECT_INDEX_BUFFER,
VALIDATION_ASPECT_COUNT
};
using ValidationAspects = std::bitset<VALIDATION_ASPECT_COUNT>;
// Queries for lazily evaluated aspects
bool RecomputeHaveAspectBindGroups();
bool RecomputeHaveAspectVertexBuffers();
bool HavePipeline() const;
MaybeError RevalidateCanDraw();
MaybeError ValidateOperation(ValidationAspects requiredAspects);
void RecomputeLazyAspects(ValidationAspects aspects);
MaybeError GenerateAspectError(ValidationAspects aspects);
void SetPipelineCommon(PipelineBase* pipeline);
ValidationAspects mAspects;
std::bitset<kMaxBindGroups> mBindgroupsSet;
std::array<BindGroupBase*, kMaxBindGroups> mBindgroups = {};
std::bitset<kMaxVertexInputs> mInputsSet;
PipelineBase* mLastPipeline = nullptr;
PipelineLayoutBase* mLastPipelineLayout = nullptr;
RenderPipelineBase* mLastRenderPipeline = nullptr;
};