OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain / src / dc_vulkan_ext.cpp

181 lines · cpp ref:
#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;
	}
}