Add query availability tracking in render pass encoder
The same query cannot be written twice in same render pass, so each
render pass also need to have its own query availability map.
Update timestamp query to only check the same query overwrite in same
render pass.
Bug: dawn:434
Change-Id: Icb070adf79a3d76c25367675f7432666eb0dd84f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/31180
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 7b4c858..e327e5e 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -398,22 +398,21 @@
mUsedQuerySets.insert(querySet);
}
- void CommandEncoder::TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex) {
- UsedQueryMap::iterator it = mUsedQueryIndices.find(querySet);
- if (it != mUsedQueryIndices.end()) {
- // Record index on existing query set
- std::vector<bool>& queryIndices = it->second;
- queryIndices[queryIndex] = 1;
- } else {
- // Record index on new query set
- std::vector<bool> queryIndices(querySet->GetQueryCount(), 0);
- queryIndices[queryIndex] = 1;
- mUsedQueryIndices.insert({querySet, std::move(queryIndices)});
+ void CommandEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) {
+ DAWN_ASSERT(querySet != nullptr);
+
+ if (GetDevice()->IsValidationEnabled()) {
+ TrackUsedQuerySet(querySet);
}
+
+ // Gets the iterator for that querySet or create a new vector of bool set to false
+ // if the querySet wasn't registered.
+ auto it = mQueryAvailabilityMap.emplace(querySet, querySet->GetQueryCount()).first;
+ it->second[queryIndex] = 1;
}
- const UsedQueryMap& CommandEncoder::GetUsedQueryIndices() const {
- return mUsedQueryIndices;
+ const QueryAvailabilityMap& CommandEncoder::GetQueryAvailabilityMap() const {
+ return mQueryAvailabilityMap;
}
// Implementation of the API's command recording methods
@@ -787,11 +786,10 @@
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet));
- DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, GetUsedQueryIndices()));
- TrackUsedQuerySet(querySet);
+ DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
}
- TrackUsedQueryIndex(querySet, queryIndex);
+ TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);
diff --git a/src/dawn_native/CommandEncoder.h b/src/dawn_native/CommandEncoder.h
index 9b992b6..cb02521 100644
--- a/src/dawn_native/CommandEncoder.h
+++ b/src/dawn_native/CommandEncoder.h
@@ -29,7 +29,7 @@
struct BeginRenderPassCmd;
- using UsedQueryMap = std::map<QuerySetBase*, std::vector<bool>>;
+ using QueryAvailabilityMap = std::map<QuerySetBase*, std::vector<bool>>;
class CommandEncoder final : public ObjectBase {
public:
@@ -39,8 +39,8 @@
CommandBufferResourceUsage AcquireResourceUsages();
void TrackUsedQuerySet(QuerySetBase* querySet);
- void TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex);
- const UsedQueryMap& GetUsedQueryIndices() const;
+ void TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex);
+ const QueryAvailabilityMap& GetQueryAvailabilityMap() const;
// Dawn API
ComputePassEncoder* BeginComputePass(const ComputePassDescriptor* descriptor);
@@ -83,7 +83,7 @@
std::set<BufferBase*> mTopLevelBuffers;
std::set<TextureBase*> mTopLevelTextures;
std::set<QuerySetBase*> mUsedQuerySets;
- UsedQueryMap mUsedQueryIndices;
+ QueryAvailabilityMap mQueryAvailabilityMap;
};
} // namespace dawn_native
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 0cbfdcb..354a113 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -351,26 +351,15 @@
return {};
}
- MaybeError ValidateTimestampQuery(QuerySetBase* querySet,
- uint32_t queryIndex,
- const UsedQueryMap& usedQueryIndices) {
+ MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex) {
if (querySet->GetQueryType() != wgpu::QueryType::Timestamp) {
- return DAWN_VALIDATION_ERROR("The query type of query set must be Timestamp");
+ return DAWN_VALIDATION_ERROR("The type of query set must be Timestamp");
}
if (queryIndex >= querySet->GetQueryCount()) {
return DAWN_VALIDATION_ERROR("Query index exceeds the number of queries in query set");
}
- UsedQueryMap::const_iterator it = usedQueryIndices.find(querySet);
- if (it != usedQueryIndices.end()) {
- // Get the used query index records
- const std::vector<bool>& queryIndices = it->second;
- if (queryIndices[queryIndex] == 1) {
- return DAWN_VALIDATION_ERROR("Duplicated query index writen");
- }
- }
-
return {};
}
diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h
index 3e46774..d2794e2 100644
--- a/src/dawn_native/CommandValidation.h
+++ b/src/dawn_native/CommandValidation.h
@@ -19,7 +19,6 @@
#include "dawn_native/Error.h"
#include "dawn_native/Texture.h"
-#include <map>
#include <vector>
namespace dawn_native {
@@ -30,8 +29,6 @@
struct PassResourceUsage;
struct TexelBlockInfo;
- using UsedQueryMap = std::map<QuerySetBase*, std::vector<bool>>;
-
MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize);
MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize);
@@ -42,9 +39,7 @@
MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage);
- MaybeError ValidateTimestampQuery(QuerySetBase* querySet,
- uint32_t queryIndex,
- const UsedQueryMap& usedQueryIndices);
+ MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex);
ResultOrError<uint64_t> ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo,
const Extent3D& copySize,
diff --git a/src/dawn_native/ComputePassEncoder.cpp b/src/dawn_native/ComputePassEncoder.cpp
index e421ea4..afa9df0 100644
--- a/src/dawn_native/ComputePassEncoder.cpp
+++ b/src/dawn_native/ComputePassEncoder.cpp
@@ -114,12 +114,10 @@
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet));
- DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex,
- mCommandEncoder->GetUsedQueryIndices()));
- mCommandEncoder->TrackUsedQuerySet(querySet);
+ DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
}
- mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex);
+ mCommandEncoder->TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);
diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp
index df99563..72dc079 100644
--- a/src/dawn_native/RenderPassEncoder.cpp
+++ b/src/dawn_native/RenderPassEncoder.cpp
@@ -28,6 +28,22 @@
#include <cstring>
namespace dawn_native {
+ namespace {
+
+ // Check the query at queryIndex is unavailable, otherwise it cannot be written.
+ MaybeError ValidateQueryIndexOverwrite(QuerySetBase* querySet,
+ uint32_t queryIndex,
+ const QueryAvailabilityMap& queryAvailabilityMap) {
+ auto it = queryAvailabilityMap.find(querySet);
+ if (it != queryAvailabilityMap.end() && it->second[queryIndex]) {
+ return DAWN_VALIDATION_ERROR(
+ "The same query cannot be written twice in same render pass.");
+ }
+
+ return {};
+ }
+
+ } // namespace
// The usage tracker is passed in here, because it is prepopulated with usages from the
// BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
@@ -58,6 +74,22 @@
return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError);
}
+ void RenderPassEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) {
+ DAWN_ASSERT(querySet != nullptr);
+
+ // Gets the iterator for that querySet or create a new vector of bool set to false
+ // if the querySet wasn't registered.
+ auto it = mQueryAvailabilityMap.emplace(querySet, querySet->GetQueryCount()).first;
+ it->second[queryIndex] = 1;
+
+ // Track it again on command encoder for zero-initializing when resolving unused queries.
+ mCommandEncoder->TrackQueryAvailability(querySet, queryIndex);
+ }
+
+ const QueryAvailabilityMap& RenderPassEncoder::GetQueryAvailabilityMap() const {
+ return mQueryAvailabilityMap;
+ }
+
void RenderPassEncoder::EndPass() {
if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
@@ -180,12 +212,12 @@
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet));
- DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex,
- mCommandEncoder->GetUsedQueryIndices()));
- mCommandEncoder->TrackUsedQuerySet(querySet);
+ DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
+ DAWN_TRY(
+ ValidateQueryIndexOverwrite(querySet, queryIndex, GetQueryAvailabilityMap()));
}
- mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex);
+ TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);
diff --git a/src/dawn_native/RenderPassEncoder.h b/src/dawn_native/RenderPassEncoder.h
index 0fac863..7bda224 100644
--- a/src/dawn_native/RenderPassEncoder.h
+++ b/src/dawn_native/RenderPassEncoder.h
@@ -35,6 +35,9 @@
CommandEncoder* commandEncoder,
EncodingContext* encodingContext);
+ void TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex);
+ const QueryAvailabilityMap& GetQueryAvailabilityMap() const;
+
void EndPass();
void SetStencilReference(uint32_t reference);
@@ -63,6 +66,11 @@
uint32_t mRenderTargetWidth;
uint32_t mRenderTargetHeight;
+
+ // This map is to indicate the availability of the queries used in render pass. The same
+ // query cannot be written twice in same render pass, so each render pass also need to have
+ // its own query availability map.
+ QueryAvailabilityMap mQueryAvailabilityMap;
};
} // namespace dawn_native
diff --git a/src/tests/unittests/validation/QuerySetValidationTests.cpp b/src/tests/unittests/validation/QuerySetValidationTests.cpp
index 25ad1cd..965db04 100644
--- a/src/tests/unittests/validation/QuerySetValidationTests.cpp
+++ b/src/tests/unittests/validation/QuerySetValidationTests.cpp
@@ -120,14 +120,6 @@
ASSERT_DEVICE_ERROR(encoder.Finish());
}
- // Fail to write timestamp to the same index twice on command encoder
- {
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- encoder.WriteTimestamp(timestampQuerySet, 0);
- encoder.WriteTimestamp(timestampQuerySet, 0);
- ASSERT_DEVICE_ERROR(encoder.Finish());
- }
-
// Fail to submit timestamp query with a destroyed query set
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@@ -172,26 +164,6 @@
ASSERT_DEVICE_ERROR(encoder.Finish());
}
- // Fail to write timestamp to the same index twice on compute encoder
- {
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
- pass.WriteTimestamp(timestampQuerySet, 0);
- pass.WriteTimestamp(timestampQuerySet, 0);
- pass.EndPass();
- ASSERT_DEVICE_ERROR(encoder.Finish());
- }
-
- // Fail to write timestamp to the same index twice on command encoder and compute encoder
- {
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- encoder.WriteTimestamp(timestampQuerySet, 0);
- wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
- pass.WriteTimestamp(timestampQuerySet, 0);
- pass.EndPass();
- ASSERT_DEVICE_ERROR(encoder.Finish());
- }
-
// Fail to submit timestamp query with a destroyed query set
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@@ -240,23 +212,36 @@
ASSERT_DEVICE_ERROR(encoder.Finish());
}
- // Fail to write timestamp to the same index twice on command encoder and render encoder
- {
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
- pass.WriteTimestamp(timestampQuerySet, 0);
- pass.WriteTimestamp(timestampQuerySet, 0);
- pass.EndPass();
- ASSERT_DEVICE_ERROR(encoder.Finish());
- }
-
- // Fail to write timestamp to the same index twice on command encoder and render encoder
+ // Success to write timestamp to the same query index twice on command encoder and render
+ // encoder
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.WriteTimestamp(timestampQuerySet, 0);
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.WriteTimestamp(timestampQuerySet, 0);
pass.EndPass();
+ encoder.Finish();
+ }
+
+ // Success to write timestamp to the same query index twice on different render encoder
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass);
+ pass0.WriteTimestamp(timestampQuerySet, 0);
+ pass0.EndPass();
+ wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass);
+ pass1.WriteTimestamp(timestampQuerySet, 0);
+ pass1.EndPass();
+ encoder.Finish();
+ }
+
+ // Fail to write timestamp to the same query index twice on same render encoder
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+ pass.WriteTimestamp(timestampQuerySet, 0);
+ pass.WriteTimestamp(timestampQuerySet, 0);
+ pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}