mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-09 21:49:22 +00:00
In the timestamp internal pipeline, the ResolveQuery buffer cannnot be binded as Storage buffer in binding group layout due to it has not Storage usage. Add InternalStorageBuffer for buffer usage and InternalStorageBufferBinding for buffer binding type, make the QueryResolve buffer implicitly get InternalStorageBuffer and only compatible with InternalStorageBufferBinding in BGL, not Storage buffer binding type. Bug: dawn:797 Change-Id: I286339e703e26d3786c706ded03f850ca17355fb Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/54400 Reviewed-by: Jiawei Shao <jiawei.shao@intel.com> Commit-Queue: Hao Li <hao.x.li@intel.com>
237 lines
11 KiB
C++
237 lines
11 KiB
C++
// Copyright 2020 The Dawn Authors
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
#include "tests/DawnTest.h"
|
||
|
||
#include "dawn_native/Buffer.h"
|
||
#include "dawn_native/CommandEncoder.h"
|
||
#include "dawn_native/QueryHelper.h"
|
||
#include "utils/WGPUHelpers.h"
|
||
|
||
namespace {
|
||
|
||
void EncodeConvertTimestampsToNanoseconds(wgpu::CommandEncoder encoder,
|
||
wgpu::Buffer timestamps,
|
||
wgpu::Buffer availability,
|
||
wgpu::Buffer params) {
|
||
ASSERT_TRUE(dawn_native::EncodeConvertTimestampsToNanoseconds(
|
||
reinterpret_cast<dawn_native::CommandEncoder*>(encoder.Get()),
|
||
reinterpret_cast<dawn_native::BufferBase*>(timestamps.Get()),
|
||
reinterpret_cast<dawn_native::BufferBase*>(availability.Get()),
|
||
reinterpret_cast<dawn_native::BufferBase*>(params.Get())).IsSuccess());
|
||
}
|
||
|
||
class InternalShaderExpectation : public detail::Expectation {
|
||
public:
|
||
~InternalShaderExpectation() override = default;
|
||
|
||
InternalShaderExpectation(const uint64_t* values, const unsigned int count) {
|
||
mExpected.assign(values, values + count);
|
||
}
|
||
|
||
// Expect the actual results are approximately equal to the expected values.
|
||
testing::AssertionResult Check(const void* data, size_t size) override {
|
||
DAWN_ASSERT(size == sizeof(uint64_t) * mExpected.size());
|
||
constexpr static float kErrorToleranceRatio = 0.002f;
|
||
|
||
const uint64_t* actual = static_cast<const uint64_t*>(data);
|
||
for (size_t i = 0; i < mExpected.size(); ++i) {
|
||
if (mExpected[i] == 0 && actual[i] != 0) {
|
||
return testing::AssertionFailure()
|
||
<< "Expected data[" << i << "] to be 0, actual " << actual[i]
|
||
<< std::endl;
|
||
}
|
||
|
||
if (abs(static_cast<int64_t>(mExpected[i] - actual[i])) >
|
||
mExpected[i] * kErrorToleranceRatio) {
|
||
return testing::AssertionFailure()
|
||
<< "Expected data[" << i << "] to be " << mExpected[i] << ", actual "
|
||
<< actual[i] << ". Error rate is larger than " << kErrorToleranceRatio
|
||
<< std::endl;
|
||
}
|
||
}
|
||
|
||
return testing::AssertionSuccess();
|
||
}
|
||
|
||
private:
|
||
std::vector<uint64_t> mExpected;
|
||
};
|
||
|
||
} // anonymous namespace
|
||
|
||
class QueryInternalShaderTests : public DawnTest {};
|
||
|
||
// Test the accuracy of timestamp compute shader which uses unsigned 32-bit integers to simulate
|
||
// unsigned 64-bit integers (timestamps) multiplied by float (period).
|
||
// The arguments pass to timestamp internal pipeline:
|
||
// - The timestamps buffer contains the original timestamps resolved from query set (created
|
||
// manually here), and will be used to store the results processed by the compute shader.
|
||
// Expect 0 for unavailable timestamps and nanoseconds for available timestamps in an expected
|
||
// error tolerance ratio.
|
||
// - The availability buffer passes the data of which slot in timestamps buffer is an initialized
|
||
// timestamp.
|
||
// - The params buffer passes the timestamp count, the offset in timestamps buffer and the
|
||
// timestamp period (here use GPU frequency (HZ) on Intel D3D12 to calculate the period in
|
||
// ns for testing).
|
||
TEST_P(QueryInternalShaderTests, TimestampComputeShader) {
|
||
// TODO(crbug.com/dawn/741): Test output is wrong with D3D12 + WARP.
|
||
DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
|
||
|
||
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
|
||
// Test for ResolveQuerySet(querySet, 0, kTimestampCount, timestampsBuffer, 0)
|
||
{
|
||
constexpr uint32_t kFirst = 0u;
|
||
constexpr uint32_t kOffset = 0u;
|
||
|
||
// 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, 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(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());
|