mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 03:41:34 +00:00
Query API: ResolveQuerySet
Add ResovleQuerySet on CommandEncoder and its validation tests. Bug: dawn:434 Change-Id: Ibba166dd11e15430cd5f6647676a47ce67481844 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24303 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Hao Li <hao.x.li@intel.com>
This commit is contained in:
parent
77eb64eb8c
commit
5c89c8dc70
13
dawn.json
13
dawn.json
@ -290,7 +290,8 @@
|
|||||||
{"value": 32, "name": "vertex"},
|
{"value": 32, "name": "vertex"},
|
||||||
{"value": 64, "name": "uniform"},
|
{"value": 64, "name": "uniform"},
|
||||||
{"value": 128, "name": "storage"},
|
{"value": 128, "name": "storage"},
|
||||||
{"value": 256, "name": "indirect"}
|
{"value": 256, "name": "indirect"},
|
||||||
|
{"value": 512, "name": "query resolve"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"char": {
|
"char": {
|
||||||
@ -411,6 +412,16 @@
|
|||||||
{"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"}
|
{"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "resolve query set",
|
||||||
|
"args": [
|
||||||
|
{"name": "query set", "type": "query set"},
|
||||||
|
{"name": "first query", "type": "uint32_t"},
|
||||||
|
{"name": "query count", "type": "uint32_t"},
|
||||||
|
{"name": "destination", "type": "buffer"},
|
||||||
|
{"name": "destination offset", "type": "uint64_t"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "write timestamp",
|
"name": "write timestamp",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -435,6 +435,44 @@ namespace dawn_native {
|
|||||||
return fixedView;
|
return fixedView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError ValidateQuerySetResolve(const QuerySetBase* querySet,
|
||||||
|
uint32_t firstQuery,
|
||||||
|
uint32_t queryCount,
|
||||||
|
const BufferBase* destination,
|
||||||
|
uint64_t destinationOffset) {
|
||||||
|
if (firstQuery >= querySet->GetQueryCount()) {
|
||||||
|
return DAWN_VALIDATION_ERROR("Query index out of bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryCount > querySet->GetQueryCount() - firstQuery) {
|
||||||
|
return DAWN_VALIDATION_ERROR(
|
||||||
|
"The sum of firstQuery and queryCount exceeds the number of queries in query "
|
||||||
|
"set");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(hao.x.li@intel.com): Validate that the queries between [firstQuery, firstQuery +
|
||||||
|
// queryCount - 1] must be available(written by query operations).
|
||||||
|
|
||||||
|
// The destinationOffset must be a multiple of 8 bytes on D3D12 and Vulkan
|
||||||
|
if (destinationOffset % 8 != 0) {
|
||||||
|
return DAWN_VALIDATION_ERROR(
|
||||||
|
"The alignment offset into the destination buffer must be a multiple of 8 "
|
||||||
|
"bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t bufferSize = destination->GetSize();
|
||||||
|
// The destination buffer must have enough storage, from destination offset, to contain
|
||||||
|
// the result of resolved queries
|
||||||
|
bool fitsInBuffer = destinationOffset <= bufferSize &&
|
||||||
|
(static_cast<uint64_t>(queryCount) * sizeof(uint64_t) <=
|
||||||
|
(bufferSize - destinationOffset));
|
||||||
|
if (!fitsInBuffer) {
|
||||||
|
return DAWN_VALIDATION_ERROR("The resolved query data would overflow the buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CommandEncoder::CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor*)
|
CommandEncoder::CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor*)
|
||||||
@ -783,6 +821,37 @@ namespace dawn_native {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandEncoder::ResolveQuerySet(QuerySetBase* querySet,
|
||||||
|
uint32_t firstQuery,
|
||||||
|
uint32_t queryCount,
|
||||||
|
BufferBase* destination,
|
||||||
|
uint64_t destinationOffset) {
|
||||||
|
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||||
|
if (GetDevice()->IsValidationEnabled()) {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateObject(querySet));
|
||||||
|
DAWN_TRY(GetDevice()->ValidateObject(destination));
|
||||||
|
|
||||||
|
DAWN_TRY(ValidateQuerySetResolve(querySet, firstQuery, queryCount, destination,
|
||||||
|
destinationOffset));
|
||||||
|
|
||||||
|
DAWN_TRY(ValidateCanUseAs(destination, wgpu::BufferUsage::QueryResolve));
|
||||||
|
|
||||||
|
TrackUsedQuerySet(querySet);
|
||||||
|
mTopLevelBuffers.insert(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveQuerySetCmd* cmd =
|
||||||
|
allocator->Allocate<ResolveQuerySetCmd>(Command::ResolveQuerySet);
|
||||||
|
cmd->querySet = querySet;
|
||||||
|
cmd->firstQuery = firstQuery;
|
||||||
|
cmd->queryCount = queryCount;
|
||||||
|
cmd->destination = destination;
|
||||||
|
cmd->destinationOffset = destinationOffset;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CommandEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
|
void CommandEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
|
||||||
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||||
if (GetDevice()->IsValidationEnabled()) {
|
if (GetDevice()->IsValidationEnabled()) {
|
||||||
@ -884,6 +953,11 @@ namespace dawn_native {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
commands->NextCommand<ResolveQuerySetCmd>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
commands->NextCommand<WriteTimestampCmd>();
|
commands->NextCommand<WriteTimestampCmd>();
|
||||||
break;
|
break;
|
||||||
|
@ -60,6 +60,11 @@ namespace dawn_native {
|
|||||||
void PopDebugGroup();
|
void PopDebugGroup();
|
||||||
void PushDebugGroup(const char* groupLabel);
|
void PushDebugGroup(const char* groupLabel);
|
||||||
|
|
||||||
|
void ResolveQuerySet(QuerySetBase* querySet,
|
||||||
|
uint32_t firstQuery,
|
||||||
|
uint32_t queryCount,
|
||||||
|
BufferBase* destination,
|
||||||
|
uint64_t destinationOffset);
|
||||||
void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
|
void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
|
||||||
|
|
||||||
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
|
CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
|
||||||
|
@ -128,6 +128,11 @@ namespace dawn_native {
|
|||||||
cmd->~PushDebugGroupCmd();
|
cmd->~PushDebugGroupCmd();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
ResolveQuerySetCmd* cmd = commands->NextCommand<ResolveQuerySetCmd>();
|
||||||
|
cmd->~ResolveQuerySetCmd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Command::SetComputePipeline: {
|
case Command::SetComputePipeline: {
|
||||||
SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
|
SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
|
||||||
cmd->~SetComputePipelineCmd();
|
cmd->~SetComputePipelineCmd();
|
||||||
@ -266,6 +271,11 @@ namespace dawn_native {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
commands->NextCommand<ResolveQuerySetCmd>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::SetComputePipeline:
|
case Command::SetComputePipeline:
|
||||||
commands->NextCommand<SetComputePipelineCmd>();
|
commands->NextCommand<SetComputePipelineCmd>();
|
||||||
break;
|
break;
|
||||||
|
@ -51,6 +51,7 @@ namespace dawn_native {
|
|||||||
InsertDebugMarker,
|
InsertDebugMarker,
|
||||||
PopDebugGroup,
|
PopDebugGroup,
|
||||||
PushDebugGroup,
|
PushDebugGroup,
|
||||||
|
ResolveQuerySet,
|
||||||
SetComputePipeline,
|
SetComputePipeline,
|
||||||
SetRenderPipeline,
|
SetRenderPipeline,
|
||||||
SetStencilReference,
|
SetStencilReference,
|
||||||
@ -186,6 +187,14 @@ namespace dawn_native {
|
|||||||
uint32_t length;
|
uint32_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ResolveQuerySetCmd {
|
||||||
|
Ref<QuerySetBase> querySet;
|
||||||
|
uint32_t firstQuery;
|
||||||
|
uint32_t queryCount;
|
||||||
|
Ref<BufferBase> destination;
|
||||||
|
uint64_t destinationOffset;
|
||||||
|
};
|
||||||
|
|
||||||
struct SetComputePipelineCmd {
|
struct SetComputePipelineCmd {
|
||||||
Ref<ComputePipelineBase> pipeline;
|
Ref<ComputePipelineBase> pipeline;
|
||||||
};
|
};
|
||||||
|
@ -836,6 +836,10 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
|
}
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
}
|
}
|
||||||
|
@ -716,6 +716,10 @@ namespace dawn_native { namespace metal {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
|
}
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
}
|
}
|
||||||
|
@ -716,6 +716,12 @@ namespace dawn_native { namespace opengl {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
// TODO(hao.x.li@intel.com): Resolve non-precise occlusion query.
|
||||||
|
SkipCommand(&mCommands, type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
// WriteTimestamp is not supported on OpenGL
|
// WriteTimestamp is not supported on OpenGL
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
@ -599,6 +599,10 @@ namespace dawn_native { namespace vulkan {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ResolveQuerySet: {
|
||||||
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
|
}
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation.");
|
||||||
}
|
}
|
||||||
|
@ -314,3 +314,149 @@ TEST_F(TimestampQueryValidationTest, WriteTimestampOnRenderPassEncoder) {
|
|||||||
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResolveQuerySetValidationTest : public QuerySetValidationTest {
|
||||||
|
protected:
|
||||||
|
wgpu::Buffer CreateBuffer(wgpu::Device cDevice, uint64_t size, wgpu::BufferUsage usage) {
|
||||||
|
wgpu::BufferDescriptor descriptor;
|
||||||
|
descriptor.size = size;
|
||||||
|
descriptor.usage = usage;
|
||||||
|
|
||||||
|
return cDevice.CreateBuffer(&descriptor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test resolve query set with invalid query set, first query and query count
|
||||||
|
TEST_F(ResolveQuerySetValidationTest, ResolveInvalidQuerySetAndIndexCount) {
|
||||||
|
constexpr uint32_t kQueryCount = 4;
|
||||||
|
|
||||||
|
wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount);
|
||||||
|
wgpu::Buffer destination =
|
||||||
|
CreateBuffer(device, kQueryCount * sizeof(uint64_t), wgpu::BufferUsage::QueryResolve);
|
||||||
|
|
||||||
|
// Success
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set from another device
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set if first query out of range
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, kQueryCount, 0, destination, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set if the sum of first query and query count is larger than queries
|
||||||
|
// number in the query set
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 1, kQueryCount, destination, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve a destroyed query set
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
querySet.Destroy();
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test resolve query set with invalid query set, first query and query count
|
||||||
|
TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) {
|
||||||
|
constexpr uint32_t kQueryCount = 4;
|
||||||
|
constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t);
|
||||||
|
|
||||||
|
wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount);
|
||||||
|
wgpu::Buffer destination = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::QueryResolve);
|
||||||
|
|
||||||
|
// Success
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 1, kQueryCount - 1, destination, 8);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a buffer created from another device
|
||||||
|
{
|
||||||
|
wgpu::Buffer bufferOnTimestamp =
|
||||||
|
CreateBuffer(deviceWithTimestamp, kBufferSize, wgpu::BufferUsage::QueryResolve);
|
||||||
|
wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, bufferOnTimestamp, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a buffer if offset is not a multiple of 8 bytes
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 4);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a buffer if the data size overflow the buffer
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 8);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a buffer if the offset is past the end of the buffer
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, 1, destination, kBufferSize);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a buffer does not have the usage of QueryResolve
|
||||||
|
{
|
||||||
|
wgpu::Buffer dstBuffer = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::CopyDst);
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, dstBuffer, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail to resolve query set to a destroyed buffer.
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
destination.Destroy();
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that in 32bit mode the computation of queryCount * sizeof(uint64_t) doesn't overflow (which
|
||||||
|
// would skip validation).
|
||||||
|
TEST_F(ResolveQuerySetValidationTest, BufferOverflowOn32Bits) {
|
||||||
|
// If compiling for 32-bits mode, the data size calculated by queryCount * sizeof(uint64_t)
|
||||||
|
// is 8, which is less than the buffer size.
|
||||||
|
constexpr uint32_t kQueryCount = std::numeric_limits<uint32_t>::max() / sizeof(uint64_t) + 2;
|
||||||
|
|
||||||
|
wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount);
|
||||||
|
wgpu::Buffer destination = CreateBuffer(device, 1024, wgpu::BufferUsage::QueryResolve);
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user