Skip validation during command recording when skip_validation is on

This patch factors resource usage tracking so it is separate from
command validation, allowing the bulk of command validation to be
completely skipped.

In DrawCallPerfRun/Vulkan_DynamicPipeline_DynamicBindGroup, disabling
validation cuts roughly 74 nanoseconds (20%) of CPU time, per draw.

In DrawCallPerfRun/Vulkan_DynamicBindGroup, disabling validation
cuts roughly 35 nanoseconds (17%) of CPU time, per draw.

In DrawCallPerfRun/Vulkan_MultipleBindGroups, disabling validation
cuts roughly 45 nanoseconds (14%) of CPU time, per draw.

Bug: dawn:271
Change-Id: I517b85840ba18c6a554b83f34a1d0aef1a8c56a6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13520
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2019-11-21 22:09:41 +00:00 committed by Commit Bot service account
parent 48bf745b96
commit 4b0b7a532a
20 changed files with 291 additions and 276 deletions

View File

@ -116,7 +116,7 @@ namespace dawn_native {
mUsage(descriptor->usage), mUsage(descriptor->usage),
mState(BufferState::Unmapped) { mState(BufferState::Unmapped) {
// Add readonly storage usage if the buffer has a storage usage. The validation rules in // Add readonly storage usage if the buffer has a storage usage. The validation rules in
// PassResourceUsageTracker::ValidateUsages will make sure we don't use both at the same // ValidatePassResourceUsage will make sure we don't use both at the same
// time. // time.
if (mUsage & wgpu::BufferUsage::Storage) { if (mUsage & wgpu::BufferUsage::Storage) {
mUsage |= kReadOnlyStorage; mUsage |= kReadOnlyStorage;

View File

@ -24,7 +24,6 @@
#include "dawn_native/ComputePassEncoder.h" #include "dawn_native/ComputePassEncoder.h"
#include "dawn_native/Device.h" #include "dawn_native/Device.h"
#include "dawn_native/ErrorData.h" #include "dawn_native/ErrorData.h"
#include "dawn_native/PassResourceUsageTracker.h"
#include "dawn_native/RenderPassEncoder.h" #include "dawn_native/RenderPassEncoder.h"
#include "dawn_native/RenderPipeline.h" #include "dawn_native/RenderPipeline.h"
#include "dawn_platform/DawnPlatform.h" #include "dawn_platform/DawnPlatform.h"
@ -243,7 +242,7 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateCanUseAs(BufferBase* buffer, wgpu::BufferUsage usage) { MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage)); ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(buffer->GetUsage() & usage)) { if (!(buffer->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage."); return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
@ -252,7 +251,7 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateCanUseAs(TextureBase* texture, wgpu::TextureUsage usage) { MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage)); ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(texture->GetUsage() & usage)) { if (!(texture->GetUsage() & usage)) {
return DAWN_VALIDATION_ERROR("texture doesn't have the required usage."); return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
@ -467,9 +466,9 @@ namespace dawn_native {
} }
CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() { CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() {
ASSERT(!mWereResourceUsagesAcquired); return CommandBufferResourceUsage{mEncodingContext.AcquirePassUsages(),
mWereResourceUsagesAcquired = true; std::move(mTopLevelBuffers),
return std::move(mResourceUsages); std::move(mTopLevelTextures)};
} }
CommandIterator CommandEncoder::AcquireCommands() { CommandIterator CommandEncoder::AcquireCommands() {
@ -503,6 +502,7 @@ namespace dawn_native {
RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) { RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) {
DeviceBase* device = GetDevice(); DeviceBase* device = GetDevice();
PassResourceUsageTracker usageTracker;
bool success = bool success =
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
uint32_t width = 0; uint32_t width = 0;
@ -520,18 +520,29 @@ namespace dawn_native {
cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor); cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor);
for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) { for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) {
cmd->colorAttachments[i].view = descriptor->colorAttachments[i].attachment; TextureViewBase* view = descriptor->colorAttachments[i].attachment;
cmd->colorAttachments[i].resolveTarget = TextureViewBase* resolveTarget = descriptor->colorAttachments[i].resolveTarget;
descriptor->colorAttachments[i].resolveTarget;
cmd->colorAttachments[i].view = view;
cmd->colorAttachments[i].resolveTarget = resolveTarget;
cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i].loadOp; cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i].loadOp;
cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i].storeOp; cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i].storeOp;
cmd->colorAttachments[i].clearColor = cmd->colorAttachments[i].clearColor =
descriptor->colorAttachments[i].clearColor; descriptor->colorAttachments[i].clearColor;
usageTracker.TextureUsedAs(view->GetTexture(),
wgpu::TextureUsage::OutputAttachment);
if (resolveTarget != nullptr) {
usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
wgpu::TextureUsage::OutputAttachment);
}
} }
if (cmd->attachmentState->HasDepthStencilAttachment()) { if (cmd->attachmentState->HasDepthStencilAttachment()) {
cmd->depthStencilAttachment.view = TextureViewBase* view = descriptor->depthStencilAttachment->attachment;
descriptor->depthStencilAttachment->attachment;
cmd->depthStencilAttachment.view = view;
cmd->depthStencilAttachment.clearDepth = cmd->depthStencilAttachment.clearDepth =
descriptor->depthStencilAttachment->clearDepth; descriptor->depthStencilAttachment->clearDepth;
cmd->depthStencilAttachment.clearStencil = cmd->depthStencilAttachment.clearStencil =
@ -544,6 +555,9 @@ namespace dawn_native {
descriptor->depthStencilAttachment->stencilLoadOp; descriptor->depthStencilAttachment->stencilLoadOp;
cmd->depthStencilAttachment.stencilStoreOp = cmd->depthStencilAttachment.stencilStoreOp =
descriptor->depthStencilAttachment->stencilStoreOp; descriptor->depthStencilAttachment->stencilStoreOp;
usageTracker.TextureUsedAs(view->GetTexture(),
wgpu::TextureUsage::OutputAttachment);
} }
cmd->width = width; cmd->width = width;
@ -553,7 +567,8 @@ namespace dawn_native {
}); });
if (success) { if (success) {
RenderPassEncoder* passEncoder = new RenderPassEncoder(device, this, &mEncodingContext); RenderPassEncoder* passEncoder =
new RenderPassEncoder(device, this, &mEncodingContext, std::move(usageTracker));
mEncodingContext.EnterPass(passEncoder); mEncodingContext.EnterPass(passEncoder);
return passEncoder; return passEncoder;
} }
@ -578,6 +593,10 @@ namespace dawn_native {
copy->destinationOffset = destinationOffset; copy->destinationOffset = destinationOffset;
copy->size = size; copy->size = size;
if (GetDevice()->IsValidationEnabled()) {
mTopLevelBuffers.insert(source);
mTopLevelBuffers.insert(destination);
}
return {}; return {};
}); });
} }
@ -610,6 +629,10 @@ namespace dawn_native {
copy->source.imageHeight = source->imageHeight; copy->source.imageHeight = source->imageHeight;
} }
if (GetDevice()->IsValidationEnabled()) {
mTopLevelBuffers.insert(source->buffer);
mTopLevelTextures.insert(destination->texture);
}
return {}; return {};
}); });
} }
@ -642,6 +665,10 @@ namespace dawn_native {
copy->destination.imageHeight = destination->imageHeight; copy->destination.imageHeight = destination->imageHeight;
} }
if (GetDevice()->IsValidationEnabled()) {
mTopLevelTextures.insert(source->texture);
mTopLevelBuffers.insert(destination->buffer);
}
return {}; return {};
}); });
} }
@ -665,6 +692,10 @@ namespace dawn_native {
copy->destination.arrayLayer = destination->arrayLayer; copy->destination.arrayLayer = destination->arrayLayer;
copy->copySize = *copySize; copy->copySize = *copySize;
if (GetDevice()->IsValidationEnabled()) {
mTopLevelTextures.insert(source->texture);
mTopLevelTextures.insert(destination->texture);
}
return {}; return {};
}); });
} }
@ -704,46 +735,49 @@ namespace dawn_native {
} }
CommandBufferBase* CommandEncoder::Finish(const CommandBufferDescriptor* descriptor) { CommandBufferBase* CommandEncoder::Finish(const CommandBufferDescriptor* descriptor) {
if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) { DeviceBase* device = GetDevice();
// Even if finish validation fails, it is now invalid to call any encoding commands on // Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal
// this object, so we set its state to finished. // state of the encoding context. The internal state is set to finished, and subsequent
return CommandBufferBase::MakeError(GetDevice()); // calls to encode commands will generate errors.
if (device->ConsumedError(mEncodingContext.Finish()) ||
(device->IsValidationEnabled() &&
device->ConsumedError(ValidateFinish(mEncodingContext.GetIterator(),
mEncodingContext.GetPassUsages())))) {
return CommandBufferBase::MakeError(device);
} }
ASSERT(!IsError()); ASSERT(!IsError());
return device->CreateCommandBuffer(this, descriptor);
return GetDevice()->CreateCommandBuffer(this, descriptor);
} }
// Implementation of the command buffer validation that can be precomputed before submit // Implementation of the command buffer validation that can be precomputed before submit
MaybeError CommandEncoder::ValidateFinish(CommandIterator* commands,
MaybeError CommandEncoder::ValidateFinish(const CommandBufferDescriptor*) { const PerPassUsages& perPassUsages) const {
TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish"); TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish");
DAWN_TRY(GetDevice()->ValidateObject(this)); DAWN_TRY(GetDevice()->ValidateObject(this));
// Even if Finish() validation fails, calling it will mutate the internal state of the for (const PassResourceUsage& passUsage : perPassUsages) {
// encoding context. Subsequent calls to encode commands will generate errors. DAWN_TRY(ValidatePassResourceUsage(passUsage));
DAWN_TRY(mEncodingContext.Finish()); }
uint64_t debugGroupStackSize = 0; uint64_t debugGroupStackSize = 0;
CommandIterator* commands = mEncodingContext.GetIterator();
commands->Reset(); commands->Reset();
Command type; Command type;
while (commands->NextCommandId(&type)) { while (commands->NextCommandId(&type)) {
switch (type) { switch (type) {
case Command::BeginComputePass: { case Command::BeginComputePass: {
commands->NextCommand<BeginComputePassCmd>(); commands->NextCommand<BeginComputePassCmd>();
DAWN_TRY(ValidateComputePass(commands, &mResourceUsages.perPass)); DAWN_TRY(ValidateComputePass(commands));
} break; } break;
case Command::BeginRenderPass: { case Command::BeginRenderPass: {
BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>(); const BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
DAWN_TRY(ValidateRenderPass(commands, cmd, &mResourceUsages.perPass)); DAWN_TRY(ValidateRenderPass(commands, cmd));
} break; } break;
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = commands->NextCommand<CopyBufferToBufferCmd>(); const CopyBufferToBufferCmd* copy =
commands->NextCommand<CopyBufferToBufferCmd>();
DAWN_TRY( DAWN_TRY(
ValidateCopySizeFitsInBuffer(copy->source, copy->sourceOffset, copy->size)); ValidateCopySizeFitsInBuffer(copy->source, copy->sourceOffset, copy->size));
@ -754,13 +788,11 @@ namespace dawn_native {
DAWN_TRY(ValidateCanUseAs(copy->source.Get(), wgpu::BufferUsage::CopySrc)); DAWN_TRY(ValidateCanUseAs(copy->source.Get(), wgpu::BufferUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(copy->destination.Get(), wgpu::BufferUsage::CopyDst)); DAWN_TRY(ValidateCanUseAs(copy->destination.Get(), wgpu::BufferUsage::CopyDst));
mResourceUsages.topLevelBuffers.insert(copy->source.Get());
mResourceUsages.topLevelBuffers.insert(copy->destination.Get());
} break; } break;
case Command::CopyBufferToTexture: { case Command::CopyBufferToTexture: {
CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>(); const CopyBufferToTextureCmd* copy =
commands->NextCommand<CopyBufferToTextureCmd>();
DAWN_TRY( DAWN_TRY(
ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get())); ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get()));
@ -789,13 +821,11 @@ namespace dawn_native {
ValidateCanUseAs(copy->source.buffer.Get(), wgpu::BufferUsage::CopySrc)); ValidateCanUseAs(copy->source.buffer.Get(), wgpu::BufferUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(), DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
wgpu::TextureUsage::CopyDst)); wgpu::TextureUsage::CopyDst));
mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get());
mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
} break; } break;
case Command::CopyTextureToBuffer: { case Command::CopyTextureToBuffer: {
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>(); const CopyTextureToBufferCmd* copy =
commands->NextCommand<CopyTextureToBufferCmd>();
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get())); DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get()));
@ -824,13 +854,10 @@ namespace dawn_native {
ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc)); ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(), DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
wgpu::BufferUsage::CopyDst)); wgpu::BufferUsage::CopyDst));
mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
} break; } break;
case Command::CopyTextureToTexture: { case Command::CopyTextureToTexture: {
CopyTextureToTextureCmd* copy = const CopyTextureToTextureCmd* copy =
commands->NextCommand<CopyTextureToTextureCmd>(); commands->NextCommand<CopyTextureToTextureCmd>();
DAWN_TRY(ValidateTextureToTextureCopyRestrictions( DAWN_TRY(ValidateTextureToTextureCopyRestrictions(
@ -852,13 +879,10 @@ namespace dawn_native {
ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc)); ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(), DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
wgpu::TextureUsage::CopyDst)); wgpu::TextureUsage::CopyDst));
mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
} break; } break;
case Command::InsertDebugMarker: { case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>(); const InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
commands->NextData<char>(cmd->length + 1); commands->NextData<char>(cmd->length + 1);
} break; } break;
@ -869,7 +893,7 @@ namespace dawn_native {
} break; } break;
case Command::PushDebugGroup: { case Command::PushDebugGroup: {
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>(); const PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
commands->NextData<char>(cmd->length + 1); commands->NextData<char>(cmd->length + 1);
debugGroupStackSize++; debugGroupStackSize++;
} break; } break;

View File

@ -61,12 +61,12 @@ namespace dawn_native {
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor); CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
private: private:
MaybeError ValidateFinish(const CommandBufferDescriptor* descriptor); MaybeError ValidateFinish(CommandIterator* commands,
const PerPassUsages& perPassUsages) const;
EncodingContext mEncodingContext; EncodingContext mEncodingContext;
std::set<BufferBase*> mTopLevelBuffers;
bool mWereResourceUsagesAcquired = false; std::set<TextureBase*> mTopLevelTextures;
CommandBufferResourceUsage mResourceUsages;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -19,7 +19,7 @@
#include "dawn_native/Buffer.h" #include "dawn_native/Buffer.h"
#include "dawn_native/CommandBufferStateTracker.h" #include "dawn_native/CommandBufferStateTracker.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
#include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/PassResourceUsage.h"
#include "dawn_native/RenderBundle.h" #include "dawn_native/RenderBundle.h"
#include "dawn_native/RenderPipeline.h" #include "dawn_native/RenderPipeline.h"
@ -27,47 +27,8 @@ namespace dawn_native {
namespace { namespace {
void TrackBindGroupResourceUsage(BindGroupBase* group,
PassResourceUsageTracker* usageTracker) {
const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
for (uint32_t i : IterateBitSet(layoutInfo.mask)) {
wgpu::BindingType type = layoutInfo.types[i];
switch (type) {
case wgpu::BindingType::UniformBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform);
} break;
case wgpu::BindingType::StorageBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage);
} break;
case wgpu::BindingType::SampledTexture: {
TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture();
usageTracker->TextureUsedAs(texture, wgpu::TextureUsage::Sampled);
} break;
case wgpu::BindingType::ReadonlyStorageBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, kReadOnlyStorage);
} break;
case wgpu::BindingType::Sampler:
break;
case wgpu::BindingType::StorageTexture:
UNREACHABLE();
break;
}
}
}
inline MaybeError ValidateRenderBundleCommand(CommandIterator* commands, inline MaybeError ValidateRenderBundleCommand(CommandIterator* commands,
Command type, Command type,
PassResourceUsageTracker* usageTracker,
CommandBufferStateTracker* commandBufferState, CommandBufferStateTracker* commandBufferState,
const AttachmentState* attachmentState, const AttachmentState* attachmentState,
uint64_t* debugGroupStackSize, uint64_t* debugGroupStackSize,
@ -84,17 +45,13 @@ namespace dawn_native {
} break; } break;
case Command::DrawIndirect: { case Command::DrawIndirect: {
DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>(); commands->NextCommand<DrawIndirectCmd>();
DAWN_TRY(commandBufferState->ValidateCanDraw()); DAWN_TRY(commandBufferState->ValidateCanDraw());
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
wgpu::BufferUsage::Indirect);
} break; } break;
case Command::DrawIndexedIndirect: { case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>(); commands->NextCommand<DrawIndexedIndirectCmd>();
DAWN_TRY(commandBufferState->ValidateCanDrawIndexed()); DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
wgpu::BufferUsage::Indirect);
} break; } break;
case Command::InsertDebugMarker: { case Command::InsertDebugMarker: {
@ -130,21 +87,16 @@ namespace dawn_native {
commands->NextData<uint32_t>(cmd->dynamicOffsetCount); commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
} }
TrackBindGroupResourceUsage(cmd->group.Get(), usageTracker);
commandBufferState->SetBindGroup(cmd->index, cmd->group.Get()); commandBufferState->SetBindGroup(cmd->index, cmd->group.Get());
} break; } break;
case Command::SetIndexBuffer: { case Command::SetIndexBuffer: {
SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>(); commands->NextCommand<SetIndexBufferCmd>();
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Index);
commandBufferState->SetIndexBuffer(); commandBufferState->SetIndexBuffer();
} break; } break;
case Command::SetVertexBuffer: { case Command::SetVertexBuffer: {
SetVertexBufferCmd* cmd = commands->NextCommand<SetVertexBufferCmd>(); SetVertexBufferCmd* cmd = commands->NextCommand<SetVertexBufferCmd>();
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Vertex);
commandBufferState->SetVertexBuffer(cmd->slot); commandBufferState->SetVertexBuffer(cmd->slot);
} break; } break;
@ -172,63 +124,31 @@ namespace dawn_native {
} }
MaybeError ValidateRenderBundle(CommandIterator* commands, MaybeError ValidateRenderBundle(CommandIterator* commands,
const AttachmentState* attachmentState, const AttachmentState* attachmentState) {
PassResourceUsage* resourceUsage) {
PassResourceUsageTracker usageTracker;
CommandBufferStateTracker commandBufferState; CommandBufferStateTracker commandBufferState;
uint64_t debugGroupStackSize = 0; uint64_t debugGroupStackSize = 0;
Command type; Command type;
while (commands->NextCommandId(&type)) { while (commands->NextCommandId(&type)) {
DAWN_TRY(ValidateRenderBundleCommand(commands, type, &usageTracker, &commandBufferState, DAWN_TRY(ValidateRenderBundleCommand(commands, type, &commandBufferState,
attachmentState, &debugGroupStackSize, attachmentState, &debugGroupStackSize,
"Command disallowed inside a render bundle")); "Command disallowed inside a render bundle"));
} }
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
ASSERT(resourceUsage != nullptr);
*resourceUsage = usageTracker.AcquireResourceUsage();
return {}; return {};
} }
MaybeError ValidateRenderPass(CommandIterator* commands, MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass) {
BeginRenderPassCmd* renderPass,
std::vector<PassResourceUsage>* perPassResourceUsages) {
PassResourceUsageTracker usageTracker;
CommandBufferStateTracker commandBufferState; CommandBufferStateTracker commandBufferState;
uint64_t debugGroupStackSize = 0; uint64_t debugGroupStackSize = 0;
// Track usage of the render pass attachments
for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
RenderPassColorAttachmentInfo* colorAttachment = &renderPass->colorAttachments[i];
TextureBase* texture = colorAttachment->view->GetTexture();
usageTracker.TextureUsedAs(texture, wgpu::TextureUsage::OutputAttachment);
TextureViewBase* resolveTarget = colorAttachment->resolveTarget.Get();
if (resolveTarget != nullptr) {
usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
wgpu::TextureUsage::OutputAttachment);
}
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
TextureBase* texture = renderPass->depthStencilAttachment.view->GetTexture();
usageTracker.TextureUsedAs(texture, wgpu::TextureUsage::OutputAttachment);
}
Command type; Command type;
while (commands->NextCommandId(&type)) { while (commands->NextCommandId(&type)) {
switch (type) { switch (type) {
case Command::EndRenderPass: { case Command::EndRenderPass: {
commands->NextCommand<EndRenderPassCmd>(); commands->NextCommand<EndRenderPassCmd>();
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
ASSERT(perPassResourceUsages != nullptr);
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
return {}; return {};
} break; } break;
@ -241,15 +161,6 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Render bundle is not compatible with render pass"); "Render bundle is not compatible with render pass");
} }
const PassResourceUsage& usages = bundles[i]->GetResourceUsage();
for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
usageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
}
for (uint32_t i = 0; i < usages.textures.size(); ++i) {
usageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
}
} }
if (cmd->count > 0) { if (cmd->count > 0) {
@ -277,9 +188,8 @@ namespace dawn_native {
default: default:
DAWN_TRY(ValidateRenderBundleCommand( DAWN_TRY(ValidateRenderBundleCommand(
commands, type, &usageTracker, &commandBufferState, commands, type, &commandBufferState, renderPass->attachmentState.Get(),
renderPass->attachmentState.Get(), &debugGroupStackSize, &debugGroupStackSize, "Command disallowed inside a render pass"));
"Command disallowed inside a render pass"));
} }
} }
@ -287,9 +197,7 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Unfinished render pass"); return DAWN_VALIDATION_ERROR("Unfinished render pass");
} }
MaybeError ValidateComputePass(CommandIterator* commands, MaybeError ValidateComputePass(CommandIterator* commands) {
std::vector<PassResourceUsage>* perPassResourceUsages) {
PassResourceUsageTracker usageTracker;
CommandBufferStateTracker commandBufferState; CommandBufferStateTracker commandBufferState;
uint64_t debugGroupStackSize = 0; uint64_t debugGroupStackSize = 0;
@ -298,11 +206,7 @@ namespace dawn_native {
switch (type) { switch (type) {
case Command::EndComputePass: { case Command::EndComputePass: {
commands->NextCommand<EndComputePassCmd>(); commands->NextCommand<EndComputePassCmd>();
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
DAWN_TRY(usageTracker.ValidateComputePassUsages());
ASSERT(perPassResourceUsages != nullptr);
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
return {}; return {};
} break; } break;
@ -312,10 +216,8 @@ namespace dawn_native {
} break; } break;
case Command::DispatchIndirect: { case Command::DispatchIndirect: {
DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>(); commands->NextCommand<DispatchIndirectCmd>();
DAWN_TRY(commandBufferState.ValidateCanDispatch()); DAWN_TRY(commandBufferState.ValidateCanDispatch());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
wgpu::BufferUsage::Indirect);
} break; } break;
case Command::InsertDebugMarker: { case Command::InsertDebugMarker: {
@ -346,8 +248,6 @@ namespace dawn_native {
if (cmd->dynamicOffsetCount > 0) { if (cmd->dynamicOffsetCount > 0) {
commands->NextData<uint32_t>(cmd->dynamicOffsetCount); commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
} }
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
commandBufferState.SetBindGroup(cmd->index, cmd->group.Get()); commandBufferState.SetBindGroup(cmd->index, cmd->group.Get());
} break; } break;
@ -360,4 +260,46 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Unfinished compute pass"); return DAWN_VALIDATION_ERROR("Unfinished compute pass");
} }
// Performs the per-pass usage validation checks
// This will eventually need to differentiate between render and compute passes.
// It will be valid to use a buffer both as uniform and storage in the same compute pass.
MaybeError ValidatePassResourceUsage(const PassResourceUsage& pass) {
// Buffers can only be used as single-write or multiple read.
for (size_t i = 0; i < pass.buffers.size(); ++i) {
const BufferBase* buffer = pass.buffers[i];
wgpu::BufferUsage usage = pass.bufferUsages[i];
if (usage & ~buffer->GetUsage()) {
return DAWN_VALIDATION_ERROR("Buffer missing usage for the pass");
}
bool readOnly = (usage & kReadOnlyBufferUsages) == usage;
bool singleUse = wgpu::HasZeroOrOneBits(usage);
if (!readOnly && !singleUse) {
return DAWN_VALIDATION_ERROR(
"Buffer used as writable usage and another usage in pass");
}
}
// Textures can only be used as single-write or multiple read.
// TODO(cwallez@chromium.org): implement per-subresource tracking
for (size_t i = 0; i < pass.textures.size(); ++i) {
const TextureBase* texture = pass.textures[i];
wgpu::TextureUsage usage = pass.textureUsages[i];
if (usage & ~texture->GetUsage()) {
return DAWN_VALIDATION_ERROR("Texture missing usage for the pass");
}
// For textures the only read-only usage in a pass is Sampled, so checking the
// usage constraint simplifies to checking a single usage bit is set.
if (!wgpu::HasZeroOrOneBits(usage)) {
return DAWN_VALIDATION_ERROR("Texture used with more than one usage in pass");
}
}
return {};
}
} // namespace dawn_native } // namespace dawn_native

View File

@ -30,13 +30,11 @@ namespace dawn_native {
MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize); MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize);
MaybeError ValidateRenderBundle(CommandIterator* commands, MaybeError ValidateRenderBundle(CommandIterator* commands,
const AttachmentState* attachmentState, const AttachmentState* attachmentState);
PassResourceUsage* resourceUsage); MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass);
MaybeError ValidateRenderPass(CommandIterator* commands, MaybeError ValidateComputePass(CommandIterator* commands);
BeginRenderPassCmd* renderPass,
std::vector<PassResourceUsage>* perPassResourceUsages); MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage);
MaybeError ValidateComputePass(CommandIterator* commands,
std::vector<PassResourceUsage>* perPassResourceUsages);
} // namespace dawn_native } // namespace dawn_native

View File

@ -48,7 +48,7 @@ namespace dawn_native {
return {}; return {};
})) { })) {
mEncodingContext->ExitPass(this); mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage());
} }
} }
@ -77,6 +77,8 @@ namespace dawn_native {
dispatch->indirectBuffer = indirectBuffer; dispatch->indirectBuffer = indirectBuffer;
dispatch->indirectOffset = indirectOffset; dispatch->indirectOffset = indirectOffset;
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
return {}; return {};
}); });
} }

View File

@ -15,9 +15,11 @@
#include "dawn_native/EncodingContext.h" #include "dawn_native/EncodingContext.h"
#include "common/Assert.h" #include "common/Assert.h"
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/Commands.h" #include "dawn_native/Commands.h"
#include "dawn_native/Device.h" #include "dawn_native/Device.h"
#include "dawn_native/ErrorData.h" #include "dawn_native/ErrorData.h"
#include "dawn_native/RenderBundleEncoder.h"
namespace dawn_native { namespace dawn_native {
@ -32,17 +34,23 @@ namespace dawn_native {
} }
CommandIterator EncodingContext::AcquireCommands() { CommandIterator EncodingContext::AcquireCommands() {
MoveToIterator();
ASSERT(!mWereCommandsAcquired); ASSERT(!mWereCommandsAcquired);
mWereCommandsAcquired = true; mWereCommandsAcquired = true;
return std::move(mIterator); return std::move(mIterator);
} }
CommandIterator* EncodingContext::GetIterator() { CommandIterator* EncodingContext::GetIterator() {
MoveToIterator();
ASSERT(!mWereCommandsAcquired);
return &mIterator;
}
void EncodingContext::MoveToIterator() {
if (!mWasMovedToIterator) { if (!mWasMovedToIterator) {
mIterator = std::move(mAllocator); mIterator = std::move(mAllocator);
mWasMovedToIterator = true; mWasMovedToIterator = true;
} }
return &mIterator;
} }
void EncodingContext::HandleError(wgpu::ErrorType type, const char* message) { void EncodingContext::HandleError(wgpu::ErrorType type, const char* message) {
@ -66,13 +74,25 @@ namespace dawn_native {
mCurrentEncoder = passEncoder; mCurrentEncoder = passEncoder;
} }
void EncodingContext::ExitPass(const ObjectBase* passEncoder) { void EncodingContext::ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsage) {
// Assert we're not at the top level. // Assert we're not at the top level.
ASSERT(mCurrentEncoder != mTopLevelEncoder); ASSERT(mCurrentEncoder != mTopLevelEncoder);
// Assert the pass encoder is current. // Assert the pass encoder is current.
ASSERT(mCurrentEncoder == passEncoder); ASSERT(mCurrentEncoder == passEncoder);
mCurrentEncoder = mTopLevelEncoder; mCurrentEncoder = mTopLevelEncoder;
mPassUsages.push_back(std::move(passUsage));
}
const PerPassUsages& EncodingContext::GetPassUsages() const {
ASSERT(!mWerePassUsagesAcquired);
return mPassUsages;
}
PerPassUsages EncodingContext::AcquirePassUsages() {
ASSERT(!mWerePassUsagesAcquired);
mWerePassUsagesAcquired = true;
return std::move(mPassUsages);
} }
MaybeError EncodingContext::Finish() { MaybeError EncodingContext::Finish() {

View File

@ -18,14 +18,15 @@
#include "dawn_native/CommandAllocator.h" #include "dawn_native/CommandAllocator.h"
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#include "dawn_native/ErrorData.h" #include "dawn_native/ErrorData.h"
#include "dawn_native/PassResourceUsageTracker.h"
#include "dawn_native/dawn_platform.h" #include "dawn_native/dawn_platform.h"
#include <string> #include <string>
namespace dawn_native { namespace dawn_native {
class ObjectBase;
class DeviceBase; class DeviceBase;
class ObjectBase;
// Base class for allocating/iterating commands. // Base class for allocating/iterating commands.
// It performs error tracking as well as encoding state for render/compute passes. // It performs error tracking as well as encoding state for render/compute passes.
@ -54,7 +55,7 @@ namespace dawn_native {
} }
template <typename EncodeFunction> template <typename EncodeFunction>
inline bool TryEncode(const void* encoder, EncodeFunction&& encodeFunction) { inline bool TryEncode(const ObjectBase* encoder, EncodeFunction&& encodeFunction) {
if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) { if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
if (mCurrentEncoder != mTopLevelEncoder) { if (mCurrentEncoder != mTopLevelEncoder) {
// The top level encoder was used when a pass encoder was current. // The top level encoder was used when a pass encoder was current.
@ -72,11 +73,15 @@ namespace dawn_native {
// Functions to set current encoder state // Functions to set current encoder state
void EnterPass(const ObjectBase* passEncoder); void EnterPass(const ObjectBase* passEncoder);
void ExitPass(const ObjectBase* passEncoder); void ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsages);
MaybeError Finish(); MaybeError Finish();
const PerPassUsages& GetPassUsages() const;
PerPassUsages AcquirePassUsages();
private: private:
bool IsFinished() const; bool IsFinished() const;
void MoveToIterator();
DeviceBase* mDevice; DeviceBase* mDevice;
@ -90,6 +95,9 @@ namespace dawn_native {
// CommandEncoder::Begin/EndPass. // CommandEncoder::Begin/EndPass.
const ObjectBase* mCurrentEncoder; const ObjectBase* mCurrentEncoder;
PerPassUsages mPassUsages;
bool mWerePassUsagesAcquired = false;
CommandAllocator mAllocator; CommandAllocator mAllocator;
CommandIterator mIterator; CommandIterator mIterator;
bool mWasMovedToIterator = false; bool mWasMovedToIterator = false;

View File

@ -36,8 +36,10 @@ namespace dawn_native {
std::vector<wgpu::TextureUsage> textureUsages; std::vector<wgpu::TextureUsage> textureUsages;
}; };
using PerPassUsages = std::vector<PassResourceUsage>;
struct CommandBufferResourceUsage { struct CommandBufferResourceUsage {
std::vector<PassResourceUsage> perPass; PerPassUsages perPass;
std::set<BufferBase*> topLevelBuffers; std::set<BufferBase*> topLevelBuffers;
std::set<TextureBase*> topLevelTextures; std::set<TextureBase*> topLevelTextures;
}; };

View File

@ -31,54 +31,6 @@ namespace dawn_native {
mTextureUsages[texture] |= usage; mTextureUsages[texture] |= usage;
} }
MaybeError PassResourceUsageTracker::ValidateComputePassUsages() const {
return ValidateUsages();
}
MaybeError PassResourceUsageTracker::ValidateRenderPassUsages() const {
return ValidateUsages();
}
// Performs the per-pass usage validation checks
MaybeError PassResourceUsageTracker::ValidateUsages() const {
// Buffers can only be used as single-write or multiple read.
for (auto& it : mBufferUsages) {
BufferBase* buffer = it.first;
wgpu::BufferUsage usage = it.second;
if (usage & ~buffer->GetUsage()) {
return DAWN_VALIDATION_ERROR("Buffer missing usage for the pass");
}
bool readOnly = (usage & kReadOnlyBufferUsages) == usage;
bool singleUse = wgpu::HasZeroOrOneBits(usage);
if (!readOnly && !singleUse) {
return DAWN_VALIDATION_ERROR(
"Buffer used as writable usage and another usage in pass");
}
}
// Textures can only be used as single-write or multiple read.
// TODO(cwallez@chromium.org): implement per-subresource tracking
for (auto& it : mTextureUsages) {
TextureBase* texture = it.first;
wgpu::TextureUsage usage = it.second;
if (usage & ~texture->GetUsage()) {
return DAWN_VALIDATION_ERROR("Texture missing usage for the pass");
}
// For textures the only read-only usage in a pass is Sampled, so checking the
// usage constraint simplifies to checking a single usage bit is set.
if (!wgpu::HasZeroOrOneBits(it.second)) {
return DAWN_VALIDATION_ERROR("Texture used with more than one usage in pass");
}
}
return {};
}
// Returns the per-pass usage for use by backends for APIs with explicit barriers. // Returns the per-pass usage for use by backends for APIs with explicit barriers.
PassResourceUsage PassResourceUsageTracker::AcquireResourceUsage() { PassResourceUsage PassResourceUsageTracker::AcquireResourceUsage() {
PassResourceUsage result; PassResourceUsage result;
@ -97,6 +49,9 @@ namespace dawn_native {
result.textureUsages.push_back(it.second); result.textureUsages.push_back(it.second);
} }
mBufferUsages.clear();
mTextureUsages.clear();
return result; return result;
} }

View File

@ -15,7 +15,6 @@
#ifndef DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_ #ifndef DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_
#define DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_ #define DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_
#include "dawn_native/Error.h"
#include "dawn_native/PassResourceUsage.h" #include "dawn_native/PassResourceUsage.h"
#include "dawn_native/dawn_platform.h" #include "dawn_native/dawn_platform.h"
@ -36,16 +35,10 @@ namespace dawn_native {
void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage); void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage);
void TextureUsedAs(TextureBase* texture, wgpu::TextureUsage usage); void TextureUsedAs(TextureBase* texture, wgpu::TextureUsage usage);
MaybeError ValidateComputePassUsages() const;
MaybeError ValidateRenderPassUsages() const;
// Returns the per-pass usage for use by backends for APIs with explicit barriers. // Returns the per-pass usage for use by backends for APIs with explicit barriers.
PassResourceUsage AcquireResourceUsage(); PassResourceUsage AcquireResourceUsage();
private: private:
// Performs the per-pass usage validation checks
MaybeError ValidateUsages() const;
std::map<BufferBase*, wgpu::BufferUsage> mBufferUsages; std::map<BufferBase*, wgpu::BufferUsage> mBufferUsages;
std::map<TextureBase*, wgpu::TextureUsage> mTextureUsages; std::map<TextureBase*, wgpu::TextureUsage> mTextureUsages;
}; };

View File

@ -14,6 +14,7 @@
#include "dawn_native/ProgrammablePassEncoder.h" #include "dawn_native/ProgrammablePassEncoder.h"
#include "common/BitSetIterator.h"
#include "dawn_native/BindGroup.h" #include "dawn_native/BindGroup.h"
#include "dawn_native/Buffer.h" #include "dawn_native/Buffer.h"
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
@ -25,6 +26,46 @@
namespace dawn_native { namespace dawn_native {
namespace {
void TrackBindGroupResourceUsage(PassResourceUsageTracker* usageTracker,
BindGroupBase* group) {
const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
for (uint32_t i : IterateBitSet(layoutInfo.mask)) {
wgpu::BindingType type = layoutInfo.types[i];
switch (type) {
case wgpu::BindingType::UniformBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform);
} break;
case wgpu::BindingType::StorageBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage);
} break;
case wgpu::BindingType::SampledTexture: {
TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture();
usageTracker->TextureUsedAs(texture, wgpu::TextureUsage::Sampled);
} break;
case wgpu::BindingType::ReadonlyStorageBuffer: {
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
usageTracker->BufferUsedAs(buffer, kReadOnlyStorage);
} break;
case wgpu::BindingType::Sampler:
break;
case wgpu::BindingType::StorageTexture:
UNREACHABLE();
break;
}
}
}
} // namespace
ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device,
EncodingContext* encodingContext) EncodingContext* encodingContext)
: ObjectBase(device), mEncodingContext(encodingContext) { : ObjectBase(device), mEncodingContext(encodingContext) {
@ -75,34 +116,36 @@ namespace dawn_native {
uint32_t dynamicOffsetCount, uint32_t dynamicOffsetCount,
const uint32_t* dynamicOffsets) { const uint32_t* dynamicOffsets) {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
DAWN_TRY(GetDevice()->ValidateObject(group)); if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(group));
if (groupIndex >= kMaxBindGroups) { if (groupIndex >= kMaxBindGroups) {
return DAWN_VALIDATION_ERROR("Setting bind group over the max"); return DAWN_VALIDATION_ERROR("Setting bind group over the max");
}
// Dynamic offsets count must match the number required by the layout perfectly.
const BindGroupLayoutBase* layout = group->GetLayout();
if (layout->GetDynamicBufferCount() != dynamicOffsetCount) {
return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch");
}
for (uint32_t i = 0; i < dynamicOffsetCount; ++i) {
if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) {
return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned");
} }
BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); // Dynamic offsets count must match the number required by the layout perfectly.
const BindGroupLayoutBase* layout = group->GetLayout();
if (layout->GetDynamicBufferCount() != dynamicOffsetCount) {
return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch");
}
// During BindGroup creation, validation ensures binding offset + binding size <= for (uint32_t i = 0; i < dynamicOffsetCount; ++i) {
// buffer size. if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) {
DAWN_ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned");
DAWN_ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= }
bufferBinding.offset);
if ((dynamicOffsets[i] > BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i);
bufferBinding.buffer->GetSize() - bufferBinding.offset - bufferBinding.size)) {
return DAWN_VALIDATION_ERROR("dynamic offset out of bounds"); // 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");
}
} }
} }
@ -115,6 +158,8 @@ namespace dawn_native {
memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint32_t)); memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint32_t));
} }
TrackBindGroupResourceUsage(&mUsageTracker, group);
return {}; return {};
}); });
} }

View File

@ -18,6 +18,7 @@
#include "dawn_native/CommandEncoder.h" #include "dawn_native/CommandEncoder.h"
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#include "dawn_native/ObjectBase.h" #include "dawn_native/ObjectBase.h"
#include "dawn_native/PassResourceUsageTracker.h"
#include "dawn_native/dawn_platform.h" #include "dawn_native/dawn_platform.h"
@ -47,6 +48,7 @@ namespace dawn_native {
ErrorTag errorTag); ErrorTag errorTag);
EncodingContext* mEncodingContext = nullptr; EncodingContext* mEncodingContext = nullptr;
PassResourceUsageTracker mUsageTracker;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -35,7 +35,8 @@ namespace dawn_native {
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) { void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
DeviceBase* device = GetDevice(); DeviceBase* device = GetDevice();
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit"); TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
if (device->ConsumedError(ValidateSubmit(commandCount, commands))) { if (device->IsValidationEnabled() &&
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
return; return;
} }
ASSERT(!IsError()); ASSERT(!IsError());

View File

@ -102,26 +102,27 @@ namespace dawn_native {
} }
RenderBundleBase* RenderBundleEncoder::Finish(const RenderBundleDescriptor* descriptor) { RenderBundleBase* RenderBundleEncoder::Finish(const RenderBundleDescriptor* descriptor) {
if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) { PassResourceUsage usages = mUsageTracker.AcquireResourceUsage();
return RenderBundleBase::MakeError(GetDevice());
}
ASSERT(!IsError());
return new RenderBundleBase(this, descriptor, mAttachmentState.Get(), DeviceBase* device = GetDevice();
std::move(mResourceUsage)); // Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal
// state of the encoding context. Subsequent calls to encode commands will generate errors.
if (device->ConsumedError(mEncodingContext.Finish()) ||
(device->IsValidationEnabled() &&
device->ConsumedError(ValidateFinish(mEncodingContext.GetIterator(), usages)))) {
return RenderBundleBase::MakeError(device);
}
ASSERT(!IsError());
return new RenderBundleBase(this, descriptor, mAttachmentState.Get(), std::move(usages));
} }
MaybeError RenderBundleEncoder::ValidateFinish(const RenderBundleDescriptor* descriptor) { MaybeError RenderBundleEncoder::ValidateFinish(CommandIterator* commands,
const PassResourceUsage& usages) const {
TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "RenderBundleEncoder::ValidateFinish"); TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "RenderBundleEncoder::ValidateFinish");
DAWN_TRY(GetDevice()->ValidateObject(this)); DAWN_TRY(GetDevice()->ValidateObject(this));
DAWN_TRY(ValidatePassResourceUsage(usages));
// Even if Finish() validation fails, calling it will mutate the internal state of the DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get()));
// encoding context. Subsequent calls to encode commands will generate errors.
DAWN_TRY(mEncodingContext.Finish());
CommandIterator* commands = mEncodingContext.GetIterator();
DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get(), &mResourceUsage));
return {}; return {};
} }

View File

@ -42,11 +42,10 @@ namespace dawn_native {
private: private:
RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag); RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag);
MaybeError ValidateFinish(const RenderBundleDescriptor* descriptor); MaybeError ValidateFinish(CommandIterator* commands, const PassResourceUsage& usages) const;
EncodingContext mEncodingContext; EncodingContext mEncodingContext;
Ref<AttachmentState> mAttachmentState; Ref<AttachmentState> mAttachmentState;
PassResourceUsage mResourceUsage;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -81,6 +81,8 @@ namespace dawn_native {
cmd->indirectBuffer = indirectBuffer; cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset; cmd->indirectOffset = indirectOffset;
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
return {}; return {};
}); });
} }
@ -100,6 +102,8 @@ namespace dawn_native {
cmd->indirectBuffer = indirectBuffer; cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset; cmd->indirectOffset = indirectOffset;
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
return {}; return {};
}); });
} }
@ -125,6 +129,8 @@ namespace dawn_native {
cmd->buffer = buffer; cmd->buffer = buffer;
cmd->offset = offset; cmd->offset = offset;
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Index);
return {}; return {};
}); });
} }
@ -139,6 +145,8 @@ namespace dawn_native {
cmd->buffer = buffer; cmd->buffer = buffer;
cmd->offset = offset; cmd->offset = offset;
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Vertex);
return {}; return {};
}); });
} }

View File

@ -27,10 +27,15 @@
namespace dawn_native { namespace dawn_native {
// The usage tracker is passed in here, because it is prepopulated with usages from the
// BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
// command, then this wouldn't be necessary.
RenderPassEncoder::RenderPassEncoder(DeviceBase* device, RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,
EncodingContext* encodingContext) EncodingContext* encodingContext,
PassResourceUsageTracker usageTracker)
: RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) { : RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) {
mUsageTracker = std::move(usageTracker);
} }
RenderPassEncoder::RenderPassEncoder(DeviceBase* device, RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
@ -52,7 +57,7 @@ namespace dawn_native {
return {}; return {};
})) { })) {
mEncodingContext->ExitPass(this); mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage());
} }
} }
@ -143,6 +148,14 @@ namespace dawn_native {
Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count); Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count);
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
bundles[i] = renderBundles[i]; bundles[i] = renderBundles[i];
const PassResourceUsage& usages = bundles[i]->GetResourceUsage();
for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
}
for (uint32_t i = 0; i < usages.textures.size(); ++i) {
mUsageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
}
} }
return {}; return {};

View File

@ -26,7 +26,8 @@ namespace dawn_native {
public: public:
RenderPassEncoder(DeviceBase* device, RenderPassEncoder(DeviceBase* device,
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,
EncodingContext* encodingContext); EncodingContext* encodingContext,
PassResourceUsageTracker usageTracker);
static RenderPassEncoder* MakeError(DeviceBase* device, static RenderPassEncoder* MakeError(DeviceBase* device,
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,

View File

@ -605,7 +605,8 @@ TEST_P(DrawCallPerf, Run) {
DAWN_INSTANTIATE_PERF_TEST_SUITE_P( DAWN_INSTANTIATE_PERF_TEST_SUITE_P(
DrawCallPerf, DrawCallPerf,
{D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend}, {D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend,
ForceWorkarounds(VulkanBackend, {"skip_validation"})},
{ {
// Baseline // Baseline
MakeParam(), MakeParam(),