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:
parent
a4314fabb4
commit
23a35c8a26
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue