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:
parent
48bf745b96
commit
4b0b7a532a
|
@ -116,7 +116,7 @@ namespace dawn_native {
|
|||
mUsage(descriptor->usage),
|
||||
mState(BufferState::Unmapped) {
|
||||
// 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.
|
||||
if (mUsage & wgpu::BufferUsage::Storage) {
|
||||
mUsage |= kReadOnlyStorage;
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "dawn_native/ComputePassEncoder.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/ErrorData.h"
|
||||
#include "dawn_native/PassResourceUsageTracker.h"
|
||||
#include "dawn_native/RenderPassEncoder.h"
|
||||
#include "dawn_native/RenderPipeline.h"
|
||||
#include "dawn_platform/DawnPlatform.h"
|
||||
|
@ -243,7 +242,7 @@ namespace dawn_native {
|
|||
return {};
|
||||
}
|
||||
|
||||
MaybeError ValidateCanUseAs(BufferBase* buffer, wgpu::BufferUsage usage) {
|
||||
MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
|
||||
ASSERT(wgpu::HasZeroOrOneBits(usage));
|
||||
if (!(buffer->GetUsage() & usage)) {
|
||||
return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
|
||||
|
@ -252,7 +251,7 @@ namespace dawn_native {
|
|||
return {};
|
||||
}
|
||||
|
||||
MaybeError ValidateCanUseAs(TextureBase* texture, wgpu::TextureUsage usage) {
|
||||
MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
|
||||
ASSERT(wgpu::HasZeroOrOneBits(usage));
|
||||
if (!(texture->GetUsage() & usage)) {
|
||||
return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
|
||||
|
@ -467,9 +466,9 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() {
|
||||
ASSERT(!mWereResourceUsagesAcquired);
|
||||
mWereResourceUsagesAcquired = true;
|
||||
return std::move(mResourceUsages);
|
||||
return CommandBufferResourceUsage{mEncodingContext.AcquirePassUsages(),
|
||||
std::move(mTopLevelBuffers),
|
||||
std::move(mTopLevelTextures)};
|
||||
}
|
||||
|
||||
CommandIterator CommandEncoder::AcquireCommands() {
|
||||
|
@ -503,6 +502,7 @@ namespace dawn_native {
|
|||
RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) {
|
||||
DeviceBase* device = GetDevice();
|
||||
|
||||
PassResourceUsageTracker usageTracker;
|
||||
bool success =
|
||||
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||
uint32_t width = 0;
|
||||
|
@ -520,18 +520,29 @@ namespace dawn_native {
|
|||
cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor);
|
||||
|
||||
for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) {
|
||||
cmd->colorAttachments[i].view = descriptor->colorAttachments[i].attachment;
|
||||
cmd->colorAttachments[i].resolveTarget =
|
||||
descriptor->colorAttachments[i].resolveTarget;
|
||||
TextureViewBase* view = descriptor->colorAttachments[i].attachment;
|
||||
TextureViewBase* 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].storeOp = descriptor->colorAttachments[i].storeOp;
|
||||
cmd->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()) {
|
||||
cmd->depthStencilAttachment.view =
|
||||
descriptor->depthStencilAttachment->attachment;
|
||||
TextureViewBase* view = descriptor->depthStencilAttachment->attachment;
|
||||
|
||||
cmd->depthStencilAttachment.view = view;
|
||||
cmd->depthStencilAttachment.clearDepth =
|
||||
descriptor->depthStencilAttachment->clearDepth;
|
||||
cmd->depthStencilAttachment.clearStencil =
|
||||
|
@ -544,6 +555,9 @@ namespace dawn_native {
|
|||
descriptor->depthStencilAttachment->stencilLoadOp;
|
||||
cmd->depthStencilAttachment.stencilStoreOp =
|
||||
descriptor->depthStencilAttachment->stencilStoreOp;
|
||||
|
||||
usageTracker.TextureUsedAs(view->GetTexture(),
|
||||
wgpu::TextureUsage::OutputAttachment);
|
||||
}
|
||||
|
||||
cmd->width = width;
|
||||
|
@ -553,7 +567,8 @@ namespace dawn_native {
|
|||
});
|
||||
|
||||
if (success) {
|
||||
RenderPassEncoder* passEncoder = new RenderPassEncoder(device, this, &mEncodingContext);
|
||||
RenderPassEncoder* passEncoder =
|
||||
new RenderPassEncoder(device, this, &mEncodingContext, std::move(usageTracker));
|
||||
mEncodingContext.EnterPass(passEncoder);
|
||||
return passEncoder;
|
||||
}
|
||||
|
@ -578,6 +593,10 @@ namespace dawn_native {
|
|||
copy->destinationOffset = destinationOffset;
|
||||
copy->size = size;
|
||||
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
mTopLevelBuffers.insert(source);
|
||||
mTopLevelBuffers.insert(destination);
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -610,6 +629,10 @@ namespace dawn_native {
|
|||
copy->source.imageHeight = source->imageHeight;
|
||||
}
|
||||
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
mTopLevelBuffers.insert(source->buffer);
|
||||
mTopLevelTextures.insert(destination->texture);
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -642,6 +665,10 @@ namespace dawn_native {
|
|||
copy->destination.imageHeight = destination->imageHeight;
|
||||
}
|
||||
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
mTopLevelTextures.insert(source->texture);
|
||||
mTopLevelBuffers.insert(destination->buffer);
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -665,6 +692,10 @@ namespace dawn_native {
|
|||
copy->destination.arrayLayer = destination->arrayLayer;
|
||||
copy->copySize = *copySize;
|
||||
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
mTopLevelTextures.insert(source->texture);
|
||||
mTopLevelTextures.insert(destination->texture);
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -704,46 +735,49 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
CommandBufferBase* CommandEncoder::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.
|
||||
return CommandBufferBase::MakeError(GetDevice());
|
||||
DeviceBase* device = GetDevice();
|
||||
// Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal
|
||||
// state of the encoding context. The internal state is set to finished, and subsequent
|
||||
// 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());
|
||||
|
||||
return GetDevice()->CreateCommandBuffer(this, descriptor);
|
||||
return device->CreateCommandBuffer(this, descriptor);
|
||||
}
|
||||
|
||||
// Implementation of the command buffer validation that can be precomputed before submit
|
||||
|
||||
MaybeError CommandEncoder::ValidateFinish(const CommandBufferDescriptor*) {
|
||||
MaybeError CommandEncoder::ValidateFinish(CommandIterator* commands,
|
||||
const PerPassUsages& perPassUsages) const {
|
||||
TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish");
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
||||
// 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());
|
||||
for (const PassResourceUsage& passUsage : perPassUsages) {
|
||||
DAWN_TRY(ValidatePassResourceUsage(passUsage));
|
||||
}
|
||||
|
||||
uint64_t debugGroupStackSize = 0;
|
||||
|
||||
CommandIterator* commands = mEncodingContext.GetIterator();
|
||||
commands->Reset();
|
||||
|
||||
Command type;
|
||||
while (commands->NextCommandId(&type)) {
|
||||
switch (type) {
|
||||
case Command::BeginComputePass: {
|
||||
commands->NextCommand<BeginComputePassCmd>();
|
||||
DAWN_TRY(ValidateComputePass(commands, &mResourceUsages.perPass));
|
||||
DAWN_TRY(ValidateComputePass(commands));
|
||||
} break;
|
||||
|
||||
case Command::BeginRenderPass: {
|
||||
BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
|
||||
DAWN_TRY(ValidateRenderPass(commands, cmd, &mResourceUsages.perPass));
|
||||
const BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
|
||||
DAWN_TRY(ValidateRenderPass(commands, cmd));
|
||||
} break;
|
||||
|
||||
case Command::CopyBufferToBuffer: {
|
||||
CopyBufferToBufferCmd* copy = commands->NextCommand<CopyBufferToBufferCmd>();
|
||||
const CopyBufferToBufferCmd* copy =
|
||||
commands->NextCommand<CopyBufferToBufferCmd>();
|
||||
|
||||
DAWN_TRY(
|
||||
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->destination.Get(), wgpu::BufferUsage::CopyDst));
|
||||
|
||||
mResourceUsages.topLevelBuffers.insert(copy->source.Get());
|
||||
mResourceUsages.topLevelBuffers.insert(copy->destination.Get());
|
||||
} break;
|
||||
|
||||
case Command::CopyBufferToTexture: {
|
||||
CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>();
|
||||
const CopyBufferToTextureCmd* copy =
|
||||
commands->NextCommand<CopyBufferToTextureCmd>();
|
||||
|
||||
DAWN_TRY(
|
||||
ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get()));
|
||||
|
@ -789,13 +821,11 @@ namespace dawn_native {
|
|||
ValidateCanUseAs(copy->source.buffer.Get(), wgpu::BufferUsage::CopySrc));
|
||||
DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
|
||||
wgpu::TextureUsage::CopyDst));
|
||||
|
||||
mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get());
|
||||
mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
|
||||
} break;
|
||||
|
||||
case Command::CopyTextureToBuffer: {
|
||||
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
|
||||
const CopyTextureToBufferCmd* copy =
|
||||
commands->NextCommand<CopyTextureToBufferCmd>();
|
||||
|
||||
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get()));
|
||||
|
||||
|
@ -824,13 +854,10 @@ namespace dawn_native {
|
|||
ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
|
||||
DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
|
||||
wgpu::BufferUsage::CopyDst));
|
||||
|
||||
mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
|
||||
mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
|
||||
} break;
|
||||
|
||||
case Command::CopyTextureToTexture: {
|
||||
CopyTextureToTextureCmd* copy =
|
||||
const CopyTextureToTextureCmd* copy =
|
||||
commands->NextCommand<CopyTextureToTextureCmd>();
|
||||
|
||||
DAWN_TRY(ValidateTextureToTextureCopyRestrictions(
|
||||
|
@ -852,13 +879,10 @@ namespace dawn_native {
|
|||
ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
|
||||
DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
|
||||
wgpu::TextureUsage::CopyDst));
|
||||
|
||||
mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
|
||||
mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
|
||||
} break;
|
||||
|
||||
case Command::InsertDebugMarker: {
|
||||
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||
const InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||
commands->NextData<char>(cmd->length + 1);
|
||||
} break;
|
||||
|
||||
|
@ -869,7 +893,7 @@ namespace dawn_native {
|
|||
} break;
|
||||
|
||||
case Command::PushDebugGroup: {
|
||||
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
|
||||
const PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
|
||||
commands->NextData<char>(cmd->length + 1);
|
||||
debugGroupStackSize++;
|
||||
} break;
|
||||
|
|
|
@ -61,12 +61,12 @@ namespace dawn_native {
|
|||
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
|
||||
|
||||
private:
|
||||
MaybeError ValidateFinish(const CommandBufferDescriptor* descriptor);
|
||||
MaybeError ValidateFinish(CommandIterator* commands,
|
||||
const PerPassUsages& perPassUsages) const;
|
||||
|
||||
EncodingContext mEncodingContext;
|
||||
|
||||
bool mWereResourceUsagesAcquired = false;
|
||||
CommandBufferResourceUsage mResourceUsages;
|
||||
std::set<BufferBase*> mTopLevelBuffers;
|
||||
std::set<TextureBase*> mTopLevelTextures;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/CommandBufferStateTracker.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/PassResourceUsageTracker.h"
|
||||
#include "dawn_native/PassResourceUsage.h"
|
||||
#include "dawn_native/RenderBundle.h"
|
||||
#include "dawn_native/RenderPipeline.h"
|
||||
|
||||
|
@ -27,47 +27,8 @@ namespace dawn_native {
|
|||
|
||||
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,
|
||||
Command type,
|
||||
PassResourceUsageTracker* usageTracker,
|
||||
CommandBufferStateTracker* commandBufferState,
|
||||
const AttachmentState* attachmentState,
|
||||
uint64_t* debugGroupStackSize,
|
||||
|
@ -84,17 +45,13 @@ namespace dawn_native {
|
|||
} break;
|
||||
|
||||
case Command::DrawIndirect: {
|
||||
DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>();
|
||||
commands->NextCommand<DrawIndirectCmd>();
|
||||
DAWN_TRY(commandBufferState->ValidateCanDraw());
|
||||
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
|
||||
wgpu::BufferUsage::Indirect);
|
||||
} break;
|
||||
|
||||
case Command::DrawIndexedIndirect: {
|
||||
DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>();
|
||||
commands->NextCommand<DrawIndexedIndirectCmd>();
|
||||
DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
|
||||
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
|
||||
wgpu::BufferUsage::Indirect);
|
||||
} break;
|
||||
|
||||
case Command::InsertDebugMarker: {
|
||||
|
@ -130,21 +87,16 @@ namespace dawn_native {
|
|||
commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
|
||||
}
|
||||
|
||||
TrackBindGroupResourceUsage(cmd->group.Get(), usageTracker);
|
||||
commandBufferState->SetBindGroup(cmd->index, cmd->group.Get());
|
||||
} break;
|
||||
|
||||
case Command::SetIndexBuffer: {
|
||||
SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
|
||||
|
||||
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Index);
|
||||
commands->NextCommand<SetIndexBufferCmd>();
|
||||
commandBufferState->SetIndexBuffer();
|
||||
} break;
|
||||
|
||||
case Command::SetVertexBuffer: {
|
||||
SetVertexBufferCmd* cmd = commands->NextCommand<SetVertexBufferCmd>();
|
||||
|
||||
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Vertex);
|
||||
commandBufferState->SetVertexBuffer(cmd->slot);
|
||||
} break;
|
||||
|
||||
|
@ -172,63 +124,31 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
MaybeError ValidateRenderBundle(CommandIterator* commands,
|
||||
const AttachmentState* attachmentState,
|
||||
PassResourceUsage* resourceUsage) {
|
||||
PassResourceUsageTracker usageTracker;
|
||||
const AttachmentState* attachmentState) {
|
||||
CommandBufferStateTracker commandBufferState;
|
||||
uint64_t debugGroupStackSize = 0;
|
||||
|
||||
Command type;
|
||||
while (commands->NextCommandId(&type)) {
|
||||
DAWN_TRY(ValidateRenderBundleCommand(commands, type, &usageTracker, &commandBufferState,
|
||||
DAWN_TRY(ValidateRenderBundleCommand(commands, type, &commandBufferState,
|
||||
attachmentState, &debugGroupStackSize,
|
||||
"Command disallowed inside a render bundle"));
|
||||
}
|
||||
|
||||
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
||||
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
|
||||
ASSERT(resourceUsage != nullptr);
|
||||
*resourceUsage = usageTracker.AcquireResourceUsage();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError ValidateRenderPass(CommandIterator* commands,
|
||||
BeginRenderPassCmd* renderPass,
|
||||
std::vector<PassResourceUsage>* perPassResourceUsages) {
|
||||
PassResourceUsageTracker usageTracker;
|
||||
MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass) {
|
||||
CommandBufferStateTracker commandBufferState;
|
||||
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;
|
||||
while (commands->NextCommandId(&type)) {
|
||||
switch (type) {
|
||||
case Command::EndRenderPass: {
|
||||
commands->NextCommand<EndRenderPassCmd>();
|
||||
|
||||
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
||||
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
|
||||
ASSERT(perPassResourceUsages != nullptr);
|
||||
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
|
||||
|
||||
return {};
|
||||
} break;
|
||||
|
||||
|
@ -241,15 +161,6 @@ namespace dawn_native {
|
|||
return DAWN_VALIDATION_ERROR(
|
||||
"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) {
|
||||
|
@ -277,9 +188,8 @@ namespace dawn_native {
|
|||
|
||||
default:
|
||||
DAWN_TRY(ValidateRenderBundleCommand(
|
||||
commands, type, &usageTracker, &commandBufferState,
|
||||
renderPass->attachmentState.Get(), &debugGroupStackSize,
|
||||
"Command disallowed inside a render pass"));
|
||||
commands, type, &commandBufferState, renderPass->attachmentState.Get(),
|
||||
&debugGroupStackSize, "Command disallowed inside a render pass"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,9 +197,7 @@ namespace dawn_native {
|
|||
return DAWN_VALIDATION_ERROR("Unfinished render pass");
|
||||
}
|
||||
|
||||
MaybeError ValidateComputePass(CommandIterator* commands,
|
||||
std::vector<PassResourceUsage>* perPassResourceUsages) {
|
||||
PassResourceUsageTracker usageTracker;
|
||||
MaybeError ValidateComputePass(CommandIterator* commands) {
|
||||
CommandBufferStateTracker commandBufferState;
|
||||
uint64_t debugGroupStackSize = 0;
|
||||
|
||||
|
@ -298,11 +206,7 @@ namespace dawn_native {
|
|||
switch (type) {
|
||||
case Command::EndComputePass: {
|
||||
commands->NextCommand<EndComputePassCmd>();
|
||||
|
||||
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
||||
DAWN_TRY(usageTracker.ValidateComputePassUsages());
|
||||
ASSERT(perPassResourceUsages != nullptr);
|
||||
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
|
||||
return {};
|
||||
} break;
|
||||
|
||||
|
@ -312,10 +216,8 @@ namespace dawn_native {
|
|||
} break;
|
||||
|
||||
case Command::DispatchIndirect: {
|
||||
DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>();
|
||||
commands->NextCommand<DispatchIndirectCmd>();
|
||||
DAWN_TRY(commandBufferState.ValidateCanDispatch());
|
||||
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
|
||||
wgpu::BufferUsage::Indirect);
|
||||
} break;
|
||||
|
||||
case Command::InsertDebugMarker: {
|
||||
|
@ -346,8 +248,6 @@ namespace dawn_native {
|
|||
if (cmd->dynamicOffsetCount > 0) {
|
||||
commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
|
||||
}
|
||||
|
||||
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
|
||||
commandBufferState.SetBindGroup(cmd->index, cmd->group.Get());
|
||||
} break;
|
||||
|
||||
|
@ -360,4 +260,46 @@ namespace dawn_native {
|
|||
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
|
||||
|
|
|
@ -30,13 +30,11 @@ namespace dawn_native {
|
|||
MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize);
|
||||
|
||||
MaybeError ValidateRenderBundle(CommandIterator* commands,
|
||||
const AttachmentState* attachmentState,
|
||||
PassResourceUsage* resourceUsage);
|
||||
MaybeError ValidateRenderPass(CommandIterator* commands,
|
||||
BeginRenderPassCmd* renderPass,
|
||||
std::vector<PassResourceUsage>* perPassResourceUsages);
|
||||
MaybeError ValidateComputePass(CommandIterator* commands,
|
||||
std::vector<PassResourceUsage>* perPassResourceUsages);
|
||||
const AttachmentState* attachmentState);
|
||||
MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass);
|
||||
MaybeError ValidateComputePass(CommandIterator* commands);
|
||||
|
||||
MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage);
|
||||
|
||||
} // namespace dawn_native
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace dawn_native {
|
|||
|
||||
return {};
|
||||
})) {
|
||||
mEncodingContext->ExitPass(this);
|
||||
mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,8 @@ namespace dawn_native {
|
|||
dispatch->indirectBuffer = indirectBuffer;
|
||||
dispatch->indirectOffset = indirectOffset;
|
||||
|
||||
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
#include "dawn_native/EncodingContext.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "dawn_native/CommandEncoder.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/ErrorData.h"
|
||||
#include "dawn_native/RenderBundleEncoder.h"
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
|
@ -32,17 +34,23 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
CommandIterator EncodingContext::AcquireCommands() {
|
||||
MoveToIterator();
|
||||
ASSERT(!mWereCommandsAcquired);
|
||||
mWereCommandsAcquired = true;
|
||||
return std::move(mIterator);
|
||||
}
|
||||
|
||||
CommandIterator* EncodingContext::GetIterator() {
|
||||
MoveToIterator();
|
||||
ASSERT(!mWereCommandsAcquired);
|
||||
return &mIterator;
|
||||
}
|
||||
|
||||
void EncodingContext::MoveToIterator() {
|
||||
if (!mWasMovedToIterator) {
|
||||
mIterator = std::move(mAllocator);
|
||||
mWasMovedToIterator = true;
|
||||
}
|
||||
return &mIterator;
|
||||
}
|
||||
|
||||
void EncodingContext::HandleError(wgpu::ErrorType type, const char* message) {
|
||||
|
@ -66,13 +74,25 @@ namespace dawn_native {
|
|||
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(mCurrentEncoder != mTopLevelEncoder);
|
||||
// Assert the pass encoder is current.
|
||||
ASSERT(mCurrentEncoder == passEncoder);
|
||||
|
||||
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() {
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
#include "dawn_native/CommandAllocator.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/ErrorData.h"
|
||||
#include "dawn_native/PassResourceUsageTracker.h"
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
class ObjectBase;
|
||||
class DeviceBase;
|
||||
class ObjectBase;
|
||||
|
||||
// Base class for allocating/iterating commands.
|
||||
// It performs error tracking as well as encoding state for render/compute passes.
|
||||
|
@ -54,7 +55,7 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
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 (mCurrentEncoder != mTopLevelEncoder) {
|
||||
// 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
|
||||
void EnterPass(const ObjectBase* passEncoder);
|
||||
void ExitPass(const ObjectBase* passEncoder);
|
||||
void ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsages);
|
||||
MaybeError Finish();
|
||||
|
||||
const PerPassUsages& GetPassUsages() const;
|
||||
PerPassUsages AcquirePassUsages();
|
||||
|
||||
private:
|
||||
bool IsFinished() const;
|
||||
void MoveToIterator();
|
||||
|
||||
DeviceBase* mDevice;
|
||||
|
||||
|
@ -90,6 +95,9 @@ namespace dawn_native {
|
|||
// CommandEncoder::Begin/EndPass.
|
||||
const ObjectBase* mCurrentEncoder;
|
||||
|
||||
PerPassUsages mPassUsages;
|
||||
bool mWerePassUsagesAcquired = false;
|
||||
|
||||
CommandAllocator mAllocator;
|
||||
CommandIterator mIterator;
|
||||
bool mWasMovedToIterator = false;
|
||||
|
|
|
@ -36,8 +36,10 @@ namespace dawn_native {
|
|||
std::vector<wgpu::TextureUsage> textureUsages;
|
||||
};
|
||||
|
||||
using PerPassUsages = std::vector<PassResourceUsage>;
|
||||
|
||||
struct CommandBufferResourceUsage {
|
||||
std::vector<PassResourceUsage> perPass;
|
||||
PerPassUsages perPass;
|
||||
std::set<BufferBase*> topLevelBuffers;
|
||||
std::set<TextureBase*> topLevelTextures;
|
||||
};
|
||||
|
|
|
@ -31,54 +31,6 @@ namespace dawn_native {
|
|||
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.
|
||||
PassResourceUsage PassResourceUsageTracker::AcquireResourceUsage() {
|
||||
PassResourceUsage result;
|
||||
|
@ -97,6 +49,9 @@ namespace dawn_native {
|
|||
result.textureUsages.push_back(it.second);
|
||||
}
|
||||
|
||||
mBufferUsages.clear();
|
||||
mTextureUsages.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#ifndef DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_
|
||||
#define DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_
|
||||
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/PassResourceUsage.h"
|
||||
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
@ -36,16 +35,10 @@ namespace dawn_native {
|
|||
void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage 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.
|
||||
PassResourceUsage AcquireResourceUsage();
|
||||
|
||||
private:
|
||||
// Performs the per-pass usage validation checks
|
||||
MaybeError ValidateUsages() const;
|
||||
|
||||
std::map<BufferBase*, wgpu::BufferUsage> mBufferUsages;
|
||||
std::map<TextureBase*, wgpu::TextureUsage> mTextureUsages;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "dawn_native/ProgrammablePassEncoder.h"
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "dawn_native/BindGroup.h"
|
||||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/CommandBuffer.h"
|
||||
|
@ -25,6 +26,46 @@
|
|||
|
||||
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,
|
||||
EncodingContext* encodingContext)
|
||||
: ObjectBase(device), mEncodingContext(encodingContext) {
|
||||
|
@ -75,34 +116,36 @@ namespace dawn_native {
|
|||
uint32_t dynamicOffsetCount,
|
||||
const uint32_t* dynamicOffsets) {
|
||||
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||
DAWN_TRY(GetDevice()->ValidateObject(group));
|
||||
if (GetDevice()->IsValidationEnabled()) {
|
||||
DAWN_TRY(GetDevice()->ValidateObject(group));
|
||||
|
||||
if (groupIndex >= kMaxBindGroups) {
|
||||
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");
|
||||
if (groupIndex >= kMaxBindGroups) {
|
||||
return DAWN_VALIDATION_ERROR("Setting bind group over the max");
|
||||
}
|
||||
|
||||
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 <=
|
||||
// buffer size.
|
||||
DAWN_ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size);
|
||||
DAWN_ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >=
|
||||
bufferBinding.offset);
|
||||
for (uint32_t i = 0; i < dynamicOffsetCount; ++i) {
|
||||
if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) {
|
||||
return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned");
|
||||
}
|
||||
|
||||
if ((dynamicOffsets[i] >
|
||||
bufferBinding.buffer->GetSize() - bufferBinding.offset - bufferBinding.size)) {
|
||||
return DAWN_VALIDATION_ERROR("dynamic offset out of bounds");
|
||||
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)) {
|
||||
return DAWN_VALIDATION_ERROR("dynamic offset out of bounds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +158,8 @@ namespace dawn_native {
|
|||
memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
TrackBindGroupResourceUsage(&mUsageTracker, group);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "dawn_native/CommandEncoder.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/ObjectBase.h"
|
||||
#include "dawn_native/PassResourceUsageTracker.h"
|
||||
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
|
@ -47,6 +48,7 @@ namespace dawn_native {
|
|||
ErrorTag errorTag);
|
||||
|
||||
EncodingContext* mEncodingContext = nullptr;
|
||||
PassResourceUsageTracker mUsageTracker;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace dawn_native {
|
|||
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
||||
DeviceBase* device = GetDevice();
|
||||
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
|
||||
if (device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
||||
if (device->IsValidationEnabled() &&
|
||||
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
||||
return;
|
||||
}
|
||||
ASSERT(!IsError());
|
||||
|
|
|
@ -102,26 +102,27 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
RenderBundleBase* RenderBundleEncoder::Finish(const RenderBundleDescriptor* descriptor) {
|
||||
if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) {
|
||||
return RenderBundleBase::MakeError(GetDevice());
|
||||
}
|
||||
ASSERT(!IsError());
|
||||
PassResourceUsage usages = mUsageTracker.AcquireResourceUsage();
|
||||
|
||||
return new RenderBundleBase(this, descriptor, mAttachmentState.Get(),
|
||||
std::move(mResourceUsage));
|
||||
DeviceBase* device = GetDevice();
|
||||
// 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");
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
||||
// 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());
|
||||
|
||||
CommandIterator* commands = mEncodingContext.GetIterator();
|
||||
|
||||
DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get(), &mResourceUsage));
|
||||
DAWN_TRY(ValidatePassResourceUsage(usages));
|
||||
DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get()));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,10 @@ namespace dawn_native {
|
|||
private:
|
||||
RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag);
|
||||
|
||||
MaybeError ValidateFinish(const RenderBundleDescriptor* descriptor);
|
||||
MaybeError ValidateFinish(CommandIterator* commands, const PassResourceUsage& usages) const;
|
||||
|
||||
EncodingContext mEncodingContext;
|
||||
Ref<AttachmentState> mAttachmentState;
|
||||
PassResourceUsage mResourceUsage;
|
||||
};
|
||||
} // namespace dawn_native
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ namespace dawn_native {
|
|||
cmd->indirectBuffer = indirectBuffer;
|
||||
cmd->indirectOffset = indirectOffset;
|
||||
|
||||
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -100,6 +102,8 @@ namespace dawn_native {
|
|||
cmd->indirectBuffer = indirectBuffer;
|
||||
cmd->indirectOffset = indirectOffset;
|
||||
|
||||
mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -125,6 +129,8 @@ namespace dawn_native {
|
|||
cmd->buffer = buffer;
|
||||
cmd->offset = offset;
|
||||
|
||||
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Index);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
@ -139,6 +145,8 @@ namespace dawn_native {
|
|||
cmd->buffer = buffer;
|
||||
cmd->offset = offset;
|
||||
|
||||
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Vertex);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,10 +27,15 @@
|
|||
|
||||
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,
|
||||
CommandEncoder* commandEncoder,
|
||||
EncodingContext* encodingContext)
|
||||
EncodingContext* encodingContext,
|
||||
PassResourceUsageTracker usageTracker)
|
||||
: RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) {
|
||||
mUsageTracker = std::move(usageTracker);
|
||||
}
|
||||
|
||||
RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
|
||||
|
@ -52,7 +57,7 @@ namespace dawn_native {
|
|||
|
||||
return {};
|
||||
})) {
|
||||
mEncodingContext->ExitPass(this);
|
||||
mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,6 +148,14 @@ namespace dawn_native {
|
|||
Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count);
|
||||
for (uint32_t i = 0; i < count; ++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 {};
|
||||
|
|
|
@ -26,7 +26,8 @@ namespace dawn_native {
|
|||
public:
|
||||
RenderPassEncoder(DeviceBase* device,
|
||||
CommandEncoder* commandEncoder,
|
||||
EncodingContext* encodingContext);
|
||||
EncodingContext* encodingContext,
|
||||
PassResourceUsageTracker usageTracker);
|
||||
|
||||
static RenderPassEncoder* MakeError(DeviceBase* device,
|
||||
CommandEncoder* commandEncoder,
|
||||
|
|
|
@ -605,7 +605,8 @@ TEST_P(DrawCallPerf, Run) {
|
|||
|
||||
DAWN_INSTANTIATE_PERF_TEST_SUITE_P(
|
||||
DrawCallPerf,
|
||||
{D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend},
|
||||
{D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend,
|
||||
ForceWorkarounds(VulkanBackend, {"skip_validation"})},
|
||||
{
|
||||
// Baseline
|
||||
MakeParam(),
|
||||
|
|
Loading…
Reference in New Issue