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

Commit

Permalink
[Impeller] Introduce mock vulkan context builder (#45834)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gaaclarke authored Sep 15, 2023
1 parent 09034a7 commit bd765e9
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 41 deletions.
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
65 changes: 53 additions & 12 deletions impeller/renderer/backend/vulkan/test/mock_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// found in the LICENSE file.

#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
#include <cstring>
#include <vector>
#include "fml/macros.h"
#include "fml/thread_local.h"
#include "impeller/base/thread_safety.h"

namespace impeller {
Expand Down Expand Up @@ -54,25 +56,41 @@ class MockDevice final {

void noop() {}

FML_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) {
strncpy(pProperties[count].extensionName, ext.c_str(),
sizeof(VkExtensionProperties::extensionName));
pProperties[count].specVersion = 0;
count++;
}
}
return VK_SUCCESS;
}

FML_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) {
strncpy(pProperties[count].layerName, layer.c_str(),
sizeof(VkLayerProperties::layerName));
pProperties[count].specVersion = 0;
count++;
}
}
return VK_SUCCESS;
}

Expand Down Expand Up @@ -415,6 +433,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 +539,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

0 comments on commit bd765e9

Please sign in to comment.