• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "dawn_native/Buffer.h"
18 #include "dawn_native/CommandEncoder.h"
19 #include "dawn_native/QueryHelper.h"
20 #include "utils/WGPUHelpers.h"
21 
22 namespace {
23 
EncodeConvertTimestampsToNanoseconds(wgpu::CommandEncoder encoder,wgpu::Buffer timestamps,wgpu::Buffer availability,wgpu::Buffer params)24     void EncodeConvertTimestampsToNanoseconds(wgpu::CommandEncoder encoder,
25                                               wgpu::Buffer timestamps,
26                                               wgpu::Buffer availability,
27                                               wgpu::Buffer params) {
28         ASSERT_TRUE(dawn_native::EncodeConvertTimestampsToNanoseconds(
29                         dawn_native::FromAPI(encoder.Get()), dawn_native::FromAPI(timestamps.Get()),
30                         dawn_native::FromAPI(availability.Get()),
31                         dawn_native::FromAPI(params.Get()))
32                         .IsSuccess());
33     }
34 
35     class InternalShaderExpectation : public detail::Expectation {
36       public:
37         ~InternalShaderExpectation() override = default;
38 
InternalShaderExpectation(const uint64_t * values,const unsigned int count)39         InternalShaderExpectation(const uint64_t* values, const unsigned int count) {
40             mExpected.assign(values, values + count);
41         }
42 
43         // Expect the actual results are approximately equal to the expected values.
Check(const void * data,size_t size)44         testing::AssertionResult Check(const void* data, size_t size) override {
45             DAWN_ASSERT(size == sizeof(uint64_t) * mExpected.size());
46             constexpr static float kErrorToleranceRatio = 0.002f;
47 
48             const uint64_t* actual = static_cast<const uint64_t*>(data);
49             for (size_t i = 0; i < mExpected.size(); ++i) {
50                 if (mExpected[i] == 0 && actual[i] != 0) {
51                     return testing::AssertionFailure()
52                            << "Expected data[" << i << "] to be 0, actual " << actual[i]
53                            << std::endl;
54                 }
55 
56                 if (abs(static_cast<int64_t>(mExpected[i] - actual[i])) >
57                     mExpected[i] * kErrorToleranceRatio) {
58                     return testing::AssertionFailure()
59                            << "Expected data[" << i << "] to be " << mExpected[i] << ", actual "
60                            << actual[i] << ". Error rate is larger than " << kErrorToleranceRatio
61                            << std::endl;
62                 }
63             }
64 
65             return testing::AssertionSuccess();
66         }
67 
68       private:
69         std::vector<uint64_t> mExpected;
70     };
71 
72 }  // anonymous namespace
73 
74 constexpr static uint64_t kSentinelValue = ~uint64_t(0u);
75 
76 // A gpu frequency on Intel D3D12 (ticks/second)
77 constexpr uint64_t kGPUFrequency = 12000048u;
78 constexpr uint64_t kNsPerSecond = 1000000000u;
79 // Timestamp period in nanoseconds
80 constexpr float kPeriod = static_cast<float>(kNsPerSecond) / kGPUFrequency;
81 
82 class QueryInternalShaderTests : public DawnTest {
83   protected:
84     // Original timestamp values in query set for testing
85     const std::vector<uint64_t> querySetValues = {
86         kSentinelValue,  // garbage data which is not written at beginning
87         10079569507,     // t0
88         10394415012,     // t1
89         kSentinelValue,  // garbage data which is not written between timestamps
90         11713454943,     // t2
91         38912556941,     // t3 (big value)
92         10080295766,     // t4 (reset)
93         12159966783,     // t5 (after reset)
94         12651224612,     // t6
95         39872473956,     // t7
96     };
97 
98     const uint32_t kQueryCount = querySetValues.size();
99 
100     // Timestamps available state
101     const std::vector<uint32_t> availabilities = {0, 1, 1, 0, 1, 1, 1, 1, 1, 1};
102 
GetExpectedResults(const std::vector<uint64_t> & origin,uint32_t start,uint32_t firstQuery,uint32_t queryCount)103     const std::vector<uint64_t> GetExpectedResults(const std::vector<uint64_t>& origin,
104                                                    uint32_t start,
105                                                    uint32_t firstQuery,
106                                                    uint32_t queryCount) {
107         std::vector<uint64_t> expected(origin.begin(), origin.end());
108         for (size_t i = 0; i < queryCount; i++) {
109             if (availabilities[firstQuery + i] == 0) {
110                 // Not a available timestamp, write 0
111                 expected[start + i] = 0u;
112             } else {
113                 // Maybe the timestamp * period is larger than the maximum of uint64, so cast the
114                 // delta value to double (higher precision than float)
115                 expected[start + i] =
116                     static_cast<uint64_t>(static_cast<double>(origin[start + i]) * kPeriod);
117             }
118         }
119         return expected;
120     }
121 
RunTest(uint32_t firstQuery,uint32_t queryCount,uint32_t destinationOffset)122     void RunTest(uint32_t firstQuery, uint32_t queryCount, uint32_t destinationOffset) {
123         ASSERT(destinationOffset % 256 == 0);
124 
125         uint64_t size = queryCount * sizeof(uint64_t) + destinationOffset;
126 
127         // The resolve buffer storing original timestamps and the converted values
128         wgpu::BufferDescriptor timestampsDesc;
129         timestampsDesc.size = size;
130         timestampsDesc.usage = wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc |
131                                wgpu::BufferUsage::CopyDst;
132         wgpu::Buffer timestampsBuffer = device.CreateBuffer(&timestampsDesc);
133 
134         // Set sentinel values to check the slots before the destination offset should not be
135         // converted
136         std::vector<uint64_t> timestampValues(size / sizeof(uint64_t), 1u);
137         uint32_t start = destinationOffset / sizeof(uint64_t);
138         for (uint32_t i = 0; i < queryCount; i++) {
139             timestampValues[start + i] = querySetValues[firstQuery + 1];
140         }
141         // Write sentinel values and orignal timestamps to timestamps buffer
142         queue.WriteBuffer(timestampsBuffer, 0, timestampValues.data(), size);
143 
144         // The buffer indicating which values are available timestamps
145         wgpu::Buffer availabilityBuffer =
146             utils::CreateBufferFromData(device, availabilities.data(),
147                                         kQueryCount * sizeof(uint32_t), wgpu::BufferUsage::Storage);
148 
149         // The params uniform buffer
150         dawn_native::TimestampParams params = {firstQuery, queryCount, destinationOffset, kPeriod};
151         wgpu::Buffer paramsBuffer = utils::CreateBufferFromData(device, &params, sizeof(params),
152                                                                 wgpu::BufferUsage::Uniform);
153 
154         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
155         EncodeConvertTimestampsToNanoseconds(encoder, timestampsBuffer, availabilityBuffer,
156                                              paramsBuffer);
157         wgpu::CommandBuffer commands = encoder.Finish();
158         queue.Submit(1, &commands);
159 
160         const std::vector<uint64_t> expected =
161             GetExpectedResults(timestampValues, start, firstQuery, queryCount);
162 
163         EXPECT_BUFFER(timestampsBuffer, 0, size,
164                       new InternalShaderExpectation(expected.data(), size / sizeof(uint64_t)));
165     }
166 
167   private:
168 };
169 
170 // Test the accuracy of timestamp compute shader which uses unsigned 32-bit integers to simulate
171 // unsigned 64-bit integers (timestamps) multiplied by float (period).
172 // The arguments pass to timestamp internal pipeline:
173 // - The timestamps buffer contains the original timestamps resolved from query set (created
174 //   manually here), and will be used to store the results processed by the compute shader.
175 //   Expect 0 for unavailable timestamps and nanoseconds for available timestamps in an expected
176 //   error tolerance ratio.
177 // - The availability buffer passes the data of which slot in timestamps buffer is an initialized
178 //   timestamp.
179 // - The params buffer passes the timestamp count, the offset in timestamps buffer and the
180 //   timestamp period (here use GPU frequency (HZ) on Intel D3D12 to calculate the period in
181 //   ns for testing).
TEST_P(QueryInternalShaderTests,TimestampComputeShader)182 TEST_P(QueryInternalShaderTests, TimestampComputeShader) {
183     // TODO(crbug.com/dawn/741): Test output is wrong with D3D12 + WARP.
184     DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsWARP());
185 
186     DAWN_TEST_UNSUPPORTED_IF(UsesWire());
187 
188     // Convert timestamps in timestamps buffer with offset 0
189     // Test for ResolveQuerySet(querySet, 0, kQueryCount, timestampsBuffer, 0)
190     RunTest(0, kQueryCount, 0);
191 
192     // Convert timestamps in timestamps buffer with offset 256
193     // Test for ResolveQuerySet(querySet, 1, kQueryCount - 1, timestampsBuffer, 256)
194     RunTest(1, kQueryCount - 1, 256);
195 
196     // Convert partial timestamps in timestamps buffer with offset 256
197     // Test for ResolveQuerySet(querySet, 1, 4, timestampsBuffer, 256)
198     RunTest(1, 4, 256);
199 }
200 
201 DAWN_INSTANTIATE_TEST(QueryInternalShaderTests, D3D12Backend(), MetalBackend(), VulkanBackend());
202