Implement drawIndexedIndirect validation
Every render pass which invokes DrawIndexedIndirect, either directly or
through a RenderBundle execution, is now preceded immediately by at
least one validation pass.
All indirect buffer offests used with DII are validated, and their
validated values are copied into a separate scratch buffer (or zeroed
out there, in the case of validation failure). All encoded DII commands
are rewritten to use the validated parameters instead of the original
ones.
Bug: dawn:809
Change-Id: I5eead937f19536f84f89e2c8e6fed7f18f0aee9f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63461
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/EncodingContext.cpp b/src/dawn_native/EncodingContext.cpp
index 9e8812d..9e7b960 100644
--- a/src/dawn_native/EncodingContext.cpp
+++ b/src/dawn_native/EncodingContext.cpp
@@ -19,6 +19,7 @@
#include "dawn_native/Commands.h"
#include "dawn_native/Device.h"
#include "dawn_native/ErrorData.h"
+#include "dawn_native/IndirectDrawValidationEncoder.h"
#include "dawn_native/RenderBundleEncoder.h"
namespace dawn_native {
@@ -47,8 +48,9 @@
}
void EncodingContext::MoveToIterator() {
+ CommitCommands(std::move(mPendingCommands));
if (!mWasMovedToIterator) {
- mIterator = CommandIterator(std::move(mAllocator));
+ mIterator.AcquireCommandBlocks(std::move(mAllocators));
mWasMovedToIterator = true;
}
}
@@ -67,6 +69,18 @@
}
}
+ void EncodingContext::WillBeginRenderPass() {
+ ASSERT(mCurrentEncoder == mTopLevelEncoder);
+ if (mDevice->IsValidationEnabled()) {
+ // When validation is enabled, we are going to want to capture all commands encoded
+ // between and including BeginRenderPassCmd and EndRenderPassCmd, and defer their
+ // sequencing util after we have a chance to insert any necessary validation
+ // commands. To support this we commit any current commands now, so that the
+ // impending BeginRenderPassCmd starts in a fresh CommandAllocator.
+ CommitCommands(std::move(mPendingCommands));
+ }
+ }
+
void EncodingContext::EnterPass(const ObjectBase* passEncoder) {
// Assert we're at the top level.
ASSERT(mCurrentEncoder == mTopLevelEncoder);
@@ -75,15 +89,34 @@
mCurrentEncoder = passEncoder;
}
- void EncodingContext::ExitPass(const ObjectBase* passEncoder, RenderPassResourceUsage usages) {
+ MaybeError EncodingContext::ExitRenderPass(const ObjectBase* passEncoder,
+ RenderPassResourceUsageTracker usageTracker,
+ CommandEncoder* commandEncoder,
+ IndirectDrawMetadata indirectDrawMetadata) {
ASSERT(mCurrentEncoder != mTopLevelEncoder);
ASSERT(mCurrentEncoder == passEncoder);
mCurrentEncoder = mTopLevelEncoder;
- mRenderPassUsages.push_back(std::move(usages));
+
+ if (mDevice->IsValidationEnabled()) {
+ // With validation enabled, commands were committed just before BeginRenderPassCmd was
+ // encoded by our RenderPassEncoder (see WillBeginRenderPass above). This means
+ // mPendingCommands contains only the commands from BeginRenderPassCmd to
+ // EndRenderPassCmd, inclusive. Now we swap out this allocator with a fresh one to give
+ // the validation encoder a chance to insert its commands first.
+ CommandAllocator renderCommands = std::move(mPendingCommands);
+ DAWN_TRY(EncodeIndirectDrawValidationCommands(mDevice, commandEncoder, &usageTracker,
+ &indirectDrawMetadata));
+ CommitCommands(std::move(mPendingCommands));
+ CommitCommands(std::move(renderCommands));
+ }
+
+ mRenderPassUsages.push_back(usageTracker.AcquireResourceUsage());
+ return {};
}
- void EncodingContext::ExitPass(const ObjectBase* passEncoder, ComputePassResourceUsage usages) {
+ void EncodingContext::ExitComputePass(const ObjectBase* passEncoder,
+ ComputePassResourceUsage usages) {
ASSERT(mCurrentEncoder != mTopLevelEncoder);
ASSERT(mCurrentEncoder == passEncoder);
@@ -126,6 +159,7 @@
// if Finish() has been called.
mCurrentEncoder = nullptr;
mTopLevelEncoder = nullptr;
+ CommitCommands(std::move(mPendingCommands));
if (mError != nullptr) {
return std::move(mError);
@@ -136,6 +170,12 @@
return {};
}
+ void EncodingContext::CommitCommands(CommandAllocator allocator) {
+ if (!allocator.IsEmpty()) {
+ mAllocators.push_back(std::move(allocator));
+ }
+ }
+
bool EncodingContext::IsFinished() const {
return mTopLevelEncoder == nullptr;
}