Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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
4 changes: 4 additions & 0 deletions shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ if (enable_unittests) {
if (test_enable_metal) {
sources += [ "tests/embedder_metal_unittests.mm" ]
}

if (test_enable_vulkan) {
sources += [ "tests/embedder_vk_unittests.cc" ]
}
}

executable("embedder_a11y_unittests") {
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,11 @@ typedef struct {
/// The queue family index of the VkQueue supplied in the next field.
uint32_t queue_family_index;
/// VkQueue handle.
/// The queue should not be used without protection from a mutex to make sure
/// it is not used simultaneously with other threads. That mutex should match
/// the one injected via the |get_instance_proc_address_callback|.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we link to flutter/flutter#134573 (bug about fixing this behavior)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

/// There is a proposal to remove the need for the mutex at
/// https://github.com/flutter/flutter/issues/134573.
FlutterVulkanQueueHandle queue;
/// The number of instance extensions available for enumerating in the next
/// field.
Expand All @@ -780,6 +785,12 @@ typedef struct {
/// For example: VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
const char** enabled_device_extensions;
/// The callback invoked when resolving Vulkan function pointers.
/// At a bare minimum this should be used to swap out any calls that operate
/// on vkQueue's for threadsafe variants that obtain locks for their duration.
/// The functions to swap out are "vkQueueSubmit" and "vkQueueWaitIdle". An
/// example of how to do that can be found in the test
/// "EmbedderTest.CanSwapOutVulkanCalls" unit-test in
/// //shell/platform/embedder/tests/embedder_vk_unittests.cc.
FlutterVulkanInstanceProcAddressCallback get_instance_proc_address_callback;
/// The callback invoked when the engine requests a VkImage from the embedder
/// for rendering the next frame.
Expand Down
20 changes: 11 additions & 9 deletions shell/platform/embedder/tests/embedder_config_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,18 @@ void EmbedderConfigBuilder::SetMetalRendererConfig(SkISize surface_size) {
#endif
}

void EmbedderConfigBuilder::SetVulkanRendererConfig(SkISize surface_size) {
void EmbedderConfigBuilder::SetVulkanRendererConfig(
SkISize surface_size,
std::optional<FlutterVulkanInstanceProcAddressCallback>
instance_proc_address_callback) {
#ifdef SHELL_ENABLE_VULKAN
renderer_config_.type = FlutterRendererType::kVulkan;
renderer_config_.vulkan = vulkan_renderer_config_;
FlutterVulkanRendererConfig vulkan_renderer_config = vulkan_renderer_config_;
if (instance_proc_address_callback.has_value()) {
vulkan_renderer_config.get_instance_proc_address_callback =
instance_proc_address_callback.value();
}
renderer_config_.vulkan = vulkan_renderer_config;
context_.SetupSurface(surface_size);
#endif
}
Expand Down Expand Up @@ -519,13 +527,7 @@ void EmbedderConfigBuilder::InitializeVulkanRendererConfig() {
static_cast<EmbedderTestContextVulkan&>(context_)
.vulkan_context_->device_->GetQueueHandle();
vulkan_renderer_config_.get_instance_proc_address_callback =
[](void* context, FlutterVulkanInstanceHandle instance,
const char* name) -> void* {
auto proc_addr = reinterpret_cast<EmbedderTestContextVulkan*>(context)
->vulkan_context_->vk_->GetInstanceProcAddr(
reinterpret_cast<VkInstance>(instance), name);
return reinterpret_cast<void*>(proc_addr);
};
EmbedderTestContextVulkan::InstanceProcAddr;
vulkan_renderer_config_.get_next_image_callback =
[](void* context,
const FlutterFrameInfo* frame_info) -> FlutterVulkanImage {
Expand Down
5 changes: 4 additions & 1 deletion shell/platform/embedder/tests/embedder_config_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ class EmbedderConfigBuilder {

void SetMetalRendererConfig(SkISize surface_size);

void SetVulkanRendererConfig(SkISize surface_size);
void SetVulkanRendererConfig(
SkISize surface_size,
std::optional<FlutterVulkanInstanceProcAddressCallback>
instance_proc_address_callback = {});

// Used to explicitly set an `open_gl.fbo_callback`. Using this method will
// cause your test to fail since the ctor for this class sets
Expand Down
10 changes: 10 additions & 0 deletions shell/platform/embedder/tests/embedder_test_context_vulkan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,15 @@ void EmbedderTestContextVulkan::SetupCompositor() {
surface_size_, vulkan_context_->GetGrDirectContext());
}

void* EmbedderTestContextVulkan::InstanceProcAddr(
void* user_data,
FlutterVulkanInstanceHandle instance,
const char* name) {
auto proc_addr = reinterpret_cast<EmbedderTestContextVulkan*>(user_data)
->vulkan_context_->vk_->GetInstanceProcAddr(
reinterpret_cast<VkInstance>(instance), name);
return reinterpret_cast<void*>(proc_addr);
}

} // namespace testing
} // namespace flutter
4 changes: 4 additions & 0 deletions shell/platform/embedder/tests/embedder_test_context_vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class EmbedderTestContextVulkan : public EmbedderTestContext {

bool PresentImage(VkImage image);

static void* InstanceProcAddr(void* user_data,
FlutterVulkanInstanceHandle instance,
const char* name);

private:
std::unique_ptr<TestVulkanSurface> surface_;

Expand Down
128 changes: 128 additions & 0 deletions shell/platform/embedder/tests/embedder_vk_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define FML_USED_ON_EMBEDDER

#include <cstring>
#include <string>
#include <utility>
#include <vector>

#include "embedder.h"
#include "embedder_engine.h"
#include "flutter/fml/synchronization/count_down_latch.h"
#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
#include "flutter/shell/platform/embedder/tests/embedder_test.h"
#include "flutter/shell/platform/embedder/tests/embedder_test_context_vulkan.h"
#include "flutter/shell/platform/embedder/tests/embedder_unittests_util.h"
#include "flutter/testing/testing.h"

// CREATE_NATIVE_ENTRY is leaky by design
// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)

namespace flutter {
namespace testing {

using EmbedderTest = testing::EmbedderTest;

////////////////////////////////////////////////////////////////////////////////
// Notice: Other Vulkan unit tests exist in embedder_gl_unittests.cc.
// See https://github.com/flutter/flutter/issues/134322
////////////////////////////////////////////////////////////////////////////////

namespace {

struct VulkanProcInfo {
decltype(vkGetInstanceProcAddr)* get_instance_proc_addr = nullptr;
decltype(vkGetDeviceProcAddr)* get_device_proc_addr = nullptr;
decltype(vkQueueSubmit)* queue_submit_proc_addr = nullptr;
bool did_call_queue_submit = false;
};

static_assert(std::is_trivially_destructible_v<VulkanProcInfo>);

VulkanProcInfo g_vulkan_proc_info;

VkResult QueueSubmit(VkQueue queue,
uint32_t submitCount,
const VkSubmitInfo* pSubmits,
VkFence fence) {
FML_DCHECK(g_vulkan_proc_info.queue_submit_proc_addr != nullptr);
g_vulkan_proc_info.did_call_queue_submit = true;
return g_vulkan_proc_info.queue_submit_proc_addr(queue, submitCount, pSubmits,
fence);
}

template <size_t N>
int StrcmpFixed(const char* str1, const char (&str2)[N]) {
return strncmp(str1, str2, N - 1);
}

PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName) {
FML_DCHECK(g_vulkan_proc_info.get_device_proc_addr != nullptr);
if (StrcmpFixed(pName, "vkQueueSubmit") == 0) {
g_vulkan_proc_info.queue_submit_proc_addr =
reinterpret_cast<decltype(vkQueueSubmit)*>(
g_vulkan_proc_info.get_device_proc_addr(device, pName));
return reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit);
}
return g_vulkan_proc_info.get_device_proc_addr(device, pName);
}

PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) {
FML_DCHECK(g_vulkan_proc_info.get_instance_proc_addr != nullptr);
if (StrcmpFixed(pName, "vkGetDeviceProcAddr") == 0) {
g_vulkan_proc_info.get_device_proc_addr =
reinterpret_cast<decltype(vkGetDeviceProcAddr)*>(
g_vulkan_proc_info.get_instance_proc_addr(instance, pName));
return reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr);
}
return g_vulkan_proc_info.get_instance_proc_addr(instance, pName);
}

template <typename T, typename U>
struct CheckSameSignature : std::false_type {};

template <typename Ret, typename... Args>
struct CheckSameSignature<Ret(Args...), Ret(Args...)> : std::true_type {};

static_assert(CheckSameSignature<decltype(GetInstanceProcAddr),
decltype(vkGetInstanceProcAddr)>::value);
static_assert(CheckSameSignature<decltype(GetDeviceProcAddr),
decltype(vkGetDeviceProcAddr)>::value);
static_assert(
CheckSameSignature<decltype(QueueSubmit), decltype(vkQueueSubmit)>::value);
} // namespace

TEST_F(EmbedderTest, CanSwapOutVulkanCalls) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kVulkanContext);
fml::AutoResetWaitableEvent latch;
context.AddIsolateCreateCallback([&latch]() { latch.Signal(); });
EmbedderConfigBuilder builder(context);
builder.SetVulkanRendererConfig(
SkISize::Make(1024, 1024),
[](void* user_data, FlutterVulkanInstanceHandle instance,
const char* name) -> void* {
if (StrcmpFixed(name, "vkGetInstanceProcAddr") == 0) {
g_vulkan_proc_info.get_instance_proc_addr =
reinterpret_cast<decltype(vkGetInstanceProcAddr)*>(
EmbedderTestContextVulkan::InstanceProcAddr(user_data,
instance, name));
return reinterpret_cast<void*>(GetInstanceProcAddr);
}
return EmbedderTestContextVulkan::InstanceProcAddr(user_data, instance,
name);
});
auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());
// Wait for the root isolate to launch.
latch.Wait();
engine.reset();
EXPECT_TRUE(g_vulkan_proc_info.did_call_queue_submit);
}

} // namespace testing
} // namespace flutter

// NOLINTEND(clang-analyzer-core.StackAddressEscape)