Update destinationOffset alignment to 256 in resolveQuerySet
From macOS 11+, Metal requires the minimum buffer offset alignment to be 256 bytes. We have updated the destinationOffset alignment of resolveQuerySet to 256 in SPEC. This PR is changing the implementation. Bug: dawn:940 Change-Id: Ie3c69d6a90adb76038dbac95ae2158a38da583f3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63762 Commit-Queue: Hao Li <hao.x.li@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
c9c366e825
commit
a10f5331f2
|
@ -382,10 +382,9 @@ namespace dawn_native {
|
||||||
"set");
|
"set");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The destinationOffset must be a multiple of 8 bytes on D3D12 and Vulkan
|
if (destinationOffset % 256 != 0) {
|
||||||
if (destinationOffset % 8 != 0) {
|
|
||||||
return DAWN_VALIDATION_ERROR(
|
return DAWN_VALIDATION_ERROR(
|
||||||
"The alignment offset into the destination buffer must be a multiple of 8 "
|
"The alignment offset into the destination buffer must be a multiple of 256 "
|
||||||
"bytes");
|
"bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1362,9 +1362,9 @@ TEST_P(BufferZeroInitTest, ResolveQuerySet) {
|
||||||
// destinationOffset > 0 and destinationOffset + 8 * queryCount <= kBufferSize
|
// destinationOffset > 0 and destinationOffset + 8 * queryCount <= kBufferSize
|
||||||
{
|
{
|
||||||
constexpr uint32_t kQueryCount = 1;
|
constexpr uint32_t kQueryCount = 1;
|
||||||
constexpr uint64_t kDestinationOffset = 8u;
|
constexpr uint64_t kDestinationOffset = 256u;
|
||||||
|
|
||||||
wgpu::Buffer destination = CreateBuffer(kBufferSize, kBufferUsage);
|
wgpu::Buffer destination = CreateBuffer(kBufferSize + kDestinationOffset, kBufferUsage);
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.WriteTimestamp(querySet, 0);
|
encoder.WriteTimestamp(querySet, 0);
|
||||||
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, kDestinationOffset);
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, kDestinationOffset);
|
||||||
|
|
|
@ -31,6 +31,8 @@ class QueryTests : public DawnTest {
|
||||||
// Clear the content of the result buffer into 0xFFFFFFFF.
|
// Clear the content of the result buffer into 0xFFFFFFFF.
|
||||||
constexpr static uint64_t kSentinelValue = ~uint64_t(0u);
|
constexpr static uint64_t kSentinelValue = ~uint64_t(0u);
|
||||||
constexpr static uint64_t kZero = 0u;
|
constexpr static uint64_t kZero = 0u;
|
||||||
|
constexpr uint64_t kMinDestinationOffset = 256;
|
||||||
|
constexpr uint64_t kMinCount = kMinDestinationOffset / sizeof(uint64_t);
|
||||||
|
|
||||||
class OcclusionExpectation : public detail::Expectation {
|
class OcclusionExpectation : public detail::Expectation {
|
||||||
public:
|
public:
|
||||||
|
@ -405,12 +407,15 @@ TEST_P(OcclusionQueryTests, ResolveToBufferWithOffset) {
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t) + kMinDestinationOffset;
|
||||||
|
constexpr uint64_t kCount = kQueryCount + kMinCount;
|
||||||
|
|
||||||
// Resolve the query result to first slot in the buffer, other slots should not be written.
|
// Resolve the query result to first slot in the buffer, other slots should not be written.
|
||||||
{
|
{
|
||||||
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
|
||||||
// Set sentinel values to check the query is resolved to the correct slot of the buffer.
|
// Set sentinel values to check the query is resolved to the correct slot of the buffer.
|
||||||
std::vector<uint64_t> sentinelValues(kQueryCount, kSentinelValue);
|
std::vector<uint64_t> sentinelValues(kCount, kSentinelValue);
|
||||||
queue.WriteBuffer(destination, 0, sentinelValues.data(), kQueryCount * sizeof(uint64_t));
|
queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
|
encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
|
||||||
|
@ -419,23 +424,24 @@ TEST_P(OcclusionQueryTests, ResolveToBufferWithOffset) {
|
||||||
|
|
||||||
EXPECT_BUFFER(destination, 0, sizeof(uint64_t),
|
EXPECT_BUFFER(destination, 0, sizeof(uint64_t),
|
||||||
new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
|
new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
|
||||||
EXPECT_BUFFER_U64_RANGE_EQ(&kSentinelValue, destination, sizeof(uint64_t), 1);
|
EXPECT_BUFFER_U64_RANGE_EQ(sentinelValues.data(), destination, sizeof(uint64_t),
|
||||||
|
kCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the query result to second slot in the buffer, the first one should not be written.
|
// Resolve the query result to second slot in the buffer, the first one should not be written.
|
||||||
{
|
{
|
||||||
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
|
||||||
// Set sentinel values to check the query is resolved to the correct slot of the buffer.
|
// Set sentinel values to check the query is resolved to the correct slot of the buffer.
|
||||||
std::vector<uint64_t> sentinelValues(kQueryCount, kSentinelValue);
|
std::vector<uint64_t> sentinelValues(kCount, kSentinelValue);
|
||||||
queue.WriteBuffer(destination, 0, sentinelValues.data(), kQueryCount * sizeof(uint64_t));
|
queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.ResolveQuerySet(querySet, 0, 1, destination, sizeof(uint64_t));
|
encoder.ResolveQuerySet(querySet, 0, 1, destination, kMinDestinationOffset);
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
EXPECT_BUFFER_U64_RANGE_EQ(&kSentinelValue, destination, 0, 1);
|
EXPECT_BUFFER_U64_RANGE_EQ(sentinelValues.data(), destination, 0, kMinCount);
|
||||||
EXPECT_BUFFER(destination, sizeof(uint64_t), sizeof(uint64_t),
|
EXPECT_BUFFER(destination, kMinDestinationOffset, sizeof(uint64_t),
|
||||||
new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
|
new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -769,35 +775,44 @@ TEST_P(TimestampQueryTests, ResolveToBufferWithOffset) {
|
||||||
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
|
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
|
||||||
|
|
||||||
constexpr uint32_t kQueryCount = 2;
|
constexpr uint32_t kQueryCount = 2;
|
||||||
constexpr uint64_t kZero = 0;
|
constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t) + kMinDestinationOffset;
|
||||||
|
constexpr uint64_t kCount = kQueryCount + kMinCount;
|
||||||
|
|
||||||
wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
|
wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
|
||||||
|
|
||||||
// Resolve the query result to first slot in the buffer, other slots should not be written
|
// Resolve the query result to first slot in the buffer, other slots should not be written
|
||||||
{
|
{
|
||||||
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.WriteTimestamp(querySet, 0);
|
encoder.WriteTimestamp(querySet, 0);
|
||||||
encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
|
encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
std::vector<uint64_t> zeros(kCount - 1, kZero);
|
||||||
EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new TimestampExpectation);
|
EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new TimestampExpectation);
|
||||||
EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, sizeof(uint64_t), 1);
|
EXPECT_BUFFER_U64_RANGE_EQ(zeros.data(), destination, sizeof(uint64_t), kCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the query result to the buffer with offset, the slots before the offset
|
// Resolve the query result to the buffer with offset, the slots before the offset
|
||||||
// should not be written
|
// should not be written
|
||||||
{
|
{
|
||||||
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
|
||||||
|
// Set sentinel values to check the query is resolved to the correct slot of the buffer.
|
||||||
|
std::vector<uint64_t> sentinelValues(kCount, kZero);
|
||||||
|
queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.WriteTimestamp(querySet, 0);
|
encoder.WriteTimestamp(querySet, 0);
|
||||||
encoder.ResolveQuerySet(querySet, 0, 1, destination, sizeof(uint64_t));
|
encoder.ResolveQuerySet(querySet, 0, 1, destination, kMinDestinationOffset);
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, 0, 1);
|
std::vector<uint64_t> zeros(kMinCount, kZero);
|
||||||
EXPECT_BUFFER(destination, sizeof(uint64_t), sizeof(uint64_t), new TimestampExpectation);
|
EXPECT_BUFFER_U64_RANGE_EQ(zeros.data(), destination, 0, kMinCount);
|
||||||
|
EXPECT_BUFFER(destination, kMinDestinationOffset, sizeof(uint64_t),
|
||||||
|
new TimestampExpectation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,17 +824,17 @@ TEST_P(TimestampQueryTests, ResolveTwiceToSameBuffer) {
|
||||||
// the issue is fixed.
|
// the issue is fixed.
|
||||||
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
|
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
|
||||||
|
|
||||||
constexpr uint32_t kQueryCount = 3;
|
constexpr uint32_t kQueryCount = kMinCount + 2;
|
||||||
|
|
||||||
wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
|
wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
|
||||||
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.WriteTimestamp(querySet, 0);
|
for (uint32_t i = 0; i < kQueryCount; i++) {
|
||||||
encoder.WriteTimestamp(querySet, 1);
|
encoder.WriteTimestamp(querySet, i);
|
||||||
encoder.WriteTimestamp(querySet, 2);
|
}
|
||||||
encoder.ResolveQuerySet(querySet, 0, 2, destination, 0);
|
encoder.ResolveQuerySet(querySet, 0, kMinCount + 1, destination, 0);
|
||||||
encoder.ResolveQuerySet(querySet, 1, 2, destination, sizeof(uint64_t));
|
encoder.ResolveQuerySet(querySet, kMinCount, 2, destination, kMinDestinationOffset);
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
|
|
@ -555,7 +555,8 @@ TEST_F(ResolveQuerySetValidationTest, ResolveInvalidQuerySetAndIndexCount) {
|
||||||
// Test resolve query set with invalid query set, first query and query count
|
// Test resolve query set with invalid query set, first query and query count
|
||||||
TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) {
|
TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) {
|
||||||
constexpr uint32_t kQueryCount = 4;
|
constexpr uint32_t kQueryCount = 4;
|
||||||
constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t);
|
constexpr uint64_t kBufferSize =
|
||||||
|
(kQueryCount - 1) * sizeof(uint64_t) + 256 /*destinationOffset*/;
|
||||||
|
|
||||||
wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount);
|
wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount);
|
||||||
wgpu::Buffer destination = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::QueryResolve);
|
wgpu::Buffer destination = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::QueryResolve);
|
||||||
|
@ -563,7 +564,7 @@ TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) {
|
||||||
// Success
|
// Success
|
||||||
{
|
{
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.ResolveQuerySet(querySet, 1, kQueryCount - 1, destination, 8);
|
encoder.ResolveQuerySet(querySet, 1, kQueryCount - 1, destination, 256);
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
|
||||||
wgpu::Queue queue = device.GetQueue();
|
wgpu::Queue queue = device.GetQueue();
|
||||||
|
@ -580,17 +581,17 @@ TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) {
|
||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail to resolve query set to a buffer if offset is not a multiple of 8 bytes
|
// Fail to resolve query set to a buffer if offset is not a multiple of 256 bytes
|
||||||
{
|
{
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 4);
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 128);
|
||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail to resolve query set to a buffer if the data size overflow the buffer
|
// Fail to resolve query set to a buffer if the data size overflow the buffer
|
||||||
{
|
{
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 8);
|
encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 256);
|
||||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,101 @@ namespace {
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class QueryInternalShaderTests : public DawnTest {};
|
constexpr static uint64_t kSentinelValue = ~uint64_t(0u);
|
||||||
|
|
||||||
|
// A gpu frequency on Intel D3D12 (ticks/second)
|
||||||
|
constexpr uint64_t kGPUFrequency = 12000048u;
|
||||||
|
constexpr uint64_t kNsPerSecond = 1000000000u;
|
||||||
|
// Timestamp period in nanoseconds
|
||||||
|
constexpr float kPeriod = static_cast<float>(kNsPerSecond) / kGPUFrequency;
|
||||||
|
|
||||||
|
class QueryInternalShaderTests : public DawnTest {
|
||||||
|
protected:
|
||||||
|
// Original timestamp values in query set for testing
|
||||||
|
const std::vector<uint64_t> querySetValues = {
|
||||||
|
kSentinelValue, // garbage data which is not written at beginning
|
||||||
|
10079569507, // t0
|
||||||
|
10394415012, // t1
|
||||||
|
kSentinelValue, // garbage data which is not written between timestamps
|
||||||
|
11713454943, // t2
|
||||||
|
38912556941, // t3 (big value)
|
||||||
|
10080295766, // t4 (reset)
|
||||||
|
12159966783, // t5 (after reset)
|
||||||
|
12651224612, // t6
|
||||||
|
39872473956, // t7
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t kQueryCount = querySetValues.size();
|
||||||
|
|
||||||
|
// Timestamps available state
|
||||||
|
const std::vector<uint32_t> availabilities = {0, 1, 1, 0, 1, 1, 1, 1, 1, 1};
|
||||||
|
|
||||||
|
const std::vector<uint64_t> GetExpectedResults(const std::vector<uint64_t>& origin,
|
||||||
|
uint32_t start,
|
||||||
|
uint32_t firstQuery,
|
||||||
|
uint32_t queryCount) {
|
||||||
|
std::vector<uint64_t> expected(origin.begin(), origin.end());
|
||||||
|
for (size_t i = 0; i < queryCount; i++) {
|
||||||
|
if (availabilities[firstQuery + i] == 0) {
|
||||||
|
// Not a available timestamp, write 0
|
||||||
|
expected[start + i] = 0u;
|
||||||
|
} else {
|
||||||
|
// Maybe the timestamp * period is larger than the maximum of uint64, so cast the
|
||||||
|
// delta value to double (higher precision than float)
|
||||||
|
expected[start + i] =
|
||||||
|
static_cast<uint64_t>(static_cast<double>(origin[start + i]) * kPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunTest(uint32_t firstQuery, uint32_t queryCount, uint32_t destinationOffset) {
|
||||||
|
ASSERT(destinationOffset % 256 == 0);
|
||||||
|
|
||||||
|
uint64_t size = queryCount * sizeof(uint64_t) + destinationOffset;
|
||||||
|
|
||||||
|
// The resolve buffer storing original timestamps and the converted values
|
||||||
|
wgpu::BufferDescriptor timestampsDesc;
|
||||||
|
timestampsDesc.size = size;
|
||||||
|
timestampsDesc.usage = wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc |
|
||||||
|
wgpu::BufferUsage::CopyDst;
|
||||||
|
wgpu::Buffer timestampsBuffer = device.CreateBuffer(×tampsDesc);
|
||||||
|
|
||||||
|
// Set sentinel values to check the slots before the destination offset should not be
|
||||||
|
// converted
|
||||||
|
std::vector<uint64_t> timestampValues(size / sizeof(uint64_t), 1u);
|
||||||
|
uint32_t start = destinationOffset / sizeof(uint64_t);
|
||||||
|
for (uint32_t i = 0; i < queryCount; i++) {
|
||||||
|
timestampValues[start + i] = querySetValues[firstQuery + 1];
|
||||||
|
}
|
||||||
|
// Write sentinel values and orignal timestamps to timestamps buffer
|
||||||
|
queue.WriteBuffer(timestampsBuffer, 0, timestampValues.data(), size);
|
||||||
|
|
||||||
|
// The buffer indicating which values are available timestamps
|
||||||
|
wgpu::Buffer availabilityBuffer =
|
||||||
|
utils::CreateBufferFromData(device, availabilities.data(),
|
||||||
|
kQueryCount * sizeof(uint32_t), wgpu::BufferUsage::Storage);
|
||||||
|
|
||||||
|
// The params uniform buffer
|
||||||
|
dawn_native::TimestampParams params = {firstQuery, queryCount, destinationOffset, kPeriod};
|
||||||
|
wgpu::Buffer paramsBuffer = utils::CreateBufferFromData(device, ¶ms, sizeof(params),
|
||||||
|
wgpu::BufferUsage::Uniform);
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
EncodeConvertTimestampsToNanoseconds(encoder, timestampsBuffer, availabilityBuffer,
|
||||||
|
paramsBuffer);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
const std::vector<uint64_t> expected =
|
||||||
|
GetExpectedResults(timestampValues, start, firstQuery, queryCount);
|
||||||
|
|
||||||
|
EXPECT_BUFFER(timestampsBuffer, 0, size,
|
||||||
|
new InternalShaderExpectation(expected.data(), size / sizeof(uint64_t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
// Test the accuracy of timestamp compute shader which uses unsigned 32-bit integers to simulate
|
// Test the accuracy of timestamp compute shader which uses unsigned 32-bit integers to simulate
|
||||||
// unsigned 64-bit integers (timestamps) multiplied by float (period).
|
// unsigned 64-bit integers (timestamps) multiplied by float (period).
|
||||||
|
@ -91,146 +185,17 @@ TEST_P(QueryInternalShaderTests, TimestampComputeShader) {
|
||||||
|
|
||||||
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
|
||||||
|
|
||||||
constexpr uint32_t kTimestampCount = 10u;
|
|
||||||
// A gpu frequency on Intel D3D12 (ticks/second)
|
|
||||||
constexpr uint64_t kGPUFrequency = 12000048u;
|
|
||||||
constexpr uint64_t kNsPerSecond = 1000000000u;
|
|
||||||
// Timestamp period in nanoseconds
|
|
||||||
constexpr float kPeriod = static_cast<float>(kNsPerSecond) / kGPUFrequency;
|
|
||||||
|
|
||||||
// Original timestamp values for testing
|
|
||||||
std::vector<uint64_t> timestamps = {
|
|
||||||
1, // garbage data which is not written at beginning
|
|
||||||
10079569507, // t0
|
|
||||||
10394415012, // t1
|
|
||||||
1, // garbage data which is not written between timestamps
|
|
||||||
11713454943, // t2
|
|
||||||
38912556941, // t3 (big value)
|
|
||||||
10080295766, // t4 (reset)
|
|
||||||
12159966783, // t5 (after reset)
|
|
||||||
12651224612, // t6
|
|
||||||
39872473956, // t7
|
|
||||||
};
|
|
||||||
|
|
||||||
// The buffer indicating which values are available timestamps
|
|
||||||
std::vector<uint32_t> availabilities = {0, 1, 1, 0, 1, 1, 1, 1, 1, 1};
|
|
||||||
wgpu::Buffer availabilityBuffer =
|
|
||||||
utils::CreateBufferFromData(device, availabilities.data(),
|
|
||||||
kTimestampCount * sizeof(uint32_t), wgpu::BufferUsage::Storage);
|
|
||||||
|
|
||||||
// The resolve buffer storing original timestamps and the converted values
|
|
||||||
wgpu::BufferDescriptor timestampsDesc;
|
|
||||||
timestampsDesc.size = kTimestampCount * sizeof(uint64_t);
|
|
||||||
timestampsDesc.usage =
|
|
||||||
wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
||||||
wgpu::Buffer timestampsBuffer = device.CreateBuffer(×tampsDesc);
|
|
||||||
|
|
||||||
auto PrepareExpectedResults = [&](uint32_t first, uint32_t count,
|
|
||||||
uint32_t offset) -> std::vector<uint64_t> {
|
|
||||||
ASSERT(offset % sizeof(uint64_t) == 0);
|
|
||||||
std::vector<uint64_t> expected;
|
|
||||||
for (size_t i = 0; i < kTimestampCount; i++) {
|
|
||||||
// The data out of the rang [first, first + count] remains as it is
|
|
||||||
if (i < first || i >= first + count) {
|
|
||||||
expected.push_back(timestamps[i]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (availabilities[i] == 0) {
|
|
||||||
// Not a available timestamp, write 0
|
|
||||||
expected.push_back(0u);
|
|
||||||
} else {
|
|
||||||
// Maybe the timestamp * period is larger than the maximum of uint64, so cast the
|
|
||||||
// delta value to double (higher precision than float)
|
|
||||||
expected.push_back(
|
|
||||||
static_cast<uint64_t>(static_cast<double>(timestamps[i]) * kPeriod));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expected;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert timestamps in timestamps buffer with offset 0
|
// Convert timestamps in timestamps buffer with offset 0
|
||||||
// Test for ResolveQuerySet(querySet, 0, kTimestampCount, timestampsBuffer, 0)
|
// Test for ResolveQuerySet(querySet, 0, kQueryCount, timestampsBuffer, 0)
|
||||||
{
|
RunTest(0, kQueryCount, 0);
|
||||||
constexpr uint32_t kFirst = 0u;
|
|
||||||
constexpr uint32_t kOffset = 0u;
|
|
||||||
|
|
||||||
// Write orignal timestamps to timestamps buffer
|
// Convert timestamps in timestamps buffer with offset 256
|
||||||
queue.WriteBuffer(timestampsBuffer, 0, timestamps.data(),
|
// Test for ResolveQuerySet(querySet, 1, kQueryCount - 1, timestampsBuffer, 256)
|
||||||
kTimestampCount * sizeof(uint64_t));
|
RunTest(1, kQueryCount - 1, 256);
|
||||||
|
|
||||||
// The params uniform buffer
|
// Convert partial timestamps in timestamps buffer with offset 256
|
||||||
dawn_native::TimestampParams params = {kFirst, kTimestampCount, kOffset, kPeriod};
|
// Test for ResolveQuerySet(querySet, 1, 4, timestampsBuffer, 256)
|
||||||
wgpu::Buffer paramsBuffer = utils::CreateBufferFromData(device, ¶ms, sizeof(params),
|
RunTest(1, 4, 256);
|
||||||
wgpu::BufferUsage::Uniform);
|
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
||||||
EncodeConvertTimestampsToNanoseconds(encoder, timestampsBuffer, availabilityBuffer,
|
|
||||||
paramsBuffer);
|
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
|
||||||
queue.Submit(1, &commands);
|
|
||||||
|
|
||||||
// Expected results: Timestamp * period
|
|
||||||
std::vector<uint64_t> expected = PrepareExpectedResults(0, kTimestampCount, kOffset);
|
|
||||||
EXPECT_BUFFER(timestampsBuffer, 0, kTimestampCount * sizeof(uint64_t),
|
|
||||||
new InternalShaderExpectation(expected.data(), kTimestampCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert timestamps in timestamps buffer with offset 8
|
|
||||||
// Test for ResolveQuerySet(querySet, 1, kTimestampCount - 1, timestampsBuffer, 8)
|
|
||||||
{
|
|
||||||
constexpr uint32_t kFirst = 1u;
|
|
||||||
constexpr uint32_t kOffset = 8u;
|
|
||||||
|
|
||||||
// Write orignal timestamps to timestamps buffer
|
|
||||||
queue.WriteBuffer(timestampsBuffer, 0, timestamps.data(),
|
|
||||||
kTimestampCount * sizeof(uint64_t));
|
|
||||||
|
|
||||||
// The params uniform buffer
|
|
||||||
dawn_native::TimestampParams params = {kFirst, kTimestampCount - kFirst, kOffset, kPeriod};
|
|
||||||
wgpu::Buffer paramsBuffer = utils::CreateBufferFromData(device, ¶ms, sizeof(params),
|
|
||||||
wgpu::BufferUsage::Uniform);
|
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
||||||
EncodeConvertTimestampsToNanoseconds(encoder, timestampsBuffer, availabilityBuffer,
|
|
||||||
paramsBuffer);
|
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
|
||||||
queue.Submit(1, &commands);
|
|
||||||
|
|
||||||
// Expected results: Timestamp * period
|
|
||||||
std::vector<uint64_t> expected =
|
|
||||||
PrepareExpectedResults(kFirst, kTimestampCount - kFirst, kOffset);
|
|
||||||
EXPECT_BUFFER(timestampsBuffer, 0, kTimestampCount * sizeof(uint64_t),
|
|
||||||
new InternalShaderExpectation(expected.data(), kTimestampCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert partial timestamps in timestamps buffer with offset 8
|
|
||||||
// Test for ResolveQuerySet(querySet, 1, 3, timestampsBuffer, 8)
|
|
||||||
{
|
|
||||||
constexpr uint32_t kFirst = 1u;
|
|
||||||
constexpr uint32_t kCount = 3u;
|
|
||||||
constexpr uint32_t kOffset = 8u;
|
|
||||||
|
|
||||||
// Write orignal timestamps to timestamps buffer
|
|
||||||
queue.WriteBuffer(timestampsBuffer, 0, timestamps.data(),
|
|
||||||
kTimestampCount * sizeof(uint64_t));
|
|
||||||
|
|
||||||
// The params uniform buffer
|
|
||||||
dawn_native::TimestampParams params = {kFirst, kCount, kOffset, kPeriod};
|
|
||||||
wgpu::Buffer paramsBuffer = utils::CreateBufferFromData(device, ¶ms, sizeof(params),
|
|
||||||
wgpu::BufferUsage::Uniform);
|
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
||||||
EncodeConvertTimestampsToNanoseconds(encoder, timestampsBuffer, availabilityBuffer,
|
|
||||||
paramsBuffer);
|
|
||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
|
||||||
queue.Submit(1, &commands);
|
|
||||||
|
|
||||||
// Expected results: Timestamp * period
|
|
||||||
std::vector<uint64_t> expected = PrepareExpectedResults(kFirst, kCount, kOffset);
|
|
||||||
EXPECT_BUFFER(timestampsBuffer, 0, kTimestampCount * sizeof(uint64_t),
|
|
||||||
new InternalShaderExpectation(expected.data(), kTimestampCount));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(QueryInternalShaderTests, D3D12Backend(), MetalBackend(), VulkanBackend());
|
DAWN_INSTANTIATE_TEST(QueryInternalShaderTests, D3D12Backend(), MetalBackend(), VulkanBackend());
|
||||||
|
|
Loading…
Reference in New Issue