From 31bb5eaa094821145fe235f9a13817b0b3b2bdee Mon Sep 17 00:00:00 2001 From: Theresa Foley <10618364+tangent-vector@users.noreply.github.com> Date: Mon, 27 Jan 2025 18:31:04 -0800 Subject: Add an example for reflection of parameter blocks (#6161) * Add an example for reflection of parameter blocks The example loads a shader program, compiles it for Vulkan, and then uses reflection information to set up descriptor set layouts and a pipeline layout. It then validates that the pipeline layout that is created is compatible with the compiled code, by using them together to make a pipeline state object with the validation layer enabled. The basic flow of the application follows the presentation in the companion article, and references its sections. This is example could be expanded in a few ways: * A D3D12 path could be added, to show the comparable operations for creating a root signature from reflection data * The existing shader could be modified to touch/use more of its parameter data, to help ensure that any errors would be caught by the validation layer * The set of shader files/modules that get loaded automatically could be expanded, to test more cases * The code could be expanded to handle more than just compute shaders, by allowing for multiple-entry-point files to be loaded for rasterization or ray-tracing pipelines * format code * fixup: build errors * format code * fixups for build * fixup * fixup --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Yong He --- .../reflection-parameter-blocks/vulkan-api.cpp | 285 +++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 examples/reflection-parameter-blocks/vulkan-api.cpp (limited to 'examples/reflection-parameter-blocks/vulkan-api.cpp') diff --git a/examples/reflection-parameter-blocks/vulkan-api.cpp b/examples/reflection-parameter-blocks/vulkan-api.cpp new file mode 100644 index 000000000..7fad578b0 --- /dev/null +++ b/examples/reflection-parameter-blocks/vulkan-api.cpp @@ -0,0 +1,285 @@ +#include "vulkan-api.h" + +#include "slang.h" + +#include +#include +#include +#include +#include + +#if SLANG_WINDOWS_FAMILY +#include +#else +#include +#endif + +#if _DEBUG +#define ENABLE_VALIDATION_LAYER 1 +#endif + +VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( + VkDebugReportFlagsEXT /*flags*/, + VkDebugReportObjectTypeEXT /*objType*/, + uint64_t /*srcObject*/, + size_t /*location*/, + int32_t /*msgCode*/, + const char* pLayerPrefix, + const char* pMsg, + void* /*pUserData*/ +) +{ + printf("[%s]: %s\n", pLayerPrefix, pMsg); + return 1; +} + +gfx::Result VulkanAPI::initGlobalProcs() +{ + // Load vulkan library. + const char* dynamicLibraryName = "Unknown"; + +#if SLANG_WINDOWS_FAMILY + dynamicLibraryName = "vulkan-1.dll"; + HMODULE module = ::LoadLibraryA(dynamicLibraryName); +#define VK_API_GET_GLOBAL_PROC(x) this->x = (PFN_##x)GetProcAddress(module, #x); +#elif SLANG_APPLE_FAMILY + dynamicLibraryName = "libvulkan.dylib"; + void* vulkanLibraryHandle = dlopen(dynamicLibraryName, RTLD_NOW); +#define VK_API_GET_GLOBAL_PROC(x) this->x = (PFN_##x)dlsym(vulkanLibraryHandle, #x); +#else + dynamicLibraryName = "libvulkan.so.1"; + void* vulkanLibraryHandle = dlopen(dynamicLibraryName, RTLD_NOW); +#define VK_API_GET_GLOBAL_PROC(x) this->x = (PFN_##x)dlsym(vulkanLibraryHandle, #x); +#endif + + // Initialize all the global functions. + VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) + if (!this->vkCreateInstance) + return -1; + + return 0; +} + +gfx::Result initializeVulkanDevice(VulkanAPI& api) +{ + if (api.initGlobalProcs() != 0) + return -1; + + // Enable validation layer if available. + std::vector layers; +#ifdef ENABLE_VALIDATION_LAYER + uint32_t propertyCount; + if (api.vkEnumerateInstanceLayerProperties(&propertyCount, nullptr) != 0) + return -1; + std::vector properties(propertyCount); + if (api.vkEnumerateInstanceLayerProperties(&propertyCount, properties.data()) != 0) + return -1; + for (const auto& p : properties) + { + if (strcmp(p.layerName, "VK_LAYER_KHRONOS_validation") == 0) + { + layers.push_back("VK_LAYER_KHRONOS_validation"); + } + } +#endif + + // Create Vulkan Instance. + VkApplicationInfo applicationInfo = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; + applicationInfo.pApplicationName = "slang-hello-world"; + applicationInfo.pEngineName = "slang-hello-world"; + applicationInfo.apiVersion = VK_API_VERSION_1_2; + applicationInfo.engineVersion = 1; + applicationInfo.applicationVersion = 1; + const char* instanceExtensions[] = { +#if SLANG_APPLE_FAMILY + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, +#endif + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + }; + VkInstanceCreateInfo instanceCreateInfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; +#if SLANG_APPLE_FAMILY + instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + instanceCreateInfo.pApplicationInfo = &applicationInfo; + instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); + instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; + if (layers.size()) + { + instanceCreateInfo.ppEnabledLayerNames = &layers[0]; + instanceCreateInfo.enabledLayerCount = (uint32_t)layers.size(); + } + if (api.vkCreateInstance(&instanceCreateInfo, nullptr, &api.instance) != 0) + return -1; + + // Load instance functions. + api.initInstanceProcs(); + +#if 0 + // Create debug report callback. + if (api.vkCreateDebugReportCallbackEXT) + { + VkDebugReportFlagsEXT debugFlags = + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT}; + debugCreateInfo.pfnCallback = &debugMessageCallback; + debugCreateInfo.pUserData = nullptr; + debugCreateInfo.flags = debugFlags; + + RETURN_ON_FAIL(api.vkCreateDebugReportCallbackEXT( + api.instance, + &debugCreateInfo, + nullptr, + &api.debugReportCallback)); + } +#endif + + // Enumerate physical devices. + uint32_t numPhysicalDevices = 0; + RETURN_ON_FAIL(api.vkEnumeratePhysicalDevices(api.instance, &numPhysicalDevices, nullptr)); + std::vector physicalDevices; + physicalDevices.resize(numPhysicalDevices); + RETURN_ON_FAIL( + api.vkEnumeratePhysicalDevices(api.instance, &numPhysicalDevices, &physicalDevices[0])); + +#if 0 + // We will use device 0. + api.initPhysicalDevice(physicalDevices[0]); + + VkDeviceCreateInfo deviceCreateInfo = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pEnabledFeatures = &api.deviceFeatures; + + // Find proper queue family index. + uint32_t numQueueFamilies = 0; + api.vkGetPhysicalDeviceQueueFamilyProperties(api.physicalDevice, &numQueueFamilies, nullptr); + + std::vector queueFamilies; + queueFamilies.resize(numQueueFamilies); + api.vkGetPhysicalDeviceQueueFamilyProperties( + api.physicalDevice, + &numQueueFamilies, + &queueFamilies[0]); + + // Find a queue that can service our needs. + auto requiredQueueFlags = VK_QUEUE_COMPUTE_BIT; + for (int i = 0; i < int(numQueueFamilies); ++i) + { + if ((queueFamilies[i].queueFlags & requiredQueueFlags) == requiredQueueFlags) + { + api.queueFamilyIndex = i; + break; + } + } + if (api.queueFamilyIndex == -1) + return -1; + +#if SLANG_APPLE_FAMILY + const char* deviceExtensions[] = { + "VK_KHR_portability_subset", + }; +#endif + + VkDeviceQueueCreateInfo queueCreateInfo = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}; + float queuePriority = 0.0f; + queueCreateInfo.queueFamilyIndex = api.queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; +#if SLANG_APPLE_FAMILY + deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions); + deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; +#endif + RETURN_ON_FAIL(api.vkCreateDevice(api.physicalDevice, &deviceCreateInfo, nullptr, &api.device)); + +#endif + + // Load device functions. + api.initDeviceProcs(); + + return 0; +} + +gfx::Result VulkanAPI::initInstanceProcs() +{ + assert(instance && vkGetInstanceProcAddr != nullptr); + +#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); + + VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) + // Get optional + VK_API_INSTANCE_PROCS_OPT(VK_API_GET_INSTANCE_PROC) + +#undef VK_API_GET_INSTANCE_PROC + + return 0; +} + +#if 0 +int VulkanAPI::initPhysicalDevice(VkPhysicalDevice inPhysicalDevice) +{ + assert(physicalDevice == VK_NULL_HANDLE); + physicalDevice = inPhysicalDevice; + + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); + + return 0; +} +#endif + +gfx::Result VulkanAPI::initDeviceProcs() +{ + assert(instance && device && vkGetDeviceProcAddr != nullptr); + +#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); + VK_API_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) +#undef VK_API_GET_DEVICE_PROC + + return 0; +} + +#if 0 +int VulkanAPI::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) +{ + assert(typeBits); + + const int numMemoryTypes = int(deviceMemoryProperties.memoryTypeCount); + + // bit holds current test bit against typeBits. Ie bit == 1 << typeBits + + uint32_t bit = 1; + for (int i = 0; i < numMemoryTypes; ++i, bit += bit) + { + auto const& memoryType = deviceMemoryProperties.memoryTypes[i]; + if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) + { + return i; + } + } + + // assert(!"failed to find a usable memory type"); + return -1; +} +#endif + +VulkanAPI::~VulkanAPI() +{ +#if 0 + if (vkDestroyDevice) + { + vkDestroyDevice(device, nullptr); + } + if (debugReportCallback && vkDestroyDebugReportCallbackEXT) + { + vkDestroyDebugReportCallbackEXT(instance, debugReportCallback, nullptr); + } + if (vkDestroyInstance) + { + vkDestroyInstance(instance, nullptr); + } +#endif +} -- cgit v1.2.3