diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp index 6d24609ef8..0fdded3f97 100644 --- a/src/dawn_native/CommandEncoder.cpp +++ b/src/dawn_native/CommandEncoder.cpp @@ -467,6 +467,24 @@ namespace dawn_native { 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& queryIndices = it->second; + queryIndices[queryIndex] = 1; + } else { + // Record index on new query set + std::vector queryIndices(querySet->GetQueryCount(), 0); + queryIndices[queryIndex] = 1; + mUsedQueryIndices.insert({querySet, std::move(queryIndices)}); + } + } + + const UsedQueryMap& CommandEncoder::GetUsedQueryIndices() const { + return mUsedQueryIndices; + } + // Implementation of the API's command recording methods ComputePassEncoder* CommandEncoder::BeginComputePass(const ComputePassDescriptor* descriptor) { @@ -850,10 +868,12 @@ namespace dawn_native { mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (GetDevice()->IsValidationEnabled()) { DAWN_TRY(GetDevice()->ValidateObject(querySet)); - DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, GetUsedQueryIndices())); TrackUsedQuerySet(querySet); } + TrackUsedQueryIndex(querySet, queryIndex); + WriteTimestampCmd* cmd = allocator->Allocate(Command::WriteTimestamp); cmd->querySet = querySet; diff --git a/src/dawn_native/CommandEncoder.h b/src/dawn_native/CommandEncoder.h index 2fe801042c..8b5ae1f959 100644 --- a/src/dawn_native/CommandEncoder.h +++ b/src/dawn_native/CommandEncoder.h @@ -22,12 +22,15 @@ #include "dawn_native/ObjectBase.h" #include "dawn_native/PassResourceUsage.h" +#include #include namespace dawn_native { struct BeginRenderPassCmd; + using UsedQueryMap = std::map>; + class CommandEncoder final : public ObjectBase { public: CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor* descriptor); @@ -36,6 +39,8 @@ namespace dawn_native { CommandBufferResourceUsage AcquireResourceUsages(); void TrackUsedQuerySet(QuerySetBase* querySet); + void TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex); + const UsedQueryMap& GetUsedQueryIndices() const; // Dawn API ComputePassEncoder* BeginComputePass(const ComputePassDescriptor* descriptor); @@ -77,6 +82,7 @@ namespace dawn_native { std::set mTopLevelBuffers; std::set mTopLevelTextures; std::set mUsedQuerySets; + UsedQueryMap mUsedQueryIndices; }; } // namespace dawn_native diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp index 348d445f79..5e98cef65b 100644 --- a/src/dawn_native/CommandValidation.cpp +++ b/src/dawn_native/CommandValidation.cpp @@ -351,7 +351,9 @@ namespace dawn_native { return {}; } - MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex) { + MaybeError ValidateTimestampQuery(QuerySetBase* querySet, + uint32_t queryIndex, + const UsedQueryMap& usedQueryIndices) { if (querySet->GetQueryType() != wgpu::QueryType::Timestamp) { return DAWN_VALIDATION_ERROR("The query type of query set must be Timestamp"); } @@ -360,6 +362,15 @@ namespace dawn_native { 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& 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 eed2e6fae2..77714f4cc5 100644 --- a/src/dawn_native/CommandValidation.h +++ b/src/dawn_native/CommandValidation.h @@ -19,6 +19,7 @@ #include "dawn_native/Error.h" #include "dawn_native/Texture.h" +#include #include namespace dawn_native { @@ -29,6 +30,8 @@ namespace dawn_native { struct PassResourceUsage; struct TexelBlockInfo; + using UsedQueryMap = std::map>; + MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize); MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize); @@ -39,7 +42,9 @@ namespace dawn_native { MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage); - MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex); + MaybeError ValidateTimestampQuery(QuerySetBase* querySet, + uint32_t queryIndex, + const UsedQueryMap& usedQueryIndices); ResultOrError ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo, const Extent3D& copySize, diff --git a/src/dawn_native/ComputePassEncoder.cpp b/src/dawn_native/ComputePassEncoder.cpp index 8d070eab27..bfb0e7eac9 100644 --- a/src/dawn_native/ComputePassEncoder.cpp +++ b/src/dawn_native/ComputePassEncoder.cpp @@ -106,10 +106,13 @@ namespace dawn_native { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (GetDevice()->IsValidationEnabled()) { DAWN_TRY(GetDevice()->ValidateObject(querySet)); - DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, + mCommandEncoder->GetUsedQueryIndices())); mCommandEncoder->TrackUsedQuerySet(querySet); } + mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex); + WriteTimestampCmd* cmd = allocator->Allocate(Command::WriteTimestamp); cmd->querySet = querySet; diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp index 4d25107d94..df995633c2 100644 --- a/src/dawn_native/RenderPassEncoder.cpp +++ b/src/dawn_native/RenderPassEncoder.cpp @@ -180,10 +180,13 @@ namespace dawn_native { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (GetDevice()->IsValidationEnabled()) { DAWN_TRY(GetDevice()->ValidateObject(querySet)); - DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, + mCommandEncoder->GetUsedQueryIndices())); mCommandEncoder->TrackUsedQuerySet(querySet); } + mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex); + WriteTimestampCmd* cmd = allocator->Allocate(Command::WriteTimestamp); cmd->querySet = querySet; diff --git a/src/tests/unittests/validation/QuerySetValidationTests.cpp b/src/tests/unittests/validation/QuerySetValidationTests.cpp index 5bf7babdd3..ef53bd30ee 100644 --- a/src/tests/unittests/validation/QuerySetValidationTests.cpp +++ b/src/tests/unittests/validation/QuerySetValidationTests.cpp @@ -185,6 +185,14 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnCommandEncoder) { ASSERT_DEVICE_ERROR(encoder.Finish()); } + // Fail to write timestamp to the same index twice on command encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.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 = deviceWithTimestamp.CreateCommandEncoder(); @@ -241,6 +249,26 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnComputePassEncoder) { ASSERT_DEVICE_ERROR(encoder.Finish()); } + // Fail to write timestamp to the same index twice on compute encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.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 = deviceWithTimestamp.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 = deviceWithTimestamp.CreateCommandEncoder(); @@ -301,6 +329,26 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnRenderPassEncoder) { ASSERT_DEVICE_ERROR(encoder.Finish()); } + // Fail to write timestamp to the same index twice on command encoder and render encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.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 + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.WriteTimestamp(timestampQuerySet, 0); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + // Fail to submit timestamp query with a destroyed query set { wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder();