Replace the OpenGL rendering backend with Vulkan across the engine: - Graphics: add VulkanContext (instance/device/swapchain/renderpass with depth-stencil), port Canvas to nanovg-on-Vulkan and DebugGUI to the imgui Vulkan backend, drop GLEW. Wait for the Wayland configure handshake before the first present and rebuild the swapchain to the confirmed size so the window maps reliably on fractional-scale outputs. - RasterRender: reimplement RenderBuffer, shaders and object pipeline on Vulkan (offscreen VkImage target with MSAA resolve, SPIR-V pipelines, push-constant MVP, depth remap for Vulkan clip space). - Sketch3D: port the stroke renderer (geometry-shader line expansion) and stroke GPU buffers to Vulkan. - 3DEditor: port the path-tracer texture upload to a Vulkan staging upload and keep raster/path-tracer viewports oriented consistently. - Add a nanovg external-VkImage bridge plus a render-target registry so 3D render targets can be shown inside the 2D canvas. - Prefer the Wayland platform in GLFW regardless of DISPLAY. - Widgets: make BasicLayout size distribution converge with a sub-pixel epsilon instead of an exact-zero comparison, which could otherwise spin forever on fractional-scale displays and block the first frame. Vendor nanovg_vulkan and add a standalone VulkanGraphics module.
140 lines
No EOL
4.1 KiB
C++
140 lines
No EOL
4.1 KiB
C++
#include "VulkanUtils.hpp"
|
|
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
|
|
bool checkValidationLayerSupport(const std::vector<const char *>& validationLayers) {
|
|
uint32_t layerCount;
|
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
|
|
|
std::vector<VkLayerProperties> availableLayers(layerCount);
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
|
|
|
for (const auto &requestedLayer: validationLayers) {
|
|
bool presents = false;
|
|
for (auto &layer: availableLayers) {
|
|
if (strcmp(requestedLayer, layer.layerName) == 0) {
|
|
presents = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!presents) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool checkDeviceExtensions(VkPhysicalDevice device, const std::vector<const char *>& deviceExtensions) {
|
|
uint32_t extensionsCount = 0;
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr);
|
|
|
|
std::vector<VkExtensionProperties> extensions(extensionsCount);
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data());
|
|
|
|
for (const auto &requiredExtension: deviceExtensions) {
|
|
bool found = false;
|
|
for (const auto &extension: extensions) {
|
|
if (strcmp(requiredExtension, extension.extensionName) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount, uint32_t apiVersion) {
|
|
VkInstance out = VK_NULL_HANDLE;
|
|
|
|
std::vector<const char *> validationLayers;
|
|
|
|
#ifndef NDEBUG
|
|
const std::vector<const char *> requestedLayers = {"VK_LAYER_KHRONOS_validation"};
|
|
if (checkValidationLayerSupport(requestedLayers)) {
|
|
validationLayers = requestedLayers;
|
|
}
|
|
#endif
|
|
|
|
VkApplicationInfo appInfo{
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pApplicationName = "App",
|
|
.applicationVersion = VK_MAKE_VERSION(0, 0, 0),
|
|
.pEngineName = "no",
|
|
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
|
|
.apiVersion = apiVersion,
|
|
};
|
|
|
|
VkInstanceCreateInfo createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &appInfo,
|
|
.enabledLayerCount = (uint32_t) validationLayers.size(),
|
|
.ppEnabledLayerNames = validationLayers.data(),
|
|
.enabledExtensionCount = extensionsCount,
|
|
.ppEnabledExtensionNames = instanceExtensions,
|
|
};
|
|
|
|
if (vkCreateInstance(&createInfo, nullptr, &out) != VK_SUCCESS) {
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
VkPhysicalDevice pickPhysicalDevice(VkInstance instance, const PhysicalDevicePickPolicy& pickPolicy) {
|
|
VkPhysicalDevice out = VK_NULL_HANDLE;
|
|
|
|
uint32_t deviceCount = 0;
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
|
|
|
if (deviceCount == 0) {
|
|
throw std::runtime_error("no gpu with vulkan support");
|
|
}
|
|
|
|
std::vector<VkPhysicalDevice> devices(deviceCount);
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
|
|
|
for (auto device: devices) {
|
|
if (pickPolicy(device)) {
|
|
out = device;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
int findQueueFamilyIndex(VkPhysicalDevice physicalDevice, const QueuePickPolicy& pickPolicy) {
|
|
int out = -1;
|
|
|
|
uint32_t queuesCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queuesCount, nullptr);
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilyProperties(queuesCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queuesCount, queueFamilyProperties.data());
|
|
|
|
int index = 0;
|
|
for (const auto &familyProperty: queueFamilyProperties) {
|
|
if (pickPolicy(familyProperty, index)) {
|
|
out = index;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags) {
|
|
VkPhysicalDeviceMemoryProperties memoryProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
|
|
|
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
|
|
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("no suitable memory for vertex buffer found");
|
|
} |