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

[Impeller] Introduce mock vulkan context builder #45834

Merged
merged 7 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace impeller {
namespace testing {

TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
auto pool = CommandPoolVK::GetThreadLocal(context.get());
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
BlitCopyTextureToTextureCommandVK cmd;
Expand All @@ -28,7 +28,7 @@ TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) {
}

TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
BlitCopyTextureToBufferCommandVK cmd;
cmd.source = context->GetResourceAllocator()->CreateTexture({
Expand All @@ -44,7 +44,7 @@ TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) {
}

TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
BlitCopyBufferToTextureCommandVK cmd;
cmd.destination = context->GetResourceAllocator()->CreateTexture({
Expand All @@ -62,7 +62,7 @@ TEST(BlitCommandVkTest, BlitCopyBufferToTextureCommandVK) {
}

TEST(BlitCommandVkTest, BlitGenerateMipmapCommandVK) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
BlitGenerateMipmapCommandVK cmd;
cmd.texture = context->GetResourceAllocator()->CreateTexture({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TEST(CommandEncoderVKTest, DeleteEncoderAfterThreadDies) {
// command buffers before it cleans up its command pool.
std::shared_ptr<std::vector<std::string>> called_functions;
{
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
called_functions = GetMockVulkanFunctions(context->GetDevice());
std::shared_ptr<CommandEncoderVK> encoder;
std::thread thread([&] {
Expand Down Expand Up @@ -46,7 +46,7 @@ TEST(CommandEncoderVKTest, CleanupAfterSubmit) {
{
fml::AutoResetWaitableEvent wait_for_submit;
fml::AutoResetWaitableEvent wait_for_thread_join;
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
std::thread thread([&] {
CommandEncoderFactoryVK factory(context);
std::shared_ptr<CommandEncoderVK> encoder = factory.Create();
Expand Down
33 changes: 27 additions & 6 deletions impeller/renderer/backend/vulkan/context_vk_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ TEST(ContextVKTest, DeletesCommandPools) {
std::weak_ptr<ContextVK> weak_context;
std::weak_ptr<CommandPoolVK> weak_pool;
{
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
std::shared_ptr<CommandPoolVK> pool =
CommandPoolVK::GetThreadLocal(context.get());
weak_pool = pool;
Expand All @@ -30,7 +30,7 @@ TEST(ContextVKTest, DeletePipelineAfterContext) {
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline;
std::shared_ptr<std::vector<std::string>> functions;
{
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
PipelineDescriptor pipeline_desc;
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
PipelineFuture<PipelineDescriptor> pipeline_future =
Expand All @@ -49,7 +49,7 @@ TEST(ContextVKTest, DeleteShaderFunctionAfterContext) {
std::shared_ptr<const ShaderFunction> shader_function;
std::shared_ptr<std::vector<std::string>> functions;
{
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
PipelineDescriptor pipeline_desc;
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
std::vector<uint8_t> data = {0x03, 0x02, 0x23, 0x07};
Expand All @@ -71,7 +71,7 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
std::shared_ptr<PipelineLibrary> pipeline_library;
std::shared_ptr<std::vector<std::string>> functions;
{
std::shared_ptr<ContextVK> context = CreateMockVulkanContext();
std::shared_ptr<ContextVK> context = MockVulkanContextBuilder().Build();
PipelineDescriptor pipeline_desc;
pipeline_desc.SetVertexDescriptor(std::make_shared<VertexDescriptor>());
pipeline_library = context->GetPipelineLibrary();
Expand All @@ -86,9 +86,30 @@ TEST(ContextVKTest, DeletePipelineLibraryAfterContext) {
TEST(ContextVKTest, CanCreateContextInAbsenceOfValidationLayers) {
// The mocked methods don't report the presence of a validation layer but we
// explicitly ask for validation. Context creation should continue anyway.
auto context = CreateMockVulkanContext(
[](auto& settings) { settings.enable_validation = true; });
auto context = MockVulkanContextBuilder()
.SetSettingsCallback([](auto& settings) {
settings.enable_validation = true;
})
.Build();
ASSERT_NE(context, nullptr);
const CapabilitiesVK* capabilites_vk =
reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
ASSERT_FALSE(capabilites_vk->AreValidationsEnabled());
}

TEST(ContextVKTest, CanCreateContextWithValidationLayers) {
auto context =
MockVulkanContextBuilder()
.SetSettingsCallback(
[](auto& settings) { settings.enable_validation = true; })
.SetInstanceExtensions(
{"VK_KHR_surface", "VK_MVK_macos_surface", "VK_EXT_debug_utils"})
.SetInstanceLayers({"VK_LAYER_KHRONOS_validation"})
.Build();
ASSERT_NE(context, nullptr);
const CapabilitiesVK* capabilites_vk =
reinterpret_cast<const CapabilitiesVK*>(context->GetCapabilities().get());
ASSERT_TRUE(capabilites_vk->AreValidationsEnabled());
}

} // namespace testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int32_t CountStringViewInstances(const std::vector<std::string>& strings,
} // namespace

TEST(PassBindingsCacheTest, bindPipeline) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
PassBindingsCache cache;
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
auto buffer = encoder->GetCommandBuffer();
Expand All @@ -38,7 +38,7 @@ TEST(PassBindingsCacheTest, bindPipeline) {
}

TEST(PassBindingsCacheTest, setStencilReference) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
PassBindingsCache cache;
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
auto buffer = encoder->GetCommandBuffer();
Expand All @@ -53,7 +53,7 @@ TEST(PassBindingsCacheTest, setStencilReference) {
}

TEST(PassBindingsCacheTest, setScissor) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
PassBindingsCache cache;
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
auto buffer = encoder->GetCommandBuffer();
Expand All @@ -66,7 +66,7 @@ TEST(PassBindingsCacheTest, setScissor) {
}

TEST(PassBindingsCacheTest, setViewport) {
auto context = CreateMockVulkanContext();
auto context = MockVulkanContextBuilder().Build();
PassBindingsCache cache;
auto encoder = std::make_unique<CommandEncoderFactoryVK>(context)->Create();
auto buffer = encoder->GetCommandBuffer();
Expand Down
61 changes: 49 additions & 12 deletions impeller/renderer/backend/vulkan/test/mock_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,39 @@ class MockDevice final {

void noop() {}

thread_local std::vector<std::string> g_instance_extensions;

VkResult vkEnumerateInstanceExtensionProperties(
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties) {
if (!pProperties) {
*pPropertyCount = 2;

*pPropertyCount = g_instance_extensions.size();
} else {
strcpy(pProperties[0].extensionName, "VK_KHR_surface");
pProperties[0].specVersion = 0;
strcpy(pProperties[1].extensionName, "VK_MVK_macos_surface");
pProperties[1].specVersion = 0;
uint32_t count = 0;
for (const std::string& ext : g_instance_extensions) {
strcpy(pProperties[count].extensionName, ext.c_str());
pProperties[count].specVersion = 0;
count++;
}
}
return VK_SUCCESS;
}

thread_local std::vector<std::string> g_instance_layers;

VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
VkLayerProperties* pProperties) {
*pPropertyCount = 0;
if (!pProperties) {
*pPropertyCount = g_instance_layers.size();
} else {
uint32_t count = 0;
for (const std::string& layer : g_instance_layers) {
strcpy(pProperties[count].layerName, layer.c_str());
pProperties[count].specVersion = 0;
count++;
}
}
return VK_SUCCESS;
}

Expand Down Expand Up @@ -415,6 +429,20 @@ VkResult vkGetFenceStatus(VkDevice device, VkFence fence) {
return VK_SUCCESS;
}

VkResult vkCreateDebugUtilsMessengerEXT(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pMessenger) {
return VK_SUCCESS;
}

VkResult vkSetDebugUtilsObjectNameEXT(
VkDevice device,
const VkDebugUtilsObjectNameInfoEXT* pNameInfo) {
return VK_SUCCESS;
}

PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
const char* pName) {
if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) {
Expand Down Expand Up @@ -507,21 +535,30 @@ PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance,
return (PFN_vkVoidFunction)vkWaitForFences;
} else if (strcmp("vkGetFenceStatus", pName) == 0) {
return (PFN_vkVoidFunction)vkGetFenceStatus;
} else if (strcmp("vkCreateDebugUtilsMessengerEXT", pName) == 0) {
return (PFN_vkVoidFunction)vkCreateDebugUtilsMessengerEXT;
} else if (strcmp("vkSetDebugUtilsObjectNameEXT", pName) == 0) {
return (PFN_vkVoidFunction)vkSetDebugUtilsObjectNameEXT;
}
return noop;
}

} // namespace

std::shared_ptr<ContextVK> CreateMockVulkanContext(
const std::function<void(ContextVK::Settings&)>& settings_callback) {
MockVulkanContextBuilder::MockVulkanContextBuilder()
: instance_extensions_({"VK_KHR_surface", "VK_MVK_macos_surface"}) {}

std::shared_ptr<ContextVK> MockVulkanContextBuilder::Build() {
auto message_loop = fml::ConcurrentMessageLoop::Create();
ContextVK::Settings settings;
settings.proc_address_callback = GetMockVulkanProcAddress;
if (settings_callback) {
settings_callback(settings);
if (settings_callback_) {
settings_callback_(settings);
}
return ContextVK::Create(std::move(settings));
g_instance_extensions = instance_extensions_;
g_instance_layers = instance_layers_;
std::shared_ptr<ContextVK> result = ContextVK::Create(std::move(settings));
return result;
}

std::shared_ptr<std::vector<std::string>> GetMockVulkanFunctions(
Expand Down
50 changes: 38 additions & 12 deletions impeller/renderer/backend/vulkan/test/mock_vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,44 @@ namespace testing {
std::shared_ptr<std::vector<std::string>> GetMockVulkanFunctions(
VkDevice device);

//------------------------------------------------------------------------------
/// @brief Create a Vulkan context with Vulkan functions mocked. The caller
/// is given a chance to tinker on the settings right before a
/// context is created.
///
/// @param[in] settings_callback The settings callback
///
/// @return A context if one can be created.
///
std::shared_ptr<ContextVK> CreateMockVulkanContext(
const std::function<void(ContextVK::Settings&)>& settings_callback =
nullptr);
class MockVulkanContextBuilder {
public:
MockVulkanContextBuilder();

//------------------------------------------------------------------------------
/// @brief Create a Vulkan context with Vulkan functions mocked. The
/// caller is given a chance to tinker on the settings right
/// before a context is created.
///
/// @return A context if one can be created.
///
std::shared_ptr<ContextVK> Build();

/// A callback that allows the modification of the ContextVK::Settings before
/// the context is made.
MockVulkanContextBuilder& SetSettingsCallback(
const std::function<void(ContextVK::Settings&)>& settings_callback) {
settings_callback_ = settings_callback;
return *this;
}

MockVulkanContextBuilder& SetInstanceExtensions(
const std::vector<std::string>& instance_extensions) {
instance_extensions_ = instance_extensions;
return *this;
}

MockVulkanContextBuilder& SetInstanceLayers(
const std::vector<std::string>& instance_layers) {
instance_layers_ = instance_layers;
return *this;
}

private:
std::function<void(ContextVK::Settings&)> settings_callback_;
std::vector<std::string> instance_extensions_;
std::vector<std::string> instance_layers_;
};

} // namespace testing
} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ TEST(MockVulkanContextTest, IsThreadSafe) {
// In a typical app, there is a single ContextVK per app, shared b/w threads.
//
// This test ensures that the (mock) ContextVK is thread-safe.
auto const context = CreateMockVulkanContext();
auto const context = MockVulkanContextBuilder().Build();

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