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),
|
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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 {};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Reference in New Issue