Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[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 all 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
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