Add Vulkan device sharing example (#3649)
Co-authored-by: Birk Skyum <74932975+birkskyum@users.noreply.github.com>
This commit is contained in:
@@ -84,16 +84,16 @@ protected:
|
||||
virtual std::vector<const char*> getDeviceExtensions();
|
||||
std::vector<const char*> getDebugExtensions();
|
||||
|
||||
void initInstance();
|
||||
void initDebug();
|
||||
void initSurface();
|
||||
void initDevice();
|
||||
void initAllocator();
|
||||
void initSwapchain();
|
||||
void initCommandPool();
|
||||
void initFrameCapture();
|
||||
virtual void initInstance();
|
||||
virtual void initDebug();
|
||||
virtual void initSurface();
|
||||
virtual void initDevice();
|
||||
virtual void initAllocator();
|
||||
virtual void initSwapchain();
|
||||
virtual void initCommandPool();
|
||||
virtual void initFrameCapture();
|
||||
|
||||
void destroyResources();
|
||||
virtual void destroyResources();
|
||||
|
||||
protected:
|
||||
vk::DynamicLoader dynamicLoader;
|
||||
@@ -120,6 +120,7 @@ protected:
|
||||
VmaAllocator allocator;
|
||||
|
||||
bool debugUtilsEnabled{false};
|
||||
bool usingSharedContext{false};
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
||||
@@ -11,6 +11,113 @@
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#ifdef USE_SHARED_VK_CONTEXT
|
||||
|
||||
// An example of an external vkInstance/vkDevice provider.
|
||||
// It uses the first available device and queue
|
||||
class VkContext {
|
||||
public:
|
||||
VkContext() = default;
|
||||
~VkContext() {
|
||||
device.reset();
|
||||
instance.reset();
|
||||
}
|
||||
|
||||
static VkContext& shared() {
|
||||
static std::unique_ptr<VkContext> context;
|
||||
if (!context) {
|
||||
context = std::make_unique<VkContext>();
|
||||
}
|
||||
|
||||
return *context;
|
||||
}
|
||||
|
||||
vk::Instance getInstance() {
|
||||
if (instance) {
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
dispatcher.init(dynamicLoader);
|
||||
|
||||
#ifdef __APPLE__
|
||||
vk::InstanceCreateFlags instanceFlags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
||||
#else
|
||||
vk::InstanceCreateFlags instanceFlags = {};
|
||||
#endif
|
||||
|
||||
const std::vector<const char*> layers = {"VK_LAYER_KHRONOS_validation"};
|
||||
std::vector<const char*> extensions = {
|
||||
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||
#ifdef __APPLE__
|
||||
"VK_KHR_portability_enumeration",
|
||||
#endif
|
||||
};
|
||||
|
||||
uint32_t glfwExtensionCount = 0;
|
||||
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||
std::copy(glfwExtensions, glfwExtensions + glfwExtensionCount, std::back_inserter(extensions));
|
||||
|
||||
vk::ApplicationInfo appInfo("maplibre-native", 1, "maplibre-native", 1, VK_API_VERSION_1_0);
|
||||
const auto createInfo = vk::InstanceCreateInfo(instanceFlags)
|
||||
.setPApplicationInfo(&appInfo)
|
||||
.setPEnabledExtensionNames(extensions)
|
||||
.setPEnabledLayerNames(layers);
|
||||
|
||||
instance = vk::createInstanceUnique(createInfo, nullptr, dispatcher);
|
||||
dispatcher.init(instance.get());
|
||||
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
vk::PhysicalDevice getPhysicalDevice() {
|
||||
if (!physicalDevice) {
|
||||
physicalDevice = instance->enumeratePhysicalDevices(dispatcher)[0];
|
||||
}
|
||||
|
||||
return physicalDevice;
|
||||
}
|
||||
|
||||
vk::Device getDevice() {
|
||||
if (device) {
|
||||
return device.get();
|
||||
}
|
||||
|
||||
const std::vector<const char*> layers = {"VK_LAYER_KHRONOS_validation"};
|
||||
const std::vector<const char*> extensions = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
#ifdef __APPLE__
|
||||
"VK_KHR_portability_subset",
|
||||
#endif
|
||||
};
|
||||
|
||||
float queuePriority = 1.0f;
|
||||
vk::DeviceQueueCreateInfo queueCreateInfo(vk::DeviceQueueCreateFlags(), getQueueIndex(), 1, &queuePriority);
|
||||
|
||||
auto createInfo = vk::DeviceCreateInfo()
|
||||
.setQueueCreateInfos(queueCreateInfo)
|
||||
.setPEnabledExtensionNames(extensions)
|
||||
.setPEnabledLayerNames(layers);
|
||||
|
||||
device = physicalDevice.createDeviceUnique(createInfo, nullptr, dispatcher);
|
||||
dispatcher.init(device.get());
|
||||
|
||||
return device.get();
|
||||
}
|
||||
|
||||
uint32_t getQueueIndex() { return graphicsQueueIndex; }
|
||||
|
||||
private:
|
||||
vk::DynamicLoader dynamicLoader;
|
||||
vk::DispatchLoaderDynamic dispatcher;
|
||||
|
||||
vk::UniqueInstance instance;
|
||||
vk::PhysicalDevice physicalDevice;
|
||||
vk::UniqueDevice device;
|
||||
uint32_t graphicsQueueIndex = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class GLFWVulkanRenderableResource final : public mbgl::vulkan::SurfaceRenderableResource {
|
||||
public:
|
||||
explicit GLFWVulkanRenderableResource(GLFWVulkanBackend& backend_, bool capFrameRate)
|
||||
@@ -75,6 +182,39 @@ std::vector<const char*> GLFWVulkanBackend::getInstanceExtensions() {
|
||||
return extensions;
|
||||
}
|
||||
|
||||
#ifdef USE_SHARED_VK_CONTEXT
|
||||
|
||||
void GLFWVulkanBackend::initInstance() {
|
||||
// tell the backend to keep the objects alive
|
||||
usingSharedContext = true;
|
||||
|
||||
// this method is required to set `instance` to a valid vkInstance
|
||||
instance = vk::UniqueInstance(VkContext::shared().getInstance(),
|
||||
vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dispatcher));
|
||||
|
||||
// debug builds also require:
|
||||
// - enabling either `VK_EXT_debug_utils` (and updating `debugUtilsEnabled`) or `VK_EXT_debug_report` extension
|
||||
// - or passing the values returned by `getDebugExtensions()` as enabled extensions during instance creation
|
||||
debugUtilsEnabled = true;
|
||||
}
|
||||
|
||||
void GLFWVulkanBackend::initDevice() {
|
||||
// this method is required to populate:
|
||||
// `physicalDevice`
|
||||
// `device`
|
||||
// `graphicsQueueIndex`/`presentQueueIndex`
|
||||
// `physicalDeviceFeatures` - enabled features (optional)
|
||||
|
||||
physicalDevice = VkContext::shared().getPhysicalDevice();
|
||||
device = vk::UniqueDevice(VkContext::shared().getDevice(),
|
||||
vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dispatcher));
|
||||
|
||||
graphicsQueueIndex = presentQueueIndex = VkContext::shared().getQueueIndex();
|
||||
physicalDeviceFeatures = vk::PhysicalDeviceFeatures();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
namespace gfx {
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include <mbgl/vulkan/renderable_resource.hpp>
|
||||
#include <mbgl/vulkan/renderer_backend.hpp>
|
||||
|
||||
// Example of using an application side VkInstance/VkDevice
|
||||
// that's shared with MapLibre's renderer backend
|
||||
// #define USE_SHARED_VK_CONTEXT
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
class GLFWVulkanBackend final : public GLFWBackend,
|
||||
@@ -29,6 +33,11 @@ public:
|
||||
protected:
|
||||
std::vector<const char*> getInstanceExtensions() override;
|
||||
|
||||
#ifdef USE_SHARED_VK_CONTEXT
|
||||
void initInstance() override;
|
||||
void initDevice() override;
|
||||
#endif
|
||||
|
||||
void activate() override {}
|
||||
void deactivate() override {}
|
||||
|
||||
|
||||
@@ -360,23 +360,31 @@ void RendererBackend::initDebug() {
|
||||
}
|
||||
|
||||
void RendererBackend::init() {
|
||||
// initialize minimal set of function pointers
|
||||
dispatcher.init(dynamicLoader);
|
||||
|
||||
initFrameCapture();
|
||||
initInstance();
|
||||
|
||||
// initialize function pointers for instance
|
||||
dispatcher.init(instance.get());
|
||||
|
||||
initDebug();
|
||||
initSurface();
|
||||
initDevice();
|
||||
|
||||
// optional function pointer specialization for device
|
||||
dispatcher.init(device.get());
|
||||
physicalDeviceProperties = physicalDevice.getProperties(dispatcher);
|
||||
if (graphicsQueueIndex != -1) graphicsQueue = device->getQueue(graphicsQueueIndex, 0, dispatcher);
|
||||
if (presentQueueIndex != -1) presentQueue = device->getQueue(presentQueueIndex, 0, dispatcher);
|
||||
|
||||
initAllocator();
|
||||
initSwapchain();
|
||||
initCommandPool();
|
||||
}
|
||||
|
||||
void RendererBackend::initInstance() {
|
||||
// VULKAN_HPP_DEFAULT_DISPATCHER is global and can lead to race conditions in multi-map environments
|
||||
static std::mutex vkDefaultDispatchLoaderMutex;
|
||||
std::lock_guard<std::mutex> lock(vkDefaultDispatchLoaderMutex);
|
||||
|
||||
// initialize minimal set of function pointers
|
||||
dispatcher.init(dynamicLoader);
|
||||
|
||||
// Vulkan 1.1 on Android is supported on 71% of devices (compared to 1.3 with 6%) as of April 23 2024
|
||||
// https://vulkan.gpuinfo.org/
|
||||
#ifdef __APPLE__
|
||||
@@ -430,14 +438,6 @@ void RendererBackend::initInstance() {
|
||||
}
|
||||
|
||||
instance = vk::createInstanceUnique(createInfo, nullptr, dispatcher);
|
||||
|
||||
// initialize function pointers for instance
|
||||
dispatcher.init(instance.get());
|
||||
|
||||
#ifdef ENABLE_VULKAN_VALIDATION
|
||||
// enable validation layer callback
|
||||
initDebug();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RendererBackend::initSurface() {
|
||||
@@ -541,8 +541,6 @@ void RendererBackend::initDevice() {
|
||||
}
|
||||
|
||||
if (!physicalDevice) throw std::runtime_error("No suitable GPU found");
|
||||
|
||||
physicalDeviceProperties = physicalDevice.getProperties(dispatcher);
|
||||
};
|
||||
|
||||
pickPhysicalDevice();
|
||||
@@ -582,20 +580,13 @@ void RendererBackend::initDevice() {
|
||||
|
||||
auto createInfo = vk::DeviceCreateInfo()
|
||||
.setQueueCreateInfos(queueCreateInfos)
|
||||
.setEnabledExtensionCount(static_cast<uint32_t>(extensions.size()))
|
||||
.setPpEnabledExtensionNames(extensions.data())
|
||||
.setPEnabledExtensionNames(extensions)
|
||||
.setPEnabledFeatures(&physicalDeviceFeatures);
|
||||
|
||||
// this is not needed for newer implementations
|
||||
createInfo.setPEnabledLayerNames(layers);
|
||||
|
||||
device = physicalDevice.createDeviceUnique(createInfo, nullptr, dispatcher);
|
||||
|
||||
// optional function pointer specialization for device
|
||||
dispatcher.init(device.get());
|
||||
|
||||
graphicsQueue = device->getQueue(graphicsQueueIndex, 0, dispatcher);
|
||||
if (presentQueueIndex != -1) presentQueue = device->getQueue(presentQueueIndex, 0, dispatcher);
|
||||
}
|
||||
|
||||
void RendererBackend::initSwapchain() {
|
||||
@@ -630,12 +621,13 @@ void RendererBackend::destroyResources() {
|
||||
|
||||
vmaDestroyAllocator(allocator);
|
||||
|
||||
device.reset();
|
||||
usingSharedContext ? void(device.release()) : device.reset();
|
||||
|
||||
// destroy this last so we have cleanup validation
|
||||
debugUtilsCallback.reset();
|
||||
debugReportCallback.reset();
|
||||
instance.reset();
|
||||
|
||||
usingSharedContext ? void(instance.release()) : instance.reset();
|
||||
}
|
||||
|
||||
/// @brief Register a list of types with a shader registry instance
|
||||
|
||||
Reference in New Issue
Block a user