Flutter Engine
 
Loading...
Searching...
No Matches
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
6#include "flutter/testing/testing.h" // IWYU pragma: keep
12#include "vulkan/vulkan_core.h"
13
14namespace impeller {
15namespace testing {
16
17TEST(ContextVKTest, CommonHardwareConcurrencyConfigurations) {
18 EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(100u), 4u);
28}
29
30TEST(ContextVKTest, DeletesCommandPools) {
31 std::weak_ptr<ContextVK> weak_context;
32 std::weak_ptr<CommandPoolVK> weak_pool;
33 {
34 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
35 auto const pool = context->GetCommandPoolRecycler()->Get();
36 weak_pool = pool;
37 weak_context = context;
38 ASSERT_TRUE(weak_pool.lock());
39 ASSERT_TRUE(weak_context.lock());
40 }
41 ASSERT_FALSE(weak_pool.lock());
42 ASSERT_FALSE(weak_context.lock());
43}
44
45TEST(ContextVKTest, DeletesCommandPoolsOnAllThreads) {
46 std::weak_ptr<ContextVK> weak_context;
47 std::weak_ptr<CommandPoolVK> weak_pool_main;
48
49 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
50 weak_pool_main = context->GetCommandPoolRecycler()->Get();
51 weak_context = context;
52 ASSERT_TRUE(weak_pool_main.lock());
53 ASSERT_TRUE(weak_context.lock());
54
55 // Start a second thread that obtains a command pool.
56 fml::AutoResetWaitableEvent latch1, latch2;
57 std::weak_ptr<CommandPoolVK> weak_pool_thread;
58 std::thread thread([&]() {
59 weak_pool_thread = context->GetCommandPoolRecycler()->Get();
60 latch1.Signal();
61 latch2.Wait();
62 });
63
64 // Delete the ContextVK on the main thread.
65 latch1.Wait();
66 context.reset();
67 ASSERT_FALSE(weak_pool_main.lock());
68 ASSERT_FALSE(weak_context.lock());
69
70 // Stop the second thread and check that its command pool has been deleted.
71 latch2.Signal();
72 thread.join();
73 ASSERT_FALSE(weak_pool_thread.lock());
74}
75
76TEST(ContextVKTest, ThreadLocalCleanupDeletesCommandPool) {
77 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
78
79 fml::AutoResetWaitableEvent latch1, latch2;
80 std::weak_ptr<CommandPoolVK> weak_pool;
81 std::thread thread([&]() {
82 weak_pool = context->GetCommandPoolRecycler()->Get();
83 context->DisposeThreadLocalCachedResources();
84 latch1.Signal();
85 latch2.Wait();
86 });
87
88 latch1.Wait();
89 ASSERT_FALSE(weak_pool.lock());
90
91 latch2.Signal();
92 thread.join();
93}
94
95TEST(ContextVKTest, DeletePipelineAfterContext) {
96 std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
97 std::shared_ptr<std::vector<std::string>> functions;
98 {
99 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
100 PipelineDescriptor pipeline_desc;
101 pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
102 PipelineFuture<PipelineDescriptor> pipeline_future =
103 context->GetPipelineLibrary()->GetPipeline(pipeline_desc);
104 pipeline = pipeline_future.Get();
105 ASSERT_TRUE(pipeline);
106 functions = GetMockVulkanFunctions(context->GetDevice());
107 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
108 "vkCreateGraphicsPipelines") != functions->end());
109 }
110 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
111 "vkDestroyDevice") != functions->end());
112}
113
114TEST(ContextVKTest, DeleteShaderFunctionAfterContext) {
115 std::shared_ptr<const ShaderFunction> shader_function;
116 std::shared_ptr<std::vector<std::string>> functions;
117 {
118 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
119 PipelineDescriptor pipeline_desc;
120 pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
121 std::vector<uint8_t> data = {0x03, 0x02, 0x23, 0x07};
122 context->GetShaderLibrary()->RegisterFunction(
123 "foobar_fragment_main", ShaderStage::kFragment,
124 std::make_shared<fml::DataMapping>(data), [](bool) {});
125 shader_function = context->GetShaderLibrary()->GetFunction(
126 "foobar_fragment_main", ShaderStage::kFragment);
127 ASSERT_TRUE(shader_function);
128 functions = GetMockVulkanFunctions(context->GetDevice());
129 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
130 "vkCreateShaderModule") != functions->end());
131 }
132 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
133 "vkDestroyDevice") != functions->end());
134}
135
136TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
137 std::shared_ptr<PipelineLibrary> pipeline_library;
138 std::shared_ptr<std::vector<std::string>> functions;
139 {
140 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
141 PipelineDescriptor pipeline_desc;
142 pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
143 pipeline_library = context->GetPipelineLibrary();
144 functions = GetMockVulkanFunctions(context->GetDevice());
145 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
146 "vkCreatePipelineCache") != functions->end());
147 }
148 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
149 "vkDestroyDevice") != functions->end());
150}
151
152TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) {
153 // The mocked methods don't report the presence of a validation layer but we
154 // explicitly ask for validation. Context creation should continue anyway.
155 auto context = MockVulkanContextBuilder()
156 .SetSettingsCallback([](auto& settings) {
157 settings.enable_validation = true;
158 })
159 .Build();
160 ASSERT_NE(context, nullptr);
161 const CapabilitiesVK* capabilites_vk =
162 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
163 ASSERT_FALSE(capabilites_vk->AreValidationsEnabled());
164}
165
166TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
167 auto context =
170 [](auto& settings) { settings.enable_validation = true; })
171 .SetInstanceExtensions(
172 {"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"})
173 .SetInstanceLayers({"VK_LAYER_KHRONOS_validation"})
174 .Build();
175 ASSERT_NE(context, nullptr);
176 const CapabilitiesVK* capabilites_vk =
177 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
178 ASSERT_TRUE(capabilites_vk->AreValidationsEnabled());
179}
180
181// In Impeller's 2D renderer, we no longer use stencil-only formats. They're
182// less widely supported than combined depth-stencil formats, so make sure we
183// don't fail initialization if we can't find a suitable stencil format.
184TEST(CapabilitiesVKTest, ContextInitializesWithNoStencilFormat) {
185 const std::shared_ptr<ContextVK> context =
188 [](VkPhysicalDevice physicalDevice, VkFormat format,
189 VkFormatProperties* pFormatProperties) {
190 if (format == VK_FORMAT_R8G8B8A8_UNORM) {
191 pFormatProperties->optimalTilingFeatures =
192 static_cast<VkFormatFeatureFlags>(
193 vk::FormatFeatureFlagBits::eColorAttachment);
194 } else if (format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
195 pFormatProperties->optimalTilingFeatures =
196 static_cast<VkFormatFeatureFlags>(
197 vk::FormatFeatureFlagBits::eDepthStencilAttachment);
198 }
199 // Ignore just the stencil format.
200 })
201 .Build();
202 ASSERT_NE(context, nullptr);
203 const CapabilitiesVK* capabilites_vk =
204 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
205 ASSERT_EQ(capabilites_vk->GetDefaultDepthStencilFormat(),
207 ASSERT_EQ(capabilites_vk->GetDefaultStencilFormat(),
209}
210
211// Impeller's 2D renderer relies on hardware support for a combined
212// depth-stencil format (widely supported). So fail initialization if a suitable
213// one couldn't be found. That way we have an opportunity to fallback to
214// OpenGLES.
215TEST(CapabilitiesVKTest,
216 ContextFailsInitializationForNoCombinedDepthStencilFormat) {
217 ScopedValidationDisable disable_validation;
218 const std::shared_ptr<ContextVK> context =
221 [](VkPhysicalDevice physicalDevice, VkFormat format,
222 VkFormatProperties* pFormatProperties) {
223 if (format == VK_FORMAT_R8G8B8A8_UNORM) {
224 pFormatProperties->optimalTilingFeatures =
225 static_cast<VkFormatFeatureFlags>(
226 vk::FormatFeatureFlagBits::eColorAttachment);
227 }
228 // Ignore combined depth-stencil formats.
229 })
230 .Build();
231 ASSERT_EQ(context, nullptr);
232}
233
234TEST(ContextVKTest, WarmUpFunctionCreatesRenderPass) {
235 const std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
236
237 context->SetOffscreenFormat(PixelFormat::kR8G8B8A8UNormInt);
238 context->InitializeCommonlyUsedShadersIfNeeded();
239
240 auto functions = GetMockVulkanFunctions(context->GetDevice());
241 ASSERT_TRUE(std::find(functions->begin(), functions->end(),
242 "vkCreateRenderPass") != functions->end());
243}
244
245TEST(ContextVKTest, FatalMissingValidations) {
246 EXPECT_DEATH(const std::shared_ptr<ContextVK> context =
248 .SetSettingsCallback([](ContextVK::Settings& settings) {
249 settings.enable_validation = true;
250 settings.fatal_missing_validations = true;
251 })
252 .Build(),
253 "");
254}
255
256TEST(ContextVKTest, HasDefaultColorFormat) {
257 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
258
259 const CapabilitiesVK* capabilites_vk =
260 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
261 ASSERT_NE(capabilites_vk->GetDefaultColorFormat(), PixelFormat::kUnknown);
262}
263
264TEST(ContextVKTest, EmbedderOverridesUsesInstanceExtensions) {
266 auto other_context = MockVulkanContextBuilder().Build();
267
268 data.instance = other_context->GetInstance();
269 data.device = other_context->GetDevice();
270 data.physical_device = other_context->GetPhysicalDevice();
271 data.queue = VkQueue{};
272 data.queue_family_index = 0;
273 // Missing surface extension.
274 data.instance_extensions = {};
275 data.device_extensions = {"VK_KHR_swapchain"};
276
279
280 EXPECT_EQ(context, nullptr);
281}
282
283TEST(ContextVKTest, EmbedderOverrides) {
285 auto other_context = MockVulkanContextBuilder().Build();
286
287 data.instance = other_context->GetInstance();
288 data.device = other_context->GetDevice();
289 data.physical_device = other_context->GetPhysicalDevice();
290 data.queue = VkQueue{};
291 data.queue_family_index = 0;
292 data.instance_extensions = {"VK_KHR_surface",
293 "VK_KHR_portability_enumeration"};
294 data.device_extensions = {"VK_KHR_swapchain"};
295
297
298 EXPECT_TRUE(context->IsValid());
299 EXPECT_EQ(context->GetInstance(), other_context->GetInstance());
300 EXPECT_EQ(context->GetDevice(), other_context->GetDevice());
301 EXPECT_EQ(context->GetPhysicalDevice(), other_context->GetPhysicalDevice());
302 EXPECT_EQ(context->GetGraphicsQueue()->GetIndex().index, 0u);
303 EXPECT_EQ(context->GetGraphicsQueue()->GetIndex().family, 0u);
304}
305
306TEST(ContextVKTest, BatchSubmitCommandBuffersOnArm) {
307 std::shared_ptr<ContextVK> context =
310 [](VkPhysicalDevice device, VkPhysicalDeviceProperties* prop) {
311 prop->vendorID = 0x13B5; // ARM
312 prop->deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
313 })
314 .Build();
315
316 EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer()));
317 EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer()));
318
319 // If command buffers are batch submitted, we should have created them but not
320 // created the fence to track them after enqueing.
321 auto functions = GetMockVulkanFunctions(context->GetDevice());
322 EXPECT_TRUE(std::find(functions->begin(), functions->end(),
323 "vkAllocateCommandBuffers") != functions->end());
324 EXPECT_TRUE(std::find(functions->begin(), functions->end(),
325 "vkCreateFence") == functions->end());
326
327 context->FlushCommandBuffers();
328
329 // After flushing, the fence should be created.
330 functions = GetMockVulkanFunctions(context->GetDevice());
331 EXPECT_TRUE(std::find(functions->begin(), functions->end(),
332 "vkCreateFence") != functions->end());
333}
334
335TEST(ContextVKTest, BatchSubmitCommandBuffersOnNonArm) {
336 std::shared_ptr<ContextVK> context =
339 [](VkPhysicalDevice device, VkPhysicalDeviceProperties* prop) {
340 prop->vendorID = 0x8686; // Made up ID
341 prop->deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
342 })
343 .Build();
344
345 EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer()));
346 EXPECT_TRUE(context->EnqueueCommandBuffer(context->CreateCommandBuffer()));
347
348 // If command buffers are batched and not submitted, we should have created
349 // them and a corresponding fence immediately.
350 auto functions = GetMockVulkanFunctions(context->GetDevice());
351 EXPECT_TRUE(std::find(functions->begin(), functions->end(),
352 "vkAllocateCommandBuffers") != functions->end());
353 EXPECT_FALSE(std::find(functions->begin(), functions->end(),
354 "vkCreateFence") != functions->end());
355}
356
357TEST(ContextVKTest, AHBSwapchainCapabilitiesCanBeMissing) {
358 {
359 std::shared_ptr<ContextVK> context =
362 settings.enable_surface_control = true;
363 })
364 .Build();
365
366 EXPECT_FALSE(context->GetShouldEnableSurfaceControlSwapchain());
367 }
368
370 auto other_context = MockVulkanContextBuilder().Build();
371
372 data.instance = other_context->GetInstance();
373 data.device = other_context->GetDevice();
374 data.physical_device = other_context->GetPhysicalDevice();
375 data.queue = VkQueue{};
376 data.queue_family_index = 0;
377 data.instance_extensions = {"VK_KHR_surface", "VK_KHR_android_surface"};
378 data.device_extensions = {"VK_KHR_swapchain",
379 VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME,
380 VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME,
381 VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
382 VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME};
383
384 auto context = MockVulkanContextBuilder()
386 settings.enable_surface_control = true;
387 })
388 .SetEmbedderData(data)
389 .Build();
390
391 EXPECT_TRUE(context->GetShouldEnableSurfaceControlSwapchain());
392
393} // namespace impeller
394
395TEST(ContextVKTest, HashIsUniqueAcrossThreads) {
396 uint64_t hash1, hash2;
397 std::thread thread1([&]() {
398 auto context = MockVulkanContextBuilder().Build();
399 hash1 = context->GetHash();
400 });
401 std::thread thread2([&]() {
402 auto context = MockVulkanContextBuilder().Build();
403 hash2 = context->GetHash();
404 });
405 thread1.join();
406 thread2.join();
407
408 EXPECT_NE(hash1, hash2);
409}
410
411} // namespace testing
412} // namespace impeller
The Vulkan layers and extensions wrangler.
PixelFormat GetDefaultStencilFormat() const override
Returns a supported PixelFormat for textures that store stencil information. May include a depth chan...
PixelFormat GetDefaultDepthStencilFormat() const override
Returns a supported PixelFormat for textures that store both a stencil and depth component....
PixelFormat GetDefaultColorFormat() const override
Returns a supported PixelFormat for textures that store 4-channel colors (red/green/blue/alpha).
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency)
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
MockVulkanContextBuilder & SetPhysicalPropertiesCallback(std::function< void(VkPhysicalDevice device, VkPhysicalDeviceProperties *physicalProperties)> physical_properties_callback)
MockVulkanContextBuilder SetEmbedderData(const ContextVK::EmbedderData &embedder_data)
std::shared_ptr< ContextVK > Build()
Create a Vulkan context with Vulkan functions mocked. The caller is given a chance to tinker on the s...
MockVulkanContextBuilder & SetPhysicalDeviceFormatPropertiesCallback(std::function< void(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties)> format_properties_callback)
Definition mock_vulkan.h:94
MockVulkanContextBuilder & SetSettingsCallback(const std::function< void(ContextVK::Settings &)> &settings_callback)
Definition mock_vulkan.h:74
VkDevice device
Definition main.cc:69
uint32_t uint32_t * format
TEST(FrameTimingsRecorderTest, RecordVsync)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
std::shared_ptr< std::vector< std::string > > GetMockVulkanFunctions(VkDevice device)
bool fatal_missing_validations
If validations are requested but cannot be enabled, log a fatal error.
Definition context_vk.h:87
const std::shared_ptr< Pipeline< T > > Get() const
Definition pipeline.h:32