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 <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2019-07-24 18:15:24 +00:00 committed by Commit Bot service account
parent 8944f0205b
commit fde94905fe
14 changed files with 691 additions and 597 deletions

View File

@ -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",

View File

@ -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<BeginComputePassCmd>(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<BeginComputePassCmd>(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<BeginRenderPassCmd>(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<BeginRenderPassCmd>(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<CopyBufferToBufferCmd>(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<CopyBufferToBufferCmd>(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<CopyBufferToTextureCmd>(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<CopyBufferToTextureCmd>(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<CopyTextureToBufferCmd>(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<CopyTextureToBufferCmd>(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<CopyTextureToTextureCmd>(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<CopyTextureToTextureCmd>(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<EndComputePassCmd>(Command::EndComputePass);
} else {
ASSERT(mEncodingState == EncodingState::RenderPass);
mAllocator.Allocate<EndRenderPassCmd>(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<BeginComputePassCmd>();
DAWN_TRY(ValidateComputePass());
commands->NextCommand<BeginComputePassCmd>();
DAWN_TRY(ValidateComputePass(commands));
} break;
case Command::BeginRenderPass: {
BeginRenderPassCmd* cmd = mIterator.NextCommand<BeginRenderPassCmd>();
DAWN_TRY(ValidateRenderPass(cmd));
BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
DAWN_TRY(ValidateRenderPass(commands, cmd));
} break;
case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mIterator.NextCommand<CopyBufferToBufferCmd>();
CopyBufferToBufferCmd* copy = commands->NextCommand<CopyBufferToBufferCmd>();
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>();
CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>();
DAWN_TRY(
ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get()));
@ -994,7 +925,7 @@ namespace dawn_native {
} break;
case Command::CopyTextureToBuffer: {
CopyTextureToBufferCmd* copy = mIterator.NextCommand<CopyTextureToBufferCmd>();
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get()));
@ -1030,7 +961,7 @@ namespace dawn_native {
case Command::CopyTextureToTexture: {
CopyTextureToTextureCmd* copy =
mIterator.NextCommand<CopyTextureToTextureCmd>();
commands->NextCommand<CopyTextureToTextureCmd>();
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<EndComputePassCmd>();
commands->NextCommand<EndComputePassCmd>();
DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize));
@ -1082,43 +1013,43 @@ namespace dawn_native {
} break;
case Command::Dispatch: {
mIterator.NextCommand<DispatchCmd>();
commands->NextCommand<DispatchCmd>();
DAWN_TRY(persistentState.ValidateCanDispatch());
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* cmd = mIterator.NextCommand<DispatchIndirectCmd>();
DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDispatch());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mIterator.NextCommand<InsertDebugMarkerCmd>();
mIterator.NextData<char>(cmd->length + 1);
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
commands->NextData<char>(cmd->length + 1);
} break;
case Command::PopDebugGroup: {
mIterator.NextCommand<PopDebugGroupCmd>();
commands->NextCommand<PopDebugGroupCmd>();
DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize));
} break;
case Command::PushDebugGroup: {
PushDebugGroupCmd* cmd = mIterator.NextCommand<PushDebugGroupCmd>();
mIterator.NextData<char>(cmd->length + 1);
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
commands->NextData<char>(cmd->length + 1);
DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize));
} break;
case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mIterator.NextCommand<SetComputePipelineCmd>();
SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
ComputePipelineBase* pipeline = cmd->pipeline.Get();
persistentState.SetComputePipeline(pipeline);
} break;
case Command::SetBindGroup: {
SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
if (cmd->dynamicOffsetCount > 0) {
mIterator.NextData<uint64_t>(cmd->dynamicOffsetCount);
commands->NextData<uint64_t>(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<EndRenderPassCmd>();
commands->NextCommand<EndRenderPassCmd>();
DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize));
@ -1170,47 +1102,47 @@ namespace dawn_native {
} break;
case Command::Draw: {
mIterator.NextCommand<DrawCmd>();
commands->NextCommand<DrawCmd>();
DAWN_TRY(persistentState.ValidateCanDraw());
} break;
case Command::DrawIndexed: {
mIterator.NextCommand<DrawIndexedCmd>();
commands->NextCommand<DrawIndexedCmd>();
DAWN_TRY(persistentState.ValidateCanDrawIndexed());
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* cmd = mIterator.NextCommand<DrawIndirectCmd>();
DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDraw());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* cmd = mIterator.NextCommand<DrawIndexedIndirectCmd>();
DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDrawIndexed());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mIterator.NextCommand<InsertDebugMarkerCmd>();
mIterator.NextData<char>(cmd->length + 1);
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
commands->NextData<char>(cmd->length + 1);
} break;
case Command::PopDebugGroup: {
mIterator.NextCommand<PopDebugGroupCmd>();
commands->NextCommand<PopDebugGroupCmd>();
DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize));
} break;
case Command::PushDebugGroup: {
PushDebugGroupCmd* cmd = mIterator.NextCommand<PushDebugGroupCmd>();
mIterator.NextData<char>(cmd->length + 1);
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
commands->NextData<char>(cmd->length + 1);
DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize));
} break;
case Command::SetRenderPipeline: {
SetRenderPipelineCmd* cmd = mIterator.NextCommand<SetRenderPipelineCmd>();
SetRenderPipelineCmd* cmd = commands->NextCommand<SetRenderPipelineCmd>();
RenderPipelineBase* pipeline = cmd->pipeline.Get();
DAWN_TRY(pipeline->ValidateCompatibleWith(renderPass));
@ -1218,25 +1150,25 @@ namespace dawn_native {
} break;
case Command::SetStencilReference: {
mIterator.NextCommand<SetStencilReferenceCmd>();
commands->NextCommand<SetStencilReferenceCmd>();
} break;
case Command::SetBlendColor: {
mIterator.NextCommand<SetBlendColorCmd>();
commands->NextCommand<SetBlendColorCmd>();
} break;
case Command::SetViewport: {
mIterator.NextCommand<SetViewportCmd>();
commands->NextCommand<SetViewportCmd>();
} break;
case Command::SetScissorRect: {
mIterator.NextCommand<SetScissorRectCmd>();
commands->NextCommand<SetScissorRectCmd>();
} break;
case Command::SetBindGroup: {
SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
if (cmd->dynamicOffsetCount > 0) {
mIterator.NextData<uint64_t>(cmd->dynamicOffsetCount);
commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
}
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
@ -1244,16 +1176,16 @@ namespace dawn_native {
} break;
case Command::SetIndexBuffer: {
SetIndexBufferCmd* cmd = mIterator.NextCommand<SetIndexBufferCmd>();
SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index);
persistentState.SetIndexBuffer();
} break;
case Command::SetVertexBuffers: {
SetVertexBuffersCmd* cmd = mIterator.NextCommand<SetVertexBuffersCmd>();
auto buffers = mIterator.NextData<Ref<BufferBase>>(cmd->count);
mIterator.NextData<uint64_t>(cmd->count);
SetVertexBuffersCmd* cmd = commands->NextCommand<SetVertexBuffersCmd>();
auto buffers = commands->NextData<Ref<BufferBase>>(cmd->count);
commands->NextData<uint64_t>(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

View File

@ -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

View File

@ -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<EndComputePassCmd>(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<DispatchCmd>(Command::Dispatch);
dispatch->x = x;
dispatch->y = y;
dispatch->z = z;
DispatchCmd* dispatch = mAllocator->Allocate<DispatchCmd>(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<DispatchIndirectCmd>(Command::DispatchIndirect);
dispatch->indirectBuffer = indirectBuffer;
dispatch->indirectOffset = indirectOffset;
DispatchIndirectCmd* dispatch =
allocator->Allocate<DispatchIndirectCmd>(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<SetComputePipelineCmd>(Command::SetComputePipeline);
cmd->pipeline = pipeline;
SetComputePipelineCmd* cmd =
allocator->Allocate<SetComputePipelineCmd>(Command::SetComputePipeline);
cmd->pipeline = pipeline;
return {};
});
}
} // namespace dawn_native

View File

@ -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<CommandEncoderBase> mCommandEncoder;
};
} // namespace dawn_native

View File

@ -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

View File

@ -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 <string>
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 <typename EncodeFunction>
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_

View File

@ -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<InsertDebugMarkerCmd>(Command::InsertDebugMarker);
cmd->length = strlen(groupLabel);
InsertDebugMarkerCmd* cmd =
mAllocator->Allocate<InsertDebugMarkerCmd>(Command::InsertDebugMarker);
cmd->length = strlen(groupLabel);
char* label = allocator->AllocateData<char>(cmd->length + 1);
memcpy(label, groupLabel, cmd->length + 1);
char* label = mAllocator->AllocateData<char>(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<PopDebugGroupCmd>(Command::PopDebugGroup);
mAllocator->Allocate<PopDebugGroupCmd>(Command::PopDebugGroup);
return {};
});
}
void ProgrammablePassEncoder::PushDebugGroup(const char* groupLabel) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) {
return;
}
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
PushDebugGroupCmd* cmd =
allocator->Allocate<PushDebugGroupCmd>(Command::PushDebugGroup);
cmd->length = strlen(groupLabel);
PushDebugGroupCmd* cmd = mAllocator->Allocate<PushDebugGroupCmd>(Command::PushDebugGroup);
cmd->length = strlen(groupLabel);
char* label = allocator->AllocateData<char>(cmd->length + 1);
memcpy(label, groupLabel, cmd->length + 1);
char* label = mAllocator->AllocateData<char>(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<SetBindGroupCmd>(Command::SetBindGroup);
cmd->index = groupIndex;
cmd->group = group;
cmd->dynamicOffsetCount = dynamicOffsetCount;
if (dynamicOffsetCount > 0) {
uint64_t* offsets = mAllocator->AllocateData<uint64_t>(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<SetBindGroupCmd>(Command::SetBindGroup);
cmd->index = groupIndex;
cmd->group = group;
cmd->dynamicOffsetCount = dynamicOffsetCount;
if (dynamicOffsetCount > 0) {
uint64_t* offsets = allocator->AllocateData<uint64_t>(cmd->dynamicOffsetCount);
memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint64_t));
}
return {};
});
}
} // namespace dawn_native

View File

@ -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<CommandEncoderBase> 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

View File

@ -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<DrawCmd>(Command::Draw);
draw->vertexCount = vertexCount;
draw->instanceCount = instanceCount;
draw->firstVertex = firstVertex;
draw->firstInstance = firstInstance;
DrawCmd* draw = mAllocator->Allocate<DrawCmd>(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<DrawIndexedCmd>(Command::DrawIndexed);
draw->indexCount = indexCount;
draw->instanceCount = instanceCount;
draw->firstIndex = firstIndex;
draw->baseVertex = baseVertex;
draw->firstInstance = firstInstance;
DrawIndexedCmd* draw = mAllocator->Allocate<DrawIndexedCmd>(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<DrawIndirectCmd>(Command::DrawIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
DrawIndirectCmd* cmd = allocator->Allocate<DrawIndirectCmd>(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<DrawIndexedIndirectCmd>(Command::DrawIndexedIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
DrawIndexedIndirectCmd* cmd =
allocator->Allocate<DrawIndexedIndirectCmd>(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<SetRenderPipelineCmd>(Command::SetRenderPipeline);
cmd->pipeline = pipeline;
SetRenderPipelineCmd* cmd =
allocator->Allocate<SetRenderPipelineCmd>(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<SetIndexBufferCmd>(Command::SetIndexBuffer);
cmd->buffer = buffer;
cmd->offset = offset;
SetIndexBufferCmd* cmd =
allocator->Allocate<SetIndexBufferCmd>(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<SetVertexBuffersCmd>(Command::SetVertexBuffers);
cmd->startSlot = startSlot;
cmd->count = count;
SetVertexBuffersCmd* cmd =
allocator->Allocate<SetVertexBuffersCmd>(Command::SetVertexBuffers);
cmd->startSlot = startSlot;
cmd->count = count;
Ref<BufferBase>* cmdBuffers = mAllocator->AllocateData<Ref<BufferBase>>(count);
for (size_t i = 0; i < count; ++i) {
cmdBuffers[i] = buffers[i];
}
Ref<BufferBase>* cmdBuffers = allocator->AllocateData<Ref<BufferBase>>(count);
for (size_t i = 0; i < count; ++i) {
cmdBuffers[i] = buffers[i];
}
uint64_t* cmdOffsets = mAllocator->AllocateData<uint64_t>(count);
memcpy(cmdOffsets, offsets, count * sizeof(uint64_t));
uint64_t* cmdOffsets = allocator->AllocateData<uint64_t>(count);
memcpy(cmdOffsets, offsets, count * sizeof(uint64_t));
return {};
});
}
} // namespace dawn_native

View File

@ -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

View File

@ -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<EndRenderPassCmd>(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<SetStencilReferenceCmd>(Command::SetStencilReference);
cmd->reference = reference;
SetStencilReferenceCmd* cmd =
mAllocator->Allocate<SetStencilReferenceCmd>(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<SetBlendColorCmd>(Command::SetBlendColor);
cmd->color = *color;
SetBlendColorCmd* cmd = mAllocator->Allocate<SetBlendColorCmd>(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<SetViewportCmd>(Command::SetViewport);
cmd->x = x;
cmd->y = y;
cmd->width = width;
cmd->height = height;
cmd->minDepth = minDepth;
cmd->maxDepth = maxDepth;
SetViewportCmd* cmd = mAllocator->Allocate<SetViewportCmd>(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<SetScissorRectCmd>(Command::SetScissorRect);
cmd->x = x;
cmd->y = y;
cmd->width = width;
cmd->height = height;
SetScissorRectCmd* cmd =
allocator->Allocate<SetScissorRectCmd>(Command::SetScissorRect);
cmd->x = x;
cmd->y = y;
cmd->width = width;
cmd->height = height;
return {};
});
}
} // namespace dawn_native

View File

@ -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<CommandEncoderBase> mCommandEncoder;
};
} // namespace dawn_native

View File

@ -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());
}