From 387a597154af4e3c8365ab42baa86f9d9811b67e Mon Sep 17 00:00:00 2001 From: Hao Li Date: Tue, 2 Feb 2021 02:38:59 +0000 Subject: [PATCH] Query API: Non-precise occlusion query on Vulkan - Implement BeginOcclusionQuery/EndOcclusionQuery on Vulkan - Add compute pipeline to convert non-zero values to 1. Bug: dawn:434 Change-Id: Ie0238078dfd26334caf36d23ded1d7742dd829a3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/38221 Reviewed-by: Corentin Wallez Commit-Queue: Hao Li --- src/dawn_native/vulkan/CommandBufferVk.cpp | 12 ++- src/tests/end2end/QueryTests.cpp | 95 +++++++++++++++------- 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp index 47adf797c0..1c7cdd28ef 100644 --- a/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -1188,11 +1188,19 @@ namespace dawn_native { namespace vulkan { } case Command::BeginOcclusionQuery: { - return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + BeginOcclusionQueryCmd* cmd = mCommands.NextCommand(); + + device->fn.CmdBeginQuery(commands, ToBackend(cmd->querySet.Get())->GetHandle(), + cmd->queryIndex, 0); + break; } case Command::EndOcclusionQuery: { - return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + EndOcclusionQueryCmd* cmd = mCommands.NextCommand(); + + device->fn.CmdEndQuery(commands, ToBackend(cmd->querySet.Get())->GetHandle(), + cmd->queryIndex); + break; } case Command::WriteTimestamp: { diff --git a/src/tests/end2end/QueryTests.cpp b/src/tests/end2end/QueryTests.cpp index 7b768fb4ca..951545ce86 100644 --- a/src/tests/end2end/QueryTests.cpp +++ b/src/tests/end2end/QueryTests.cpp @@ -33,6 +33,46 @@ class QueryTests : public DawnTest { } }; +// Clear the content of the result buffer into 0xFFFFFFFF. +constexpr static uint64_t kSentinelValue = ~uint64_t(0); + +class OcclusionExpectation : public detail::Expectation { + public: + enum class Result { Zero, NonZero }; + + ~OcclusionExpectation() override = default; + + OcclusionExpectation(Result expected) { + mExpected = expected; + } + + testing::AssertionResult Check(const void* data, size_t size) override { + ASSERT(size % sizeof(uint64_t) == 0); + const uint64_t* actual = static_cast(data); + for (size_t i = 0; i < size / sizeof(uint64_t); i++) { + if (actual[i] == kSentinelValue) { + return testing::AssertionFailure() + << "Data[" << i << "] was not written (it kept the sentinel value of " + << kSentinelValue << ")." << std::endl; + } + if (mExpected == Result::Zero && actual[i] != 0) { + return testing::AssertionFailure() + << "Expected data[" << i << "] to be zero, actual: " << actual[i] << "." + << std::endl; + } + if (mExpected == Result::NonZero && actual[i] == 0) { + return testing::AssertionFailure() + << "Expected data[" << i << "] to be non-zero." << std::endl; + } + } + + return testing::AssertionSuccess(); + } + + private: + Result mExpected; +}; + class OcclusionQueryTests : public QueryTests { protected: void SetUp() override { @@ -80,7 +120,7 @@ class OcclusionQueryTests : public QueryTests { void TestOcclusionQueryWithDepthStencilTest(bool depthTestEnabled, bool stencilTestEnabled, - uint64_t expected) { + OcclusionExpectation::Result expected) { utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; @@ -108,10 +148,9 @@ class OcclusionQueryTests : public QueryTests { wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount); wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t)); - uint64_t myData = ~uint64_t(1); // Set all bits in buffer to check 0 is correctly written if there is no sample passed the // occlusion testing - queue.WriteBuffer(destination, 0, &myData, sizeof(myData)); + queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue)); utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView); renderPass.occlusionQuerySet = querySet; @@ -129,10 +168,11 @@ class OcclusionQueryTests : public QueryTests { wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_BUFFER_U64_EQ(expected, destination, 0); + EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new OcclusionExpectation(expected)); } - void TestOcclusionQueryWithScissorTest(ScissorRect rect, uint64_t expected) { + void TestOcclusionQueryWithScissorTest(ScissorRect rect, + OcclusionExpectation::Result expected) { utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; @@ -141,10 +181,9 @@ class OcclusionQueryTests : public QueryTests { wgpu::QuerySet querySet = CreateOcclusionQuerySet(kQueryCount); wgpu::Buffer destination = CreateResolveBuffer(kQueryCount * sizeof(uint64_t)); - uint64_t myData = ~uint64_t(1); // Set all bits in buffer to check 0 is correctly written if there is no sample passed the // occlusion testing - queue.WriteBuffer(destination, 0, &myData, sizeof(myData)); + queue.WriteBuffer(destination, 0, &kSentinelValue, sizeof(kSentinelValue)); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); renderPass.renderPassInfo.occlusionQuerySet = querySet; @@ -162,7 +201,7 @@ class OcclusionQueryTests : public QueryTests { wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_BUFFER_U64_EQ(expected, destination, 0); + EXPECT_BUFFER(destination, 0, sizeof(uint64_t), new OcclusionExpectation(expected)); } wgpu::ShaderModule vsModule; @@ -184,39 +223,39 @@ TEST_P(OcclusionQueryTests, QuerySetDestroy) { } // Draw a bottom right triangle with depth/stencil testing enabled and check whether there is -// sample passed the testing by non-precise occlusion query with the binary results: -// 0 indicates that no sample passed depth/stencil testing, -// 1 indicates that at least one sample passed depth/stencil testing. +// sample passed the testing by non-precise occlusion query with the results: +// zero indicates that no sample passed depth/stencil testing, +// non-zero indicates that at least one sample passed depth/stencil testing. TEST_P(OcclusionQueryTests, QueryWithDepthStencilTest) { - // TODO(hao.x.li@intel.com): Implement non-precise occlusion on Metal and Vulkan - DAWN_SKIP_TEST_IF(IsMetal() || IsVulkan()); + // TODO(hao.x.li@intel.com): Implement non-precise occlusion on Metal + DAWN_SKIP_TEST_IF(IsMetal()); // Disable depth/stencil testing, the samples always pass the testing, the expected occlusion - // result is 1. - TestOcclusionQueryWithDepthStencilTest(false, false, 1); + // result is non-zero. + TestOcclusionQueryWithDepthStencilTest(false, false, OcclusionExpectation::Result::NonZero); // Only enable depth testing and set the samples never pass the testing, the expected occlusion - // result is 0. - TestOcclusionQueryWithDepthStencilTest(true, false, 0); + // result is zero. + TestOcclusionQueryWithDepthStencilTest(true, false, OcclusionExpectation::Result::Zero); // Only enable stencil testing and set the samples never pass the testing, the expected - // occlusion result is 0. - TestOcclusionQueryWithDepthStencilTest(false, true, 0); + // occlusion result is zero. + TestOcclusionQueryWithDepthStencilTest(false, true, OcclusionExpectation::Result::Zero); } // Draw a bottom right triangle with scissor testing enabled and check whether there is -// sample passed the testing by non-precise occlusion query with the binary results: -// 0 indicates that no sample passed scissor testing, -// 1 indicates that at least one sample passed scissor testing. +// sample passed the testing by non-precise occlusion query with the results: +// zero indicates that no sample passed scissor testing, +// non-zero indicates that at least one sample passed scissor testing. TEST_P(OcclusionQueryTests, QueryWithScissorTest) { - // TODO(hao.x.li@intel.com): Implement non-precise occlusion on Metal and Vulkan - DAWN_SKIP_TEST_IF(IsMetal() || IsVulkan()); + // TODO(hao.x.li@intel.com): Implement non-precise occlusion on Metal + DAWN_SKIP_TEST_IF(IsMetal()); - // Test there are samples passed scissor testing, the expected occlusion result is 1. - TestOcclusionQueryWithScissorTest({2, 1, 2, 1}, 1); + // Test there are samples passed scissor testing, the expected occlusion result is non-zero. + TestOcclusionQueryWithScissorTest({2, 1, 2, 1}, OcclusionExpectation::Result::NonZero); - // Test there is no sample passed scissor testing, the expected occlusion result is 0. - TestOcclusionQueryWithScissorTest({0, 0, 2, 1}, 0); + // Test there is no sample passed scissor testing, the expected occlusion result is zero. + TestOcclusionQueryWithScissorTest({0, 0, 2, 1}, OcclusionExpectation::Result::Zero); } DAWN_INSTANTIATE_TEST(OcclusionQueryTests, D3D12Backend(), MetalBackend(), VulkanBackend());