Add more tests in white_box/D3D12GPUTimestampCalibrationTests.cpp
Add more coverage for timestamp query on D3D12 backend to make sure
timestamps are converted correctly:
- All timestamp queries inside and outside passes
- The 'disable_timestamp_query_conversion' toggle disabled and enabled
Bug: dawn:1250
Change-Id: Ibdc6b35faed7cc1e1a8b60df4a5032914b411bc1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108022
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/tests/white_box/D3D12GPUTimestampCalibrationTests.cpp b/src/dawn/tests/white_box/D3D12GPUTimestampCalibrationTests.cpp
index 2bad627..a6681e3 100644
--- a/src/dawn/tests/white_box/D3D12GPUTimestampCalibrationTests.cpp
+++ b/src/dawn/tests/white_box/D3D12GPUTimestampCalibrationTests.cpp
@@ -22,23 +22,54 @@
namespace dawn::native::d3d12 {
namespace {
+
+using FeatureName = wgpu::FeatureName;
+enum class EncoderType { NonPass, ComputePass, RenderPass };
+
+std::ostream& operator<<(std::ostream& o, EncoderType type) {
+ switch (type) {
+ case EncoderType::NonPass:
+ o << "NonPass";
+ break;
+ case EncoderType::ComputePass:
+ o << "ComputePass";
+ break;
+ case EncoderType::RenderPass:
+ o << "RenderPass";
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ return o;
+}
+
+DAWN_TEST_PARAM_STRUCT(GPUTimestampCalibrationTestParams, FeatureName, EncoderType);
+
class ExpectBetweenTimestamps : public ::detail::Expectation {
public:
~ExpectBetweenTimestamps() override = default;
- ExpectBetweenTimestamps(uint64_t value0, uint64_t value1) {
- mValue0 = value0;
- mValue1 = value1;
- }
+ ExpectBetweenTimestamps(uint64_t minValue, uint64_t maxValue, float errorToleranceRatio = 0.0f)
+ : mMinValue(minValue), mMaxValue(maxValue), mErrorToleranceRatio(errorToleranceRatio) {}
- // Expect the actual results are between mValue0 and mValue1.
+ // Expect the actual results are between mMinValue and mMaxValue with error tolerance ratio.
testing::AssertionResult Check(const void* data, size_t size) override {
const uint64_t* actual = static_cast<const uint64_t*>(data);
+
+ if (mErrorToleranceRatio != 0.0f) {
+ mMinValue -=
+ static_cast<uint64_t>(static_cast<double>(mMinValue * mErrorToleranceRatio));
+ mMaxValue +=
+ static_cast<uint64_t>(static_cast<double>(mMaxValue * mErrorToleranceRatio));
+ }
+
for (size_t i = 0; i < size / sizeof(uint64_t); ++i) {
- if (actual[i] < mValue0 || actual[i] > mValue1) {
+ if (actual[i] < mMinValue || actual[i] > mMaxValue) {
return testing::AssertionFailure()
- << "Expected data[" << i << "] to be between " << mValue0 << " and "
- << mValue1 << ", actual " << actual[i] << std::endl;
+ << "Expected data[" << i << "] to be between " << mMinValue << " and "
+ << mMaxValue << ", actual " << actual[i] << std::endl;
}
}
@@ -46,75 +77,186 @@
}
private:
- uint64_t mValue0;
- uint64_t mValue1;
+ uint64_t mMinValue;
+ uint64_t mMaxValue;
+ float mErrorToleranceRatio;
};
} // anonymous namespace
-class D3D12GPUTimestampCalibrationTests : public DawnTest {
+class D3D12GPUTimestampCalibrationTests
+ : public DawnTestWithParams<GPUTimestampCalibrationTestParams> {
protected:
void SetUp() override {
- DawnTest::SetUp();
+ DawnTestWithParams<GPUTimestampCalibrationTestParams>::SetUp();
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
// Requires that timestamp query feature is enabled and timestamp query conversion is
// disabled.
- DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::TimestampQuery}) ||
- !HasToggleEnabled("disable_timestamp_query_conversion"));
+ DAWN_TEST_UNSUPPORTED_IF(!mIsFeatureSupported);
+ // The "timestamp-query-inside-passes" feature is not supported on command encoder.
+ DAWN_TEST_UNSUPPORTED_IF(GetParam().mFeatureName ==
+ wgpu::FeatureName::TimestampQueryInsidePasses &&
+ GetParam().mEncoderType == EncoderType::NonPass);
}
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
std::vector<wgpu::FeatureName> requiredFeatures = {};
- if (SupportsFeatures({wgpu::FeatureName::TimestampQuery})) {
- requiredFeatures.push_back(wgpu::FeatureName::TimestampQuery);
+ if (SupportsFeatures({GetParam().mFeatureName})) {
+ requiredFeatures.push_back(GetParam().mFeatureName);
+ mIsFeatureSupported = true;
}
return requiredFeatures;
}
+
+ void EncodeTimestampQueryOnComputePass(const wgpu::CommandEncoder& encoder,
+ const wgpu::QuerySet& querySet) {
+ switch (GetParam().mFeatureName) {
+ case wgpu::FeatureName::TimestampQuery: {
+ std::vector<wgpu::ComputePassTimestampWrite> timestampWrites;
+ timestampWrites.push_back(
+ {querySet, 0, wgpu::ComputePassTimestampLocation::Beginning});
+ timestampWrites.push_back({querySet, 1, wgpu::ComputePassTimestampLocation::End});
+
+ wgpu::ComputePassDescriptor descriptor;
+ descriptor.timestampWriteCount = timestampWrites.size();
+ descriptor.timestampWrites = timestampWrites.data();
+
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass(&descriptor);
+ pass.End();
+ break;
+ }
+ case wgpu::FeatureName::TimestampQueryInsidePasses: {
+ wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
+ pass.WriteTimestamp(querySet, 0);
+ pass.WriteTimestamp(querySet, 1);
+ pass.End();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ void EncodeTimestampQueryOnRenderPass(const wgpu::CommandEncoder& encoder,
+ const wgpu::QuerySet& querySet) {
+ constexpr static unsigned int kRTSize = 4;
+ utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
+
+ switch (GetParam().mFeatureName) {
+ case wgpu::FeatureName::TimestampQuery: {
+ std::vector<wgpu::RenderPassTimestampWrite> timestampWrites;
+ timestampWrites.push_back(
+ {querySet, 0, wgpu::RenderPassTimestampLocation::Beginning});
+ timestampWrites.push_back({querySet, 1, wgpu::RenderPassTimestampLocation::End});
+
+ renderPass.renderPassInfo.timestampWriteCount = timestampWrites.size();
+ renderPass.renderPassInfo.timestampWrites = timestampWrites.data();
+
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.End();
+ break;
+ }
+ case wgpu::FeatureName::TimestampQueryInsidePasses: {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.WriteTimestamp(querySet, 0);
+ pass.WriteTimestamp(querySet, 1);
+ pass.End();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ void RunTest() {
+ constexpr uint32_t kQueryCount = 2;
+
+ // Create query set
+ wgpu::QuerySetDescriptor querySetDescriptor;
+ querySetDescriptor.count = kQueryCount;
+ querySetDescriptor.type = wgpu::QueryType::Timestamp;
+ wgpu::QuerySet querySet = device.CreateQuerySet(&querySetDescriptor);
+
+ // Create resolve buffer
+ wgpu::BufferDescriptor bufferDescriptor;
+ bufferDescriptor.size = kQueryCount * sizeof(uint64_t);
+ bufferDescriptor.usage = wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc |
+ wgpu::BufferUsage::CopyDst;
+ wgpu::Buffer destination = device.CreateBuffer(&bufferDescriptor);
+
+ // Encode timestamp query
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ switch (GetParam().mEncoderType) {
+ case EncoderType::NonPass: {
+ // The "timestamp-query-inside-passes" feature is not supported on command encoder
+ ASSERT(GetParam().mFeatureName != wgpu::FeatureName::TimestampQueryInsidePasses);
+ encoder.WriteTimestamp(querySet, 0);
+ encoder.WriteTimestamp(querySet, 1);
+ break;
+ }
+ case EncoderType::ComputePass: {
+ EncodeTimestampQueryOnComputePass(encoder, querySet);
+ break;
+ }
+ case EncoderType::RenderPass: {
+ EncodeTimestampQueryOnRenderPass(encoder, querySet);
+ break;
+ }
+ }
+ wgpu::CommandBuffer commands = encoder.Finish();
+
+ // Start calibration between GPU timestamp and CPU timestamp
+ Device* d3DDevice = reinterpret_cast<Device*>(device.Get());
+ uint64_t gpuTimestamp0, gpuTimestamp1;
+ uint64_t cpuTimestamp0, cpuTimestamp1;
+ d3DDevice->GetCommandQueue()->GetClockCalibration(&gpuTimestamp0, &cpuTimestamp0);
+ queue.Submit(1, &commands);
+ WaitForAllOperations();
+ d3DDevice->GetCommandQueue()->GetClockCalibration(&gpuTimestamp1, &cpuTimestamp1);
+
+ // Separate resolve queryset to reduce the execution time of the queue with WriteTimestamp,
+ // so that the timestamp in the querySet will be closer to both gpuTimestamps from
+ // GetClockCalibration.
+ wgpu::CommandEncoder resolveEncoder = device.CreateCommandEncoder();
+ resolveEncoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
+ wgpu::CommandBuffer resolveCommands = resolveEncoder.Finish();
+ queue.Submit(1, &resolveCommands);
+
+ float errorToleranceRatio = 0.0f;
+ if (!HasToggleEnabled("disable_timestamp_query_conversion")) {
+ uint64_t gpuFrequency;
+ d3DDevice->GetCommandQueue()->GetTimestampFrequency(&gpuFrequency);
+ float period = static_cast<float>(1e9) / gpuFrequency;
+ gpuTimestamp0 = static_cast<uint64_t>(static_cast<double>(gpuTimestamp0 * period));
+ gpuTimestamp1 = static_cast<uint64_t>(static_cast<double>(gpuTimestamp1 * period));
+
+ // We have 15 bits of precision in the timestamp query conversion so we
+ // expect that for the error tolerance.
+ errorToleranceRatio = 1.0 / (1 << 15); // about 3e-5.
+ }
+
+ EXPECT_BUFFER(
+ destination, 0, kQueryCount * sizeof(uint64_t),
+ new ExpectBetweenTimestamps(gpuTimestamp0, gpuTimestamp1, errorToleranceRatio));
+ }
+
+ private:
+ bool mIsFeatureSupported = false;
};
// Check that the timestamps got by timestamp query are between the two timestamps from
-// GetClockCalibration() after the timestamp conversion is disabled.
-TEST_P(D3D12GPUTimestampCalibrationTests, TimestampsInOrder) {
- constexpr uint32_t kQueryCount = 2;
-
- wgpu::QuerySetDescriptor querySetDescriptor;
- querySetDescriptor.count = kQueryCount;
- querySetDescriptor.type = wgpu::QueryType::Timestamp;
- wgpu::QuerySet querySet = device.CreateQuerySet(&querySetDescriptor);
-
- wgpu::BufferDescriptor bufferDescriptor;
- bufferDescriptor.size = kQueryCount * sizeof(uint64_t);
- bufferDescriptor.usage =
- wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
- wgpu::Buffer destination = device.CreateBuffer(&bufferDescriptor);
-
- wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- encoder.WriteTimestamp(querySet, 0);
- encoder.WriteTimestamp(querySet, 1);
- wgpu::CommandBuffer commands = encoder.Finish();
-
- Device* d3DDevice = reinterpret_cast<Device*>(device.Get());
- uint64_t gpuTimestamp0, gpuTimestamp1;
- uint64_t cpuTimestamp0, cpuTimestamp1;
- d3DDevice->GetCommandQueue()->GetClockCalibration(&gpuTimestamp0, &cpuTimestamp0);
- queue.Submit(1, &commands);
- WaitForAllOperations();
- d3DDevice->GetCommandQueue()->GetClockCalibration(&gpuTimestamp1, &cpuTimestamp1);
-
- // Separate resolve queryset to reduce the execution time of the queue with WriteTimestamp,
- // so that the timestamp in the querySet will be closer to both gpuTimestamps from
- // GetClockCalibration.
- wgpu::CommandEncoder resolveEncoder = device.CreateCommandEncoder();
- resolveEncoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
- wgpu::CommandBuffer resolveCommands = resolveEncoder.Finish();
- queue.Submit(1, &resolveCommands);
-
- EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t),
- new ExpectBetweenTimestamps(gpuTimestamp0, gpuTimestamp1));
+// GetClockCalibration() with the 'disable_timestamp_query_conversion' toggle disabled or enabled.
+TEST_P(D3D12GPUTimestampCalibrationTests, TimestampsCalibration) {
+ RunTest();
}
-DAWN_INSTANTIATE_TEST(D3D12GPUTimestampCalibrationTests,
- D3D12Backend({"disable_timestamp_query_conversion"}));
+DAWN_INSTANTIATE_TEST_P(
+ D3D12GPUTimestampCalibrationTests,
+ // Test with the disable_timestamp_query_conversion toggle forced on and off.
+ {D3D12Backend({"disable_timestamp_query_conversion"}, {}),
+ D3D12Backend({}, {"disable_timestamp_query_conversion"})},
+ {wgpu::FeatureName::TimestampQuery, wgpu::FeatureName::TimestampQueryInsidePasses},
+ {EncoderType::NonPass, EncoderType::ComputePass, EncoderType::RenderPass});
} // namespace dawn::native::d3d12