Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit bd765e9

Browse files
authored
[Impeller] Introduce mock vulkan context builder (#45834)
This allows us to have better control over the mock context and allows us to test situations like having the validations available, versus not available. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 09034a7 commit bd765e9

File tree

7 files changed

+129
-41
lines changed

7 files changed

+129
-41
lines changed

impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace impeller {
1111
namespace testing {
1212

1313
TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) {
14-
auto context = CreateMockVulkanContext();
14+
auto context = MockVulkanContextBuilder().Build();
1515
auto pool = CommandPoolVK::GetThreadLocal(context.get());
1616
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
1717
BlitCopyTextureToTextureCommandVK cmd;
@@ -28,7 +28,7 @@ TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) {
2828
}
2929

3030
TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) {
31-
auto context = CreateMockVulkanContext();
31+
auto context = MockVulkanContextBuilder().Build();
3232
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
3333
BlitCopyTextureToBufferCommandVK cmd;
3434
cmd.source = context->GetResourceAllocator()->CreateTexture({
@@ -44,7 +44,7 @@ TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) {
4444
}
4545

4646
TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) {
47-
auto context = CreateMockVulkanContext();
47+
auto context = MockVulkanContextBuilder().Build();
4848
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
4949
BlitCopyBufferToTextureCommandVK cmd;
5050
cmd.destination = context->GetResourceAllocator()->CreateTexture({
@@ -62,7 +62,7 @@ TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) {
6262
}
6363

6464
TEST(BlitCommandVkTest, BlitGenerateMipmapCommandVK) {
65-
auto context = CreateMockVulkanContext();
65+
auto context = MockVulkanContextBuilder().Build();
6666
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
6767
BlitGenerateMipmapCommandVK cmd;
6868
cmd.texture = context->GetResourceAllocator()->CreateTexture({

impeller/renderer/backend/vulkan/command_encoder_vk_unittests.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ TEST(CommandEncoderVKTest, DeleteEncoderAfterThreadDies) {
1818
// command buffers before it cleans up its command pool.
1919
std::shared_ptr<std::vector<std::string>> called_functions;
2020
{
21-
auto context = CreateMockVulkanContext();
21+
auto context = MockVulkanContextBuilder().Build();
2222
called_functions = GetMockVulkanFunctions(context->GetDevice());
2323
std::shared_ptr<CommandEncoderVK> encoder;
2424
std::thread thread([&] {
@@ -46,7 +46,7 @@ TEST(CommandEncoderVKTest, CleanupAfterSubmit) {
4646
{
4747
fml::AutoResetWaitableEvent wait_for_submit;
4848
fml::AutoResetWaitableEvent wait_for_thread_join;
49-
auto context = CreateMockVulkanContext();
49+
auto context = MockVulkanContextBuilder().Build();
5050
std::thread thread([&] {
5151
CommandEncoderFactoryVK factory(context);
5252
std::shared_ptr<CommandEncoderVK> encoder = factory.Create();

impeller/renderer/backend/vulkan/context_vk_unittests.cc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ TEST(ContextVKTest, DeletesCommandPools) {
1414
std::weak_ptr<ContextVK> weak_context;
1515
std::weak_ptr<CommandPoolVK> weak_pool;
1616
{
17-
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
17+
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
1818
std::shared_ptr<CommandPoolVK> pool =
1919
CommandPoolVK::GetThreadLocal(context.get());
2020
weak_pool = pool;
@@ -30,7 +30,7 @@ TEST(ContextVKTest, DeletePipelineAfterContext) {
3030
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
3131
std::shared_ptr<std::vector<std::string>> functions;
3232
{
33-
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
33+
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
3434
PipelineDescriptor pipeline_desc;
3535
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
3636
PipelineFuture<PipelineDescriptor> pipeline_future =
@@ -49,7 +49,7 @@ TEST(ContextVKTest, DeleteShaderFunctionAfterContext) {
4949
std::shared_ptr<const ShaderFunction> shader_function;
5050
std::shared_ptr<std::vector<std::string>> functions;
5151
{
52-
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
52+
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
5353
PipelineDescriptor pipeline_desc;
5454
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
5555
std::vector<uint8_t> data = {0x03, 0x02, 0x23, 0x07};
@@ -71,7 +71,7 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
7171
std::shared_ptr<PipelineLibrary> pipeline_library;
7272
std::shared_ptr<std::vector<std::string>> functions;
7373
{
74-
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
74+
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
7575
PipelineDescriptor pipeline_desc;
7676
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
7777
pipeline_library = context->GetPipelineLibrary();
@@ -86,9 +86,30 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
8686
TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) {
8787
// The mocked methods don't report the presence of a validation layer but we
8888
// explicitly ask for validation. Context creation should continue anyway.
89-
auto context = CreateMockVulkanContext(
90-
[](auto& settings) { settings.enable_validation = true; });
89+
auto context = MockVulkanContextBuilder()
90+
.SetSettingsCallback([](auto& settings) {
91+
settings.enable_validation = true;
92+
})
93+
.Build();
9194
ASSERT_NE(context, nullptr);
95+
const CapabilitiesVK* capabilites_vk =
96+
reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
97+
ASSERT_FALSE(capabilites_vk->AreValidationsEnabled());
98+
}
99+
100+
TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
101+
auto context =
102+
MockVulkanContextBuilder()
103+
.SetSettingsCallback(
104+
[](auto& settings) { settings.enable_validation = true; })
105+
.SetInstanceExtensions(
106+
{"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"})
107+
.SetInstanceLayers({"VK_LAYER_KHRONOS_validation"})
108+
.Build();
109+
ASSERT_NE(context, nullptr);
110+
const CapabilitiesVK* capabilites_vk =
111+
reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
112+
ASSERT_TRUE(capabilites_vk->AreValidationsEnabled());
92113
}
93114

94115
} // namespace testing

impeller/renderer/backend/vulkan/pass_bindings_cache_unittests.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ int32_t CountStringViewInstances(const std::vector<std::string>& strings,
2424
} // namespace
2525

2626
TEST(PassBindingsCacheTest, bindPipeline) {
27-
auto context = CreateMockVulkanContext();
27+
auto context = MockVulkanContextBuilder().Build();
2828
PassBindingsCache cache;
2929
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
3030
auto buffer = encoder->GetCommandBuffer();
@@ -38,7 +38,7 @@ TEST(PassBindingsCacheTest, bindPipeline) {
3838
}
3939

4040
TEST(PassBindingsCacheTest, setStencilReference) {
41-
auto context = CreateMockVulkanContext();
41+
auto context = MockVulkanContextBuilder().Build();
4242
PassBindingsCache cache;
4343
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
4444
auto buffer = encoder->GetCommandBuffer();
@@ -53,7 +53,7 @@ TEST(PassBindingsCacheTest, setStencilReference) {
5353
}
5454

5555
TEST(PassBindingsCacheTest, setScissor) {
56-
auto context = CreateMockVulkanContext();
56+
auto context = MockVulkanContextBuilder().Build();
5757
PassBindingsCache cache;
5858
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
5959
auto buffer = encoder->GetCommandBuffer();
@@ -66,7 +66,7 @@ TEST(PassBindingsCacheTest, setScissor) {
6666
}
6767

6868
TEST(PassBindingsCacheTest, setViewport) {
69-
auto context = CreateMockVulkanContext();
69+
auto context = MockVulkanContextBuilder().Build();
7070
PassBindingsCache cache;
7171
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
7272
auto buffer = encoder->GetCommandBuffer();

impeller/renderer/backend/vulkan/test/mock_vulkan.cc

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
// found in the LICENSE file.
44

55
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
6+
#include <cstring>
67
#include <vector>
78
#include "fml/macros.h"
9+
#include "fml/thread_local.h"
810
#include "impeller/base/thread_safety.h"
911

1012
namespace impeller {
@@ -54,25 +56,41 @@ class MockDevice final {
5456

5557
void noop() {}
5658

59+
FML_THREAD_LOCAL std::vector<std::string> g_instance_extensions;
60+
5761
VkResult vkEnumerateInstanceExtensionProperties(
5862
const char* pLayerName,
5963
uint32_t* pPropertyCount,
6064
VkExtensionProperties* pProperties) {
6165
if (!pProperties) {
62-
*pPropertyCount = 2;
63-
66+
*pPropertyCount = g_instance_extensions.size();
6467
} else {
65-
strcpy(pProperties[0].extensionName, "VK_KHR_surface");
66-
pProperties[0].specVersion = 0;
67-
strcpy(pProperties[1].extensionName, "VK_MVK_macos_surface");
68-
pProperties[1].specVersion = 0;
68+
uint32_t count = 0;
69+
for (const std::string& ext : g_instance_extensions) {
70+
strncpy(pProperties[count].extensionName, ext.c_str(),
71+
sizeof(VkExtensionProperties::extensionName));
72+
pProperties[count].specVersion = 0;
73+
count++;
74+
}
6975
}
7076
return VK_SUCCESS;
7177
}
7278

79+
FML_THREAD_LOCAL std::vector<std::string> g_instance_layers;
80+
7381
VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
7482
VkLayerProperties* pProperties) {
75-
*pPropertyCount = 0;
83+
if (!pProperties) {
84+
*pPropertyCount = g_instance_layers.size();
85+
} else {
86+
uint32_t count = 0;
87+
for (const std::string& layer : g_instance_layers) {
88+
strncpy(pProperties[count].layerName, layer.c_str(),
89+
sizeof(VkLayerProperties::layerName));
90+
pProperties[count].specVersion = 0;
91+
count++;
92+
}
93+
}
7694
return VK_SUCCESS;
7795
}
7896

@@ -415,6 +433,20 @@ VkResult vkGetFenceStatus(VkDevice device, VkFence fence) {
415433
return VK_SUCCESS;
416434
}
417435

436+
VkResult vkCreateDebugUtilsMessengerEXT(
437+
VkInstance instance,
438+
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
439+
const VkAllocationCallbacks* pAllocator,
440+
VkDebugUtilsMessengerEXT* pMessenger) {
441+
return VK_SUCCESS;
442+
}
443+
444+
VkResult vkSetDebugUtilsObjectNameEXT(
445+
VkDevice device,
446+
const VkDebugUtilsObjectNameInfoEXT* pNameInfo) {
447+
return VK_SUCCESS;
448+
}
449+
418450
PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
419451
const char* pName) {
420452
if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) {
@@ -507,21 +539,30 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
507539
return (PFN_vkVoidFunction)vkWaitForFences;
508540
} else if (strcmp("vkGetFenceStatus", pName) == 0) {
509541
return (PFN_vkVoidFunction)vkGetFenceStatus;
542+
} else if (strcmp("vkCreateDebugUtilsMessengerEXT", pName) == 0) {
543+
return (PFN_vkVoidFunction)vkCreateDebugUtilsMessengerEXT;
544+
} else if (strcmp("vkSetDebugUtilsObjectNameEXT", pName) == 0) {
545+
return (PFN_vkVoidFunction)vkSetDebugUtilsObjectNameEXT;
510546
}
511547
return noop;
512548
}
513549

514550
} // namespace
515551

516-
std::shared_ptr<ContextVK> CreateMockVulkanContext(
517-
const std::function<void(ContextVK::Settings&)>& settings_callback) {
552+
MockVulkanContextBuilder::MockVulkanContextBuilder()
553+
: instance_extensions_({"VK_KHR_surface", "VK_MVK_macos_surface"}) {}
554+
555+
std::shared_ptr<ContextVK> MockVulkanContextBuilder::Build() {
518556
auto message_loop = fml::ConcurrentMessageLoop::Create();
519557
ContextVK::Settings settings;
520558
settings.proc_address_callback = GetMockVulkanProcAddress;
521-
if (settings_callback) {
522-
settings_callback(settings);
559+
if (settings_callback_) {
560+
settings_callback_(settings);
523561
}
524-
return ContextVK::Create(std::move(settings));
562+
g_instance_extensions = instance_extensions_;
563+
g_instance_layers = instance_layers_;
564+
std::shared_ptr<ContextVK> result = ContextVK::Create(std::move(settings));
565+
return result;
525566
}
526567

527568
std::shared_ptr<std::vector<std::string>> GetMockVulkanFunctions(

impeller/renderer/backend/vulkan/test/mock_vulkan.h

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,44 @@ namespace testing {
1616
std::shared_ptr<std::vector<std::string>> GetMockVulkanFunctions(
1717
VkDevice device);
1818

19-
//------------------------------------------------------------------------------
20-
/// @brief Create a Vulkan context with Vulkan functions mocked. The caller
21-
/// is given a chance to tinker on the settings right before a
22-
/// context is created.
23-
///
24-
/// @param[in] settings_callback The settings callback
25-
///
26-
/// @return A context if one can be created.
27-
///
28-
std::shared_ptr<ContextVK> CreateMockVulkanContext(
29-
const std::function<void(ContextVK::Settings&)>& settings_callback =
30-
nullptr);
19+
class MockVulkanContextBuilder {
20+
public:
21+
MockVulkanContextBuilder();
22+
23+
//------------------------------------------------------------------------------
24+
/// @brief Create a Vulkan context with Vulkan functions mocked. The
25+
/// caller is given a chance to tinker on the settings right
26+
/// before a context is created.
27+
///
28+
/// @return A context if one can be created.
29+
///
30+
std::shared_ptr<ContextVK> Build();
31+
32+
/// A callback that allows the modification of the ContextVK::Settings before
33+
/// the context is made.
34+
MockVulkanContextBuilder& SetSettingsCallback(
35+
const std::function<void(ContextVK::Settings&)>& settings_callback) {
36+
settings_callback_ = settings_callback;
37+
return *this;
38+
}
39+
40+
MockVulkanContextBuilder& SetInstanceExtensions(
41+
const std::vector<std::string>& instance_extensions) {
42+
instance_extensions_ = instance_extensions;
43+
return *this;
44+
}
45+
46+
MockVulkanContextBuilder& SetInstanceLayers(
47+
const std::vector<std::string>& instance_layers) {
48+
instance_layers_ = instance_layers;
49+
return *this;
50+
}
51+
52+
private:
53+
std::function<void(ContextVK::Settings&)> settings_callback_;
54+
std::vector<std::string> instance_extensions_;
55+
std::vector<std::string> instance_layers_;
56+
};
3157

3258
} // namespace testing
3359
} // namespace impeller

impeller/renderer/backend/vulkan/test/mock_vulkan_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ TEST(MockVulkanContextTest, IsThreadSafe) {
1414
// In a typical app, there is a single ContextVK per app, shared b/w threads.
1515
//
1616
// This test ensures that the (mock) ContextVK is thread-safe.
17-
auto const context = CreateMockVulkanContext();
17+
auto const context = MockVulkanContextBuilder().Build();
1818

1919
// Spawn two threads, and have them create a CommandPoolVK each.
2020
std::thread thread1([&context]() {

0 commit comments

Comments
 (0)