Flutter Engine
The Flutter Engine
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
12
13namespace impeller {
14namespace testing {
15
16TEST(ContextVKTest, CommonHardwareConcurrencyConfigurations) {
17 EXPECT_EQ(ContextVK::ChooseThreadCountForWorkers(100u), 4u);
27}
28
29TEST(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
44TEST(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
75TEST(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>());
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
94TEST(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
116TEST(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
132TEST(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
146TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
147 auto context =
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.
164TEST(CapabilitiesVKTest, ContextInitializesWithNoStencilFormat) {
165 const std::shared_ptr<ContextVK> context =
168 [](VkPhysicalDevice physicalDevice, VkFormat format,
169 VkFormatProperties* pFormatProperties) {
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.
195TEST(CapabilitiesVKTest,
196 ContextFailsInitializationForNoCombinedDepthStencilFormat) {
197 ScopedValidationDisable disable_validation;
198 const std::shared_ptr<ContextVK> context =
201 [](VkPhysicalDevice physicalDevice, VkFormat format,
202 VkFormatProperties* pFormatProperties) {
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
214TEST(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
225TEST(ContextVKTest, FatalMissingValidations) {
226 EXPECT_DEATH(const std::shared_ptr<ContextVK> context =
228 .SetSettingsCallback([](ContextVK::Settings& settings) {
229 settings.enable_validation = true;
230 settings.fatal_missing_validations = true;
231 })
232 .Build(),
233 "");
234}
235
236TEST(ContextVKTest, HasDefaultColorFormat) {
237 std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
238
239 const CapabilitiesVK* capabilites_vk =
240 reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
241 ASSERT_NE(capabilites_vk->GetDefaultColorFormat(), PixelFormat::kUnknown);
242}
243
244} // namespace testing
245} // namespace impeller
AutoreleasePool pool
int find(T *array, int N, T item)
The Vulkan layers and extensions wrangler.
bool AreValidationsEnabled() const
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)
Definition: context_vk.cc:110
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
std::shared_ptr< ContextVK > Build()
Create a Vulkan context with Vulkan functions mocked. The caller is given a chance to tinker on the s...
Definition: mock_vulkan.cc:912
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
uint32_t uint32_t * format
const GrXPFactory * Get(SkBlendMode mode)
def Build(configs, env, options)
Definition: build.py:232
std::shared_ptr< std::vector< std::string > > GetMockVulkanFunctions(VkDevice device)
Definition: mock_vulkan.cc:926
TEST(AiksCanvasTest, EmptyCullRect)
VkFormatFeatureFlags optimalTilingFeatures
Definition: vulkan_core.h:3014
const std::shared_ptr< Pipeline< T > > Get() const
Definition: pipeline.h:29
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
VkFlags VkFormatFeatureFlags
Definition: vulkan_core.h:2307
VkFormat
Definition: vulkan_core.h:1458
@ VK_FORMAT_R8G8B8A8_UNORM
Definition: vulkan_core.h:1496
@ VK_FORMAT_D32_SFLOAT_S8_UINT
Definition: vulkan_core.h:1589