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 "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/WGPUHelpers.h"
19
20 class QueryTests : public DawnTest {
21 protected:
CreateResolveBuffer(uint64_t size)22 wgpu::Buffer CreateResolveBuffer(uint64_t size) {
23 wgpu::BufferDescriptor descriptor;
24 descriptor.size = size;
25 descriptor.usage = wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc |
26 wgpu::BufferUsage::CopyDst;
27 return device.CreateBuffer(&descriptor);
28 }
29 };
30
31 // Clear the content of the result buffer into 0xFFFFFFFF.
32 constexpr static uint64_t kSentinelValue = ~uint64_t(0u);
33 constexpr static uint64_t kZero = 0u;
34 constexpr uint64_t kMinDestinationOffset = 256;
35 constexpr uint64_t kMinCount = kMinDestinationOffset / sizeof(uint64_t);
36
37 class OcclusionExpectation : public detail::Expectation {
38 public:
39 enum class Result { Zero, NonZero };
40
41 ~OcclusionExpectation() override = default;
42
OcclusionExpectation(Result expected)43 OcclusionExpectation(Result expected) {
44 mExpected = expected;
45 }
46
Check(const void * data,size_t size)47 testing::AssertionResult Check(const void* data, size_t size) override {
48 ASSERT(size % sizeof(uint64_t) == 0);
49 const uint64_t* actual = static_cast<const uint64_t*>(data);
50 for (size_t i = 0; i < size / sizeof(uint64_t); i++) {
51 if (actual[i] == kSentinelValue) {
52 return testing::AssertionFailure()
53 << "Data[" << i << "] was not written (it kept the sentinel value of "
54 << kSentinelValue << ")." << std::endl;
55 }
56 if (mExpected == Result::Zero && actual[i] != 0) {
57 return testing::AssertionFailure()
58 << "Expected data[" << i << "] to be zero, actual: " << actual[i] << "."
59 << std::endl;
60 }
61 if (mExpected == Result::NonZero && actual[i] == 0) {
62 return testing::AssertionFailure()
63 << "Expected data[" << i << "] to be non-zero." << std::endl;
64 }
65 }
66
67 return testing::AssertionSuccess();
68 }
69
70 private:
71 Result mExpected;
72 };
73
74 class OcclusionQueryTests : public QueryTests {
75 protected:
SetUp()76 void SetUp() override {
77 DawnTest::SetUp();
78
79 // Create basic render pipeline
80 vsModule = utils::CreateShaderModule(device, R"(
81 [[stage(vertex)]]
82 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
83 var pos = array<vec2<f32>, 3>(
84 vec2<f32>( 1.0, 1.0),
85 vec2<f32>(-1.0, -1.0),
86 vec2<f32>( 1.0, -1.0));
87 return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
88 })");
89
90 fsModule = utils::CreateShaderModule(device, R"(
91 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
92 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
93 })");
94
95 utils::ComboRenderPipelineDescriptor descriptor;
96 descriptor.vertex.module = vsModule;
97 descriptor.cFragment.module = fsModule;
98
99 pipeline = device.CreateRenderPipeline(&descriptor);
100 }
101
102 struct ScissorRect {
103 uint32_t x;
104 uint32_t y;
105 uint32_t width;
106 uint32_t height;
107 };
108
CreateOcclusionQuerySet(uint32_t count)109 wgpu::QuerySet CreateOcclusionQuerySet(uint32_t count) {
110 wgpu::QuerySetDescriptor descriptor;
111 descriptor.count = count;
112 descriptor.type = wgpu::QueryType::Occlusion;
113 return device.CreateQuerySet(&descriptor);
114 }
115
CreateRenderTexture(wgpu::TextureFormat format)116 wgpu::Texture CreateRenderTexture(wgpu::TextureFormat format) {
117 wgpu::TextureDescriptor descriptor;
118 descriptor.size = {kRTSize, kRTSize, 1};
119 descriptor.format = format;
120 descriptor.usage = wgpu::TextureUsage::RenderAttachment;
121 return device.CreateTexture(&descriptor);
122 }
123
TestOcclusionQueryWithDepthStencilTest(bool depthTestEnabled,bool stencilTestEnabled,OcclusionExpectation::Result expected)124 void TestOcclusionQueryWithDepthStencilTest(bool depthTestEnabled,
125 bool stencilTestEnabled,
126 OcclusionExpectation::Result expected) {
127 constexpr uint32_t kQueryCount = 1;
128
129 utils::ComboRenderPipelineDescriptor descriptor;
130 descriptor.vertex.module = vsModule;
131 descriptor.cFragment.module = fsModule;
132
133 // Enable depth and stencil tests and set comparison tests never pass.
134 wgpu::DepthStencilState* depthStencil =
135 descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8);
136 depthStencil->depthCompare =
137 depthTestEnabled ? wgpu::CompareFunction::Never : wgpu::CompareFunction::Always;
138 depthStencil->stencilFront.compare =
139 stencilTestEnabled ? wgpu::CompareFunction::Never : wgpu::CompareFunction::Always;
140 depthStencil->stencilBack.compare =
141 stencilTestEnabled ? wgpu::CompareFunction::Never : wgpu::CompareFunction::Always;
142
143 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&descriptor);
144
145 wgpu::Texture renderTarget = CreateRenderTexture(wgpu::TextureFormat::RGBA8Unorm);
146 wgpu::TextureView renderTargetView = renderTarget.CreateView();
147
148 wgpu::Texture depthTexture = CreateRenderTexture(wgpu::TextureFormat::Depth24PlusStencil8);
149 wgpu::TextureView depthTextureView = depthTexture.CreateView();
150
151 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
152 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
153 // Set all bits in buffer to check 0 is correctly written if there is no sample passed the
154 // occlusion testing
155 queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue));
156
157 utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView);
158 renderPass.occlusionQuerySet = querySet;
159
160 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
161 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
162 pass.SetPipeline(renderPipeline);
163 pass.SetStencilReference(0);
164 pass.BeginOcclusionQuery(0);
165 pass.Draw(3);
166 pass.EndOcclusionQuery();
167 pass.EndPass();
168
169 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
170 wgpu::CommandBuffer commands = encoder.Finish();
171 queue.Submit(1, &commands);
172
173 EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new OcclusionExpectation(expected));
174 }
175
TestOcclusionQueryWithScissorTest(ScissorRect rect,OcclusionExpectation::Result expected)176 void TestOcclusionQueryWithScissorTest(ScissorRect rect,
177 OcclusionExpectation::Result expected) {
178 constexpr uint32_t kQueryCount = 1;
179
180 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
181 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
182 // Set all bits in buffer to check 0 is correctly written if there is no sample passed the
183 // occlusion testing
184 queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue));
185
186 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
187 renderPass.renderPassInfo.occlusionQuerySet = querySet;
188
189 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
190 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
191 pass.SetPipeline(pipeline);
192 pass.SetScissorRect(rect.x, rect.y, rect.width, rect.height);
193 pass.BeginOcclusionQuery(0);
194 pass.Draw(3);
195 pass.EndOcclusionQuery();
196 pass.EndPass();
197
198 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
199 wgpu::CommandBuffer commands = encoder.Finish();
200 queue.Submit(1, &commands);
201
202 EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new OcclusionExpectation(expected));
203 }
204
205 wgpu::ShaderModule vsModule;
206 wgpu::ShaderModule fsModule;
207
208 wgpu::RenderPipeline pipeline;
209
210 constexpr static unsigned int kRTSize = 4;
211 };
212
213 // Test creating query set with the type of Occlusion
TEST_P(OcclusionQueryTests,QuerySetCreation)214 TEST_P(OcclusionQueryTests, QuerySetCreation) {
215 // Zero-sized query set is allowed.
216 CreateOcclusionQuerySet(0);
217
218 CreateOcclusionQuerySet(1);
219 }
220
221 // Test destroying query set
TEST_P(OcclusionQueryTests,QuerySetDestroy)222 TEST_P(OcclusionQueryTests, QuerySetDestroy) {
223 wgpu::QuerySet querySet = CreateOcclusionQuerySet(1);
224 querySet.Destroy();
225 }
226
227 // Draw a bottom right triangle with depth/stencil testing enabled and check whether there is
228 // sample passed the testing by non-precise occlusion query with the results:
229 // zero indicates that no sample passed depth/stencil testing,
230 // non-zero indicates that at least one sample passed depth/stencil testing.
TEST_P(OcclusionQueryTests,QueryWithDepthStencilTest)231 TEST_P(OcclusionQueryTests, QueryWithDepthStencilTest) {
232 // Disable depth/stencil testing, the samples always pass the testing, the expected occlusion
233 // result is non-zero.
234 TestOcclusionQueryWithDepthStencilTest(false, false, OcclusionExpectation::Result::NonZero);
235
236 // Only enable depth testing and set the samples never pass the testing, the expected occlusion
237 // result is zero.
238 TestOcclusionQueryWithDepthStencilTest(true, false, OcclusionExpectation::Result::Zero);
239
240 // Only enable stencil testing and set the samples never pass the testing, the expected
241 // occlusion result is zero.
242 TestOcclusionQueryWithDepthStencilTest(false, true, OcclusionExpectation::Result::Zero);
243 }
244
245 // Draw a bottom right triangle with scissor testing enabled and check whether there is
246 // sample passed the testing by non-precise occlusion query with the results:
247 // zero indicates that no sample passed scissor testing,
248 // non-zero indicates that at least one sample passed scissor testing.
TEST_P(OcclusionQueryTests,QueryWithScissorTest)249 TEST_P(OcclusionQueryTests, QueryWithScissorTest) {
250 // TODO(hao.x.li@intel.com): It's failed weirdly on Intel TGL(Window Vulkan) which says
251 // the destination buffer keep sentinel value in the second case, it cannot be reproduced with
252 // any debug actions including Vulkan validation layers enabled, and takes time to find out if
253 // the WriteBuffer and ResolveQuerySet are not executed in order or the ResolveQuerySet does not
254 // copy the result to the buffer. In order to integrate end2end tests to Intel driver CL without
255 // unknown issues, skip it until we find the root cause.
256 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
257
258 // Test there are samples passed scissor testing, the expected occlusion result is non-zero.
259 TestOcclusionQueryWithScissorTest({2, 1, 2, 1}, OcclusionExpectation::Result::NonZero);
260
261 // Test there is no sample passed scissor testing, the expected occlusion result is zero.
262 TestOcclusionQueryWithScissorTest({0, 0, 2, 1}, OcclusionExpectation::Result::Zero);
263 }
264
265 // Test begin occlusion query with same query index on different render pass
TEST_P(OcclusionQueryTests,Rewrite)266 TEST_P(OcclusionQueryTests, Rewrite) {
267 constexpr uint32_t kQueryCount = 1;
268
269 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
270 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
271 // Set all bits in buffer to check 0 is correctly written if there is no sample passed the
272 // occlusion testing
273 queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue));
274
275 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
276 renderPass.renderPassInfo.occlusionQuerySet = querySet;
277
278 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
279
280 // Begin occlusion without draw call
281 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
282 pass.BeginOcclusionQuery(0);
283 pass.EndOcclusionQuery();
284 pass.EndPass();
285
286 // Begin occlusion with same query index with draw call
287 wgpu::RenderPassEncoder rewritePass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
288 rewritePass.SetPipeline(pipeline);
289 rewritePass.BeginOcclusionQuery(0);
290 rewritePass.Draw(3);
291 rewritePass.EndOcclusionQuery();
292 rewritePass.EndPass();
293
294 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
295 wgpu::CommandBuffer commands = encoder.Finish();
296 queue.Submit(1, &commands);
297
298 EXPECT_BUFFER(destination, 0, sizeof(uint64_t),
299 new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
300 }
301
302 // Test resolving occlusion query correctly if the queries are written sparsely, which also tests
303 // the query resetting at the start of render passes on Vulkan backend.
TEST_P(OcclusionQueryTests,ResolveSparseQueries)304 TEST_P(OcclusionQueryTests, ResolveSparseQueries) {
305 // TODO(hao.x.li@intel.com): Fails on Intel Windows Vulkan due to a driver issue that
306 // vkCmdFillBuffer and vkCmdCopyQueryPoolResults are not executed in order, skip it util
307 // the issue is fixed.
308 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
309
310 // TODO(hao.x.li@intel.com): Investigate why it's failed on D3D12 on Nvidia when running with
311 // the previous occlusion tests. Expect resolve to 0 for these unwritten queries but the
312 // occlusion result of the previous tests is got.
313 DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
314
315 constexpr uint32_t kQueryCount = 7;
316
317 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
318 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
319 // Set sentinel values to check the queries are resolved correctly if the queries are
320 // written sparsely.
321 std::vector<uint64_t> sentinelValues(kQueryCount, kSentinelValue);
322 queue.WriteBuffer(destination, 0, sentinelValues.data(), kQueryCount * sizeof(uint64_t));
323
324 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
325 renderPass.renderPassInfo.occlusionQuerySet = querySet;
326
327 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
328 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
329 pass.SetPipeline(pipeline);
330
331 // Write queries sparsely for testing the query resetting on Vulkan and resolving unwritten
332 // queries to 0.
333 // 0 - not written (tests starting with not written).
334 // 1 - written (tests combing multiple written, although other tests already do it).
335 // 2 - written.
336 // 3 - not written (tests skipping over not written in the middle).
337 // 4 - not written.
338 // 5 - written (tests another written query in the middle).
339 // 6 - not written (tests the last query not being written).
340 pass.BeginOcclusionQuery(1);
341 pass.Draw(3);
342 pass.EndOcclusionQuery();
343 pass.BeginOcclusionQuery(2);
344 pass.Draw(3);
345 pass.EndOcclusionQuery();
346 pass.BeginOcclusionQuery(5);
347 pass.Draw(3);
348 pass.EndOcclusionQuery();
349 pass.EndPass();
350
351 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
352 wgpu::CommandBuffer commands = encoder.Finish();
353 queue.Submit(1, &commands);
354
355 // The query at index 0 should be resolved to 0.
356 EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, 0, 1);
357 EXPECT_BUFFER(destination, sizeof(uint64_t), 2 * sizeof(uint64_t),
358 new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
359 // The queries at index 3 and 4 should be resolved to 0.
360 std::vector<uint64_t> zeros(2, kZero);
361 EXPECT_BUFFER_U64_RANGE_EQ(zeros.data(), destination, 3 * sizeof(uint64_t), 2);
362 EXPECT_BUFFER(destination, 5 * sizeof(uint64_t), sizeof(uint64_t),
363 new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
364 // The query at index 6 should be resolved to 0.
365 EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, 6 * sizeof(uint64_t), 1);
366 }
367
368 // Test resolving occlusion query to 0 if all queries are not written
TEST_P(OcclusionQueryTests,ResolveWithoutWritten)369 TEST_P(OcclusionQueryTests, ResolveWithoutWritten) {
370 // TODO(hao.x.li@intel.com): Investigate why it's failed on D3D12 on Nvidia when running with
371 // the previous occlusion tests. Expect resolve to 0 but the occlusion result of the previous
372 // tests is got.
373 DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
374
375 constexpr uint32_t kQueryCount = 1;
376
377 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
378 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
379 // Set sentinel values to check 0 is correctly written if resolving query set without
380 // any written.
381 queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue));
382
383 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
384 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
385 wgpu::CommandBuffer commands = encoder.Finish();
386 queue.Submit(1, &commands);
387
388 EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, 0, 1);
389 }
390
391 // Test resolving occlusion query to the destination buffer with offset
TEST_P(OcclusionQueryTests,ResolveToBufferWithOffset)392 TEST_P(OcclusionQueryTests, ResolveToBufferWithOffset) {
393 constexpr uint32_t kQueryCount = 2;
394
395 wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount);
396
397 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
398 renderPass.renderPassInfo.occlusionQuerySet = querySet;
399
400 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
401 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
402 pass.SetPipeline(pipeline);
403 pass.BeginOcclusionQuery(0);
404 pass.Draw(3);
405 pass.EndOcclusionQuery();
406 pass.EndPass();
407 wgpu::CommandBuffer commands = encoder.Finish();
408 queue.Submit(1, &commands);
409
410 constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t) + kMinDestinationOffset;
411 constexpr uint64_t kCount = kQueryCount + kMinCount;
412
413 // Resolve the query result to first slot in the buffer, other slots should not be written.
414 {
415 wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
416 // Set sentinel values to check the query is resolved to the correct slot of the buffer.
417 std::vector<uint64_t> sentinelValues(kCount, kSentinelValue);
418 queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
419
420 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
421 encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
422 wgpu::CommandBuffer commands = encoder.Finish();
423 queue.Submit(1, &commands);
424
425 EXPECT_BUFFER(destination, 0, sizeof(uint64_t),
426 new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
427 EXPECT_BUFFER_U64_RANGE_EQ(sentinelValues.data(), destination, sizeof(uint64_t),
428 kCount - 1);
429 }
430
431 // Resolve the query result to second slot in the buffer, the first one should not be written.
432 {
433 wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
434 // Set sentinel values to check the query is resolved to the correct slot of the buffer.
435 std::vector<uint64_t> sentinelValues(kCount, kSentinelValue);
436 queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
437
438 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
439 encoder.ResolveQuerySet(querySet, 0, 1, destination, kMinDestinationOffset);
440 wgpu::CommandBuffer commands = encoder.Finish();
441 queue.Submit(1, &commands);
442
443 EXPECT_BUFFER_U64_RANGE_EQ(sentinelValues.data(), destination, 0, kMinCount);
444 EXPECT_BUFFER(destination, kMinDestinationOffset, sizeof(uint64_t),
445 new OcclusionExpectation(OcclusionExpectation::Result::NonZero));
446 }
447 }
448
449 DAWN_INSTANTIATE_TEST(OcclusionQueryTests, D3D12Backend(), MetalBackend(), VulkanBackend());
450
451 class PipelineStatisticsQueryTests : public QueryTests {
452 protected:
SetUp()453 void SetUp() override {
454 DawnTest::SetUp();
455
456 // Skip all tests if pipeline statistics feature is not supported
457 DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({"pipeline-statistics-query"}));
458 }
459
GetRequiredFeatures()460 std::vector<const char*> GetRequiredFeatures() override {
461 std::vector<const char*> requiredFeatures = {};
462 if (SupportsFeatures({"pipeline-statistics-query"})) {
463 requiredFeatures.push_back("pipeline-statistics-query");
464 }
465
466 return requiredFeatures;
467 }
468
CreateQuerySetForPipelineStatistics(uint32_t queryCount,std::vector<wgpu::PipelineStatisticName> pipelineStatistics={})469 wgpu::QuerySet CreateQuerySetForPipelineStatistics(
470 uint32_t queryCount,
471 std::vector<wgpu::PipelineStatisticName> pipelineStatistics = {}) {
472 wgpu::QuerySetDescriptor descriptor;
473 descriptor.count = queryCount;
474 descriptor.type = wgpu::QueryType::PipelineStatistics;
475
476 if (pipelineStatistics.size() > 0) {
477 descriptor.pipelineStatistics = pipelineStatistics.data();
478 descriptor.pipelineStatisticsCount = pipelineStatistics.size();
479 }
480 return device.CreateQuerySet(&descriptor);
481 }
482 };
483
484 // Test creating query set with the type of PipelineStatistics
TEST_P(PipelineStatisticsQueryTests,QuerySetCreation)485 TEST_P(PipelineStatisticsQueryTests, QuerySetCreation) {
486 // Zero-sized query set is allowed.
487 CreateQuerySetForPipelineStatistics(0, {wgpu::PipelineStatisticName::ClipperInvocations,
488 wgpu::PipelineStatisticName::VertexShaderInvocations});
489
490 CreateQuerySetForPipelineStatistics(1, {wgpu::PipelineStatisticName::ClipperInvocations,
491 wgpu::PipelineStatisticName::VertexShaderInvocations});
492 }
493
494 DAWN_INSTANTIATE_TEST(PipelineStatisticsQueryTests,
495 D3D12Backend(),
496 MetalBackend(),
497 OpenGLBackend(),
498 OpenGLESBackend(),
499 VulkanBackend());
500
501 class TimestampExpectation : public detail::Expectation {
502 public:
503 ~TimestampExpectation() override = default;
504
505 // Expect the timestamp results are greater than 0.
Check(const void * data,size_t size)506 testing::AssertionResult Check(const void* data, size_t size) override {
507 ASSERT(size % sizeof(uint64_t) == 0);
508 const uint64_t* timestamps = static_cast<const uint64_t*>(data);
509 for (size_t i = 0; i < size / sizeof(uint64_t); i++) {
510 if (timestamps[i] == 0) {
511 return testing::AssertionFailure()
512 << "Expected data[" << i << "] to be greater than 0." << std::endl;
513 }
514 }
515
516 return testing::AssertionSuccess();
517 }
518 };
519
520 class TimestampQueryTests : public QueryTests {
521 protected:
SetUp()522 void SetUp() override {
523 DawnTest::SetUp();
524
525 // Skip all tests if timestamp feature is not supported
526 DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({"timestamp-query"}));
527 }
528
GetRequiredFeatures()529 std::vector<const char*> GetRequiredFeatures() override {
530 std::vector<const char*> requiredFeatures = {};
531 if (SupportsFeatures({"timestamp-query"})) {
532 requiredFeatures.push_back("timestamp-query");
533 }
534 return requiredFeatures;
535 }
536
CreateQuerySetForTimestamp(uint32_t queryCount)537 wgpu::QuerySet CreateQuerySetForTimestamp(uint32_t queryCount) {
538 wgpu::QuerySetDescriptor descriptor;
539 descriptor.count = queryCount;
540 descriptor.type = wgpu::QueryType::Timestamp;
541 return device.CreateQuerySet(&descriptor);
542 }
543 };
544
545 // Test creating query set with the type of Timestamp
TEST_P(TimestampQueryTests,QuerySetCreation)546 TEST_P(TimestampQueryTests, QuerySetCreation) {
547 // Zero-sized query set is allowed.
548 CreateQuerySetForTimestamp(0);
549
550 CreateQuerySetForTimestamp(1);
551 }
552
553 // Test calling timestamp query from command encoder
TEST_P(TimestampQueryTests,TimestampOnCommandEncoder)554 TEST_P(TimestampQueryTests, TimestampOnCommandEncoder) {
555 constexpr uint32_t kQueryCount = 2;
556
557 // Write timestamp with different query indexes
558 {
559 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
560 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
561
562 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
563 encoder.WriteTimestamp(querySet, 0);
564 encoder.WriteTimestamp(querySet, 1);
565 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
566 wgpu::CommandBuffer commands = encoder.Finish();
567 queue.Submit(1, &commands);
568
569 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
570 }
571
572 // Write timestamp with same query index outside pass on same encoder
573 {
574 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
575 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
576
577 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
578 encoder.WriteTimestamp(querySet, 0);
579 encoder.WriteTimestamp(querySet, 1);
580 encoder.WriteTimestamp(querySet, 0);
581 encoder.WriteTimestamp(querySet, 1);
582 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
583 wgpu::CommandBuffer commands = encoder.Finish();
584 queue.Submit(1, &commands);
585
586 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
587 }
588 }
589
590 // Test calling timestamp query from render pass encoder
TEST_P(TimestampQueryTests,TimestampOnRenderPass)591 TEST_P(TimestampQueryTests, TimestampOnRenderPass) {
592 constexpr uint32_t kQueryCount = 2;
593
594 // Write timestamp with different query indexes
595 {
596 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
597 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
598
599 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
600 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
601 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
602 pass.WriteTimestamp(querySet, 0);
603 pass.WriteTimestamp(querySet, 1);
604 pass.EndPass();
605 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
606 wgpu::CommandBuffer commands = encoder.Finish();
607 queue.Submit(1, &commands);
608
609 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
610 }
611
612 // Write timestamp with same query index, not need test rewrite inside render pass due to it's
613 // not allowed
614 {
615 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
616 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
617
618 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
619 encoder.WriteTimestamp(querySet, 0);
620 encoder.WriteTimestamp(querySet, 1);
621
622 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1);
623 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
624 pass.WriteTimestamp(querySet, 0);
625 pass.WriteTimestamp(querySet, 1);
626 pass.EndPass();
627 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
628 wgpu::CommandBuffer commands = encoder.Finish();
629 queue.Submit(1, &commands);
630
631 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
632 }
633 }
634
635 // Test calling timestamp query from compute pass encoder
TEST_P(TimestampQueryTests,TimestampOnComputePass)636 TEST_P(TimestampQueryTests, TimestampOnComputePass) {
637 constexpr uint32_t kQueryCount = 2;
638
639 // Write timestamp with different query indexes
640 {
641 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
642 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
643
644 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
645 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
646 pass.WriteTimestamp(querySet, 0);
647 pass.WriteTimestamp(querySet, 1);
648 pass.EndPass();
649 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
650 wgpu::CommandBuffer commands = encoder.Finish();
651 queue.Submit(1, &commands);
652
653 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
654 }
655
656 // Write timestamp with same query index on both the outside and the inside of the compute pass
657 {
658 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
659 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
660
661 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
662 encoder.WriteTimestamp(querySet, 0);
663 encoder.WriteTimestamp(querySet, 1);
664
665 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
666 pass.WriteTimestamp(querySet, 0);
667 pass.WriteTimestamp(querySet, 1);
668 pass.EndPass();
669
670 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
671 wgpu::CommandBuffer commands = encoder.Finish();
672 queue.Submit(1, &commands);
673
674 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
675 }
676
677 // Write timestamp with same query index inside compute pass
678 {
679 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
680 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
681
682 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
683 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
684 pass.WriteTimestamp(querySet, 0);
685 pass.WriteTimestamp(querySet, 1);
686 pass.WriteTimestamp(querySet, 0);
687 pass.WriteTimestamp(querySet, 1);
688 pass.EndPass();
689
690 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
691 wgpu::CommandBuffer commands = encoder.Finish();
692 queue.Submit(1, &commands);
693
694 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
695 }
696 }
697
698 // Test resolving timestamp query from another different encoder
TEST_P(TimestampQueryTests,ResolveFromAnotherEncoder)699 TEST_P(TimestampQueryTests, ResolveFromAnotherEncoder) {
700 constexpr uint32_t kQueryCount = 2;
701
702 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
703 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
704
705 wgpu::CommandEncoder timestampEncoder = device.CreateCommandEncoder();
706 timestampEncoder.WriteTimestamp(querySet, 0);
707 timestampEncoder.WriteTimestamp(querySet, 1);
708 wgpu::CommandBuffer timestampCommands = timestampEncoder.Finish();
709 queue.Submit(1, ×tampCommands);
710
711 wgpu::CommandEncoder resolveEncoder = device.CreateCommandEncoder();
712 resolveEncoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
713 wgpu::CommandBuffer resolveCommands = resolveEncoder.Finish();
714 queue.Submit(1, &resolveCommands);
715
716 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
717 }
718
719 // Test resolving timestamp query correctly if the queries are written sparsely
TEST_P(TimestampQueryTests,ResolveSparseQueries)720 TEST_P(TimestampQueryTests, ResolveSparseQueries) {
721 // TODO(hao.x.li@intel.com): Fails on Intel Windows Vulkan due to a driver issue that
722 // vkCmdFillBuffer and vkCmdCopyQueryPoolResults are not executed in order, skip it util
723 // the issue is fixed.
724 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
725
726 constexpr uint32_t kQueryCount = 4;
727
728 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
729 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
730 // Set sentinel values to check the queries are resolved correctly if the queries are
731 // written sparsely
732 std::vector<uint64_t> sentinelValues{0, kSentinelValue, 0, kSentinelValue};
733 queue.WriteBuffer(destination, 0, sentinelValues.data(), kQueryCount * sizeof(uint64_t));
734
735 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
736 encoder.WriteTimestamp(querySet, 0);
737 encoder.WriteTimestamp(querySet, 2);
738 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
739 wgpu::CommandBuffer commands = encoder.Finish();
740 queue.Submit(1, &commands);
741
742 EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new TimestampExpectation);
743 // The query with no value written should be resolved to 0.
744 EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, sizeof(uint64_t), 1);
745 EXPECT_BUFFER(destination, 2 * sizeof(uint64_t), sizeof(uint64_t), new TimestampExpectation);
746 // The query with no value written should be resolved to 0.
747 EXPECT_BUFFER_U64_RANGE_EQ(&kZero, destination, 3 * sizeof(uint64_t), 1);
748 }
749
750 // Test resolving timestamp query to 0 if all queries are not written
TEST_P(TimestampQueryTests,ResolveWithoutWritten)751 TEST_P(TimestampQueryTests, ResolveWithoutWritten) {
752 constexpr uint32_t kQueryCount = 2;
753
754 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
755 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
756 // Set sentinel values to check 0 is correctly written if resolving query set with no
757 // query is written
758 std::vector<uint64_t> sentinelValues(kQueryCount, kSentinelValue);
759 queue.WriteBuffer(destination, 0, sentinelValues.data(), kQueryCount * sizeof(uint64_t));
760
761 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
762 encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0);
763 wgpu::CommandBuffer commands = encoder.Finish();
764 queue.Submit(1, &commands);
765
766 std::vector<uint64_t> expectedZeros(kQueryCount);
767 EXPECT_BUFFER_U64_RANGE_EQ(expectedZeros.data(), destination, 0, kQueryCount);
768 }
769
770 // Test resolving timestamp query to one slot in the buffer
TEST_P(TimestampQueryTests,ResolveToBufferWithOffset)771 TEST_P(TimestampQueryTests, ResolveToBufferWithOffset) {
772 // TODO(hao.x.li@intel.com): Fails on Intel Windows Vulkan due to a driver issue that
773 // vkCmdFillBuffer and vkCmdCopyQueryPoolResults are not executed in order, skip it util
774 // the issue is fixed.
775 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
776
777 constexpr uint32_t kQueryCount = 2;
778 constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t) + kMinDestinationOffset;
779 constexpr uint64_t kCount = kQueryCount + kMinCount;
780
781 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
782
783 // Resolve the query result to first slot in the buffer, other slots should not be written
784 {
785 wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
786
787 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
788 encoder.WriteTimestamp(querySet, 0);
789 encoder.ResolveQuerySet(querySet, 0, 1, destination, 0);
790 wgpu::CommandBuffer commands = encoder.Finish();
791 queue.Submit(1, &commands);
792
793 std::vector<uint64_t> zeros(kCount - 1, kZero);
794 EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new TimestampExpectation);
795 EXPECT_BUFFER_U64_RANGE_EQ(zeros.data(), destination, sizeof(uint64_t), kCount - 1);
796 }
797
798 // Resolve the query result to the buffer with offset, the slots before the offset
799 // should not be written
800 {
801 wgpu::Buffer destination = CreateResolveBuffer(kBufferSize);
802 // Set sentinel values to check the query is resolved to the correct slot of the buffer.
803 std::vector<uint64_t> sentinelValues(kCount, kZero);
804 queue.WriteBuffer(destination, 0, sentinelValues.data(), kBufferSize);
805
806 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
807 encoder.WriteTimestamp(querySet, 0);
808 encoder.ResolveQuerySet(querySet, 0, 1, destination, kMinDestinationOffset);
809 wgpu::CommandBuffer commands = encoder.Finish();
810 queue.Submit(1, &commands);
811
812 std::vector<uint64_t> zeros(kMinCount, kZero);
813 EXPECT_BUFFER_U64_RANGE_EQ(zeros.data(), destination, 0, kMinCount);
814 EXPECT_BUFFER(destination, kMinDestinationOffset, sizeof(uint64_t),
815 new TimestampExpectation);
816 }
817 }
818
819 // Test resolving a query set twice into the same destination buffer with potentially overlapping
820 // ranges
TEST_P(TimestampQueryTests,ResolveTwiceToSameBuffer)821 TEST_P(TimestampQueryTests, ResolveTwiceToSameBuffer) {
822 // TODO(hao.x.li@intel.com): Fails on Intel Windows Vulkan due to a driver issue that
823 // vkCmdFillBuffer and vkCmdCopyQueryPoolResults are not executed in order, skip it util
824 // the issue is fixed.
825 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsIntel());
826
827 constexpr uint32_t kQueryCount = kMinCount + 2;
828
829 wgpu::QuerySet querySet = CreateQuerySetForTimestamp(kQueryCount);
830 wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t));
831
832 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
833 for (uint32_t i = 0; i < kQueryCount; i++) {
834 encoder.WriteTimestamp(querySet, i);
835 }
836 encoder.ResolveQuerySet(querySet, 0, kMinCount + 1, destination, 0);
837 encoder.ResolveQuerySet(querySet, kMinCount, 2, destination, kMinDestinationOffset);
838 wgpu::CommandBuffer commands = encoder.Finish();
839 queue.Submit(1, &commands);
840
841 EXPECT_BUFFER(destination, 0, kQueryCount * sizeof(uint64_t), new TimestampExpectation);
842 }
843
844 DAWN_INSTANTIATE_TEST(TimestampQueryTests,
845 D3D12Backend(),
846 MetalBackend(),
847 OpenGLBackend(),
848 OpenGLESBackend(),
849 VulkanBackend());
850