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>
This commit is contained in:
Hao Li 2020-11-16 02:24:06 +00:00 committed by Commit Bot service account
parent aabde6c88f
commit 575729e8dd
8 changed files with 91 additions and 86 deletions

View File

@ -398,22 +398,21 @@ namespace dawn_native {
mUsedQuerySets.insert(querySet); mUsedQuerySets.insert(querySet);
} }
void CommandEncoder::TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex) { void CommandEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) {
UsedQueryMap::iterator it = mUsedQueryIndices.find(querySet); DAWN_ASSERT(querySet != nullptr);
if (it != mUsedQueryIndices.end()) {
// Record index on existing query set if (GetDevice()->IsValidationEnabled()) {
std::vector<bool>& queryIndices = it->second; TrackUsedQuerySet(querySet);
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)});
} }
// 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 { const QueryAvailabilityMap& CommandEncoder::GetQueryAvailabilityMap() const {
return mUsedQueryIndices; return mQueryAvailabilityMap;
} }
// Implementation of the API's command recording methods // Implementation of the API's command recording methods
@ -787,11 +786,10 @@ namespace dawn_native {
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) { if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet)); DAWN_TRY(GetDevice()->ValidateObject(querySet));
DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, GetUsedQueryIndices())); DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
TrackUsedQuerySet(querySet);
} }
TrackUsedQueryIndex(querySet, queryIndex); TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd = WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp); allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);

View File

@ -29,7 +29,7 @@ namespace dawn_native {
struct BeginRenderPassCmd; struct BeginRenderPassCmd;
using UsedQueryMap = std::map<QuerySetBase*, std::vector<bool>>; using QueryAvailabilityMap = std::map<QuerySetBase*, std::vector<bool>>;
class CommandEncoder final : public ObjectBase { class CommandEncoder final : public ObjectBase {
public: public:
@ -39,8 +39,8 @@ namespace dawn_native {
CommandBufferResourceUsage AcquireResourceUsages(); CommandBufferResourceUsage AcquireResourceUsages();
void TrackUsedQuerySet(QuerySetBase* querySet); void TrackUsedQuerySet(QuerySetBase* querySet);
void TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex); void TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex);
const UsedQueryMap& GetUsedQueryIndices() const; const QueryAvailabilityMap& GetQueryAvailabilityMap() const;
// Dawn API // Dawn API
ComputePassEncoder* BeginComputePass(const ComputePassDescriptor* descriptor); ComputePassEncoder* BeginComputePass(const ComputePassDescriptor* descriptor);
@ -83,7 +83,7 @@ namespace dawn_native {
std::set<BufferBase*> mTopLevelBuffers; std::set<BufferBase*> mTopLevelBuffers;
std::set<TextureBase*> mTopLevelTextures; std::set<TextureBase*> mTopLevelTextures;
std::set<QuerySetBase*> mUsedQuerySets; std::set<QuerySetBase*> mUsedQuerySets;
UsedQueryMap mUsedQueryIndices; QueryAvailabilityMap mQueryAvailabilityMap;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -351,26 +351,15 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateTimestampQuery(QuerySetBase* querySet, MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex) {
uint32_t queryIndex,
const UsedQueryMap& usedQueryIndices) {
if (querySet->GetQueryType() != wgpu::QueryType::Timestamp) { 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()) { if (queryIndex >= querySet->GetQueryCount()) {
return DAWN_VALIDATION_ERROR("Query index exceeds the number of queries in query set"); 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 {}; return {};
} }

View File

@ -19,7 +19,6 @@
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#include "dawn_native/Texture.h" #include "dawn_native/Texture.h"
#include <map>
#include <vector> #include <vector>
namespace dawn_native { namespace dawn_native {
@ -30,8 +29,6 @@ namespace dawn_native {
struct PassResourceUsage; struct PassResourceUsage;
struct TexelBlockInfo; struct TexelBlockInfo;
using UsedQueryMap = std::map<QuerySetBase*, std::vector<bool>>;
MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize); MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize);
MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize); MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize);
@ -42,9 +39,7 @@ namespace dawn_native {
MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage); MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage);
MaybeError ValidateTimestampQuery(QuerySetBase* querySet, MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex);
uint32_t queryIndex,
const UsedQueryMap& usedQueryIndices);
ResultOrError<uint64_t> ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo, ResultOrError<uint64_t> ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo,
const Extent3D& copySize, const Extent3D& copySize,

View File

@ -114,12 +114,10 @@ namespace dawn_native {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) { if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet)); DAWN_TRY(GetDevice()->ValidateObject(querySet));
DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
mCommandEncoder->GetUsedQueryIndices()));
mCommandEncoder->TrackUsedQuerySet(querySet);
} }
mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex); mCommandEncoder->TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd = WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp); allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);

View File

@ -28,6 +28,22 @@
#include <cstring> #include <cstring>
namespace dawn_native { 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 // The usage tracker is passed in here, because it is prepopulated with usages from the
// BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the // BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
@ -58,6 +74,22 @@ namespace dawn_native {
return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError); 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() { void RenderPassEncoder::EndPass() {
if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass); allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
@ -180,12 +212,12 @@ namespace dawn_native {
mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
if (GetDevice()->IsValidationEnabled()) { if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(querySet)); DAWN_TRY(GetDevice()->ValidateObject(querySet));
DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
mCommandEncoder->GetUsedQueryIndices())); DAWN_TRY(
mCommandEncoder->TrackUsedQuerySet(querySet); ValidateQueryIndexOverwrite(querySet, queryIndex, GetQueryAvailabilityMap()));
} }
mCommandEncoder->TrackUsedQueryIndex(querySet, queryIndex); TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd = WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp); allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);

View File

@ -35,6 +35,9 @@ namespace dawn_native {
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,
EncodingContext* encodingContext); EncodingContext* encodingContext);
void TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex);
const QueryAvailabilityMap& GetQueryAvailabilityMap() const;
void EndPass(); void EndPass();
void SetStencilReference(uint32_t reference); void SetStencilReference(uint32_t reference);
@ -63,6 +66,11 @@ namespace dawn_native {
uint32_t mRenderTargetWidth; uint32_t mRenderTargetWidth;
uint32_t mRenderTargetHeight; 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 } // namespace dawn_native

View File

@ -120,14 +120,6 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnCommandEncoder) {
ASSERT_DEVICE_ERROR(encoder.Finish()); 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 // Fail to submit timestamp query with a destroyed query set
{ {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@ -172,26 +164,6 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnComputePassEncoder) {
ASSERT_DEVICE_ERROR(encoder.Finish()); 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 // Fail to submit timestamp query with a destroyed query set
{ {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@ -240,23 +212,36 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnRenderPassEncoder) {
ASSERT_DEVICE_ERROR(encoder.Finish()); 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();
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 = device.CreateCommandEncoder(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.WriteTimestamp(timestampQuerySet, 0); encoder.WriteTimestamp(timestampQuerySet, 0);
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.WriteTimestamp(timestampQuerySet, 0); pass.WriteTimestamp(timestampQuerySet, 0);
pass.EndPass(); 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()); ASSERT_DEVICE_ERROR(encoder.Finish());
} }