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 {
|
||||
|
||||
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 @@ class ExpectBetweenTimestamps : public ::detail::Expectation {
|
|||
}
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue