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>
This commit is contained in:
Li Hao 2022-11-04 08:43:57 +00:00 committed by Dawn LUCI CQ
parent a4314fabb4
commit 23a35c8a26
1 changed files with 198 additions and 56 deletions

View File

@ -22,23 +22,54 @@
namespace dawn::native::d3d12 { namespace dawn::native::d3d12 {
namespace { 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 { class ExpectBetweenTimestamps : public ::detail::Expectation {
public: public:
~ExpectBetweenTimestamps() override = default; ~ExpectBetweenTimestamps() override = default;
ExpectBetweenTimestamps(uint64_t value0, uint64_t value1) { ExpectBetweenTimestamps(uint64_t minValue, uint64_t maxValue, float errorToleranceRatio = 0.0f)
mValue0 = value0; : mMinValue(minValue), mMaxValue(maxValue), mErrorToleranceRatio(errorToleranceRatio) {}
mValue1 = value1;
}
// 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 { testing::AssertionResult Check(const void* data, size_t size) override {
const uint64_t* actual = static_cast<const uint64_t*>(data); 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) { 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() return testing::AssertionFailure()
<< "Expected data[" << i << "] to be between " << mValue0 << " and " << "Expected data[" << i << "] to be between " << mMinValue << " and "
<< mValue1 << ", actual " << actual[i] << std::endl; << mMaxValue << ", actual " << actual[i] << std::endl;
} }
} }
@ -46,75 +77,186 @@ class ExpectBetweenTimestamps : public ::detail::Expectation {
} }
private: private:
uint64_t mValue0; uint64_t mMinValue;
uint64_t mValue1; uint64_t mMaxValue;
float mErrorToleranceRatio;
}; };
} // anonymous namespace } // anonymous namespace
class D3D12GPUTimestampCalibrationTests : public DawnTest { class D3D12GPUTimestampCalibrationTests
: public DawnTestWithParams<GPUTimestampCalibrationTestParams> {
protected: protected:
void SetUp() override { void SetUp() override {
DawnTest::SetUp(); DawnTestWithParams<GPUTimestampCalibrationTestParams>::SetUp();
DAWN_TEST_UNSUPPORTED_IF(UsesWire()); DAWN_TEST_UNSUPPORTED_IF(UsesWire());
// Requires that timestamp query feature is enabled and timestamp query conversion is // Requires that timestamp query feature is enabled and timestamp query conversion is
// disabled. // disabled.
DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::TimestampQuery}) || DAWN_TEST_UNSUPPORTED_IF(!mIsFeatureSupported);
!HasToggleEnabled("disable_timestamp_query_conversion")); // 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> GetRequiredFeatures() override {
std::vector<wgpu::FeatureName> requiredFeatures = {}; std::vector<wgpu::FeatureName> requiredFeatures = {};
if (SupportsFeatures({wgpu::FeatureName::TimestampQuery})) { if (SupportsFeatures({GetParam().mFeatureName})) {
requiredFeatures.push_back(wgpu::FeatureName::TimestampQuery); requiredFeatures.push_back(GetParam().mFeatureName);
mIsFeatureSupported = true;
} }
return requiredFeatures; 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 // Check that the timestamps got by timestamp query are between the two timestamps from
// GetClockCalibration() after the timestamp conversion is disabled. // GetClockCalibration() with the 'disable_timestamp_query_conversion' toggle disabled or enabled.
TEST_P(D3D12GPUTimestampCalibrationTests, TimestampsInOrder) { TEST_P(D3D12GPUTimestampCalibrationTests, TimestampsCalibration) {
constexpr uint32_t kQueryCount = 2; RunTest();
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));
} }
DAWN_INSTANTIATE_TEST(D3D12GPUTimestampCalibrationTests, DAWN_INSTANTIATE_TEST_P(
D3D12Backend({"disable_timestamp_query_conversion"})); 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 } // namespace dawn::native::d3d12