| 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
7 #include "impeller/base/validation.h"
8 #include "impeller/core/formats.h"
9 #include "impeller/renderer/backend/vulkan/command_pool_vk.h"
10 #include "impeller/renderer/backend/vulkan/context_vk.h"
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, ThreadLocalCleanupDeletesCommandPool) {
76 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
77
78 fml::AutoResetWaitableEvent latch1, latch2;
79 std::weak_ptr<CommandPoolVK> weak_pool;
80 std::thread thread([&]() {
81 weak_pool = context->GetCommandPoolRecycler()->Get();
82 context->DisposeThreadLocalCachedResources();
83 latch1.Signal();
84 latch2.Wait();
85 });
86
87 latch1.Wait();
88 ASSERT_FALSE(weak_pool.lock());
89
90 latch2.Signal();
91 thread.join();
92 }
93
94 TEST(ContextVKTest, DeletePipelineAfterContext) {
95 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
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 PipelineFuture<PipelineDescriptor> pipeline_future =
102 context->GetPipelineLibrary()->GetPipeline(pipeline_desc);
103 pipeline = pipeline_future.Get();
104 ASSERT_TRUE(pipeline);
105 functions = GetMockVulkanFunctions(context->GetDevice());
106 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
107 "vkCreateGraphicsPipelines") != functions->end());
108 }
109 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
110 "vkDestroyDevice") != functions->end());
111 }
112
113 TEST(ContextVKTest, DeleteShaderFunctionAfterContext) {
114 std::shared_ptr<const ShaderFunction> shader_function;
115 std::shared_ptr<std::vector<std::string>> functions;
116 {
117 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
118 PipelineDescriptor pipeline_desc;
119 pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
120 std::vector<uint8_t> data = {0x03, 0x02, 0x23, 0x07};
121 context->GetShaderLibrary()->RegisterFunction(
122 "foobar_fragment_main", ShaderStage::kFragment,
123 std::make_shared<fml::DataMapping>(data), [](bool) {});
124 shader_function = context->GetShaderLibrary()->GetFunction(
125 "foobar_fragment_main", ShaderStage::kFragment);
126 ASSERT_TRUE(shader_function);
127 functions = GetMockVulkanFunctions(context->GetDevice());
128 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
129 "vkCreateShaderModule") != functions->end());
130 }
131 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
132 "vkDestroyDevice") != functions->end());
133 }
134
135 TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
136 std::shared_ptr<PipelineLibrary> pipeline_library;
137 std::shared_ptr<std::vector<std::string>> functions;
138 {
139 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
140 PipelineDescriptor pipeline_desc;
141 pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
142 pipeline_library = context->GetPipelineLibrary();
143 functions = GetMockVulkanFunctions(context->GetDevice());
144 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
145 "vkCreatePipelineCache") != functions->end());
146 }
147 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
148 "vkDestroyDevice") != functions->end());
149 }
150
151 TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) {
152 // The mocked methods don't report the presence of a validation layer but we
153 // explicitly ask for validation. Context creation should continue anyway.
154 auto context = MockVulkanContextBuilder()
155 .SetSettingsCallback([](auto& settings) {
156 settings.enable_validation = true;
157 })
158 .Build();
159 ASSERT_NE(context, nullptr);
160 const CapabilitiesVK* capabilites_vk =
161 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
162 ASSERT_FALSE(capabilites_vk->AreValidationsEnabled());
163 }
164
165 TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
166 auto context =
167 MockVulkanContextBuilder()
168 .SetSettingsCallback(
169 [](auto& settings) { settings.enable_validation = true; })
170 .SetInstanceExtensions(
171 {"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"})
172 .SetInstanceLayers({"VK_LAYER_KHRONOS_validation"})
173 .Build();
174 ASSERT_NE(context, nullptr);
175 const CapabilitiesVK* capabilites_vk =
176 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
177 ASSERT_TRUE(capabilites_vk->AreValidationsEnabled());
178 }
179
180 // In Impeller's 2D renderer, we no longer use stencil-only formats. They're
181 // less widely supported than combined depth-stencil formats, so make sure we
182 // don't fail initialization if we can't find a suitable stencil format.
183 TEST(CapabilitiesVKTest, ContextInitializesWithNoStencilFormat) {
184 const std::shared_ptr<ContextVK> context =
185 MockVulkanContextBuilder()
186 .SetPhysicalDeviceFormatPropertiesCallback(
187 [](VkPhysicalDevice physicalDevice, VkFormat format,
188 VkFormatProperties* pFormatProperties) {
189 if (format == VK_FORMAT_R8G8B8A8_UNORM) {
190 pFormatProperties->optimalTilingFeatures =
191 static_cast<VkFormatFeatureFlags>(
192 vk::FormatFeatureFlagBits::eColorAttachment);
193 } else if (format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
194 pFormatProperties->optimalTilingFeatures =
195 static_cast<VkFormatFeatureFlags>(
196 vk::FormatFeatureFlagBits::eDepthStencilAttachment);
197 }
198 // Ignore just the stencil format.
199 })
200 .Build();
201 ASSERT_NE(context, nullptr);
202 const CapabilitiesVK* capabilites_vk =
203 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
204 ASSERT_EQ(capabilites_vk->GetDefaultDepthStencilFormat(),
205 PixelFormat::kD32FloatS8UInt);
206 ASSERT_EQ(capabilites_vk->GetDefaultStencilFormat(),
207 PixelFormat::kD32FloatS8UInt);
208 }
209
210 // Impeller's 2D renderer relies on hardware support for a combined
211 // depth-stencil format (widely supported). So fail initialization if a suitable
212 // one couldn't be found. That way we have an opportunity to fallback to
213 // OpenGLES.
214 TEST(CapabilitiesVKTest,
215 ContextFailsInitializationForNoCombinedDepthStencilFormat) {
216 ScopedValidationDisable disable_validation;
217 const std::shared_ptr<ContextVK> context =
218 MockVulkanContextBuilder()
219 .SetPhysicalDeviceFormatPropertiesCallback(
220 [](VkPhysicalDevice physicalDevice, VkFormat format,
221 VkFormatProperties* pFormatProperties) {
222 if (format == VK_FORMAT_R8G8B8A8_UNORM) {
223 pFormatProperties->optimalTilingFeatures =
224 static_cast<VkFormatFeatureFlags>(
225 vk::FormatFeatureFlagBits::eColorAttachment);
226 }
227 // Ignore combined depth-stencil formats.
228 })
229 .Build();
230 ASSERT_EQ(context, nullptr);
231 }
232
233 TEST(ContextVKTest, WarmUpFunctionCreatesRenderPass) {
234 const std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
235
236 context->SetOffscreenFormat(PixelFormat::kR8G8B8A8UNormInt);
237 context->InitializeCommonlyUsedShadersIfNeeded();
238
239 auto functions = GetMockVulkanFunctions(context->GetDevice());
240 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
241 "vkCreateRenderPass") != functions->end());
242 }
243
244 TEST(ContextVKTest, FatalMissingValidations) {
245 EXPECT_DEATH(const std::shared_ptr<ContextVK> context =
246 MockVulkanContextBuilder()
247 .SetSettingsCallback([](ContextVK::Settings& settings) {
248 settings.enable_validation = true;
249 settings.fatal_missing_validations = true;
250 })
251 .Build(),
252 "");
253 }
254
255 TEST(ContextVKTest, HasDefaultColorFormat) {
256 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
257
258 const CapabilitiesVK* capabilites_vk =
259 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
260 ASSERT_NE(capabilites_vk->GetDefaultColorFormat(), PixelFormat::kUnknown);
261 }
262
263 } // namespace testing
264 } // namespace impeller
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:24
impeller::ContextVK::Settings::enable_validation
bool enable_validation
Definition: context_vk.h:50
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
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:625
formats.h
validation.h
impeller::PipelineFuture
Definition: pipeline.h:25
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
command_pool_vk.h
impeller::ContextVK::Settings
Definition: context_vk.h:46
impeller::ContextVK::Settings::fatal_missing_validations
bool fatal_missing_validations
If validations are requested but cannot be enabled, log a fatal error.
Definition: context_vk.h:54
impeller::CapabilitiesVK::GetDefaultColorFormat
PixelFormat GetDefaultColorFormat() const override
Returns a supported PixelFormat for textures that store 4-channel colors (red/green/blue/alpha).
Definition: capabilities_vk.cc:620
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:630
impeller::ShaderStage::kFragment
@ kFragment
impeller::CapabilitiesVK
The Vulkan layers and extensions wrangler.
Definition: capabilities_vk.h:169
impeller::PixelFormat::kUnknown
@ kUnknown
impeller::ContextVK::ChooseThreadCountForWorkers
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency)
Definition: context_vk.cc:108
impeller::PipelineFuture::Get
const std::shared_ptr< Pipeline< T > > Get() const
Definition: pipeline.h:29
impeller::CapabilitiesVK::AreValidationsEnabled
bool AreValidationsEnabled() const
Definition: capabilities_vk.cc:66
context_vk.h
impeller::ScopedValidationDisable
Definition: validation.h:56
impeller
Definition: allocation.cc:12
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller::PipelineDescriptor::SetVertexDescriptor
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
Definition: pipeline_descriptor.cc:96