• 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 "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, &timestampCommands);
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