Flutter Impeller
context_vk_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/fml/synchronization/waitable_event.h"
6 #include "flutter/testing/testing.h" // IWYU pragma: keep
11 #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
12 
13 namespace impeller {
14 namespace testing {
15 
16 TEST(ContextVKTest, CommonHardwareConcurrencyConfigurations) {
17  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(100u), 4u);
18  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(9u), 4u);
19  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(8u), 4u);
20  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(7u), 3u);
21  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(6u), 3u);
22  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(5u), 2u);
23  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(4u), 2u);
24  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(3u), 1u);
25  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(2u), 1u);
26  EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(1u), 1u);
27 }
28 
29 TEST(ContextVKTest, DeletesCommandPools) {
30  std::weak_ptr<ContextVK> weak_context;
31  std::weak_ptr<CommandPoolVK> weak_pool;
32  {
33  std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
34  auto const pool = context->GetCommandPoolRecycler()->Get();
35  weak_pool = pool;
36  weak_context = context;
37  ASSERT_TRUE(weak_pool.lock());
38  ASSERT_TRUE(weak_context.lock());
39  }
40  ASSERT_FALSE(weak_pool.lock());
41  ASSERT_FALSE(weak_context.lock());
42 }
43 
44 TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) {
45  std::weak_ptr<ContextVK> weak_context;
46  std::weak_ptr<CommandPoolVK> weak_pool_main;
47 
48  std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
49  weak_pool_main = context->GetCommandPoolRecycler()->Get();
50  weak_context = context;
51  ASSERT_TRUE(weak_pool_main.lock());
52  ASSERT_TRUE(weak_context.lock());
53 
54  // Start a second thread that obtains a command pool.
55  fml::AutoResetWaitableEvent latch1, latch2;
56  std::weak_ptr<CommandPoolVK> weak_pool_thread;
57  std::thread thread([&]() {
58  weak_pool_thread = context->GetCommandPoolRecycler()->Get();
59  latch1.Signal();
60  latch2.Wait();
61  });
62 
63  // Delete the ContextVK on the main thread.
64  latch1.Wait();
65  context.reset();
66  ASSERT_FALSE(weak_pool_main.lock());
67  ASSERT_FALSE(weak_context.lock());
68 
69  // Stop the second thread and check that its command pool has been deleted.
70  latch2.Signal();
71  thread.join();
72  ASSERT_FALSE(weak_pool_thread.lock());
73 }
74 
75 TEST(ContextVKTest, DeletePipelineAfterContext) {
76  std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
77  std::shared_ptr<std::vector<std::string>> functions;
78  {
79  std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
80  PipelineDescriptor pipeline_desc;
81  pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
82  PipelineFuture<PipelineDescriptor> pipeline_future =
83  context->GetPipelineLibrary()->GetPipeline(pipeline_desc);
84  pipeline = pipeline_future.Get();
85  ASSERT_TRUE(pipeline);
86  functions = GetMockVulkanFunctions(context->GetDevice());
87  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
88  "vkCreateGraphicsPipelines") != functions->end());
89  }
90  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
91  "vkDestroyDevice") != functions->end());
92 }
93 
94 TEST(ContextVKTest, DeleteShaderFunctionAfterContext) {
95  std::shared_ptr<const ShaderFunction> shader_function;
96  std::shared_ptr<std::vector<std::string>> functions;
97  {
98  std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
99  PipelineDescriptor pipeline_desc;
100  pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
101  std::vector<uint8_t> data = {0x03, 0x02, 0x23, 0x07};
102  context->GetShaderLibrary()->RegisterFunction(
103  "foobar_fragment_main", ShaderStage::kFragment,
104  std::make_shared<fml::DataMapping>(data), [](bool) {});
105  shader_function = context->GetShaderLibrary()->GetFunction(
106  "foobar_fragment_main", ShaderStage::kFragment);
107  ASSERT_TRUE(shader_function);
108  functions = GetMockVulkanFunctions(context->GetDevice());
109  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
110  "vkCreateShaderModule") != functions->end());
111  }
112  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
113  "vkDestroyDevice") != functions->end());
114 }
115 
116 TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
117  std::shared_ptr<PipelineLibrary> pipeline_library;
118  std::shared_ptr<std::vector<std::string>> functions;
119  {
120  std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
121  PipelineDescriptor pipeline_desc;
122  pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
123  pipeline_library = context->GetPipelineLibrary();
124  functions = GetMockVulkanFunctions(context->GetDevice());
125  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
126  "vkCreatePipelineCache") != functions->end());
127  }
128  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
129  "vkDestroyDevice") != functions->end());
130 }
131 
132 TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) {
133  // The mocked methods don't report the presence of a validation layer but we
134  // explicitly ask for validation. Context creation should continue anyway.
135  auto context = MockVulkanContextBuilder()
136  .SetSettingsCallback([](auto& settings) {
137  settings.enable_validation = true;
138  })
139  .Build();
140  ASSERT_NE(context, nullptr);
141  const CapabilitiesVK* capabilites_vk =
142  reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
143  ASSERT_FALSE(capabilites_vk->AreValidationsEnabled());
144 }
145 
146 TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
147  auto context =
148  MockVulkanContextBuilder()
149  .SetSettingsCallback(
150  [](auto& settings) { settings.enable_validation = true; })
151  .SetInstanceExtensions(
152  {"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"})
153  .SetInstanceLayers({"VK_LAYER_KHRONOS_validation"})
154  .Build();
155  ASSERT_NE(context, nullptr);
156  const CapabilitiesVK* capabilites_vk =
157  reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
158  ASSERT_TRUE(capabilites_vk->AreValidationsEnabled());
159 }
160 
161 // In Impeller's 2D renderer, we no longer use stencil-only formats. They're
162 // less widely supported than combined depth-stencil formats, so make sure we
163 // don't fail initialization if we can't find a suitable stencil format.
164 TEST(CapabilitiesVKTest, ContextInitializesWithNoStencilFormat) {
165  const std::shared_ptr<ContextVK> context =
166  MockVulkanContextBuilder()
167  .SetPhysicalDeviceFormatPropertiesCallback(
168  [](VkPhysicalDevice physicalDevice, VkFormat format,
169  VkFormatProperties* pFormatProperties) {
170  if (format == VK_FORMAT_B8G8R8A8_UNORM) {
171  pFormatProperties->optimalTilingFeatures =
172  static_cast<VkFormatFeatureFlags>(
173  vk::FormatFeatureFlagBits::eColorAttachment);
174  } else if (format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
175  pFormatProperties->optimalTilingFeatures =
176  static_cast<VkFormatFeatureFlags>(
177  vk::FormatFeatureFlagBits::eDepthStencilAttachment);
178  }
179  // Ignore just the stencil format.
180  })
181  .Build();
182  ASSERT_NE(context, nullptr);
183  const CapabilitiesVK* capabilites_vk =
184  reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
185  ASSERT_EQ(capabilites_vk->GetDefaultDepthStencilFormat(),
187  ASSERT_EQ(capabilites_vk->GetDefaultStencilFormat(),
189 }
190 
191 // Impeller's 2D renderer relies on hardware support for a combined
192 // depth-stencil format (widely supported). So fail initialization if a suitable
193 // one couldn't be found. That way we have an opportunity to fallback to
194 // OpenGLES.
195 TEST(CapabilitiesVKTest,
196  ContextFailsInitializationForNoCombinedDepthStencilFormat) {
197  ScopedValidationDisable disable_validation;
198  const std::shared_ptr<ContextVK> context =
199  MockVulkanContextBuilder()
200  .SetPhysicalDeviceFormatPropertiesCallback(
201  [](VkPhysicalDevice physicalDevice, VkFormat format,
202  VkFormatProperties* pFormatProperties) {
203  if (format == VK_FORMAT_B8G8R8A8_UNORM) {
204  pFormatProperties->optimalTilingFeatures =
205  static_cast<VkFormatFeatureFlags>(
206  vk::FormatFeatureFlagBits::eColorAttachment);
207  }
208  // Ignore combined depth-stencil formats.
209  })
210  .Build();
211  ASSERT_EQ(context, nullptr);
212 }
213 
214 TEST(ContextVKTest, WarmUpFunctionCreatesRenderPass) {
215  const std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
216 
217  context->SetOffscreenFormat(PixelFormat::kR8G8B8A8UNormInt);
218  context->InitializeCommonlyUsedShadersIfNeeded();
219 
220  auto functions = GetMockVulkanFunctions(context->GetDevice());
221  ASSERT_TRUE(std::find(functions->begin(), functions->end(),
222  "vkCreateRenderPass") != functions->end());
223 }
224 
225 } // namespace testing
226 } // namespace impeller
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:24
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
formats.h
impeller::CapabilitiesVK::GetDefaultStencilFormat
PixelFormat GetDefaultStencilFormat() const override
Returns a supported PixelFormat for textures that store stencil information. May include a depth chan...
Definition: capabilities_vk.cc:557
validation.h
impeller::PipelineFuture
Definition: pipeline.h:24
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
command_pool_vk.h
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:65
impeller::CapabilitiesVK::GetDefaultDepthStencilFormat
PixelFormat GetDefaultDepthStencilFormat() const override
Returns a supported PixelFormat for textures that store both a stencil and depth component....
Definition: capabilities_vk.cc:562
impeller::ShaderStage::kFragment
@ kFragment
impeller::CapabilitiesVK
The Vulkan layers and extensions wrangler.
Definition: capabilities_vk.h:113
impeller::ContextVK::ChooseThreadCountForWorkers
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency)
Definition: context_vk.cc:110
impeller::PipelineFuture::Get
const std::shared_ptr< Pipeline< T > > Get() const
Definition: pipeline.h:28
impeller::CapabilitiesVK::AreValidationsEnabled
bool AreValidationsEnabled() const
Definition: capabilities_vk.cc:61
context_vk.h
impeller::ScopedValidationDisable
Definition: validation.h:38
impeller
Definition: aiks_blur_unittests.cc:20
impeller::PipelineDescriptor::SetVertexDescriptor
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
Definition: pipeline_descriptor.cc:96