Files
MapLibreParticleLab/src/mbgl/vulkan/renderer_backend.cpp
2025-02-28 21:49:20 +00:00

702 lines
27 KiB
C++

#include <mbgl/vulkan/renderer_backend.hpp>
#include <mbgl/vulkan/context.hpp>
#include <mbgl/vulkan/renderable_resource.hpp>
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/gfx/shader_registry.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/shaders/vulkan/shader_group.hpp>
#include <mbgl/shaders/vulkan/background.hpp>
#include <mbgl/shaders/vulkan/circle.hpp>
#include <mbgl/shaders/vulkan/clipping_mask.hpp>
#include <mbgl/shaders/vulkan/collision.hpp>
#include <mbgl/shaders/vulkan/custom_geometry.hpp>
#include <mbgl/shaders/vulkan/custom_symbol_icon.hpp>
#include <mbgl/shaders/vulkan/debug.hpp>
#include <mbgl/shaders/vulkan/fill.hpp>
#include <mbgl/shaders/vulkan/fill_extrusion.hpp>
#include <mbgl/shaders/vulkan/heatmap.hpp>
#include <mbgl/shaders/vulkan/heatmap_texture.hpp>
#include <mbgl/shaders/vulkan/hillshade.hpp>
#include <mbgl/shaders/vulkan/hillshade_prepare.hpp>
#include <mbgl/shaders/vulkan/line.hpp>
#include <mbgl/shaders/vulkan/location_indicator.hpp>
#include <mbgl/shaders/vulkan/raster.hpp>
#include <mbgl/shaders/vulkan/symbol.hpp>
#include <mbgl/shaders/vulkan/widevector.hpp>
#include <cassert>
#include <string>
#ifdef ENABLE_VULKAN_VALIDATION
#include <vulkan/vulkan_to_string.hpp>
#endif
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
#ifdef ENABLE_VMA_DEBUG
#define VMA_DEBUG_MARGIN 16
#define VMA_DEBUG_DETECT_CORRUPTION 1
#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
#define VMA_LEAK_LOG_FORMAT(format, ...) \
{ \
char buffer[4096]; \
sprintf(buffer, format, __VA_ARGS__); \
mbgl::Log::Info(mbgl::Event::Render, buffer); \
}
#endif
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
#ifdef _WIN32
#include <windows.h>
#endif
#include "renderdoc_app.h"
static struct {
RENDERDOC_API_1_1_2* api = nullptr;
bool loop = false;
int32_t frameDelay = 0;
uint32_t frameCaptureCount = 0;
} g_rdoc;
#endif
namespace mbgl {
namespace vulkan {
template <typename T, typename F>
static bool checkAvailability(const std::vector<T>& availableValues,
const std::vector<const char*>& requiredValues,
const F& getter) {
for (const auto& requiredValue : requiredValues) {
bool found = false;
for (const auto& availableValue : availableValues) {
if (strcmp(requiredValue, getter(availableValue)) == 0) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
RendererBackend::RendererBackend(const gfx::ContextMode contextMode_)
: gfx::RendererBackend(contextMode_),
allocator(nullptr) {}
RendererBackend::~RendererBackend() {
destroyResources();
}
std::unique_ptr<gfx::Context> RendererBackend::createContext() {
return std::make_unique<vulkan::Context>(*this);
}
std::vector<const char*> RendererBackend::getLayers() {
return {
#ifdef ENABLE_VULKAN_VALIDATION
"VK_LAYER_KHRONOS_validation",
#endif
#ifdef _WIN32
"VK_LAYER_LUNARG_monitor",
#endif
};
}
std::vector<const char*> RendererBackend::getInstanceExtensions() {
return {
#ifdef __APPLE__
"VK_KHR_portability_enumeration"
#endif
};
}
std::vector<const char*> RendererBackend::getDeviceExtensions() {
auto extensions = getDefaultRenderable().getResource<SurfaceRenderableResource>().getDeviceExtensions();
#ifdef __APPLE__
extensions.push_back("VK_KHR_portability_subset");
#endif
return extensions;
}
std::vector<const char*> RendererBackend::getDebugExtensions() {
std::vector<const char*> extensions;
const auto& availableExtensions = vk::enumerateInstanceExtensionProperties();
debugUtilsEnabled = checkAvailability(
availableExtensions, {VK_EXT_DEBUG_UTILS_EXTENSION_NAME}, [](const vk::ExtensionProperties& value) {
return value.extensionName.data();
});
if (debugUtilsEnabled) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
} else {
// check for VK_EXT_debug_report (deprecated)
bool debugReportAvailable = checkAvailability(
availableExtensions, {VK_EXT_DEBUG_REPORT_EXTENSION_NAME}, [](const vk::ExtensionProperties& value) {
return value.extensionName.data();
});
if (debugReportAvailable) {
extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
} else {
mbgl::Log::Error(mbgl::Event::Render, "No debugging extension available");
}
}
return extensions;
}
void RendererBackend::beginDebugLabel([[maybe_unused]] const vk::CommandBuffer& buffer,
[[maybe_unused]] const char* name,
[[maybe_unused]] const std::array<float, 4>& color) const {
#ifdef ENABLE_VULKAN_VALIDATION
if (!debugUtilsEnabled) return;
buffer.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT().setPLabelName(name).setColor(color));
#endif
}
void RendererBackend::endDebugLabel([[maybe_unused]] const vk::CommandBuffer& buffer) const {
#ifdef ENABLE_VULKAN_VALIDATION
if (!debugUtilsEnabled) return;
buffer.endDebugUtilsLabelEXT();
#endif
}
void RendererBackend::insertDebugLabel([[maybe_unused]] const vk::CommandBuffer& buffer,
[[maybe_unused]] const char* name) const {
#ifdef ENABLE_VULKAN_VALIDATION
if (!debugUtilsEnabled) return;
buffer.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT().setPLabelName(name));
#endif
}
void RendererBackend::initFrameCapture() {
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
#ifdef _WIN32
if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&g_rdoc.api);
assert(ret == 1);
}
#elif __unix__
if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD)) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void**)&g_rdoc.api);
assert(ret == 1);
}
#endif
#endif
}
void RendererBackend::startFrameCapture() {
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
if (!g_rdoc.api) {
return;
}
if (g_rdoc.loop) {
RENDERDOC_DevicePointer devicePtr = RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->operator VkInstance_T*());
g_rdoc.api->StartFrameCapture(devicePtr, nullptr);
} else {
if (g_rdoc.frameCaptureCount > 0 && g_rdoc.frameDelay == 0) {
g_rdoc.api->TriggerMultiFrameCapture(g_rdoc.frameCaptureCount);
}
--g_rdoc.frameDelay;
}
#endif
}
void RendererBackend::endFrameCapture() {
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
if (g_rdoc.api && g_rdoc.loop) {
RENDERDOC_DevicePointer devicePtr = RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->operator VkInstance_T*());
g_rdoc.api->EndFrameCapture(devicePtr, nullptr);
}
#endif
}
void RendererBackend::setFrameCaptureLoop([[maybe_unused]] bool value) {
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
g_rdoc.loop = value;
#endif
}
void RendererBackend::triggerFrameCapture([[maybe_unused]] uint32_t frameCount, [[maybe_unused]] uint32_t frameDelay) {
#ifdef ENABLE_RENDERDOC_FRAME_CAPTURE
if (!g_rdoc.api) {
return;
}
g_rdoc.frameCaptureCount = frameCount;
g_rdoc.frameDelay = frameDelay;
#endif
}
#ifdef ENABLE_VULKAN_VALIDATION
static VKAPI_ATTR VkBool32 VKAPI_CALL vkDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
void*) {
EventSeverity mbglSeverity = EventSeverity::Debug;
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
mbglSeverity = EventSeverity::Debug;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
mbglSeverity = EventSeverity::Info;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
mbglSeverity = EventSeverity::Warning;
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
mbglSeverity = EventSeverity::Error;
break;
default:
return VK_FALSE;
}
mbgl::Log::Record(mbglSeverity, mbgl::Event::Render, callbackData->pMessage);
return VK_FALSE;
}
static VKAPI_ATTR VkBool32 vkDebugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
[[maybe_unused]] uint64_t object,
[[maybe_unused]] size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage,
[[maybe_unused]] void* pUserData) {
EventSeverity mbglSeverity = EventSeverity::Debug;
if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
mbglSeverity = EventSeverity::Info;
} else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
mbglSeverity = EventSeverity::Warning;
} else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
mbglSeverity = EventSeverity::Warning;
} else if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
mbglSeverity = EventSeverity::Error;
} else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
mbglSeverity = EventSeverity::Debug;
}
const std::string message = "[" + vk::to_string(vk::DebugReportObjectTypeEXT(objectType)) + "]" + "[code - " +
std::to_string(messageCode) + "]" + "[layer - " + pLayerPrefix + "]" + pMessage;
mbgl::Log::Record(mbglSeverity, mbgl::Event::Render, message);
return VK_FALSE;
}
#endif
void RendererBackend::initDebug() {
#ifdef ENABLE_VULKAN_VALIDATION
if (debugUtilsEnabled) {
const vk::DebugUtilsMessageSeverityFlagsEXT severity = vk::DebugUtilsMessageSeverityFlagsEXT() |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError;
const vk::DebugUtilsMessageTypeFlagsEXT type = vk::DebugUtilsMessageTypeFlagsEXT() |
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance |
vk::DebugUtilsMessageTypeFlagBitsEXT::eDeviceAddressBinding;
const auto createInfo =
vk::DebugUtilsMessengerCreateInfoEXT().setMessageSeverity(severity).setMessageType(type).setPfnUserCallback(
vkDebugUtilsCallback);
debugUtilsCallback = instance->createDebugUtilsMessengerEXTUnique(createInfo);
if (!debugUtilsCallback) {
mbgl::Log::Error(mbgl::Event::Render, "Failed to register Vulkan debug utils callback");
}
} else {
const vk::DebugReportFlagsEXT flags = vk::DebugReportFlagsEXT() | vk::DebugReportFlagBitsEXT::eDebug |
vk::DebugReportFlagBitsEXT::eError |
vk::DebugReportFlagBitsEXT::eInformation |
vk::DebugReportFlagBitsEXT::ePerformanceWarning |
vk::DebugReportFlagBitsEXT::eWarning;
const auto createInfo = vk::DebugReportCallbackCreateInfoEXT().setFlags(flags).setPfnCallback(
vkDebugReportCallback);
debugReportCallback = instance->createDebugReportCallbackEXTUnique(createInfo);
if (!debugReportCallback) {
mbgl::Log::Error(mbgl::Event::Render, "Failed to register Vulkan debug report callback");
}
}
#endif
}
void RendererBackend::init() {
initFrameCapture();
initInstance();
initSurface();
initDevice();
initAllocator();
initSwapchain();
initCommandPool();
}
void RendererBackend::initInstance() {
// initialize minimal set of function pointers
VULKAN_HPP_DEFAULT_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__
vk::InstanceCreateFlags instanceFlags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
#else
vk::InstanceCreateFlags instanceFlags = {};
#endif
vk::ApplicationInfo appInfo("maplibre-native", 1, "maplibre-native", 1, VK_API_VERSION_1_0);
vk::InstanceCreateInfo createInfo(instanceFlags, &appInfo);
const auto& layers = getLayers();
bool layersAvailable = checkAvailability(vk::enumerateInstanceLayerProperties(),
layers,
[](const vk::LayerProperties& value) { return value.layerName.data(); });
if (layersAvailable) {
createInfo.setPEnabledLayerNames(layers);
} else {
mbgl::Log::Error(mbgl::Event::Render, "Vulkan layers not found");
}
auto extensions = getInstanceExtensions();
bool extensionsAvailable = checkAvailability(
vk::enumerateInstanceExtensionProperties(), extensions, [](const vk::ExtensionProperties& value) {
return value.extensionName.data();
});
#ifdef ENABLE_VULKAN_VALIDATION
const auto& debugExtensions = getDebugExtensions();
extensions.insert(extensions.end(), debugExtensions.begin(), debugExtensions.end());
#ifdef ENABLE_VULKAN_GPU_ASSISTED_VALIDATION
appInfo.setApiVersion(VK_API_VERSION_1_1);
const std::array<vk::ValidationFeatureEnableEXT, 2> validationFeatures = {
vk::ValidationFeatureEnableEXT::eGpuAssisted, vk::ValidationFeatureEnableEXT::eGpuAssistedReserveBindingSlot};
const vk::ValidationFeaturesEXT validationFeatureInfo(validationFeatures);
createInfo.setPNext(&validationFeatureInfo);
#endif
#endif
if (extensionsAvailable) {
createInfo.setPEnabledExtensionNames(extensions);
} else {
mbgl::Log::Error(mbgl::Event::Render, "Vulkan extensions not found");
}
instance = vk::createInstanceUnique(createInfo);
// initialize function pointers for instance
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get());
#ifdef ENABLE_VULKAN_VALIDATION
// enable validation layer callback
initDebug();
#endif
}
void RendererBackend::initSurface() {
getDefaultRenderable().getResource<SurfaceRenderableResource>().createPlatformSurface();
}
void RendererBackend::initAllocator() {
VmaVulkanFunctions functions = {};
functions.vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr;
functions.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr;
functions.vkGetPhysicalDeviceProperties = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceProperties;
functions.vkGetPhysicalDeviceMemoryProperties = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceMemoryProperties;
functions.vkAllocateMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkAllocateMemory;
functions.vkFreeMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkFreeMemory;
functions.vkMapMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkMapMemory;
functions.vkUnmapMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkUnmapMemory;
functions.vkFlushMappedMemoryRanges = VULKAN_HPP_DEFAULT_DISPATCHER.vkFlushMappedMemoryRanges;
functions.vkInvalidateMappedMemoryRanges = VULKAN_HPP_DEFAULT_DISPATCHER.vkInvalidateMappedMemoryRanges;
functions.vkBindBufferMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkBindBufferMemory;
functions.vkBindImageMemory = VULKAN_HPP_DEFAULT_DISPATCHER.vkBindImageMemory;
functions.vkGetBufferMemoryRequirements = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetBufferMemoryRequirements;
functions.vkGetImageMemoryRequirements = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetImageMemoryRequirements;
functions.vkCreateBuffer = VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateBuffer;
functions.vkDestroyBuffer = VULKAN_HPP_DEFAULT_DISPATCHER.vkDestroyBuffer;
functions.vkCreateImage = VULKAN_HPP_DEFAULT_DISPATCHER.vkCreateImage;
functions.vkDestroyImage = VULKAN_HPP_DEFAULT_DISPATCHER.vkDestroyImage;
functions.vkCmdCopyBuffer = VULKAN_HPP_DEFAULT_DISPATCHER.vkCmdCopyBuffer;
functions.vkGetBufferMemoryRequirements2KHR = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetBufferMemoryRequirements2KHR;
functions.vkGetImageMemoryRequirements2KHR = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetImageMemoryRequirements2KHR;
functions.vkBindBufferMemory2KHR = VULKAN_HPP_DEFAULT_DISPATCHER.vkBindBufferMemory2KHR;
functions.vkBindImageMemory2KHR = VULKAN_HPP_DEFAULT_DISPATCHER.vkBindImageMemory2KHR;
functions.vkGetPhysicalDeviceMemoryProperties2KHR =
VULKAN_HPP_DEFAULT_DISPATCHER.vkGetPhysicalDeviceMemoryProperties2KHR;
VmaAllocatorCreateInfo allocatorCreateInfo = {};
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_0;
allocatorCreateInfo.physicalDevice = physicalDevice;
allocatorCreateInfo.device = device.get();
allocatorCreateInfo.instance = instance.get();
allocatorCreateInfo.pVulkanFunctions = &functions;
VkResult result = vmaCreateAllocator(&allocatorCreateInfo, &allocator);
if (result != VK_SUCCESS) {
throw std::runtime_error("Vulkan allocator init failed");
}
}
void RendererBackend::initDevice() {
const auto& extensions = getDeviceExtensions();
const auto& layers = getLayers();
const auto& surface = getDefaultRenderable().getResource<SurfaceRenderableResource>().getPlatformSurface().get();
const auto& isPhysicalDeviceCompatible = [&](const vk::PhysicalDevice& candidate) -> bool {
bool extensionsAvailable = checkAvailability(
candidate.enumerateDeviceExtensionProperties(), extensions, [](const vk::ExtensionProperties& value) {
return value.extensionName.data();
});
if (!extensionsAvailable) return false;
graphicsQueueIndex = -1;
presentQueueIndex = -1;
const auto& queues = candidate.getQueueFamilyProperties();
// Use to test on specific GPU type (if multiple)
// if (candidate.getProperties().deviceType != vk::PhysicalDeviceType::eIntegratedGpu) return false;
for (auto i = 0u; i < queues.size(); ++i) {
const auto& queue = queues[i];
if (queue.queueCount == 0) continue;
if (queue.queueFlags & vk::QueueFlagBits::eGraphics) graphicsQueueIndex = i;
if (surface && candidate.getSurfaceSupportKHR(i, surface)) presentQueueIndex = i;
if (graphicsQueueIndex != -1 && (!surface || presentQueueIndex != -1)) break;
}
if (graphicsQueueIndex == -1 || (surface && presentQueueIndex == -1)) return false;
if (surface) {
if (candidate.getSurfaceFormatsKHR(surface).empty()) return false;
if (candidate.getSurfacePresentModesKHR(surface).empty()) return false;
}
return true;
};
const auto& pickPhysicalDevice = [&]() {
const auto& physicalDevices = instance->enumeratePhysicalDevices();
if (physicalDevices.empty()) throw std::runtime_error("No Vulkan compatible GPU found");
for (const auto& candidate : physicalDevices) {
if (isPhysicalDeviceCompatible(candidate)) {
physicalDevice = candidate;
break;
}
}
if (!physicalDevice) throw std::runtime_error("No suitable GPU found");
physicalDeviceProperties = physicalDevice.getProperties();
};
pickPhysicalDevice();
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
float queuePriority = 1.0f;
queueCreateInfos.emplace_back(vk::DeviceQueueCreateFlags(), graphicsQueueIndex, 1, &queuePriority);
if (surface && graphicsQueueIndex != presentQueueIndex)
queueCreateInfos.emplace_back(vk::DeviceQueueCreateFlags(), presentQueueIndex, 1, &queuePriority);
[[maybe_unused]] const auto& supportedDeviceFeatures = physicalDevice.getFeatures();
physicalDeviceFeatures = vk::PhysicalDeviceFeatures();
// TODO
// - WideLines disabled on Android (20.77% device coverage https://vulkan.gpuinfo.org/listfeaturescore10.php)
// - Rework this to a dynamic toggle based on MLN_TRIANGULATE_FILL_OUTLINES/MLN_ENABLE_POLYLINE_DRAWABLES
#if !defined(__ANDROID__) && !defined(__APPLE__)
if (supportedDeviceFeatures.wideLines) {
physicalDeviceFeatures.setWideLines(true);
// more wideLines info
// physicalDeviceProperties.limits.lineWidthRange;
// physicalDeviceProperties.limits.lineWidthGranularity;
} else {
mbgl::Log::Error(mbgl::Event::Render, "Feature not available: wideLines");
}
#endif
if (supportedDeviceFeatures.samplerAnisotropy) {
physicalDeviceFeatures.setSamplerAnisotropy(true);
} else {
mbgl::Log::Error(mbgl::Event::Render, "Feature not available: samplerAnisotropy");
}
auto createInfo = vk::DeviceCreateInfo()
.setQueueCreateInfos(queueCreateInfos)
.setEnabledExtensionCount(static_cast<uint32_t>(extensions.size()))
.setPpEnabledExtensionNames(extensions.data())
.setPEnabledFeatures(&physicalDeviceFeatures);
// this is not needed for newer implementations
createInfo.setPEnabledLayerNames(layers);
device = physicalDevice.createDeviceUnique(createInfo);
// optional function pointer specialization for device
VULKAN_HPP_DEFAULT_DISPATCHER.init(device.get());
graphicsQueue = device->getQueue(graphicsQueueIndex, 0);
if (presentQueueIndex != -1) presentQueue = device->getQueue(presentQueueIndex, 0);
}
void RendererBackend::initSwapchain() {
auto& renderable = getDefaultRenderable();
auto& renderableResource = renderable.getResource<SurfaceRenderableResource>();
const auto& size = renderable.getSize();
// buffer resources if rendering to a surface
// no buffering when using headless
maxFrames = renderableResource.getPlatformSurface() ? 2 : 1;
renderableResource.init(size.width, size.height);
if (renderableResource.hasSurfaceTransformSupport()) {
auto& renderableImpl = static_cast<Renderable&>(renderable);
const auto& extent = renderableResource.getExtent();
renderableImpl.setSize({extent.width, extent.height});
}
}
void RendererBackend::initCommandPool() {
const vk::CommandPoolCreateInfo createInfo(vk::CommandPoolCreateFlagBits::eResetCommandBuffer, graphicsQueueIndex);
commandPool = device->createCommandPoolUnique(createInfo);
}
void RendererBackend::destroyResources() {
if (device) device->waitIdle();
context.reset();
commandPool.reset();
vmaDestroyAllocator(allocator);
device.reset();
// destroy this last so we have cleanup validation
debugUtilsCallback.reset();
debugReportCallback.reset();
instance.reset();
}
/// @brief Register a list of types with a shader registry instance
/// @tparam ...ShaderID Pack of BuiltIn:: shader IDs
/// @param registry A shader registry instance
/// @param programParameters ProgramParameters used to initialize each instance
template <shaders::BuiltIn... ShaderID>
void registerTypes(gfx::ShaderRegistry& registry, const ProgramParameters& programParameters) {
/// The following fold expression will create a shader for every type
/// in the parameter pack and register it with the shader registry.
// Registration calls are wrapped in a lambda that throws on registration
// failure, we shouldn't expect registration to faill unless the shader
// registry instance provided already has conflicting programs present.
(
[&]() {
using namespace std::string_literals;
using ShaderClass = shaders::ShaderSource<ShaderID, gfx::Backend::Type::Vulkan>;
auto group = std::make_shared<ShaderGroup<ShaderID>>(programParameters);
if (!registry.registerShaderGroup(std::move(group), ShaderClass::name)) {
assert(!"duplicate shader group");
throw std::runtime_error("Failed to register "s + ShaderClass::name + " with shader registry!");
}
}(),
...);
}
void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramParameters& programParameters) {
registerTypes<shaders::BuiltIn::BackgroundShader,
shaders::BuiltIn::BackgroundPatternShader,
shaders::BuiltIn::CircleShader,
shaders::BuiltIn::ClippingMaskProgram,
shaders::BuiltIn::CollisionBoxShader,
shaders::BuiltIn::CollisionCircleShader,
shaders::BuiltIn::CustomGeometryShader,
shaders::BuiltIn::CustomSymbolIconShader,
shaders::BuiltIn::DebugShader,
shaders::BuiltIn::FillShader,
shaders::BuiltIn::FillOutlineShader,
shaders::BuiltIn::FillPatternShader,
shaders::BuiltIn::FillOutlinePatternShader,
shaders::BuiltIn::FillOutlineTriangulatedShader,
shaders::BuiltIn::FillExtrusionShader,
shaders::BuiltIn::FillExtrusionPatternShader,
shaders::BuiltIn::HeatmapShader,
shaders::BuiltIn::HeatmapTextureShader,
shaders::BuiltIn::HillshadeShader,
shaders::BuiltIn::HillshadePrepareShader,
shaders::BuiltIn::LineShader,
shaders::BuiltIn::LineGradientShader,
shaders::BuiltIn::LineSDFShader,
shaders::BuiltIn::LinePatternShader,
shaders::BuiltIn::LocationIndicatorShader,
shaders::BuiltIn::LocationIndicatorTexturedShader,
shaders::BuiltIn::RasterShader,
shaders::BuiltIn::SymbolIconShader,
shaders::BuiltIn::SymbolSDFIconShader,
shaders::BuiltIn::SymbolTextAndIconShader,
shaders::BuiltIn::WideVectorShader>(shaders, programParameters);
}
} // namespace vulkan
} // namespace mbgl