#include "dc_vulkan_ext.h"
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/rendering_device.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
using godot::RenderingServer;
using godot::RenderingDevice;
using godot::UtilityFunctions;
using godot::RID;
bool DCVulkanContext::initialize() {
// Load vkGetInstanceProcAddr from the system Vulkan loader. The loader has a
// different name and a different API per platform, but nothing below this
// differs — Vulkan itself is the portable part.
#ifdef _WIN32
HMODULE vk_lib = LoadLibraryA("vulkan-1.dll");
if (!vk_lib) {
UtilityFunctions::print("[DCVulkan] Failed to load vulkan-1.dll");
return false;
}
auto vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)GetProcAddress(vk_lib, "vkGetInstanceProcAddr");
#else
void *vk_lib = dlopen("libvulkan.so.1", RTLD_LAZY);
if (!vk_lib) vk_lib = dlopen("libvulkan.so", RTLD_LAZY);
if (!vk_lib) {
UtilityFunctions::print("[DCVulkan] Failed to load libvulkan.so");
return false;
}
auto vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(vk_lib, "vkGetInstanceProcAddr");
#endif
if (!vkGetInstanceProcAddr) {
UtilityFunctions::print("[DCVulkan] Failed to find vkGetInstanceProcAddr");
return false;
}
// Get Vulkan handles from Godot's RenderingDevice
RenderingServer *rs = RenderingServer::get_singleton();
if (!rs) {
UtilityFunctions::print("[DCVulkan] No RenderingServer");
return false;
}
RenderingDevice *rd = rs->get_rendering_device();
if (!rd) {
UtilityFunctions::print("[DCVulkan] No RenderingDevice");
return false;
}
instance = (VkInstance)rd->get_driver_resource(
RenderingDevice::DRIVER_RESOURCE_VULKAN_INSTANCE, RID(), 0);
physical_device = (VkPhysicalDevice)rd->get_driver_resource(
RenderingDevice::DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE, RID(), 0);
device = (VkDevice)rd->get_driver_resource(
RenderingDevice::DRIVER_RESOURCE_VULKAN_DEVICE, RID(), 0);
queue = (VkQueue)rd->get_driver_resource(
RenderingDevice::DRIVER_RESOURCE_VULKAN_QUEUE, RID(), 0);
queue_family = (uint32_t)rd->get_driver_resource(
RenderingDevice::DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX, RID(), 0);
if (!device || !queue) {
UtilityFunctions::print("[DCVulkan] Failed to get VkDevice/VkQueue from Godot");
return false;
}
UtilityFunctions::print("[DCVulkan] VkDevice=", (uint64_t)device,
" queue_family=", queue_family);
// Load device-level function pointers
auto vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)
vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr");
if (!vkGetDeviceProcAddr) {
UtilityFunctions::print("[DCVulkan] Failed to find vkGetDeviceProcAddr");
return false;
}
#define LOAD_VK(name) name = (PFN_vk##name)vkGetDeviceProcAddr(device, "vk" #name)
LOAD_VK(AllocateCommandBuffers);
LOAD_VK(FreeCommandBuffers);
LOAD_VK(BeginCommandBuffer);
LOAD_VK(EndCommandBuffer);
LOAD_VK(QueueSubmit);
LOAD_VK(QueueWaitIdle);
LOAD_VK(CreateCommandPool);
LOAD_VK(DestroyCommandPool);
LOAD_VK(CreateBuffer);
LOAD_VK(DestroyBuffer);
LOAD_VK(AllocateMemory);
LOAD_VK(FreeMemory);
LOAD_VK(BindBufferMemory);
LOAD_VK(GetBufferMemoryRequirements);
LOAD_VK(GetBufferDeviceAddress);
LOAD_VK(CmdPipelineBarrier);
LOAD_VK(MapMemory);
LOAD_VK(UnmapMemory);
LOAD_VK(DeviceWaitIdle);
#undef LOAD_VK
// Instance-level functions
GetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties");
// Load CLAS extension functions
GetClusterAccelerationStructureBuildSizesNV =
(PFN_vkGetClusterAccelerationStructureBuildSizesNV)
vkGetDeviceProcAddr(device, "vkGetClusterAccelerationStructureBuildSizesNV");
CmdBuildClusterAccelerationStructureIndirectNV =
(PFN_vkCmdBuildClusterAccelerationStructureIndirectNV)
vkGetDeviceProcAddr(device, "vkCmdBuildClusterAccelerationStructureIndirectNV");
// Function pointers resolve on all driver versions, but CLAS requires
// hardware support (Ampere+). Check the feature bit via vkGetPhysicalDeviceFeatures2.
clas_supported = false;
if (GetClusterAccelerationStructureBuildSizesNV && CmdBuildClusterAccelerationStructureIndirectNV) {
auto vkGetPhysicalDeviceFeatures2 = (PFN_vkGetPhysicalDeviceFeatures2)
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2");
if (vkGetPhysicalDeviceFeatures2) {
VkPhysicalDeviceClusterAccelerationStructureFeaturesNV clas_feats = {};
clas_feats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CLUSTER_ACCELERATION_STRUCTURE_FEATURES_NV;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
feats2.pNext = &clas_feats;
vkGetPhysicalDeviceFeatures2(physical_device, &feats2);
clas_supported = (clas_feats.clusterAccelerationStructure == VK_TRUE);
}
}
if (clas_supported) {
UtilityFunctions::print("[DCVulkan] VK_NV_cluster_acceleration_structure: SUPPORTED");
} else {
UtilityFunctions::print("[DCVulkan] VK_NV_cluster_acceleration_structure: NOT SUPPORTED");
}
// Load KHR RT functions
CreateAccelerationStructureKHR = (PFN_vkCreateAccelerationStructureKHR)
vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR");
DestroyAccelerationStructureKHR = (PFN_vkDestroyAccelerationStructureKHR)
vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR");
GetAccelerationStructureDeviceAddressKHR = (PFN_vkGetAccelerationStructureDeviceAddressKHR)
vkGetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR");
GetAccelerationStructureBuildSizesKHR = (PFN_vkGetAccelerationStructureBuildSizesKHR)
vkGetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR");
CmdBuildAccelerationStructuresKHR = (PFN_vkCmdBuildAccelerationStructuresKHR)
vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR");
rt_supported = (CreateAccelerationStructureKHR != nullptr &&
CmdBuildAccelerationStructuresKHR != nullptr);
UtilityFunctions::print("[DCVulkan] VK_KHR_acceleration_structure: ",
rt_supported ? "AVAILABLE" : "NOT AVAILABLE");
// Create command pool for AS operations
VkCommandPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_info.queueFamilyIndex = queue_family;
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
if (CreateCommandPool(device, &pool_info, nullptr, &command_pool) != VK_SUCCESS) {
UtilityFunctions::print("[DCVulkan] Failed to create command pool");
return false;
}
UtilityFunctions::print("[DCVulkan] Initialized (CLAS=", clas_supported ? "yes" : "no", ")");
return true;
}
void DCVulkanContext::cleanup() {
if (command_pool && device && DestroyCommandPool) {
DestroyCommandPool(device, command_pool, nullptr);
command_pool = VK_NULL_HANDLE;
}
}