├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── GLFWVulkanWindow.cpp ├── GLFWVulkanWindow.h ├── LICENSE.md ├── README.md ├── Renderer.cpp ├── Renderer.h ├── Statistics.cpp ├── Statistics.h ├── StatisticsEntry.cpp ├── StatisticsEntry.h ├── Vertex.cpp ├── Vertex.h ├── defines.glsl ├── main.cpp ├── models └── city.obj ├── pvs.h ├── sample.h ├── scenes.txt ├── settings └── s0.txt ├── shaders ├── compileShaders.bat ├── compileShaders.sh ├── errorCalculation.comp ├── gpuHashSetBulkInsert.comp ├── halton.comp ├── rasterVisibility.comp ├── rayVisualizationShader.frag ├── rayVisualizationShader.vert ├── rt │ ├── defines.glsl │ ├── gpuHashSet.glsl │ ├── raytrace.rchit │ ├── raytrace.rgen │ ├── raytrace.rmiss │ ├── raytrace_abs.rchit │ ├── raytrace_abs.rgen │ ├── raytrace_abs.rmiss │ ├── reverse_sampling_new.rgen │ └── util.glsl ├── shader.frag └── shader.vert ├── viewcell.cpp ├── viewcell.h ├── visibilitymanager.cpp ├── visibilitymanager.h ├── vulkanutil.cpp └── vulkanutil.h /.gitignore: -------------------------------------------------------------------------------- 1 | models/* 2 | out 3 | *.user* 4 | .vs 5 | screenshot.png 6 | *.spv 7 | cmake-build-debug 8 | cmake-build-release 9 | .idea 10 | external 11 | 12 | abs_rs_halton.vs 13 | new.vs 14 | old.vs 15 | *pvs.txt 16 | defines.glsl 17 | 18 | !models/city.obj 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | project(Vulkan_RTX_Master_Qt_CMake LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 10 | 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX -D_USE_MATH_DEFINES") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR -DUSEVULKANSDK -DVK_ENABLE_BETA_EXTENSIONS -DVK_USE_PLATFORM_XLIB_KHR -DVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 -DGLFW_INCLUDE_VULKAN") 13 | 14 | include_directories($ENV{VULKAN_SDK}/include) 15 | include_directories(${CMAKE_SOURCE_DIR}/external/glm) 16 | include_directories(${CMAKE_SOURCE_DIR}/external/tinyobjloader) 17 | include_directories(${CMAKE_SOURCE_DIR}/external/glfw) 18 | 19 | file(GLOB SRC_FILES 20 | "*.h" 21 | "*.hpp" 22 | "*.cpp" 23 | "*.cu" 24 | "gpuHashTable/*" 25 | ) 26 | add_executable(Vulkan_RTX_Master_Qt_CMake ${SRC_FILES}) 27 | 28 | target_link_libraries(Vulkan_RTX_Master_Qt_CMake -lGL -lGLU ${CMAKE_SOURCE_DIR}/external/glfw/libglfw3.a -lX11 -lXxf86vm -lXrandr -lpthread -lXi -ldl $ENV{VULKAN_SDK}/lib/libvulkan.so) 29 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com//fwlink//?linkid=834763 for more information about this file. 3 | "configurations": [ 4 | { 5 | "name": "x64-Debug", 6 | "generator": "Ninja", 7 | "configurationType": "Debug", 8 | "inheritEnvironments": [ "msvc_x64_x64" ], 9 | "buildRoot": "${projectDir}\\out\\build\\${name}", 10 | "installRoot": "${projectDir}\\out\\install\\${name}", 11 | "cmakeCommandArgs": "", 12 | "buildCommandArgs": "", 13 | "ctestCommandArgs": "" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /GLFWVulkanWindow.cpp: -------------------------------------------------------------------------------- 1 | // Based on https://vulkan-tutorial.com 2 | 3 | #include "GLFWVulkanWindow.h" 4 | #include "vulkanutil.h" 5 | 6 | #include "Renderer.h" 7 | 8 | float GLFWVulkanWindow::cameraSpeed = 200.0f; 9 | 10 | void GLFWVulkanWindow::initWindow() { 11 | glfwInit(); 12 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // Tell GLFW to not create an OpenGL context 13 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 14 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 15 | } 16 | 17 | void GLFWVulkanWindow::initVulkan() { 18 | createInstance(); 19 | createSurface(); 20 | pickPhysicalDevice(); 21 | createLogicalDevice(); 22 | createSwapChain(); 23 | createImageViews(); 24 | createRenderPass(); 25 | createCommandPool(); 26 | createColorResources(); 27 | createDepthResources(); 28 | createFramebuffers(); 29 | createCommandBuffers(); 30 | createSyncObjects(); 31 | } 32 | 33 | void GLFWVulkanWindow::initRenderer() { 34 | renderer = new VulkanRenderer(this); 35 | renderer->startVisibilityThread(); 36 | } 37 | 38 | void GLFWVulkanWindow::createSurface() { 39 | if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { 40 | throw std::runtime_error("failed to create window surface"); 41 | } 42 | } 43 | 44 | void GLFWVulkanWindow::createInstance() { 45 | if (enableValidationLayers && !checkValidationLayerSupport()) { 46 | throw std::runtime_error("validation layers requested, but not available"); 47 | } 48 | 49 | // Optional application info 50 | VkApplicationInfo appInfo = {}; 51 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 52 | appInfo.pApplicationName = "Hello Triangle"; 53 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 54 | appInfo.pEngineName = "No Engine"; 55 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 56 | appInfo.apiVersion = VK_API_VERSION_1_2; 57 | 58 | VkInstanceCreateInfo createInfo = {}; 59 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 60 | createInfo.pApplicationInfo = &appInfo; 61 | if (enableValidationLayers) { 62 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 63 | createInfo.ppEnabledLayerNames = validationLayers.data(); 64 | } else { 65 | createInfo.enabledLayerCount = 0; 66 | } 67 | 68 | std::vector instanceExtensions = {VK_KHR_SURFACE_EXTENSION_NAME}; 69 | instanceExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); 70 | createInfo.enabledExtensionCount = (uint32_t) instanceExtensions.size(); 71 | createInfo.ppEnabledExtensionNames = instanceExtensions.data(); 72 | 73 | // Get list of supported extensions 74 | uint32_t extensionCount = 0; 75 | vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); 76 | // Get extension details 77 | std::vector extensions(extensionCount); 78 | vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); 79 | // Print supported extensions 80 | /* 81 | std::cout << "available extensions:" << std::endl; 82 | std::cout << extensionCount << std::endl; 83 | for (const auto& extension : extensions) { 84 | std::cout << "\t" << extension.extensionName << std::endl; 85 | } 86 | */ 87 | 88 | if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 89 | throw std::runtime_error("failed to create Vulkan instance"); 90 | } 91 | } 92 | 93 | bool GLFWVulkanWindow::checkValidationLayerSupport() { 94 | uint32_t layerCount; 95 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 96 | 97 | std::vector availableLayers(layerCount); 98 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 99 | 100 | for (const char* layerName : validationLayers) { 101 | bool layerFound = false; 102 | 103 | for (const auto& layerProperties : availableLayers) { 104 | if (strcmp(layerName, layerProperties.layerName) == 0) { 105 | layerFound = true; 106 | break; 107 | } 108 | } 109 | 110 | if (!layerFound) { 111 | return false; 112 | } 113 | } 114 | 115 | return true; 116 | } 117 | 118 | /* 119 | * Select suitable GPU 120 | */ 121 | void GLFWVulkanWindow::pickPhysicalDevice() { 122 | // Get number of physical devices 123 | uint32_t deviceCount = 0; 124 | vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 125 | if (deviceCount == 0) { 126 | throw std::runtime_error("failed to find GPUs with Vulkan support"); 127 | } 128 | 129 | // Get physical devices 130 | std::vector devices(deviceCount); 131 | vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 132 | 133 | for (const auto& device : devices) { 134 | if (isDeviceSuitable(device)) { 135 | physicalDevice = device; 136 | //msaaSamples = VulkanUtil::getMaxUsableMSAASampleCount(physicalDevice); 137 | msaaSamples = VK_SAMPLE_COUNT_1_BIT; 138 | break; 139 | } 140 | } 141 | if (physicalDevice == VK_NULL_HANDLE) { 142 | throw std::runtime_error("failed to find a suitable GPU"); 143 | } 144 | } 145 | 146 | bool GLFWVulkanWindow::isDeviceSuitable(VkPhysicalDevice device) { 147 | QueueFamilyIndices indices = findQueueFamilies(device); 148 | bool extensionsSupported = checkDeviceExtensionSupport(device); 149 | 150 | // Check if there are supported image formats and presentation modes 151 | bool swapChainAdequate = false; 152 | if (extensionsSupported) { 153 | SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(device); 154 | swapChainAdequate = !swapChainSupportDetails.formats.empty() && !swapChainSupportDetails.presentModes.empty(); 155 | } 156 | 157 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 158 | } 159 | 160 | /* 161 | * Check if all required extensions are available on the passed physical device 162 | */ 163 | bool GLFWVulkanWindow::checkDeviceExtensionSupport(VkPhysicalDevice device) { 164 | uint32_t extensionCount; 165 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); 166 | 167 | std::vector availableExtensions(extensionCount); 168 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); 169 | 170 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 171 | 172 | for (const auto& extension : availableExtensions) { 173 | requiredExtensions.erase(extension.extensionName); 174 | } 175 | 176 | return requiredExtensions.empty(); 177 | } 178 | 179 | QueueFamilyIndices GLFWVulkanWindow::findQueueFamilies(VkPhysicalDevice device) { 180 | QueueFamilyIndices indices; 181 | 182 | uint32_t queueFamilyCount = 0; 183 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 184 | 185 | std::vector queueFamilies(queueFamilyCount); 186 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 187 | 188 | int i = 0; 189 | for (const auto& queueFamily : queueFamilies) { 190 | if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT && !indices.graphicsFamily.has_value()) { 191 | indices.graphicsFamily = i; 192 | } 193 | 194 | VkBool32 presentSupport = false; 195 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); 196 | if (presentSupport && !indices.presentFamily.has_value()) { 197 | indices.presentFamily = i; 198 | } 199 | 200 | // Find a separate compute queue family 201 | if (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT && !indices.computeFamily.has_value() && 202 | i != indices.graphicsFamily && i != indices.presentFamily 203 | ) { 204 | indices.computeFamily = i; 205 | } 206 | 207 | // Find a separate transfer queue family 208 | if (queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT && !indices.transferFamily.has_value() && 209 | i != indices.graphicsFamily && i != indices.presentFamily && i != indices.computeFamily 210 | ) { 211 | indices.transferFamily = i; 212 | } 213 | 214 | if (indices.isComplete()) { 215 | break; 216 | } 217 | i++; 218 | } 219 | 220 | return indices; 221 | } 222 | 223 | void GLFWVulkanWindow::createLogicalDevice() { 224 | // Create a queue for all queue families 225 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 226 | std::vector queueCreateInfos; 227 | std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value(), indices.computeFamily.value(), indices.transferFamily.value() }; 228 | for (uint32_t queueFamily : uniqueQueueFamilies) { 229 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 230 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 231 | queueCreateInfo.queueFamilyIndex = queueFamily; 232 | queueCreateInfo.queueCount = 1; 233 | float queuePriority = 1.0f; 234 | queueCreateInfo.pQueuePriorities = &queuePriority; 235 | queueCreateInfos.push_back(queueCreateInfo); 236 | } 237 | 238 | // Specify device features 239 | VkPhysicalDeviceFeatures deviceFeatures = {}; 240 | deviceFeatures.fillModeNonSolid = VK_TRUE; // WIREFRAME 241 | deviceFeatures.wideLines = VK_TRUE; 242 | deviceFeatures.multiViewport = VK_TRUE; 243 | deviceFeatures.geometryShader = VK_TRUE; 244 | 245 | // Create logical device 246 | VkDeviceCreateInfo createInfo = {}; 247 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 248 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 249 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 250 | createInfo.queueCreateInfoCount = queueCreateInfos.size(); 251 | createInfo.pEnabledFeatures = &deviceFeatures; 252 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 253 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 254 | if (enableValidationLayers) { 255 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 256 | createInfo.ppEnabledLayerNames = validationLayers.data(); 257 | } else { 258 | createInfo.enabledLayerCount = 0; 259 | } 260 | 261 | if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { 262 | throw std::runtime_error("failed to create logical device"); 263 | } 264 | 265 | // Get queue handles 266 | vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); 267 | vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); 268 | 269 | 270 | VkPhysicalDeviceIDProperties vkPhysicalDeviceIDProperties = {}; 271 | vkPhysicalDeviceIDProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; 272 | vkPhysicalDeviceIDProperties.pNext = NULL; 273 | 274 | VkPhysicalDeviceProperties2 vkPhysicalDeviceProperties2 = {}; 275 | vkPhysicalDeviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; 276 | vkPhysicalDeviceProperties2.pNext = &vkPhysicalDeviceIDProperties; 277 | 278 | PFN_vkGetPhysicalDeviceProperties2 fpGetPhysicalDeviceProperties2; 279 | fpGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2"); 280 | if (fpGetPhysicalDeviceProperties2 == NULL) { 281 | throw std::runtime_error("Vulkan: Proc address for \"vkGetPhysicalDeviceProperties2KHR\" not found.\n"); 282 | } 283 | 284 | fpGetPhysicalDeviceProperties2(physicalDevice, &vkPhysicalDeviceProperties2); 285 | 286 | memcpy(deviceUUID.data(), vkPhysicalDeviceIDProperties.deviceUUID, VK_UUID_SIZE); 287 | } 288 | 289 | void GLFWVulkanWindow::createSwapChain() { 290 | SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(physicalDevice); 291 | VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupportDetails.formats); 292 | VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupportDetails.presentModes); 293 | swapChainImageSize = chooseSwapExtent(swapChainSupportDetails.capabilities); 294 | 295 | // Specify min number of image in the swap chain that can be rendered to 296 | imageCount = swapChainSupportDetails.capabilities.minImageCount; 297 | if (swapChainSupportDetails.capabilities.maxImageCount > 0 && imageCount > swapChainSupportDetails.capabilities.maxImageCount) { 298 | imageCount = swapChainSupportDetails.capabilities.maxImageCount; 299 | } 300 | 301 | VkSwapchainCreateInfoKHR createInfo = {}; 302 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 303 | createInfo.surface = surface; 304 | createInfo.minImageCount = imageCount; 305 | createInfo.imageFormat = surfaceFormat.format; 306 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 307 | createInfo.imageExtent = swapChainImageSize; 308 | createInfo.imageArrayLayers = 1; 309 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; 310 | 311 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 312 | uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 313 | if (indices.graphicsFamily != indices.presentFamily) { 314 | // If the graphics queue family is different from the presentation queue family, draw images from 315 | // the graphics queue and submit them on the presentation queue 316 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 317 | createInfo.queueFamilyIndexCount = 2; 318 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 319 | } else { 320 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 321 | createInfo.queueFamilyIndexCount = 0; 322 | createInfo.pQueueFamilyIndices = nullptr; 323 | } 324 | 325 | createInfo.preTransform = swapChainSupportDetails.capabilities.currentTransform; // Apply no special pre-transformation 326 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 327 | createInfo.presentMode = presentMode; 328 | createInfo.clipped = VK_TRUE; // Clip pixels that are occluded by other windows 329 | createInfo.oldSwapchain = VK_NULL_HANDLE; 330 | 331 | if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { 332 | throw std::runtime_error("failed to create swap chain"); 333 | } 334 | 335 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); 336 | swapChainImages.resize(imageCount); 337 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); 338 | 339 | swapChainImageFormat = surfaceFormat.format; 340 | swapChainExtent = swapChainImageSize; 341 | } 342 | 343 | SwapChainSupportDetails GLFWVulkanWindow::querySwapChainSupport(VkPhysicalDevice device) { 344 | SwapChainSupportDetails details; 345 | 346 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); 347 | 348 | // Query supported surface formats 349 | uint32_t formatCount; 350 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); 351 | if (formatCount != 0) { 352 | details.formats.resize(formatCount); 353 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); 354 | } 355 | 356 | // Query supported presentation modes 357 | uint32_t presentModeCount; 358 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); 359 | if (presentModeCount != 0) { 360 | details.presentModes.resize(presentModeCount); 361 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); 362 | } 363 | 364 | return details; 365 | } 366 | 367 | /* 368 | * Choose SRGB and B8G8R8A8 format 369 | */ 370 | VkSurfaceFormatKHR GLFWVulkanWindow::chooseSwapSurfaceFormat(const std::vector& availableFormats) { 371 | for (const auto& availableFormat : availableFormats) { 372 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 373 | return availableFormat; 374 | } 375 | } 376 | 377 | return availableFormats[0]; 378 | } 379 | 380 | VkPresentModeKHR GLFWVulkanWindow::chooseSwapPresentMode(const std::vector& availablePresentModes) { 381 | for (const auto& availablePresentMode : availablePresentModes) { 382 | if (availablePresentMode == VK_PRESENT_MODE_FIFO_KHR) { 383 | return availablePresentMode; 384 | } 385 | } 386 | 387 | return VK_PRESENT_MODE_FIFO_KHR; 388 | } 389 | 390 | /* 391 | * Set the resolution of the swap chain images. 392 | */ 393 | VkExtent2D GLFWVulkanWindow::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 394 | if (capabilities.currentExtent.width != UINT32_MAX) { 395 | return capabilities.currentExtent; 396 | } else { 397 | VkExtent2D actualExtent = { WIDTH, HEIGHT }; 398 | // Clamp actualExtent 399 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 400 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 401 | 402 | return actualExtent; 403 | } 404 | } 405 | 406 | /* 407 | * Create an image view for every image in the swap chain. 408 | */ 409 | void GLFWVulkanWindow::createImageViews() { 410 | swapChainImageViews.resize(swapChainImages.size()); 411 | 412 | for (size_t i = 0; i < swapChainImages.size(); i++) { 413 | VkImageViewCreateInfo createInfo = {}; 414 | createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 415 | createInfo.image = swapChainImages[i]; 416 | createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 417 | createInfo.format = swapChainImageFormat; 418 | 419 | createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 420 | createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 421 | createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 422 | createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 423 | 424 | // Image will be used as color target without any mipmapping levels or multiple layers 425 | createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 426 | createInfo.subresourceRange.baseMipLevel = 0; 427 | createInfo.subresourceRange.levelCount = 1; 428 | createInfo.subresourceRange.baseArrayLayer = 0; 429 | createInfo.subresourceRange.layerCount = 1; 430 | 431 | if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { 432 | throw std::runtime_error("failed to create image view"); 433 | } 434 | } 435 | } 436 | 437 | void GLFWVulkanWindow::createRenderPass() { 438 | VkAttachmentDescription colorAttachment = {}; 439 | colorAttachment.format = swapChainImageFormat; 440 | colorAttachment.samples = msaaSamples; 441 | colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Clear framebuffer before rendering 442 | colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 443 | colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // No stencil buffer is used 444 | colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // No stencil buffer is used 445 | colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // We don't care about the layout before rendering 446 | colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // Multisamples images cannot be presented directly 447 | // Describe subpasses (may be used for multiple post-processing subpasses during a single render pass) 448 | VkAttachmentReference colorAttachmentReference = {}; 449 | colorAttachmentReference.attachment = 0; 450 | colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 451 | 452 | VkAttachmentDescription depthAttachment = {}; 453 | depthAttachment.format = findDepthFormat(); 454 | depthAttachment.samples = msaaSamples; 455 | depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 456 | depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 457 | depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 458 | depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 459 | depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 460 | depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 461 | VkAttachmentReference depthAttachmentReference = {}; 462 | depthAttachmentReference.attachment = 1; 463 | depthAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 464 | 465 | VkAttachmentDescription colorAttachmentResolve = {}; // The multisampled image is resolved to this attachment 466 | colorAttachmentResolve.format = swapChainImageFormat; 467 | colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; 468 | colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 469 | colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 470 | colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 471 | colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 472 | colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 473 | colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; 474 | VkAttachmentReference colorAttachmentResolveReference = {}; 475 | colorAttachmentResolveReference.attachment = 2; 476 | colorAttachmentResolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 477 | 478 | VkSubpassDescription subpassDescription = {}; 479 | subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 480 | subpassDescription.colorAttachmentCount = 1; 481 | subpassDescription.pColorAttachments = &colorAttachmentReference; 482 | subpassDescription.pDepthStencilAttachment = &depthAttachmentReference; 483 | subpassDescription.pResolveAttachments = &colorAttachmentResolveReference; 484 | 485 | VkSubpassDependency dependency = {}; 486 | dependency.srcSubpass = VK_SUBPASS_EXTERNAL; 487 | dependency.dstSubpass = 0; 488 | dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 489 | dependency.srcAccessMask = 0; 490 | dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 491 | dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; 492 | 493 | std::array attachments = { 494 | colorAttachment, depthAttachment, colorAttachmentResolve 495 | }; 496 | VkRenderPassCreateInfo renderPassInfo = {}; 497 | renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; 498 | renderPassInfo.attachmentCount = static_cast(attachments.size()); 499 | renderPassInfo.pAttachments = attachments.data(); 500 | renderPassInfo.subpassCount = 1; 501 | renderPassInfo.pSubpasses = &subpassDescription; 502 | renderPassInfo.dependencyCount = 1; 503 | renderPassInfo.pDependencies = &dependency; 504 | 505 | if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { 506 | throw std::runtime_error("failed to create render pass"); 507 | } 508 | } 509 | 510 | /* 511 | * Create a framebuffer for all image (views) of the swap chain. 512 | */ 513 | void GLFWVulkanWindow::createFramebuffers() { 514 | swapChainFramebuffers.resize(swapChainImageViews.size()); 515 | 516 | for (size_t i = 0; i < swapChainImageViews.size(); i++) { 517 | std::array attachments = { 518 | colorImageView, 519 | depthImageView, 520 | swapChainImageViews[i] 521 | }; 522 | 523 | VkFramebufferCreateInfo framebufferInfo = {}; 524 | framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; 525 | framebufferInfo.renderPass = renderPass; 526 | framebufferInfo.attachmentCount = static_cast(attachments.size());; 527 | framebufferInfo.pAttachments = attachments.data(); 528 | framebufferInfo.width = swapChainExtent.width; 529 | framebufferInfo.height = swapChainExtent.height; 530 | framebufferInfo.layers = 1; 531 | 532 | if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { 533 | throw std::runtime_error("failed to create framebuffer"); 534 | } 535 | } 536 | } 537 | 538 | void GLFWVulkanWindow::mainLoop() { 539 | glfwSetWindowUserPointer(window, this); 540 | 541 | glfwSetKeyCallback(window, GLFWVulkanWindow::keyCallback); 542 | 543 | while (!glfwWindowShouldClose(window)) { 544 | float currentTime = glfwGetTime(); 545 | deltaTime = currentTime - lastFrame; 546 | lastFrame = currentTime; 547 | 548 | glfwPollEvents(); 549 | 550 | float cameraSpeed = this->cameraSpeed * deltaTime; 551 | if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { 552 | renderer->cameraPos += cameraSpeed * renderer->cameraForward; 553 | } 554 | if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { 555 | renderer->cameraPos -= cameraSpeed * renderer->cameraForward; 556 | } 557 | if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { 558 | renderer->cameraPos += glm::normalize(glm::cross(renderer->cameraForward, renderer->cameraUp)) * cameraSpeed; 559 | } 560 | if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { 561 | renderer->cameraPos -= glm::normalize(glm::cross(renderer->cameraForward, renderer->cameraUp)) * cameraSpeed; 562 | } 563 | if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { 564 | renderer->cameraPos -= renderer->cameraUp * cameraSpeed; 565 | } 566 | if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) { 567 | renderer->cameraPos += renderer->cameraUp * cameraSpeed; 568 | } 569 | 570 | uint32_t imageIndex; 571 | vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); 572 | 573 | // Check if a previous frame is using this image (i.e. there is its fence to wait on) 574 | if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) { 575 | vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX); 576 | } 577 | // Mark the image as now being in use by this frame 578 | imagesInFlight[imageIndex] = inFlightFences[currentFrame]; 579 | 580 | VkCommandBufferBeginInfo beginInfo = {}; 581 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 582 | if (vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo) != VK_SUCCESS) { 583 | throw std::runtime_error("failed to begin recording command buffer!"); 584 | } 585 | renderer->startNextFrame(imageIndex, swapChainFramebuffers[imageIndex], commandBuffers[imageIndex], renderPass); 586 | if (vkEndCommandBuffer(commandBuffers[imageIndex]) != VK_SUCCESS) { 587 | throw std::runtime_error("failed to record command buffer!"); 588 | } 589 | 590 | VkSubmitInfo submitInfo = {}; 591 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 592 | 593 | // Wait on imageAvailableSemaphore in the stage that write to the color attachment 594 | VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; 595 | VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; 596 | submitInfo.waitSemaphoreCount = 1; 597 | submitInfo.pWaitSemaphores = waitSemaphores; 598 | submitInfo.pWaitDstStageMask = waitStages; 599 | submitInfo.commandBufferCount = 1; 600 | submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; // Submit command buffer that binds the current swap chain image 601 | 602 | VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; 603 | submitInfo.signalSemaphoreCount = 1; 604 | submitInfo.pSignalSemaphores = signalSemaphores; 605 | 606 | vkResetFences(device, 1, &inFlightFences[currentFrame]); 607 | if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { 608 | throw std::runtime_error("failed to submit draw command buffer!"); 609 | } 610 | 611 | VkPresentInfoKHR presentInfo = {}; 612 | presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 613 | presentInfo.waitSemaphoreCount = 1; 614 | presentInfo.pWaitSemaphores = signalSemaphores; // Semaphore to wait on before presenting 615 | 616 | VkSwapchainKHR swapChains[] = {swapChain}; 617 | presentInfo.swapchainCount = 1; 618 | presentInfo.pSwapchains = swapChains; // Swapchain to present images to 619 | 620 | presentInfo.pImageIndices = &imageIndex; 621 | 622 | vkQueuePresentKHR(presentQueue, &presentInfo); 623 | 624 | currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; 625 | } 626 | 627 | vkDeviceWaitIdle(device); 628 | } 629 | 630 | void GLFWVulkanWindow::createCommandPool() { 631 | VkCommandPoolCreateInfo poolInfo = {}; 632 | poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 633 | poolInfo.queueFamilyIndex = VulkanUtil::findQueueFamilies(physicalDevice, VK_QUEUE_GRAPHICS_BIT); 634 | poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; 635 | if (vkCreateCommandPool(device, &poolInfo, nullptr, &graphicsCommandPool) != VK_SUCCESS) { 636 | throw std::runtime_error("failed to create command pool!"); 637 | } 638 | } 639 | 640 | void GLFWVulkanWindow::createSyncObjects() { 641 | imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 642 | renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 643 | inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); 644 | imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE); 645 | 646 | VkSemaphoreCreateInfo semaphoreInfo{}; 647 | semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 648 | 649 | VkFenceCreateInfo fenceInfo{}; 650 | fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 651 | fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; 652 | 653 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 654 | if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || 655 | vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || 656 | vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { 657 | 658 | throw std::runtime_error("failed to create synchronization objects for a frame!"); 659 | } 660 | } 661 | } 662 | 663 | void GLFWVulkanWindow::createCommandBuffers() { 664 | commandBuffers.resize(swapChainFramebuffers.size()); // One command buffer per swapchain image 665 | 666 | VkCommandBufferAllocateInfo allocInfo{}; 667 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 668 | allocInfo.commandPool = graphicsCommandPool; 669 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 670 | allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); 671 | 672 | if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { 673 | throw std::runtime_error("failed to allocate command buffers!"); 674 | } 675 | } 676 | 677 | void GLFWVulkanWindow::createDepthResources() { 678 | VkFormat format = findDepthFormat(); 679 | VulkanUtil::createImage( 680 | physicalDevice, device, swapChainExtent.width, swapChainExtent.height, msaaSamples, format, 681 | VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 682 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory 683 | ); 684 | depthImageView = VulkanUtil::createImageView( 685 | device, depthImage, format, VK_IMAGE_ASPECT_DEPTH_BIT 686 | ); 687 | } 688 | 689 | void GLFWVulkanWindow::createColorResources() { 690 | VkFormat format = swapChainImageFormat; 691 | VulkanUtil::createImage( 692 | physicalDevice, device, swapChainExtent.width, swapChainExtent.height, msaaSamples, format, 693 | VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 694 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, colorImage, colorImageMemory 695 | ); 696 | colorImageView = VulkanUtil::createImageView( 697 | device, colorImage, format, VK_IMAGE_ASPECT_COLOR_BIT 698 | ); 699 | } 700 | 701 | VkFormat GLFWVulkanWindow::findSupportedFormat( 702 | const std::vector &candidates, VkImageTiling tiling, VkFormatFeatureFlags features 703 | ) { 704 | for (VkFormat format : candidates) { 705 | VkFormatProperties props; 706 | vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); 707 | 708 | if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) { 709 | return format; 710 | } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) { 711 | return format; 712 | } 713 | } 714 | 715 | throw std::runtime_error("failed to find supported format!"); 716 | } 717 | 718 | VkFormat GLFWVulkanWindow::findDepthFormat() { 719 | return findSupportedFormat( 720 | {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}, 721 | VK_IMAGE_TILING_OPTIMAL, 722 | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT 723 | ); 724 | } 725 | 726 | void GLFWVulkanWindow::cleanup() { 727 | vkDestroyImageView(device, colorImageView, nullptr); 728 | vkDestroyImage(device, colorImage, nullptr); 729 | vkFreeMemory(device, colorImageMemory, nullptr); 730 | 731 | vkDestroyImageView(device, depthImageView, nullptr); 732 | vkDestroyImage(device, depthImage, nullptr); 733 | vkFreeMemory(device, depthImageMemory, nullptr); 734 | 735 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 736 | vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); 737 | vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); 738 | vkDestroyFence(device, inFlightFences[i], nullptr); 739 | } 740 | 741 | // Destroy framebuffers 742 | for (auto framebuffer : swapChainFramebuffers) { 743 | vkDestroyFramebuffer(device, framebuffer, nullptr); 744 | } 745 | 746 | // Destroy graphics pipeline 747 | vkDestroyPipeline(device, pipeline, nullptr); 748 | 749 | // Destroy pipeline layout 750 | vkDestroyPipelineLayout(device, pipelineLayout, nullptr); 751 | 752 | // Destroy render pass 753 | vkDestroyRenderPass(device, renderPass, nullptr); 754 | 755 | // Destroy image views 756 | for (auto imageView : swapChainImageViews) { 757 | vkDestroyImageView(device, imageView, nullptr); 758 | } 759 | 760 | // Destroy swap chain (swap chain images don't have to be destroyed explicitly 761 | // since they are created by the swap chain) 762 | vkDestroySwapchainKHR(device, swapChain, nullptr); 763 | 764 | // Destroy virtual device 765 | vkDestroyDevice(device, nullptr); 766 | 767 | // Destroy window surface 768 | vkDestroySurfaceKHR(instance, surface, nullptr); 769 | 770 | // Destroy Vulkan instance 771 | vkDestroyInstance(instance, nullptr); 772 | 773 | vkDestroyCommandPool(device, graphicsCommandPool, nullptr); 774 | 775 | glfwDestroyWindow(window); 776 | 777 | glfwTerminate(); 778 | } 779 | 780 | void GLFWVulkanWindow::mouseCallback(GLFWwindow *window, double xpos, double ypos) { 781 | // https://learnopengl.com/ 782 | GLFWVulkanWindow *vulkanWindow = static_cast(glfwGetWindowUserPointer(window)); 783 | 784 | if (vulkanWindow->firstMouse) { 785 | vulkanWindow->lastX = xpos; 786 | vulkanWindow->lastY = ypos; 787 | vulkanWindow->firstMouse = false; 788 | } 789 | 790 | float xoffset = xpos - vulkanWindow->lastX; 791 | float yoffset = vulkanWindow->lastY - ypos; 792 | vulkanWindow->lastX = xpos; 793 | vulkanWindow->lastY = ypos; 794 | 795 | float sensitivity = 0.1f; 796 | xoffset *= sensitivity; 797 | yoffset *= sensitivity; 798 | 799 | vulkanWindow->yaw += xoffset; 800 | vulkanWindow->pitch += yoffset; 801 | 802 | if (vulkanWindow->pitch > 89.0f) { 803 | vulkanWindow->pitch = 89.0f; 804 | } else if (vulkanWindow->pitch < -89.0f) { 805 | vulkanWindow->pitch = -89.0f; 806 | } 807 | 808 | glm::vec3 direction; 809 | direction.x = cos(glm::radians(vulkanWindow->yaw)) * cos(glm::radians(vulkanWindow->pitch)); 810 | direction.y = sin(glm::radians(vulkanWindow->pitch)); 811 | direction.z = sin(glm::radians(vulkanWindow->yaw)) * cos(glm::radians(vulkanWindow->pitch)); 812 | vulkanWindow->renderer->cameraForward = glm::normalize(direction); 813 | } 814 | 815 | void GLFWVulkanWindow::keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { 816 | if (action == GLFW_RELEASE) { 817 | if (key == GLFW_KEY_V) { 818 | static_cast(glfwGetWindowUserPointer(window))->renderer->toggleShadedRendering(); 819 | } 820 | if (key == GLFW_KEY_G) { 821 | static_cast(glfwGetWindowUserPointer(window))->renderer->toggleViewCellRendering(); 822 | } 823 | if (key == GLFW_KEY_T) { 824 | static_cast(glfwGetWindowUserPointer(window))->renderer->toggleRayRendering(); 825 | } 826 | if (key == GLFW_KEY_R) { 827 | static_cast(glfwGetWindowUserPointer(window))->renderer->showMaxErrorDirection(); 828 | } 829 | 830 | if (key == GLFW_KEY_ESCAPE) { 831 | if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL) { 832 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 833 | glfwSetCursorPosCallback(window, GLFWVulkanWindow::mouseCallback); 834 | } else { 835 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 836 | glfwSetCursorPosCallback(window, nullptr); 837 | } 838 | } 839 | 840 | if (key == GLFW_KEY_C) { 841 | static_cast(glfwGetWindowUserPointer(window))->renderer->nextViewCell(); 842 | } 843 | if (key == GLFW_KEY_F) { 844 | static_cast(glfwGetWindowUserPointer(window))->renderer->nextCorner(); 845 | } 846 | if (key == GLFW_KEY_N) { 847 | static_cast(glfwGetWindowUserPointer(window))->renderer->alignCameraWithViewCellNormal(); 848 | } 849 | 850 | if (key == GLFW_KEY_Q) { 851 | static_cast(glfwGetWindowUserPointer(window))->renderer->printCamera(); 852 | } 853 | 854 | if (key == GLFW_KEY_1) { 855 | cameraSpeed *= 0.5f; 856 | } 857 | if (key == GLFW_KEY_2) { 858 | cameraSpeed *= 1.5f; 859 | } 860 | } 861 | } 862 | -------------------------------------------------------------------------------- /GLFWVulkanWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFWWINDOW_H 2 | #define GLFWWINDOW_H 3 | 4 | #define GLFW_INCLUDE_VULKAN 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef _WIN64 20 | #define NOMINMAX 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #endif 27 | 28 | class VulkanRenderer; 29 | 30 | const int WIDTH = 1024; 31 | const int HEIGHT = 1024; 32 | 33 | const std::vector validationLayers = { 34 | "VK_LAYER_KHRONOS_validation" 35 | }; 36 | 37 | const std::vector deviceExtensions = { 38 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, 39 | VK_NV_RAY_TRACING_EXTENSION_NAME, 40 | VK_KHR_MAINTENANCE3_EXTENSION_NAME, 41 | VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 42 | VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 43 | VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, 44 | VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, 45 | VK_KHR_MULTIVIEW_EXTENSION_NAME, 46 | #ifdef _WIN64 47 | VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, 48 | VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, 49 | #else 50 | VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, 51 | VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME 52 | #endif /* _WIN64 */ 53 | }; 54 | 55 | struct SwapChainSupportDetails { 56 | VkSurfaceCapabilitiesKHR capabilities; 57 | std::vector formats; 58 | std::vector presentModes; 59 | }; 60 | 61 | struct QueueFamilyIndices { 62 | std::optional graphicsFamily; 63 | std::optional presentFamily; 64 | std::optional computeFamily; 65 | std::optional transferFamily; 66 | 67 | bool isComplete() { 68 | return graphicsFamily.has_value() && presentFamily.has_value() && computeFamily.has_value() && transferFamily.has_value(); 69 | } 70 | }; 71 | 72 | const bool enableValidationLayers = false; 73 | 74 | class GLFWVulkanWindow { 75 | public: 76 | VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 77 | VkDevice device; 78 | std::array deviceUUID; 79 | uint32_t imageCount; 80 | VkRenderPass renderPass; 81 | VkQueue graphicsQueue; 82 | VkExtent2D swapChainImageSize; 83 | VkCommandPool graphicsCommandPool; 84 | VkSampleCountFlagBits msaaSamples; 85 | std::vector swapChainFramebuffers; 86 | VkExtent2D swapChainExtent; 87 | std::vector swapChainImages; 88 | std::vector swapChainImageViews; 89 | 90 | void initWindow(); 91 | void initVulkan(); 92 | void initRenderer(); 93 | void mainLoop(); 94 | VkFormat findDepthFormat(); 95 | 96 | private: 97 | const int MAX_FRAMES_IN_FLIGHT = 2; 98 | static float cameraSpeed; 99 | 100 | GLFWwindow* window; 101 | VkInstance instance; 102 | 103 | VkQueue presentQueue; 104 | VkSurfaceKHR surface; 105 | 106 | VkPipeline pipeline; 107 | VkPipelineLayout pipelineLayout; 108 | VulkanRenderer *renderer; 109 | 110 | VkImage depthImage; 111 | VkDeviceMemory depthImageMemory; 112 | VkImageView depthImageView; 113 | VkImage colorImage; // MSAA render target 114 | VkDeviceMemory colorImageMemory; 115 | VkImageView colorImageView; 116 | 117 | VkSwapchainKHR swapChain; 118 | VkFormat swapChainImageFormat; 119 | 120 | size_t currentFrame = 0; 121 | std::vector imageAvailableSemaphores; 122 | std::vector renderFinishedSemaphores; 123 | std::vector inFlightFences; 124 | std::vector imagesInFlight; 125 | std::vector commandBuffers; 126 | 127 | float deltaTime = 0.0f; 128 | float lastFrame = 0.0f; 129 | bool firstMouse = true; 130 | double lastX; 131 | double lastY; 132 | double yaw = 0.0f; 133 | double pitch = 0.0f; 134 | 135 | void createSurface(); 136 | void createInstance(); 137 | bool checkValidationLayerSupport(); 138 | void pickPhysicalDevice(); 139 | bool isDeviceSuitable(VkPhysicalDevice device); 140 | bool checkDeviceExtensionSupport(VkPhysicalDevice device); 141 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); 142 | void createLogicalDevice(); 143 | void createSwapChain(); 144 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); 145 | VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); 146 | VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); 147 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); 148 | void createImageViews(); 149 | void createRenderPass(); 150 | void createFramebuffers(); 151 | void createCommandPool(); 152 | void createSyncObjects(); 153 | void createCommandBuffers(); 154 | void createDepthResources(); 155 | void createColorResources(); 156 | VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); 157 | void cleanup(); 158 | static void mouseCallback(GLFWwindow* window, double xpos, double ypos); 159 | static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 160 | }; 161 | 162 | #endif // GLFWWINDOW_H 163 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Thomas Koch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guided Visibility Sampling++ 2 | **[Paper link](https://www.kocht.eu/GVS++_author_version.pdf)** 3 | 4 | This repository contains the reference implementation of our GVS++ visibility algorithm. The main parts of the algorithm are implemented in the shaders [raytrace.rgen](shaders/rt/raytrace.rgen) (random sampling), [raytrace_abs.rgen](shaders/rt/raytrace_abs.rgen) (adaptive border sampling), and [reverse_sampling_new.rgen](shaders/rt/reverse_sampling_new.rgen) (reverse sampling). 5 | 6 | ## Requirements 7 | The application was developed using the following libraries (note that the code may also compile wither newer library versions): 8 | * Vulkan SDK 1.2.135.0 9 | * GLFW 3.3.2 10 | * GLM 0.9.9.7 11 | * tinyobjloader 1.0.7 12 | 13 | The library paths are set in [CMakeLists.txt](CMakeLists.txt#L14). 14 | 15 | ## Usage 16 | ### Parameters 17 | The [settings file](settings/s0.txt) (`--- SETTINGS ---` section in the file) contains various parameters that control the behavior of the algorithm. 18 | Various parameters that control the number of samples that are used can be set. These parameters can be used to fine-tune the algorithm for specific scenes, if necessary. We found that a standard set of parameters performs well for all tested scenes and view cell placements. 19 | 20 | #### Random Sampling 21 | * `RANDOM_RAYS_PER_ITERATION`, the number of random rays/samples used during one iteration of random sampling, e.g. 10000000. 22 | 23 | #### Adaptive Border Sampling (ABS) 24 | * `ABS_DELTA`, the distance of the enlarged polygon to the original triangle, e.g. 0.001. 25 | * `ABS_NUM_SAMPLES_PER_EDGE`, the number of samples along each edge of the enlarged polygon, e.g. 40. 26 | 27 | #### Reverse Sampling (RS) 28 | * `REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE`, the number of samples along each edge of the view cell for the reverse sampling, e.g. 40. 29 | * `REVERSE_SAMPLING_HALTON_NUM_HALTON_SAMPLES`, the number of samples on the view cell for the reverse sampling, e.g. 40. 30 | 31 | #### Termination 32 | The termination of the PVS search can be controlled via two parameters: GVS++ terminates if less than `NEW_TRIANGLE_TERMINATION_THRESHOLD` new triangles have been found during the last `NEW_TRIANGLE_TERMINATION_THRESHOLD_COUNT` iterations. 33 | 34 | #### View Cell 35 | * `USE_3D_VIEW_CELL true/false` 36 | * `true`: Use 3D view cells. 37 | * `false`: Use 2D view cells. 38 | 39 | #### Visualization 40 | * `COMPUTE_ERROR true/false` 41 | * `true`: Calculate and print pixel error, and render the whole scene in red and the PVS in green. 42 | * `false`: Only render the PVS. 43 | * `FIRST_RAY_HIT_VISUALIZATION true/false` 44 | * `true`: Visualize rays that discovered new triangles (toggle with `T`). 45 | 46 | ### Scene Specification 47 | The scene and the file in which the resulting PVS is stored is also specified in the [settings file](settings/s0.txt) (`--- SCENE ---` section in the file). 48 | 49 | Example: 50 | ``` 51 | --- SCENE --- 52 | CALCPVS pvs.txt 53 | CITY 54 | SPECIFY_VIEW_CELL_CENTER false 55 | ``` 56 | * `CALCPVS` specifies that the PVS is calculated and stored in `pvs.txt`. Alternative are `CALCPVS_NOSTORE`, where the PVS is only calculated, and `LOADPVS`, where the PVS is loaded from the specified file. 57 | * `CITY` is the name of the scene for which GVS++ is executed. 58 | * `SPECIFY_VIEW_CELL_CENTER` specifies whether the position of the view cell (see below) defines the center or bottom left corner of the view cell. 59 | 60 | Scenes and view cells are specified in the [scenes file](scenes.txt). Example: 61 | ``` 62 | CITY models/city.obj 63 | 30 3 150 64 | 40 50 150 65 | 0 0 66 | -135 3 40 67 | 40 50 165 68 | 0 90 69 | ``` 70 | * `CITY` is the name of the scene followed by the location of the obj file. 71 | * The view cell specification contains the position (`30 3 150`), size (`40 50 150`) and rotation around the x- and y-axis (`0 0`). Multiple view cells can be specified as in the example above. In this example, 3D view cells are used, therefore `USE_3D_VIEW_CELL = true` has to be set. 72 | 73 | ### Key bindings 74 | #### View Cells 75 | * `C` switch to the next view cell. 76 | * `F` cycle through the corners of the current view cell. 77 | 78 | #### Visualization 79 | * `G` toggle rendering of a mesh that represents the current view cell. 80 | * `V` toggle model shading. 81 | * `T` toggle ray visualization (only if `FIRST_RAY_HIT_VISUALIZATION true` is set). 82 | 83 | #### Camera and Mouse 84 | * `Esc` release the mouse. 85 | * `Q` print camera position. 86 | * `1`/`2` decrease/increase camera movement speed. 87 | * `W`, `A`, `S`, `D`, `Space`, `Shift` move camera. 88 | 89 | ### Miscellaneous 90 | 91 | Vulkan validation layers can be enabled in [GLFWVulkanWindow.h](GLFWVulkanWindow.h#L72). 92 | -------------------------------------------------------------------------------- /Renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERER_H 2 | #define RENDERER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "GLFWVulkanWindow.h" 8 | #include "vulkanutil.h" 9 | #include "Vertex.h" 10 | #include "visibilitymanager.h" 11 | 12 | #include "viewcell.h" 13 | 14 | struct Settings { 15 | std::string modelName; 16 | int viewCellIndex; 17 | }; 18 | 19 | struct ViewCellGeometry { 20 | VkBuffer vertexBuffer; 21 | VkDeviceMemory vertexBufferMemory; 22 | 23 | ViewCellGeometry(VkBuffer vertexBuffer, VkDeviceMemory vertexBufferMemory) 24 | : vertexBuffer(vertexBuffer), vertexBufferMemory(vertexBufferMemory) 25 | { 26 | } 27 | }; 28 | 29 | class VulkanRenderer { 30 | public: 31 | glm::vec3 cameraPos; 32 | glm::vec3 cameraForward; 33 | glm::vec3 cameraRight; 34 | glm::vec3 cameraUp; 35 | VisibilityManager *visibilityManager; 36 | std::vector indices; 37 | 38 | VulkanRenderer(GLFWVulkanWindow *w); 39 | virtual ~VulkanRenderer(); 40 | 41 | void startNextFrame( 42 | uint32_t swapChainImageIndex, VkFramebuffer framebuffer, VkCommandBuffer commandBuffer, 43 | VkRenderPass renderPass 44 | ); 45 | void toggleShadedRendering(); 46 | void toggleViewCellRendering(); 47 | void toggleRayRendering(); 48 | void showMaxErrorDirection(); 49 | void nextCorner(); 50 | void nextViewCell(); 51 | void printCamera(); 52 | void alignCameraWithViewCellNormal(); 53 | void startVisibilityThread(); 54 | 55 | private: 56 | GLFWVulkanWindow *window; 57 | std::vector> settingsKeys; 58 | 59 | VkDescriptorPool descriptorPool; 60 | std::vector descriptorSets; 61 | VkDescriptorSetLayout descriptorSetLayout; 62 | std::vector computeDescriptorSet; 63 | VkDescriptorSetLayout computeDescriptorSetLayout; 64 | VkSampler framebufferSampler; 65 | 66 | VkPipeline pipeline; 67 | VkPipelineLayout pipelineLayout; 68 | VkPipeline rayVisualizationPipeline; 69 | VkPipelineLayout rayVisualizationPipelineLayout; 70 | VkPipeline computePipeline; 71 | VkPipelineLayout computePipelineLayout; 72 | std::vector computeCommandBuffers; 73 | VkFence fence; 74 | VkBuffer renderedBuffer; 75 | VkDeviceMemory renderedBufferMemory; 76 | 77 | std::unordered_map uniqueVertices; 78 | std::vector vertices; 79 | std::vector> pvsTriangleIDs; 80 | std::vector> pvsVertices; 81 | std::vector> pvsIndices; 82 | std::vector viewCellGeometry; 83 | VkBuffer vertexBuffer; 84 | VkDeviceMemory vertexBufferMemory; 85 | VkBuffer pvsVerticesBuffer; 86 | VkDeviceMemory pvsVerticesBufferMemory; 87 | VkBuffer indexBuffer; 88 | VkDeviceMemory indexBufferMemory; 89 | VkBuffer pvsIndicesBuffer; 90 | VkDeviceMemory pvsIndicesBufferMemory; 91 | VkBuffer rayVertexBuffer; 92 | VkDeviceMemory rayVertexBufferMemory; 93 | VkBuffer errorBuffer; 94 | VkDeviceMemory errorBufferMemory; 95 | 96 | VkImage errorDepthImage; 97 | VkDeviceMemory errorDepthImageMemory; 98 | VkImageView errorDepthImageView; 99 | VkImage errorColorImage; 100 | VkDeviceMemory errorColorImageMemory; 101 | VkImageView errorColorImageView; 102 | VkSampler errorColorImageSampler; 103 | VkFramebuffer errorFramebuffer; 104 | VkRenderPass errorRenderPass; 105 | 106 | std::vector uniformBuffers; 107 | std::vector uniformBuffersMemory; 108 | 109 | VkImage textureImage; 110 | VkDeviceMemory textureImageMemory; 111 | VkImageView textureImageView; 112 | VkSampler textureSampler; 113 | int currentViewCellCornerView = 0; 114 | int currentViewCellIndex = 0; 115 | int settingsIndex = 0; 116 | std::vector settingsFilePaths; 117 | 118 | glm::vec3 maxErrorCameraForward; 119 | glm::vec3 maxErrorCameraPos; 120 | 121 | void createGraphicsPipeline( 122 | VkPipeline &pipeline, VkPipelineLayout &pipelineLayout, std::string vertShaderPath, 123 | std::string fragShaderPath, 124 | VkPipelineLayoutCreateInfo pipelineLayoutInfo, 125 | VkPrimitiveTopology primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST 126 | ); 127 | void createVertexBuffer(std::vector &vertices, VkBuffer &vertexBuffer, VkDeviceMemory &vertexBufferMemory); 128 | void updateVertexBuffer(std::vector &vertices, VkBuffer &vertexBuffer, VkDeviceMemory &vertexBufferMemory); 129 | void createIndexBuffer(); 130 | void createErrorBuffer(); 131 | 132 | void createComputePipeline(); 133 | void createComputeDescriptorSets(); 134 | void createComputeDescriptorLayout(); 135 | void createComputeCommandBuffer(); 136 | 137 | void loadModel(const std::string& modelPath); 138 | void createTextureSampler(); 139 | 140 | void createDescriptorSetLayout(); 141 | void createDescriptorPool(); 142 | void createDescriptorSets(); 143 | void createUniformBuffers(); 144 | void updateUniformBuffer(uint32_t swapChainImageIndex); 145 | 146 | // Visibility 147 | bool shadedRendering = true; 148 | bool viewCellRendering = false; 149 | bool rayRendering = false; 150 | std::string pvsStorageFile; 151 | bool loadPVS; 152 | bool storePVS; 153 | std::vector viewCells; 154 | VkFramebuffer primitiveIDFramebuffer; 155 | float totalError; 156 | float maxError; 157 | 158 | std::vector loadSceneFile(const Settings& settings); 159 | Settings loadSettingsFile(); 160 | void writeShaderDefines(int settingsIndex); 161 | float calculateError(const ViewCell &viewCell, const std::vector &haltonPoints); 162 | void loadPVSFromFile(std::string file); 163 | 164 | std::vector viewCellSizes; 165 | std::vector viewCellMatrices; 166 | }; 167 | 168 | #endif // RENDERER_H 169 | -------------------------------------------------------------------------------- /Statistics.cpp: -------------------------------------------------------------------------------- 1 | #include "Statistics.h" 2 | 3 | #include "StatisticsEntry.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | const float Statistics::div = 1000000.0f; 10 | 11 | Statistics::Statistics(int samplesPerLine) 12 | : samplesPerLine(samplesPerLine) 13 | { 14 | entries.push_back(StatisticsEntry()); 15 | 16 | for (int i = 0; i < elapsedTimes.size(); i++) { 17 | elapsedTimes[i] = 0; 18 | } 19 | } 20 | 21 | void Statistics::update() { 22 | if (entries.back().totalRays() >= samplesPerLine) { 23 | addLine(); 24 | } 25 | } 26 | 27 | void Statistics::addLine() { 28 | if (entries.size() == 1) { 29 | entries[0].newTriangles = entries[0].pvsSize; 30 | } else { 31 | entries.back().newTriangles = entries.back().pvsSize - entries[entries.size() - 2].pvsSize; 32 | } 33 | 34 | entries.push_back(StatisticsEntry()); 35 | } 36 | 37 | void Statistics::print() { 38 | if (entries.size() == 1) { 39 | entries[0].newTriangles = entries[0].pvsSize; 40 | } else { 41 | entries.back().newTriangles = entries.back().pvsSize - entries[entries.size() - 2].pvsSize; 42 | if (entries.back().numShaderExecutions == 0) { 43 | entries.pop_back(); 44 | } 45 | } 46 | 47 | setlocale(LC_NUMERIC, ""); 48 | setlocale(LC_ALL, ""); 49 | printf("\n"); 50 | printf( 51 | "%16s|| %16s|| %16s%16s%16s%16s%16s%16s|| %16s%16s%16s%16s%16s|| %16s%16s\n", "Ray Shader Calls", "Raster Cubes", 52 | "Rand Rays", "ABS Rays", "ABS RS Rays", "ES Rays", "ES RS Rays", "Total Rays", "Rand Tri", 53 | "ABS Tri", "ABS RS Tri", "ES Tri", "ES RS Tri", "PVS Size", "New Tri" 54 | ); 55 | std::array sums; 56 | for (int i = 0; i < sums.size(); i++) { 57 | sums[i] = 0; 58 | } 59 | for (int i = 0; i < entries.size(); i++) { 60 | printf( 61 | "%16lu|| %16lu|| %16lu%16lu%16lu%16lu|| %16lu%16lu%16lu|| %16lu%16lu\n", 62 | entries[i].numShaderExecutions, 63 | entries[i].rasterHemicubes, 64 | entries[i].rnsRays, 65 | entries[i].absRays, 66 | entries[i].absRsRays, 67 | entries[i].totalRays(), 68 | entries[i].rnsTris, 69 | entries[i].absTris, 70 | entries[i].absRsTris, 71 | entries[i].pvsSize, 72 | entries[i].newTriangles 73 | ); 74 | sums[0] += entries[i].numShaderExecutions; 75 | sums[1] += entries[i].rasterHemicubes; 76 | sums[2] += entries[i].rnsRays; 77 | sums[3] += entries[i].absRays; 78 | sums[4] += entries[i].absRsRays; 79 | sums[7] += entries[i].totalRays(); 80 | sums[8] += entries[i].rnsTris; 81 | sums[9] += entries[i].absTris; 82 | sums[10] += entries[i].absRsTris; 83 | sums[13] = entries[i].pvsSize; 84 | } 85 | printf("%s", "============================================================================================================================================================================================================================================================\n"); 86 | float totalRaySum = sums[7]; 87 | 88 | for (int i = 0; i < sums.size(); i++) { 89 | if (i == 1 || i == 2 || i == 8 || i == 13) { 90 | printf("|| %16lu%", sums[i]); 91 | } else { 92 | printf("%16lu%", sums[i]); 93 | } 94 | } 95 | printf("\n"); 96 | printf( 97 | "%16s|| %16s|| %15.2f%%%15.2f%%%15.2f%%%15.2f%%%15.2f%%%16s||\n", "", 98 | "", (sums[2] / totalRaySum) * 100, (sums[3] / totalRaySum) * 100, 99 | (sums[4] / totalRaySum) * 100, (sums[5] / totalRaySum) * 100, (sums[6] / totalRaySum) * 100, 100 | "100%" 101 | ); 102 | printf("\n\n"); 103 | 104 | printElapsedTimes(elapsedTimes); 105 | printf("\n\n"); 106 | 107 | setlocale(LC_NUMERIC, "en_US"); 108 | } 109 | 110 | void Statistics::startOperation(OPERATION_TYPE operationType) { 111 | startTimes[operationType] = std::chrono::steady_clock::now(); 112 | } 113 | 114 | void Statistics::endOperation(OPERATION_TYPE operationType) { 115 | auto end = std::chrono::steady_clock::now(); 116 | elapsedTimes[operationType] += std::chrono::duration_cast(end - startTimes[operationType]).count(); 117 | } 118 | 119 | long long Statistics::getTotalTracedRays() { 120 | long long sum = 0; 121 | for (auto e : entries) { 122 | sum += e.totalRays(); 123 | } 124 | return sum; 125 | } 126 | 127 | long Statistics::getPVSSize() { 128 | long sum = 0; 129 | for (auto e : entries) { 130 | sum += e.pvsSize; 131 | } 132 | return sum; 133 | } 134 | 135 | void Statistics::reset() { 136 | entries.clear(); 137 | entries.push_back(StatisticsEntry()); 138 | for (int i = 0; i < elapsedTimes.size(); i++) { 139 | elapsedTimes[i] = 0; 140 | } 141 | } 142 | 143 | void Statistics::printElapsedTimes(const std::array &elapsedTimes) { 144 | auto rasterSum = (elapsedTimes[RASTER_VISIBILITY_RENDER] + elapsedTimes[RASTER_VISIBILITY_COMPUTE]) / div; 145 | auto randSum = (elapsedTimes[RANDOM_SAMPLING] + elapsedTimes[RANDOM_SAMPLING_INSERT]) / div; 146 | auto absSum = (elapsedTimes[ADAPTIVE_BORDER_SAMPLING] + elapsedTimes[ADAPTIVE_BORDER_SAMPLING_INSERT]) / div; 147 | 148 | printf("%24s%24s%24s\n", "Raster", "Raster", "Total (ms)"); 149 | printf("%24s%24s%24s\n", "(Hemicube Rendering)", "(Compute)", ""); 150 | printf( 151 | "%24.1f%24.1f%24.1f\n\n", elapsedTimes[RASTER_VISIBILITY_RENDER]/ div, 152 | elapsedTimes[RASTER_VISIBILITY_COMPUTE]/ div, rasterSum 153 | ); 154 | printf("%24s%24s%24s\n", "Random", "Random", "Total (ms)"); 155 | printf("%24s%24s%24s\n", "(Shader)", "(Sample Queue Insert)", ""); 156 | printf( 157 | "%24.1f%24.1f%24.1f\n\n", elapsedTimes[RANDOM_SAMPLING]/ div, 158 | elapsedTimes[RANDOM_SAMPLING_INSERT]/ div, randSum 159 | ); 160 | printf("%24s%24s%24s\n", "ABS", "ABS", "Total (ms)"); 161 | printf("%24s%24s%24s\n", "(Shader)", "(Sample Queue Insert)", ""); 162 | printf( 163 | "%24.1f%24.1f%24.1f\n\n", elapsedTimes[ADAPTIVE_BORDER_SAMPLING] / div, 164 | elapsedTimes[ADAPTIVE_BORDER_SAMPLING_INSERT] / div, absSum 165 | ); 166 | printf(" ===== ===== =====\n"); 167 | printf( 168 | "%24.1f%24.1f%24.1f\n", 169 | (elapsedTimes[RANDOM_SAMPLING] + elapsedTimes[ADAPTIVE_BORDER_SAMPLING]) / div, 170 | (elapsedTimes[RANDOM_SAMPLING_INSERT] + elapsedTimes[ADAPTIVE_BORDER_SAMPLING_INSERT]) / div, 171 | rasterSum + randSum + absSum 172 | ); 173 | 174 | printf("\n"); 175 | 176 | printf("Halton sequence generation time: %.2fms\n", elapsedTimes[HALTON_GENERATION] / div); 177 | printf("GPU hash set resize: %.2fms\n", elapsedTimes[GPU_HASH_SET_RESIZE] / div); 178 | printf("Total time: %.2fms\n", elapsedTimes[VISIBILITY_SAMPLING] / div); 179 | } 180 | 181 | void Statistics::printAverageStatistics(const std::vector &statistics) { 182 | printf("==================================== AVERAGE> ====================================\n"); 183 | std::array avgElapsedTimes; 184 | for (int i = 0; i < avgElapsedTimes.size(); i++) { 185 | avgElapsedTimes[i] = 0; 186 | } 187 | 188 | for (auto s : statistics) { 189 | for (int i = 0; i < s.elapsedTimes.size(); i++) { 190 | avgElapsedTimes[i] += s.elapsedTimes[i]; 191 | } 192 | } 193 | 194 | for (int i = 0; i < avgElapsedTimes.size(); i++) { 195 | avgElapsedTimes[i] /= float(statistics.size()); 196 | } 197 | 198 | Statistics::printElapsedTimes(avgElapsedTimes); 199 | 200 | int pvsSize = 0; 201 | for (auto s : statistics) { 202 | pvsSize += s.entries.back().pvsSize; 203 | } 204 | pvsSize /= statistics.size(); 205 | printf("PVS size: %i\n", pvsSize); 206 | printf("==================================== 5 | #include 6 | #include 7 | 8 | #include "StatisticsEntry.h" 9 | 10 | enum OPERATION_TYPE { 11 | RANDOM_SAMPLING = 0, 12 | RANDOM_SAMPLING_INSERT = 1, 13 | ADAPTIVE_BORDER_SAMPLING = 2, 14 | ADAPTIVE_BORDER_SAMPLING_INSERT = 3, 15 | HALTON_GENERATION = 6, 16 | VISIBILITY_SAMPLING = 7, 17 | GPU_HASH_SET_RESIZE = 8, 18 | RASTER_VISIBILITY_RENDER = 9, 19 | RASTER_VISIBILITY_COMPUTE = 10 20 | }; 21 | 22 | class Statistics { 23 | public: 24 | std::vector entries; 25 | std::array elapsedTimes; 26 | 27 | Statistics(int samplesPerLine); 28 | void update(); 29 | void addLine(); 30 | void print(); 31 | void startOperation(OPERATION_TYPE operationType); 32 | void endOperation(OPERATION_TYPE operationType); 33 | long long getTotalTracedRays(); 34 | long getPVSSize(); 35 | void reset(); 36 | static void printElapsedTimes(const std::array &elapsedTimes); 37 | static void printAverageStatistics(const std::vector &statistics); 38 | 39 | private: 40 | static const float div; 41 | int samplesPerLine; 42 | std::array startTimes; 43 | }; 44 | 45 | #endif // STATISTICS_H 46 | -------------------------------------------------------------------------------- /StatisticsEntry.cpp: -------------------------------------------------------------------------------- 1 | #include "StatisticsEntry.h" 2 | #include 3 | -------------------------------------------------------------------------------- /StatisticsEntry.h: -------------------------------------------------------------------------------- 1 | #ifndef STATISTICSENTRY_H 2 | #define STATISTICSENTRY_H 3 | 4 | class StatisticsEntry { 5 | public: 6 | long long rasterHemicubes = 0; 7 | 8 | long long rnsRays = 0; 9 | long long absRays = 0; 10 | long long absRsRays = 0; 11 | 12 | long long rnsTris = 0; 13 | long long absTris = 0; 14 | long long absRsTris = 0; 15 | 16 | long long newTriangles = 0; 17 | long long pvsSize = 0; 18 | 19 | long long numShaderExecutions = 0; 20 | 21 | long long totalRays() { 22 | return rnsRays + absRays + absRsRays; 23 | } 24 | 25 | long long totalFoundTriangles() { 26 | return rnsTris + absTris + absRsTris; 27 | } 28 | }; 29 | 30 | #endif // STATISTICSENTRY_H 31 | -------------------------------------------------------------------------------- /Vertex.cpp: -------------------------------------------------------------------------------- 1 | #include "Vertex.h" 2 | 3 | VkVertexInputBindingDescription Vertex::getBindingDescription() { 4 | VkVertexInputBindingDescription bindingDesc = {}; 5 | bindingDesc.binding = 0; 6 | bindingDesc.stride = sizeof(Vertex); 7 | bindingDesc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 8 | 9 | return bindingDesc; 10 | } 11 | 12 | std::array Vertex::getAttributeDescriptions() { 13 | std::array attributeDescriptions = {}; 14 | 15 | // One attribute description per attribute 16 | // Position 17 | attributeDescriptions[0].binding = 0; 18 | attributeDescriptions[0].location = 0; 19 | attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; 20 | attributeDescriptions[0].offset = offsetof(Vertex, pos); 21 | 22 | // Normal 23 | attributeDescriptions[1].binding = 0; 24 | attributeDescriptions[1].location = 1; 25 | attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; 26 | attributeDescriptions[1].offset = offsetof(Vertex, normal); 27 | 28 | // Color 29 | attributeDescriptions[2].binding = 0; 30 | attributeDescriptions[2].location = 2; 31 | attributeDescriptions[2].format = VK_FORMAT_R32G32B32_SFLOAT; 32 | attributeDescriptions[2].offset = offsetof(Vertex, color); 33 | 34 | // Texture coordinate 35 | attributeDescriptions[3].binding = 0; 36 | attributeDescriptions[3].location = 3; 37 | attributeDescriptions[3].format = VK_FORMAT_R32G32B32_SFLOAT; 38 | attributeDescriptions[3].offset = offsetof(Vertex, texCoord); 39 | 40 | return attributeDescriptions; 41 | } 42 | 43 | bool Vertex::operator==(const Vertex &other) const { 44 | return pos == other.pos && 45 | normal == other.normal && 46 | texCoord == other.texCoord; 47 | } 48 | -------------------------------------------------------------------------------- /Vertex.h: -------------------------------------------------------------------------------- 1 | #ifndef VERTEX_H 2 | #define VERTEX_H 3 | 4 | #include 5 | #include 6 | #define GLM_ENABLE_EXPERIMENTAL 7 | #include 8 | #include 9 | 10 | class Vertex { 11 | public: 12 | glm::vec3 pos; 13 | glm::vec3 normal; 14 | glm::vec3 color; 15 | glm::vec3 texCoord; 16 | 17 | static VkVertexInputBindingDescription getBindingDescription(); 18 | static std::array getAttributeDescriptions(); 19 | bool operator==(const Vertex &other) const; 20 | }; 21 | 22 | // See https://en.cppreference.com/w/cpp/utility/hash 23 | namespace std { 24 | template<> 25 | struct hash { 26 | size_t operator()(Vertex const &vertex) const { 27 | return ((hash()(vertex.pos) ^ 28 | (hash()(vertex.texCoord) << 1)) >> 1) ^ 29 | (hash()(vertex.normal) << 1); 30 | } 31 | }; 32 | } 33 | 34 | #endif // VERTEX_H 35 | -------------------------------------------------------------------------------- /defines.glsl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einthomas/GVSPP/d1116d5732a7ca9fc538450a7dff6ce94c533b33/defines.glsl -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "GLFWVulkanWindow.h" 4 | #include "Renderer.h" 5 | 6 | int main(int argc, char *argv[]) { 7 | GLFWVulkanWindow app; 8 | 9 | try { 10 | app.initWindow(); 11 | app.initVulkan(); 12 | app.initRenderer(); 13 | app.mainLoop(); 14 | } catch (const std::exception & e) { 15 | std::cerr << e.what() << std::endl; 16 | return EXIT_FAILURE; 17 | } 18 | 19 | return EXIT_SUCCESS; 20 | } 21 | -------------------------------------------------------------------------------- /pvs.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCURRENTUNORDEREDSET_H 2 | #define CONCURRENTUNORDEREDSET_H 3 | 4 | template 5 | class PVS { 6 | public: 7 | std::vector pvsVector; 8 | 9 | PVS() { 10 | } 11 | }; 12 | 13 | #endif // CONCURRENTUNORDEREDSET_H 14 | -------------------------------------------------------------------------------- /sample.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLE_H 2 | #define SAMPLE_H 3 | 4 | #define GLM_ENABLE_EXPERIMENTAL 5 | #include 6 | 7 | #include 8 | 9 | struct Sample { 10 | alignas(4) int triangleID; 11 | alignas(16) glm::vec3 rayOrigin; // Origin of the ray that hit the triangle 12 | alignas(16) glm::vec3 hitPos; // Position where the triangle was hit 13 | alignas(16) glm::vec3 pos; // Position of the sample itself 14 | 15 | friend std::ostream &operator<<(std::ostream &stream, const Sample &sample) { 16 | return stream 17 | << "triangleID: " << sample.triangleID 18 | << " rayOrigin: (" << sample.rayOrigin.x << ", " << sample.rayOrigin.y << ", " << sample.rayOrigin.z << ") " 19 | << "hitPos: (" << sample.hitPos.x << ", " << sample.hitPos.y << ", " << sample.hitPos.z << ") " 20 | << "pos: (" << sample.pos.x << ", " << sample.pos.y << ", " << sample.pos.z << ") "; 21 | } 22 | }; 23 | 24 | #endif // SAMPLE_H 25 | -------------------------------------------------------------------------------- /scenes.txt: -------------------------------------------------------------------------------- 1 | CITY models/city.obj 2 | 30 3 150 3 | 40 50 150 4 | 0 0 0 5 | 30 3 0 6 | 40 50 150 7 | 0 0 0 8 | -135 3 40 9 | 40 50 165 10 | 0 90 0 11 | -300 3 40 12 | 40 50 165 13 | 0 90 0 14 | -120 3 -150 15 | 40 50 150 16 | 0 0 0 17 | -120 3 -300 18 | 40 50 150 19 | 0 0 0 20 | -------------------------------------------------------------------------------- /settings/s0.txt: -------------------------------------------------------------------------------- 1 | --- SETTINGS --- 2 | /* 3 | The shader compile script is executed during runtime since a number of settings are written to 4 | defines.glsl 5 | */ 6 | SHADER_COMPILE_SCRIPT shaders/compileShaders.sh shaders 7 | 8 | /* 9 | Ray tracing and sample count parameters 10 | */ 11 | RANDOM_RAYS_PER_ITERATION 10000000 12 | ABS_DELTA 0.001 13 | ABS_NUM_SAMPLES_PER_EDGE 40 14 | REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE 20 15 | REVERSE_SAMPLING_HALTON_NUM_HALTON_SAMPLES 20 16 | 17 | /* 18 | GVS++ terminates if less than NEW_TRIANGLE_TERMINATION_THRESHOLD new triangles have been found during 19 | the last NEW_TRIANGLE_TERMINATION_THRESHOLD_COUNT iterations. 20 | */ 21 | NEW_TRIANGLE_TERMINATION_THRESHOLD 10 22 | NEW_TRIANGLE_TERMINATION_THRESHOLD_COUNT 3 23 | 24 | USE_3D_VIEW_CELL true 25 | 26 | /* 27 | Visualization options 28 | COMPUTE_ERROR true -> Renders the whole scene (in red) and the PVS (in green) 29 | COMPUTE_ERROR false -> Only renders the PVS (in green) 30 | FIRST_RAY_HIT_VISUALIZATION true -> Visualize rays that discovered new triangles 31 | */ 32 | COMPUTE_ERROR false 33 | FIRST_RAY_HIT_VISUALIZATION false 34 | 35 | /* 36 | SET_TYPE 0 Set implemented as an array that's as big as there are triangles 37 | in the scene. 38 | SET_TYPE 1 EXPERIMENTAL! 39 | Hash set. Needs less space than SET_TYPE 1. Recommended for scenes 40 | with >= 10,000,000 triangles. If the hash set runs out of space, 41 | it is resized. 42 | */ 43 | SET_TYPE 0 44 | MAX_BULK_INSERT_BUFFER_SIZE 10000000 45 | /* 46 | If this is set to 0, the initial hash set size is equal to 2^(ceil(log2(number_of_triangles_in_scene / 2))) 47 | */ 48 | INITIAL_HASH_SET_SIZE 0 49 | 50 | --- SCENE --- 51 | CALCPVS pvs.txt 52 | CITY 53 | SPECIFY_VIEW_CELL_CENTER false 54 | -------------------------------------------------------------------------------- /shaders/compileShaders.bat: -------------------------------------------------------------------------------- 1 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/shader.frag -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/shader.frag.spv 2 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/shader.vert -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/shader.vert.spv 3 | 4 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rayVisualizationShader.frag -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rayVisualizationShader.frag.spv 5 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rayVisualizationShader.vert -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rayVisualizationShader.vert.spv 6 | 7 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rgen -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rgen.spv 8 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rmiss -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rmiss.spv 9 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rchit -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace.rchit.spv 10 | 11 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rgen -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rgen.spv 12 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rmiss -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rmiss.spv 13 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rchit -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_abs.rchit.spv 14 | 15 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_subdiv.rgen -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rt/raytrace_subdiv.rgen.spv 16 | 17 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/gpuHashSetBulkInsert.comp -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/gpuHashSetBulkInsert.comp.spv 18 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/halton.comp -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/halton.comp.spv 19 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/errorCalculation.comp -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/errorCalculation.comp.spv 20 | E:/VulkanSDK/1.2.135.0/Bin/glslc.exe -O C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rasterVisibility.comp -o C:/Users/thoma/Documents/dev/Master/Vulkan_RTX_Master_Qt_CMake/shaders/rasterVisibility.comp.spv 21 | -------------------------------------------------------------------------------- /shaders/compileShaders.sh: -------------------------------------------------------------------------------- 1 | cd $1 2 | for file in `find -type f -regex ".*\.\(frag\|vert\|rchit\|rmiss\|rgen\|comp\)"` 3 | do 4 | #echo "compiling $file" 5 | $VULKAN_SDK/bin/glslc --target-spv=spv1.5 "$file" -o "$file".spv 6 | done 7 | -------------------------------------------------------------------------------- /shaders/errorCalculation.comp: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | #include "rt/defines.glsl" 4 | 5 | layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 6 | 7 | layout(binding = 0, set = 0) uniform sampler2D framebuffer; 8 | layout(binding = 1, set = 0) writeonly buffer errorBuffer { 9 | uint error; 10 | }; 11 | 12 | void main() { 13 | vec3 color = texelFetch(framebuffer, ivec2(gl_GlobalInvocationID.xy), 0).xyz; 14 | if (color == vec3(1.0f, 0.0f, 0.0f)) { 15 | atomicAdd(error, 1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /shaders/gpuHashSetBulkInsert.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0, set = 0) buffer pvsValues { 4 | int values[]; 5 | }; 6 | layout(binding = 1, set = 0) buffer setBuffer { 7 | int set[]; 8 | }; 9 | layout(binding = 2, set = 0) uniform pvsBufferCapacity { 10 | int pvsCapacity; 11 | }; 12 | 13 | #define BULK_INSERT 14 | #include "rt/defines.glsl" 15 | #include "rt/gpuHashSet.glsl" 16 | 17 | void main() { 18 | uint index = gl_GlobalInvocationID.x; 19 | insert(values[index]); 20 | } 21 | -------------------------------------------------------------------------------- /shaders/halton.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 256, local_size_y = 4, local_size_z = 1) in; 4 | 5 | layout(binding = 0, set = 0) writeonly buffer haltonBuffer { 6 | float halton[]; 7 | }; 8 | 9 | layout(push_constant) uniform PushConstants { 10 | float rand; 11 | int size; 12 | } pushConstants; 13 | 14 | void main() { 15 | /* 16 | Randomized Halton, based on Train, Kenneth E. Discrete choice methods with simulation. Cambridge university press, 2009. 17 | */ 18 | const int offset = int(gl_GlobalInvocationID.x); 19 | if (offset < pushConstants.size) { 20 | const int i = int(gl_LocalInvocationID.y); 21 | const int bases[4] = { 2, 3, 5, 7 }; 22 | 23 | float f = 1.0f; 24 | float r = 0.0f; 25 | int k = offset + 1; 26 | while (k > 0) { 27 | f /= bases[i]; 28 | r = r + f * (k % bases[i]); 29 | k = int(k / bases[i]); 30 | } 31 | halton[offset * 4 + i] = mod(r + pushConstants.rand, 1.0f); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /shaders/rasterVisibility.comp: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | #include "rt/defines.glsl" 4 | 5 | struct Sample { 6 | int triangleID; 7 | vec3 rayOrigin; 8 | vec3 rayHitPos; 9 | vec3 pos; 10 | }; 11 | 12 | layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 13 | 14 | layout(binding = 0, set = 0) uniform isampler2DArray ids; 15 | layout(binding = 1, set = 0) buffer writeonly sampleOutputBuffer { 16 | Sample sampleOutput[]; 17 | }; 18 | layout(binding = 2, set = 0) uniform cubePosUniform { 19 | vec3 cubePos; 20 | }; 21 | layout(binding = 3, set = 0) buffer writeonly numSamplesBuffer { 22 | int numSamples; 23 | }; 24 | layout(binding = 4, set = 0) buffer setBuffer { 25 | int set[]; 26 | }; 27 | layout(binding = 5, set = 0) buffer triangleCounterBuffer { 28 | uint triangleCounter; 29 | uint rsTriangleCounter; 30 | uint rayCounter; 31 | uint rsRayCounter; 32 | uint pvsSize; 33 | }; 34 | 35 | #include "rt/gpuHashSet.glsl" 36 | 37 | void main() { 38 | // Texture fetches are batched, as suggested in https://developer.nvidia.com/blog/the-peak-performance-analysis-method-for-optimizing-any-gpu-workload/#appendix2 39 | const int triangleID0 = int(texelFetch(ids, ivec3(gl_GlobalInvocationID.xy, 0), 0).x); 40 | const int triangleID1 = int(texelFetch(ids, ivec3(gl_GlobalInvocationID.xy, 1), 0).x); 41 | const int triangleID2 = int(texelFetch(ids, ivec3(gl_GlobalInvocationID.xy, 2), 0).x); 42 | const int triangleID3 = int(texelFetch(ids, ivec3(gl_GlobalInvocationID.xy, 3), 0).x); 43 | const int triangleID4 = int(texelFetch(ids, ivec3(gl_GlobalInvocationID.xy, 4), 0).x); 44 | 45 | if (insert(triangleID0)) sampleOutput[atomicAdd(numSamples, 1)] = Sample(triangleID0, cubePos, vec3(0.0f), vec3(0.0f)); 46 | if (insert(triangleID1)) sampleOutput[atomicAdd(numSamples, 1)] = Sample(triangleID1, cubePos, vec3(0.0f), vec3(0.0f)); 47 | if (insert(triangleID2)) sampleOutput[atomicAdd(numSamples, 1)] = Sample(triangleID2, cubePos, vec3(0.0f), vec3(0.0f)); 48 | if (insert(triangleID3)) sampleOutput[atomicAdd(numSamples, 1)] = Sample(triangleID3, cubePos, vec3(0.0f), vec3(0.0f)); 49 | if (insert(triangleID4)) sampleOutput[atomicAdd(numSamples, 1)] = Sample(triangleID4, cubePos, vec3(0.0f), vec3(0.0f)); 50 | } 51 | -------------------------------------------------------------------------------- /shaders/rayVisualizationShader.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | layout(location = 0) out vec4 color; 4 | 5 | layout(location = 0) in vec3 fragColor; 6 | 7 | void main() { 8 | color = vec4(fragColor, 1.0f); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/rayVisualizationShader.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | layout(binding = 0, set = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 projection; 7 | } ubo; 8 | 9 | layout(location = 0) in vec3 inPosition; 10 | layout(location = 2) in vec3 inColor; 11 | 12 | layout(location = 0) flat out vec3 fragColor; 13 | 14 | void main() { 15 | gl_Position = ubo.projection * ubo.view * vec4(inPosition, 1.0); 16 | fragColor = inColor; 17 | } 18 | -------------------------------------------------------------------------------- /shaders/rt/defines.glsl: -------------------------------------------------------------------------------- 1 | const float ABS_DELTA = 0.001; 2 | const int ABS_NUM_SAMPLES_PER_EDGE = 40; 3 | const int REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE = 20; 4 | const int REVERSE_SAMPLING_HALTON_NUM_HALTON_SAMPLES = 20; 5 | #define SET_TYPE 0 6 | #define USE_3D_VIEW_CELL 7 | -------------------------------------------------------------------------------- /shaders/rt/gpuHashSet.glsl: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/nosferalatu/SimpleGPUHashTable 2 | 3 | const int HASH_SET_EMPTY_ENTRY = -1; 4 | 5 | // 32 bit Murmur3 hash 6 | int hash(int k, int capacity) { 7 | k ^= k >> 16; 8 | k *= 0x85ebca6b; 9 | k ^= k >> 13; 10 | k *= 0xc2b2ae35; 11 | k ^= k >> 16; 12 | return k & (capacity - 1); 13 | } 14 | 15 | bool insert(const int key) { 16 | #if SET_TYPE == 0 17 | if (key < 0) { 18 | return false; 19 | } 20 | 21 | int prev = atomicCompSwap(set[key], HASH_SET_EMPTY_ENTRY, key); 22 | if (prev == HASH_SET_EMPTY_ENTRY) { 23 | #ifndef BULK_INSERT 24 | atomicAdd(pvsSize, 1); 25 | #endif 26 | return true; 27 | } else { 28 | return false; 29 | } 30 | #elif SET_TYPE == 1 31 | if (key < 0) { 32 | return false; 33 | } 34 | 35 | int slot = hash(key, pvsCapacity); 36 | while (true) { 37 | int prev = atomicCompSwap(set[slot], HASH_SET_EMPTY_ENTRY, key); 38 | if (prev == HASH_SET_EMPTY_ENTRY) { 39 | #ifndef BULK_INSERT 40 | atomicAdd(pvsSize, 1); 41 | #endif 42 | return true; 43 | } else if (prev == key) { 44 | return false; 45 | } 46 | 47 | slot = (slot + 1) & (pvsCapacity - 1); 48 | } 49 | #endif 50 | } 51 | -------------------------------------------------------------------------------- /shaders/rt/raytrace.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | struct Vertex { 6 | vec3 pos; 7 | vec3 worldPos; 8 | vec3 normal; 9 | vec3 color; 10 | vec2 uv; 11 | }; 12 | 13 | layout(location = 0) rayPayloadInNV vec4 hitInfo; 14 | 15 | layout(binding = 1, set = 0) uniform cameraProperties { 16 | mat4 model; // This is called camera properties, however this model matrix doesn't have anything to do with the camera, this is model specific. Instead, use a buffer of model matrices and access the buffer via gl_InstanceID (in case there are different models/instances) (see nvidia Vulkan tutorial) 17 | mat4 view; 18 | mat4 projection; 19 | } camera; 20 | layout(binding = 2, set = 0) buffer Vertices { 21 | vec4 v[]; 22 | } vertices; 23 | layout(binding = 3, set = 0) buffer Indices { 24 | uint i[]; 25 | } indices; 26 | 27 | hitAttributeNV vec3 attribs; 28 | 29 | #include "util.glsl" 30 | 31 | void main() { 32 | const ivec3 index = ivec3( 33 | indices.i[3 * gl_PrimitiveID + 0], 34 | indices.i[3 * gl_PrimitiveID + 1], 35 | indices.i[3 * gl_PrimitiveID + 2] 36 | ); 37 | const vec3 v0Pos = getVertexPos(index.x); 38 | const vec3 v1Pos = getVertexPos(index.y); 39 | const vec3 v2Pos = getVertexPos(index.z); 40 | 41 | const vec3 barycentricCoords = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 42 | const vec3 pos = v0Pos * barycentricCoords.x + v1Pos * barycentricCoords.y + v2Pos * barycentricCoords.z; 43 | const vec3 worldPos = vec3(camera.model * vec4(pos, 1.0)); 44 | 45 | hitInfo = vec4(worldPos, gl_PrimitiveID); 46 | } 47 | -------------------------------------------------------------------------------- /shaders/rt/raytrace.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | 4 | #include "defines.glsl" 5 | 6 | struct Sample { 7 | int triangleID; 8 | vec3 rayOrigin; 9 | vec3 rayHitPos; 10 | vec3 pos; 11 | }; 12 | 13 | layout(binding = 0, set = 0) uniform accelerationStructureNV topLevelAS; 14 | layout(binding = 1, set = 0) uniform cameraProperties { 15 | mat4 model; 16 | mat4 view; 17 | mat4 projection; 18 | } camera; 19 | layout(binding = 4, set = 0) readonly buffer haltonPoints { 20 | float points[]; 21 | }; 22 | layout(binding = 5, set = 0) uniform viewCellProperties { 23 | vec3 pos; 24 | vec3 size; 25 | vec3 right; 26 | vec3 up; 27 | vec3 normal; 28 | } viewCell; 29 | layout(binding = 6, set = 0) writeonly buffer randomSamplingOutputBuffer { 30 | Sample randomSamplingOutput[]; 31 | }; 32 | layout(binding = 8, set = 0) buffer triangleCounterBuffer { 33 | uint triangleCounter; 34 | uint rsTriangleCounter; 35 | uint rayCounter; 36 | uint rsRayCounter; 37 | uint pvsSize; 38 | }; 39 | 40 | layout(binding = 9, set = 0) buffer setBuffer { 41 | int set[]; 42 | }; 43 | 44 | layout(binding = 10, set = 0) uniform pvsBufferCapacity { 45 | int pvsCapacity; 46 | }; 47 | 48 | layout(location = 0) rayPayloadNV vec4 hitInfo; 49 | 50 | #include "gpuHashSet.glsl" 51 | 52 | void main() { 53 | // Calculate 1D launch index 54 | uint launchIndex = gl_LaunchIDNV.x; 55 | 56 | vec3 viewCellSize = viewCell.size * 2.0f; 57 | vec3 viewCellRight = viewCell.right; 58 | vec3 viewCellUp = viewCell.up; 59 | vec3 viewCellNormal = viewCell.normal; 60 | vec3 viewCellPos = viewCell.pos; 61 | 62 | int sampleDir = 1; 63 | #ifdef USE_3D_VIEW_CELL 64 | const float temp = launchIndex / float(gl_LaunchSizeNV.x / 6.0f); 65 | const int faceIndex = int(temp); 66 | sampleDir = temp > 0.5 ? 1 : -1; 67 | 68 | const vec3 faceRights[6] = { viewCellRight, -viewCellNormal, -viewCellRight, viewCellNormal, viewCellRight, viewCellRight }; 69 | const vec3 faceUps[6] = { viewCellUp, viewCellUp, viewCellUp, viewCellUp, -viewCellNormal, viewCellNormal }; 70 | const vec3 faceNormals[6] = { viewCellNormal, viewCellRight, -viewCellNormal, -viewCellRight, viewCellUp, -viewCellUp }; 71 | const vec3 faceSizes[6] = { 72 | viewCellSize.xyz, 73 | viewCellSize.zyx, 74 | viewCellSize.xyz, 75 | viewCellSize.zyx, 76 | viewCellSize.xzy, 77 | viewCellSize.xzy 78 | }; 79 | 80 | viewCellNormal = faceNormals[faceIndex]; 81 | viewCellRight = faceRights[faceIndex]; 82 | viewCellUp = faceUps[faceIndex]; 83 | viewCellSize = faceSizes[faceIndex]; 84 | viewCellPos = viewCellPos + viewCellNormal * viewCellSize.z * 0.5; 85 | #endif 86 | 87 | // Map Halton point to point on view cell 88 | vec2 haltonPoint = vec2(points[launchIndex * 4] - 0.5f, points[launchIndex * 4 + 1] - 0.5f); 89 | vec3 rayOrigin = viewCellPos + haltonPoint.x * viewCellSize.x * viewCellRight + haltonPoint.y * viewCellSize.y * viewCellUp; 90 | 91 | // Use a different Halton point to calculate the ray direction 92 | haltonPoint = vec2(points[launchIndex * 4 + 2], points[launchIndex * 4 + 3]); 93 | 94 | // Map Halton point to point on hemisphere 95 | float phi = 2.0 * 3.1415926 * haltonPoint.x; 96 | float r = sqrt(max(1.0 - haltonPoint.y * haltonPoint.y, 0.0)); 97 | vec3 rayDir = vec3(r * cos(phi), r * sin(phi), haltonPoint.y); 98 | 99 | // Transform direction to view cell coordinate system 100 | rayDir = normalize(rayDir.x * viewCellRight + rayDir.y * viewCellUp + (rayDir.z * viewCellNormal) * sampleDir); 101 | 102 | // Trace ray 103 | uint rayFlags = gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV; 104 | uint cullMask = 0xff; 105 | float tmin = 0.001; 106 | float tmax = 100000.0; 107 | traceNV( 108 | topLevelAS, // acceleration structure 109 | rayFlags, // rayFlags 110 | cullMask, // cullMask 111 | 0, // sbtRecordOffset 112 | 0, // sbtRecordStride 113 | 0, // missIndex 114 | rayOrigin.xyz, // ray origin 115 | tmin, // ray min range 116 | rayDir.xyz, // ray direction 117 | tmax, // ray max range 118 | 0 // payload (location = 0) 119 | ); 120 | 121 | // Get the intersected triangle ID 122 | int triangleID = int(hitInfo.w); 123 | 124 | // If a triangle has been intersected and its ID is not yet part of the PVS, insert it into the PVS 125 | // and into the output buffer which is later used to populate the sample buffer on the CPU side 126 | if (triangleID != -1 && insert(triangleID)) { 127 | uint index = atomicAdd(triangleCounter, 1); 128 | randomSamplingOutput[index] = Sample(triangleID, rayOrigin, hitInfo.xyz, hitInfo.xyz); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /shaders/rt/raytrace.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | layout(location = 0) rayPayloadInNV vec4 hitInfo; 6 | 7 | void main() { 8 | hitInfo = vec4(-1.0); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/rt/raytrace_abs.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | struct Vertex { 6 | vec3 pos; 7 | vec3 worldPos; 8 | vec3 normal; 9 | vec3 color; 10 | vec2 uv; 11 | }; 12 | 13 | layout(location = 0) rayPayloadInNV vec4 hitInfo; 14 | 15 | layout(binding = 1, set = 0) uniform cameraProperties { 16 | mat4 model; // This is called camera properties, however this model matrix doesn't have anything to do with the camera, this is model specific. Instead, use a buffer of model matrices and access the buffer via gl_InstanceID (in case there are different models/instances) (see nvidia Vulkan tutorial) 17 | mat4 view; 18 | mat4 projection; 19 | } camera; 20 | layout(binding = 2, set = 0) readonly buffer Vertices { 21 | vec4 v[]; 22 | } vertices; 23 | layout(binding = 3, set = 0) readonly buffer Indices { 24 | uint i[]; 25 | } indices; 26 | 27 | hitAttributeNV vec3 attribs; 28 | 29 | #include "util.glsl" 30 | 31 | void main() { 32 | ivec3 index = ivec3( 33 | indices.i[3 * gl_PrimitiveID + 0], 34 | indices.i[3 * gl_PrimitiveID + 1], 35 | indices.i[3 * gl_PrimitiveID + 2] 36 | ); 37 | 38 | const vec3 v0Pos = getVertexPos(index.x); 39 | const vec3 v1Pos = getVertexPos(index.y); 40 | const vec3 v2Pos = getVertexPos(index.z); 41 | 42 | vec3 barycentricCoords = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 43 | vec3 pos = v0Pos * barycentricCoords.x + v1Pos * barycentricCoords.y + v2Pos * barycentricCoords.z; 44 | vec3 worldPos = vec3(camera.model * vec4(pos, 1.0)); 45 | 46 | hitInfo = vec4(worldPos, gl_PrimitiveID); 47 | } 48 | -------------------------------------------------------------------------------- /shaders/rt/raytrace_abs.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | 4 | #include "defines.glsl" 5 | 6 | struct Vertex { 7 | vec3 pos; 8 | vec3 worldPos; 9 | vec3 normal; 10 | vec3 color; 11 | vec2 uv; 12 | }; 13 | struct Sample { 14 | int triangleID; 15 | vec3 rayOrigin; 16 | vec3 rayHitPos; 17 | vec3 pos; 18 | }; 19 | 20 | layout(binding = 0, set = 0) uniform accelerationStructureNV topLevelAS; 21 | layout(binding = 1, set = 0) uniform cameraProperties { 22 | mat4 model; 23 | mat4 view; 24 | mat4 projection; 25 | } camera; 26 | layout(binding = 2, set = 0) readonly buffer Vertices { 27 | vec4 v[]; 28 | } vertices; 29 | layout(binding = 3, set = 0) readonly buffer Indices { 30 | uint i[]; 31 | } indices; 32 | layout(binding = 4, set = 0) readonly buffer haltonPoints { 33 | float points[]; 34 | }; 35 | layout(binding = 5, set = 0) uniform viewCellProperties { 36 | vec3 pos; 37 | vec3 size; 38 | vec3 right; 39 | vec3 up; 40 | vec3 normal; 41 | } viewCell; 42 | layout(binding = 8, set = 0) buffer triangleCounterBuffer { 43 | uint triangleCounter; 44 | uint rsTriangleCounter; 45 | uint rayCounter; 46 | uint rsRayCounter; 47 | uint pvsSize; 48 | }; 49 | 50 | layout(binding = 9, set = 0) buffer setBuffer { 51 | int set[]; 52 | }; 53 | layout(binding = 10, set = 0) uniform pvsBufferCapacity { 54 | int pvsCapacity; 55 | }; 56 | 57 | layout(binding = 6, set = 0) buffer randomSamplingOutputBuffer { 58 | Sample randomSamplingOutput[]; 59 | }; 60 | 61 | layout(binding = 0, set = 1) readonly buffer absWorkingBuffer { 62 | Sample absWorking[]; 63 | }; 64 | 65 | layout(location = 0) rayPayloadNV vec4 hitInfo; 66 | 67 | #include "util.glsl" 68 | #include "gpuHashSet.glsl" 69 | 70 | int storeReverseSample(uint launchIndex, Sample s, bool targetSampleReached) { 71 | int index = -1; 72 | if (insert(s.triangleID)) { 73 | atomicAdd(rsTriangleCounter, 1); 74 | randomSamplingOutput[int(atomicAdd(triangleCounter, 1))] = s; 75 | } 76 | 77 | return index; 78 | } 79 | 80 | #include "reverse_sampling_new.rgen" 81 | 82 | void main() { 83 | const uint launchIndex = gl_LaunchIDNV.x; 84 | const uint currentTriangleIndex = uint(floor(launchIndex / float((ABS_NUM_SAMPLES_PER_EDGE + 3) * 3))); // for each triangle ABS_NUM_SAMPLES_PER_EDGE * 3 rays are shot i.e. for each triangle the shader is invoked ABS_NUM_SAMPLES_PER_EDGE * 3 times 85 | 86 | const uint currentVertexIndex = uint(mod(floor(launchIndex / float((ABS_NUM_SAMPLES_PER_EDGE + 3))), 3)); 87 | const uint nextVertexIndex = uint(mod(currentVertexIndex + 1, 3)); 88 | const uint lastVertexIndex = uint(mod(currentVertexIndex + 2, 3)); 89 | 90 | const uint currentVertexSamplePosIndex = uint(mod(launchIndex, ABS_NUM_SAMPLES_PER_EDGE)); 91 | const uint nextVertexSamplePosIndex = uint(mod(currentVertexSamplePosIndex + 1, ABS_NUM_SAMPLES_PER_EDGE)); 92 | 93 | const Sample s = absWorking[currentTriangleIndex]; 94 | 95 | const vec3 rayOrigin = s.rayOrigin; 96 | 97 | const vec3 currentWP = getVertexWorldPos(indices.i[3 * s.triangleID + currentVertexIndex]); 98 | const vec3 nextWP = getVertexWorldPos(indices.i[3 * s.triangleID + nextVertexIndex]); 99 | const vec3 lastWP = getVertexWorldPos(indices.i[3 * s.triangleID + lastVertexIndex]); 100 | 101 | vec3 samplePos; 102 | const uint currentEdgeSampleIndex = uint(mod(launchIndex, (ABS_NUM_SAMPLES_PER_EDGE + 3))); 103 | const vec3 vec = normalize(currentWP - rayOrigin); 104 | if (currentEdgeSampleIndex < 3) { 105 | // Calculate the position of one of the three border polygon vertices close to the 106 | // current original vertex 107 | vec3 d[3]; 108 | d[0] = normalize(cross(vec, nextWP - currentWP)); 109 | d[1] = normalize(cross(vec, currentWP - lastWP)); 110 | 111 | if (dot(d[0], d[1]) < 0) { 112 | d[2] = normalize(d[0] + d[1]); 113 | } else { 114 | d[2] = normalize(normalize(d[1] + cross(vec, d[1])) + normalize(d[0] + cross(d[0], vec))); 115 | } 116 | 117 | samplePos = currentWP + ABS_DELTA * d[currentEdgeSampleIndex]; 118 | } else { 119 | // Calculate the position of a sample point along an edge of the border polygon 120 | samplePos = mix( 121 | nextWP + ABS_DELTA * normalize(cross(normalize(nextWP - rayOrigin), nextWP - currentWP)),// + offsetA * absDelta, 122 | currentWP + ABS_DELTA * normalize(cross(normalize(currentWP - rayOrigin), nextWP - currentWP)),// + offsetB * absDelta, 123 | ((currentEdgeSampleIndex - 2)) / float(ABS_NUM_SAMPLES_PER_EDGE + 1) 124 | ); 125 | } 126 | 127 | vec3 rayDir = normalize(samplePos - rayOrigin); 128 | 129 | #ifndef USE_3D_VIEW_CELL 130 | // Get view cell coordinate system 131 | vec3 viewCellSize = viewCell.size * 2.0f; 132 | vec3 viewCellRight = viewCell.right; 133 | vec3 viewCellUp = viewCell.up; 134 | vec3 viewCellNormal = viewCell.normal; 135 | vec3 viewCellPos = viewCell.pos; 136 | 137 | if (dot(rayDir, viewCellNormal) > 0) { 138 | #endif 139 | // Trace ray 140 | uint rayFlags = gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV; 141 | uint cullMask = 0xff; 142 | float tmin = 0.001; 143 | float tmax = 100000.0; 144 | traceNV( 145 | topLevelAS, // acceleration structure 146 | rayFlags, // rayFlags 147 | cullMask, // cullMask 148 | 0, // sbtRecordOffset 149 | 0, // sbtRecordStride 150 | 0, // missIndex 151 | rayOrigin, // ray origin 152 | tmin, // ray min range 153 | rayDir, // ray direction 154 | tmax, // ray max range 155 | 0 // payload (location = 0) 156 | ); 157 | 158 | int triangleID = int(hitInfo.w); 159 | vec3 hitPoint = hitInfo.xyz; 160 | 161 | // Reverse sampling 162 | Sample foundSample; 163 | reverseSampling(launchIndex, rayOrigin, rayDir, triangleID, samplePos, hitPoint, foundSample); 164 | 165 | // If a triangle has been intersected and its ID is not yet part of the PVS, insert it into the PVS 166 | // and into the output buffer which is later used to populate the sample buffer on the CPU side 167 | if (triangleID != -1 && insert(triangleID)) { 168 | uint index = atomicAdd(triangleCounter, 1); 169 | randomSamplingOutput[index] = Sample(triangleID, rayOrigin, hitPoint, samplePos); 170 | } 171 | #ifndef USE_3D_VIEW_CELL 172 | } 173 | #endif 174 | } 175 | -------------------------------------------------------------------------------- /shaders/rt/raytrace_abs.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_NV_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | 5 | layout(location = 0) rayPayloadInNV vec4 hitInfo; 6 | 7 | void main() { 8 | hitInfo = vec4(-1.0); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/rt/reverse_sampling_new.rgen: -------------------------------------------------------------------------------- 1 | bool reverseSampling(uint launchIndex, vec3 rayOrigin, vec3 rayDir, const int occludingTriangleID, const vec3 samplePos, vec3 hitPoint, out Sample foundSample) { 2 | bool discontinuityFound = false; 3 | // Check for discontinuity 4 | const float samplePointDistance = length(samplePos - rayOrigin); 5 | if (occludingTriangleID == -1 || samplePointDistance - length(hitPoint - rayOrigin) > 0.985) { 6 | foundSample.triangleID = -2; 7 | discontinuityFound = true; 8 | 9 | vec3 viewCellSize = viewCell.size * 2.0f; 10 | vec3 viewCellRight = viewCell.right; 11 | vec3 viewCellUp = viewCell.up; 12 | vec3 viewCellNormal = viewCell.normal; 13 | vec3 viewCellPos = viewCell.pos; 14 | 15 | #ifdef USE_3D_VIEW_CELL 16 | const vec3 faceRights[6] = { viewCellRight, -viewCellNormal, -viewCellRight, viewCellNormal, viewCellRight, viewCellRight }; 17 | const vec3 faceUps[6] = { viewCellUp, viewCellUp, viewCellUp, viewCellUp, -viewCellNormal, viewCellNormal }; 18 | const vec3 faceNormals[6] = { viewCellNormal, viewCellRight, -viewCellNormal, -viewCellRight, viewCellUp, -viewCellUp }; 19 | const vec3 faceSizes[6] = { 20 | viewCellSize.xyz, 21 | viewCellSize.zyx, 22 | viewCellSize.xyz, 23 | viewCellSize.zyx, 24 | viewCellSize.xzy, 25 | viewCellSize.xzy 26 | }; 27 | 28 | vec3 startRayDir = rayDir; 29 | for (int m = 0; m < 6; m++) { 30 | if (dot(startRayDir, faceNormals[m]) < 0) { 31 | continue; 32 | } 33 | 34 | viewCellNormal = faceNormals[m]; 35 | viewCellRight = faceRights[m]; 36 | viewCellUp = faceUps[m]; 37 | viewCellSize = faceSizes[m]; 38 | viewCellPos = viewCell.pos + viewCellNormal * viewCellSize.z * 0.5; 39 | #endif 40 | 41 | if (occludingTriangleID == -1) { 42 | // Reverse sampling if no triangle has been hit 43 | 44 | // Get the corners of the view cell 45 | vec3 corners[4]; 46 | corners[0] = viewCellPos - viewCellRight * viewCellSize.x * 0.5 - viewCellUp * viewCellSize.y * 0.5; // bottom left 47 | corners[1] = viewCellPos - viewCellRight * viewCellSize.x * 0.5 + viewCellUp * viewCellSize.y * 0.5; // top left 48 | corners[2] = viewCellPos + viewCellRight * viewCellSize.x * 0.5 + viewCellUp * viewCellSize.y * 0.5; // top right 49 | corners[3] = viewCellPos + viewCellRight * viewCellSize.x * 0.5 - viewCellUp * viewCellSize.y * 0.5; // bottom right 50 | 51 | // Trace rays from the four edges of the view cell 52 | for (int i = 0; i < 4; i++) { 53 | vec3 vec = (corners[int(mod(i + 1, 4))] - corners[i]) / float(REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE - 1); 54 | for (int k = 0; k < REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE - 1; k++) { 55 | const vec3 viewCellSamplePoint = corners[i] + vec * k; 56 | 57 | const vec3 rayOrigin = viewCellSamplePoint; 58 | traceNV(topLevelAS, gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV, 0xff, 0, 0, 0, rayOrigin, 0.001, normalize(samplePos - viewCellSamplePoint), 100000.0, 0); 59 | atomicAdd(rsRayCounter, 1); 60 | 61 | int triangleID = int(hitInfo.w); 62 | if (triangleID != -1) { 63 | bool targetSampleReached = foundSample.triangleID == -2; 64 | targetSampleReached = false; 65 | if (targetSampleReached) { 66 | foundSample = Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos); 67 | } 68 | int index = storeReverseSample( 69 | launchIndex, 70 | Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos), 71 | targetSampleReached 72 | ); 73 | } 74 | } 75 | } 76 | 77 | // Trace rays from the area of the view cell. Sample positions are distributed using Halton points 78 | for (int i = 0; i < REVERSE_SAMPLING_HALTON_NUM_HALTON_SAMPLES; i++) { 79 | vec2 haltonPoint = vec2(points[i * 2] - 0.5f, points[i * 2 + 1] - 0.5f); 80 | const vec3 viewCellSamplePoint = viewCellPos + haltonPoint.x * viewCellSize.x * viewCellRight + haltonPoint.y * viewCellSize.y * viewCellUp; 81 | 82 | const vec3 rayOrigin = viewCellSamplePoint; 83 | traceNV(topLevelAS, gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV, 0xff, 0, 0, 0, rayOrigin, 0.001, normalize(samplePos - viewCellSamplePoint), 100000.0, 0); 84 | atomicAdd(rsRayCounter, 1); 85 | 86 | int triangleID = int(hitInfo.w); 87 | if (triangleID != -1) { 88 | bool targetSampleReached = foundSample.triangleID == -2; 89 | targetSampleReached = false; 90 | if (targetSampleReached) { 91 | foundSample = Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos); 92 | } 93 | int index = storeReverseSample( 94 | launchIndex, 95 | Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos), 96 | targetSampleReached 97 | ); 98 | } 99 | } 100 | } else { 101 | vec3 occludingTriangleVerticesWorldPos[3]; 102 | for (int i = 0; i < 3; i++) { 103 | occludingTriangleVerticesWorldPos[i] = getVertexPos(indices.i[3 * occludingTriangleID + i]); 104 | } 105 | bool invalidRayPlaneIntersection = false; 106 | vec3 rayViewCellHitPoints[3]; 107 | for (int i = 0; i < 3; i++) { 108 | // Intersect a ray originating at samplePos through the vertices of the occluding triangle with the view 109 | // cell i.e. the occluding triangle is projected onto the view cell 110 | if (!intersectRayPlane( 111 | viewCellPos, viewCellNormal, samplePos, normalize(occludingTriangleVerticesWorldPos[i] - samplePos), 112 | rayViewCellHitPoints[i] 113 | ) 114 | ) { 115 | invalidRayPlaneIntersection = true; 116 | break; 117 | } 118 | } 119 | 120 | if (!invalidRayPlaneIntersection) { 121 | const vec3 v0 = rayViewCellHitPoints[1] - rayViewCellHitPoints[0]; 122 | const vec3 v1 = rayViewCellHitPoints[2] - rayViewCellHitPoints[0]; 123 | 124 | const float dot00 = dot(v0, v0); 125 | const float dot01 = dot(v0, v1); 126 | const float dot11 = dot(v1, v1); 127 | const float denom = dot00 * dot11 - dot01 * dot01; 128 | 129 | // Get the corners of the view cell 130 | vec3 corners[4]; 131 | corners[0] = viewCellPos - viewCellRight * viewCellSize.x * 0.5 - viewCellUp * viewCellSize.y * 0.5; // bottom left 132 | corners[1] = viewCellPos - viewCellRight * viewCellSize.x * 0.5 + viewCellUp * viewCellSize.y * 0.5; // top left 133 | corners[2] = viewCellPos + viewCellRight * viewCellSize.x * 0.5 + viewCellUp * viewCellSize.y * 0.5; // top right 134 | corners[3] = viewCellPos + viewCellRight * viewCellSize.x * 0.5 - viewCellUp * viewCellSize.y * 0.5; // bottom right 135 | 136 | // Mutate the ray origin along the edges of the view cell. If the mutated ray origin lies outside the 137 | // projected triangle, a ray is traced using the mutated ray origin 138 | for (int i = 0; i < 4; i++) { 139 | vec3 vec = (corners[int(mod(i + 1, 4))] - corners[i]) / float(REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE - 1); 140 | for (int k = 0; k < REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE - 1; k++) { 141 | const vec3 viewCellSamplePoint = corners[i] + vec * k; 142 | 143 | // Check if viewCellSamplePoint is within the occluding triangle projected onto the view cell 144 | // Ericson, Christer. Real-time collision detection. CRC Press, 2004. 145 | const vec3 v2 = viewCellSamplePoint - rayViewCellHitPoints[0]; 146 | const float dot20 = dot(v2, v0); 147 | const float dot21 = dot(v2, v1); 148 | const float v = (dot11 * dot20 - dot01 * dot21) / denom; 149 | const float w = (dot00 * dot21 - dot01 * dot20) / denom; 150 | if (any(bvec3(w < 0.0f || w > 1.0f, v < 0.0f || v > 1.0f, w + v > 1.0f))) { 151 | const vec3 rayOrigin = viewCellSamplePoint; 152 | traceNV(topLevelAS, gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV, 0xff, 0, 0, 0, rayOrigin, 0.001, normalize(samplePos - viewCellSamplePoint), 100000.0, 0); 153 | atomicAdd(rsRayCounter, 1); 154 | 155 | int triangleID = int(hitInfo.w); 156 | if (triangleID != -1) { 157 | bool targetSampleReached = (length(hitInfo.xyz - rayOrigin) - samplePointDistance >= 0.0f) && (foundSample.triangleID == -2); 158 | if (targetSampleReached) { 159 | foundSample = Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos); 160 | } 161 | int index = storeReverseSample( 162 | launchIndex, 163 | Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos), 164 | targetSampleReached 165 | ); 166 | } 167 | } 168 | } 169 | } 170 | 171 | for (int i = 0; i < REVERSE_SAMPLING_HALTON_NUM_HALTON_SAMPLES; i++) { 172 | vec2 haltonPoint = vec2(points[i * 2] - 0.5f, points[i * 2 + 1] - 0.5f); 173 | const vec3 viewCellSamplePoint = viewCellPos + haltonPoint.x * viewCellSize.x * viewCellRight + haltonPoint.y * viewCellSize.y * viewCellUp; 174 | 175 | // Check if viewCellSamplePoint is within the occluding triangle projected onto the view cell 176 | // Ericson, Christer. Real-time collision detection. CRC Press, 2004. 177 | const vec3 v2 = viewCellSamplePoint - rayViewCellHitPoints[0]; 178 | const float dot20 = dot(v2, v0); 179 | const float dot21 = dot(v2, v1); 180 | const float v = (dot11 * dot20 - dot01 * dot21) / denom; 181 | const float w = (dot00 * dot21 - dot01 * dot20) / denom; 182 | if (any(bvec3(w < 0.0f || w > 1.0f, v < 0.0f || v > 1.0f, w + v > 1.0f))) { 183 | const vec3 rayOrigin = viewCellSamplePoint; 184 | traceNV(topLevelAS, gl_RayFlagsCullBackFacingTrianglesNV | gl_RayFlagsOpaqueNV, 0xff, 0, 0, 0, rayOrigin, 0.001, normalize(samplePos - viewCellSamplePoint), 100000.0, 0); 185 | atomicAdd(rsRayCounter, 1); 186 | 187 | int triangleID = int(hitInfo.w); 188 | if (triangleID != -1) { 189 | bool targetSampleReached = (length(hitInfo.xyz - rayOrigin) - samplePointDistance >= 0.0f) && (foundSample.triangleID == -2); 190 | if (targetSampleReached) { 191 | foundSample = Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos); 192 | } 193 | int index = storeReverseSample( 194 | launchIndex, 195 | Sample(triangleID, rayOrigin, hitInfo.xyz, samplePos), 196 | targetSampleReached 197 | ); 198 | } 199 | } 200 | } 201 | } 202 | } 203 | #ifdef USE_3D_VIEW_CELL 204 | } 205 | #endif 206 | } 207 | 208 | return discontinuityFound; 209 | } 210 | -------------------------------------------------------------------------------- /shaders/rt/util.glsl: -------------------------------------------------------------------------------- 1 | Vertex unpackVertexData(uint index) { 2 | vec4 d0 = vertices.v[3 * index + 0]; 3 | vec4 d1 = vertices.v[3 * index + 1]; 4 | vec4 d2 = vertices.v[3 * index + 2]; 5 | 6 | Vertex vertex; 7 | vertex.pos = d0.xyz; 8 | vertex.worldPos = vec3(camera.model * vec4(vertex.pos, 1.0)); 9 | vertex.normal = vec3(d0.w, d1.xy); 10 | vertex.color = vec3(d1.zw, d2.x); 11 | vertex.uv = d2.yz; 12 | 13 | return vertex; 14 | } 15 | 16 | vec3 getVertexPos(uint index) { 17 | return vertices.v[3 * index].xyz; 18 | } 19 | 20 | vec3 getVertexWorldPos(uint index) { 21 | return vec3(camera.model * vec4( 22 | vertices.v[3 * index + 0].xyz, 23 | 1.0) 24 | ); 25 | } 26 | 27 | bool intersectRayPlane(vec3 d, vec3 normal, vec3 rayOrigin, vec3 rayDir, out vec3 hitPoint) { 28 | float denom = dot(rayDir, normal); 29 | if (abs(denom) > 1e-6) { 30 | float t = dot(d - rayOrigin, normal) / denom; 31 | hitPoint = rayOrigin + t * rayDir; 32 | 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | bool intersectRayPlane2(vec3 d, vec3 normal, vec3 rayOrigin, vec3 rayDir, out vec3 hitPoint, out float t) { 40 | float denom = dot(rayDir, normal); 41 | if (abs(denom) > 1e-6) { 42 | t = dot(d - rayOrigin, normal) / denom; 43 | hitPoint = rayOrigin + t * rayDir; 44 | 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | -------------------------------------------------------------------------------- /shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | layout(binding = 0, set = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 projection; 7 | } ubo; 8 | 9 | layout(binding = 1) uniform sampler2D tex; 10 | 11 | layout(location = 0) flat in vec3 fragColor; 12 | layout(location = 1) in vec3 fragNormal; 13 | layout(location = 2) in vec3 worldPos; 14 | 15 | layout(location = 0) out vec4 color; 16 | 17 | layout(push_constant) uniform PushConstants { 18 | layout(offset = 64) bool shadedRendering; 19 | } pushConstants; 20 | 21 | void main() { 22 | if (pushConstants.shadedRendering) { 23 | mat4 m = inverse(ubo.view); 24 | vec3 cameraWorldPos = vec3(m[3][0], m[3][1], m[3][2]); 25 | color = vec4(fragColor * max(0.0f, dot(fragNormal, normalize(cameraWorldPos - worldPos))), 1.0f); 26 | } else { 27 | color = vec4(fragColor, 1.0f); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | layout(binding = 0, set = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 projection; 7 | } ubo; 8 | 9 | layout(push_constant) uniform PushConstants { 10 | mat4 model; 11 | } pushConstants; 12 | 13 | 14 | layout(location = 0) in vec3 inPosition; 15 | layout(location = 1) in vec3 inNormal; 16 | layout(location = 2) in vec3 inColor; 17 | layout(location = 3) in vec2 inTexCoord; 18 | 19 | layout(location = 0) flat out vec3 fragColor; 20 | layout(location = 1) out vec3 fragNormal; 21 | layout(location = 2) out vec3 worldPos; 22 | 23 | void main() { 24 | gl_Position = ubo.projection * ubo.view * pushConstants.model * vec4(inPosition, 1.0); 25 | fragColor = inColor; 26 | fragNormal = inNormal; 27 | worldPos = vec3(pushConstants.model * vec4(inPosition, 1.0)); 28 | } 29 | -------------------------------------------------------------------------------- /viewcell.cpp: -------------------------------------------------------------------------------- 1 | #include "viewcell.h" 2 | 3 | ViewCell::ViewCell() { 4 | } 5 | 6 | ViewCell::ViewCell(glm::vec3 pos, glm::vec3 size, glm::vec3 right, glm::vec3 up, glm::vec3 normal) 7 | : pos(pos), size(size), right(right), up(up), normal(normal) 8 | { 9 | } 10 | -------------------------------------------------------------------------------- /viewcell.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEWCELL_H 2 | #define VIEWCELL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class ViewCell { 11 | public: 12 | alignas(16) glm::vec3 pos; 13 | alignas(16) glm::vec3 size; 14 | alignas(16) glm::vec3 right; 15 | alignas(16) glm::vec3 up; 16 | alignas(16) glm::vec3 normal; 17 | 18 | ViewCell(); 19 | 20 | ViewCell(glm::vec3 pos, glm::vec3 size, glm::vec3 right, glm::vec3 up, glm::vec3 normal); 21 | 22 | bool operator() (const ViewCell& lhs, const ViewCell& rhs) const { 23 | return glm::length(lhs.pos) < glm::length(lhs.pos); 24 | } 25 | 26 | bool operator <(const ViewCell& rhs) const { 27 | return glm::length(rhs.pos) < glm::length(pos); 28 | } 29 | }; 30 | 31 | #endif // VIEWCELL_H 32 | -------------------------------------------------------------------------------- /visibilitymanager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "Vertex.h" 15 | #include "sample.h" 16 | #include "pvs.h" 17 | #include "Statistics.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | class ViewCell; 24 | 25 | struct AccelerationStructure { 26 | VkDeviceMemory deviceMemory; 27 | VkAccelerationStructureNV as; 28 | uint64_t handle; 29 | }; 30 | 31 | // Ray tracing geometry instance 32 | struct GeometryInstance { 33 | glm::mat3x4 transform; 34 | uint32_t instanceId : 24; 35 | uint32_t mask : 8; 36 | uint32_t instanceOffset : 24; 37 | uint32_t flags : 8; 38 | uint64_t accelerationStructureHandle; 39 | }; 40 | 41 | struct ShaderExecutionInfo { 42 | unsigned int numTriangles; 43 | unsigned int numRsTriangles; 44 | unsigned int numRays; 45 | unsigned int numRsRays; 46 | }; 47 | 48 | class VisibilityManager { 49 | public: 50 | std::vector viewCells; 51 | PVS pvs; 52 | std::vector> rayVertices; 53 | bool visualizeFirstRays = false; 54 | VkQueue computeQueue; 55 | VkQueue transferQueue; 56 | VkCommandPool commandPool; 57 | VkCommandPool transferCommandPool; 58 | std::vector statistics; 59 | VkBuffer randomSamplingOutputBuffer; 60 | VkBuffer pvsBuffer; 61 | VkBuffer triangleCounterBuffer; 62 | 63 | VisibilityManager( 64 | long NEW_TRIANGLE_TERMINATION_THRESHOLD_COUNT, 65 | long NEW_TRIANGLE_TERMINATION_THRESHOLD, 66 | long RANDOM_RAYS_PER_ITERATION, 67 | long NUM_ABS_SAMPLES, 68 | long REVERSE_SAMPLING_NUM_SAMPLES_ALONG_EDGE, 69 | long MAX_BULK_INSERT_BUFFER_SIZE, 70 | int GPU_SET_TYPE, 71 | long INITIAL_HASH_SET_SIZE, 72 | VkPhysicalDevice physicalDevice, 73 | VkDevice logicalDevice, 74 | VkBuffer indexBuffer, 75 | const std::vector &indices, 76 | VkBuffer vertexBuffer, 77 | const std::vector &vertices, 78 | const std::vector &uniformBuffers, 79 | std::array deviceUUID, 80 | std::vector viewCells, 81 | VkCommandPool graphicsCommandPool, 82 | VkQueue graphicsQueue, 83 | uint32_t frameBufferWidth, 84 | uint32_t frameBufferHeight, 85 | VkFormat depthFormat, 86 | bool visualizeFirstRays 87 | ); 88 | ~VisibilityManager(); 89 | 90 | template 91 | std::vector> generateHaltonPoints2d(std::array bases, int n, std::array lastHaltonPoints) { 92 | std::vector> haltonPoints; 93 | haltonPoints.resize(n); 94 | 95 | /* 96 | This is the incremental version to generate the halton squence of 97 | quasi-random numbers of a given base. It has been taken from: 98 | Keller, Alexander. "Instant radiosity." Proceedings of the 24th annual conference on Computer graphics and interactive techniques. 1997. 99 | 100 | Train, Kenneth E. Discrete choice methods with simulation. Cambridge university press, 2009. 101 | */ 102 | for (int k = 0; k < bases.size(); k++) { 103 | double inverseBase = 1.0 / bases[k]; 104 | double value = 0.0; 105 | 106 | for (int i = 0; i < n; i++) { 107 | double r = 1.0 - value - 1e-10; 108 | 109 | if (inverseBase < r) { 110 | value += inverseBase; 111 | } else { 112 | double h = inverseBase * inverseBase; 113 | double hh = inverseBase; 114 | while (h >= r) { 115 | hh = h; 116 | h *= inverseBase; 117 | } 118 | value += hh + h - 1.0; 119 | } 120 | 121 | haltonPoints[i][k] = value; 122 | } 123 | } 124 | 125 | return haltonPoints; 126 | } 127 | 128 | void rayTrace(int viewCellIndex); 129 | void releaseResources(); 130 | void fetchPVS(); 131 | void printAverageStatistics(); 132 | 133 | private: 134 | int pvsSize = 0; 135 | 136 | const long NEW_TRIANGLE_TERMINATION_THRESHOLD; 137 | const long NEW_TRIANGLE_TERMINATION_THRESHOLD_COUNT; 138 | const long NUM_ABS_SAMPLES; 139 | const long NUM_REVERSE_SAMPLING_SAMPLES; 140 | const long MAX_BULK_INSERT_BUFFER_SIZE; 141 | const long MAX_TRIANGLE_COUNT; 142 | const int GPU_SET_TYPE; 143 | 144 | const long RANDOM_RAYS_PER_ITERATION; 145 | const int MIN_ABS_TRIANGLES_PER_ITERATION = 1; 146 | const long MAX_ABS_TRIANGLES_PER_ITERATION = 100000; 147 | const uint32_t RT_SHADER_INDEX_RAYGEN = 0; 148 | const uint32_t RT_SHADER_INDEX_MISS = 1; 149 | const uint32_t RT_SHADER_INDEX_CLOSEST_HIT = 2; 150 | 151 | unsigned int pvsBufferCapacity; 152 | 153 | std::vector> haltonPoints; 154 | glm::vec4 lastHaltonPoints; 155 | std::random_device rd; 156 | std::mt19937 gen; 157 | std::atomic tracedRays; 158 | 159 | VkPhysicalDevice physicalDevice; 160 | VkDevice logicalDevice; 161 | std::array deviceUUID; 162 | 163 | VkCommandBuffer commandBuffer; 164 | VkCommandBuffer commandBufferABS; 165 | VkCommandBuffer commandBufferCompute; 166 | VkCommandBuffer commandBufferHaltonCompute; 167 | VkFence commandBufferFence; 168 | VkPhysicalDeviceRayTracingPropertiesNV rayTracingProperties; 169 | VkPipeline pipeline; 170 | VkPipelineLayout pipelineLayout; 171 | VkPipeline pipelineABS; 172 | VkPipelineLayout pipelineABSLayout; 173 | VkPipeline pipelineCompute; 174 | VkPipelineLayout pipelineComputeLayout; 175 | VkPipeline pipelineHaltonCompute; 176 | VkPipelineLayout pipelineHaltonComputeLayout; 177 | 178 | VkDescriptorPool descriptorPool; 179 | VkDescriptorSet descriptorSet; 180 | VkDescriptorSetLayout descriptorSetLayout; 181 | VkDescriptorSet descriptorSetABS; 182 | VkDescriptorSetLayout descriptorSetLayoutABS; 183 | VkDescriptorSet descriptorSetCompute; 184 | VkDescriptorSetLayout descriptorSetLayoutCompute; 185 | VkDescriptorSet descriptorSetHaltonCompute; 186 | VkDescriptorSetLayout descriptorSetLayoutHaltonCompute; 187 | 188 | VkImageView storageImageView; 189 | VkBuffer shaderBindingTable; 190 | VkDeviceMemory shaderBindingTableMemory; 191 | VkBuffer shaderBindingTableABS; 192 | VkDeviceMemory shaderBindingTableMemoryABS; 193 | VkBuffer haltonPointsBuffer; 194 | VkDeviceMemory haltonPointsBufferMemory; 195 | VkBuffer viewCellBuffer; 196 | VkDeviceMemory viewCellBufferMemory; 197 | 198 | VkDeviceMemory randomSamplingOutputBufferMemory; 199 | VkBuffer absWorkingBuffer; 200 | VkDeviceMemory absWorkingBufferMemory; 201 | VkDeviceMemory triangleCounterBufferMemory; 202 | VkBuffer randomSamplingOutputHostBuffer; 203 | VkDeviceMemory randomSamplingOutputHostBufferMemory; 204 | void* randomSamplingOutputPointer; 205 | 206 | VkBuffer pvsBulkInsertBuffer; 207 | VkDeviceMemory pvsBulkInsertBufferMemory; 208 | 209 | VkDeviceMemory pvsBufferMemory; 210 | std::vector pvsPointer; 211 | VkBuffer pvsCapacityUniformBuffer; 212 | VkDeviceMemory pvsCapacityUniformMemory; 213 | 214 | AccelerationStructure bottomLevelAS; 215 | AccelerationStructure topLevelAS; 216 | 217 | PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; 218 | PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV; 219 | PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; 220 | PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; 221 | PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; 222 | PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; 223 | PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; 224 | PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; 225 | PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; 226 | 227 | VkDeviceSize bindingOffsetRayGenShader; 228 | VkDeviceSize bindingOffsetMissShader; 229 | VkDeviceSize bindingOffsetHitShader; 230 | VkDeviceSize bindingStride; 231 | 232 | void initRayTracing( 233 | VkBuffer indexBuffer, VkBuffer vertexBuffer, const std::vector &indices, 234 | const std::vector &vertices, const std::vector &uniformBuffers 235 | ); 236 | void createBottomLevelAS(const VkGeometryNV *geometry); 237 | void createTopLevelAS(); 238 | void buildAS(const VkBuffer instanceBuffer, const VkGeometryNV *geometry); 239 | void createDescriptorSetLayout(); 240 | void createDescriptorSets(VkBuffer indexBuffer, VkBuffer vertexBuffer, const std::vector &uniformBuffers); 241 | void createDescriptorPool(); 242 | void createPipeline( 243 | const std::array &shaderStages, 244 | VkPipelineLayout &pipelineLayout, VkPipeline &pipeline, 245 | const std::vector &descriptorSetLayouts, 246 | std::vector pushConstantRanges 247 | ); 248 | void createShaderBindingTable( 249 | VkBuffer &shaderBindingTable, VkDeviceMemory &shaderBindingTableMemory, VkPipeline &pipeline 250 | ); 251 | VkDeviceSize copyShaderIdentifier( 252 | uint8_t* data, const uint8_t* shaderHandleStorage, uint32_t groupIndex 253 | ); 254 | void createCommandBuffers(); 255 | void copyHaltonPointsToBuffer(); 256 | void updateViewCellBuffer(int viewCellIndex); 257 | void createBuffers(const std::vector &indices); 258 | void createDescriptorSets(); 259 | void createRandomSamplingPipeline(); 260 | void createABSDescriptorSetLayout(); 261 | void createABSDescriptorSets(VkBuffer vertexBuffer); 262 | void createABSPipeline(); 263 | void createComputeDescriptorSets(); 264 | void createComputeDescriptorSetLayout(); 265 | void createComputePipeline(); 266 | void createHaltonComputeDescriptorSets(); 267 | void createHaltonComputeDescriptorSetLayout(); 268 | void createHaltonComputePipeline(); 269 | void resetPVSGPUBuffer(); 270 | void resetAtomicBuffers(); 271 | void resizeHashSetPVSBuffer(int newSize); 272 | void generateHaltonSequence(int n, float rand); 273 | 274 | ShaderExecutionInfo randomSample(int numRays, int viewCellIndex); 275 | ShaderExecutionInfo adaptiveBorderSample(const std::vector &absWorkingVector, int viewCellIndex); 276 | }; 277 | -------------------------------------------------------------------------------- /vulkanutil.cpp: -------------------------------------------------------------------------------- 1 | #include "vulkanutil.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void VulkanUtil::createBuffer( 7 | VkPhysicalDevice physicalDevice, VkDevice logicalDevice, VkDeviceSize size, 8 | VkBufferUsageFlags usageFlags, VkBuffer &buffer, VkDeviceMemory &bufferMemory, 9 | VkMemoryPropertyFlags properties, bool allocateDedicatedMemory 10 | ) { 11 | // Create buffer 12 | VkBufferCreateInfo bufferInfo = {}; 13 | bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 14 | bufferInfo.size = size; 15 | bufferInfo.usage = usageFlags; 16 | bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 17 | 18 | if (vkCreateBuffer(logicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { 19 | throw std::runtime_error("failed to create buffer"); 20 | } 21 | 22 | // Allocate memory 23 | VkMemoryRequirements memoryReq; 24 | vkGetBufferMemoryRequirements(logicalDevice, buffer, &memoryReq); 25 | 26 | VkMemoryAllocateInfo memoryAllocInfo = {}; 27 | memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 28 | memoryAllocInfo.allocationSize = memoryReq.size; 29 | memoryAllocInfo.memoryTypeIndex = findMemoryType( 30 | physicalDevice, memoryReq.memoryTypeBits, properties 31 | ); 32 | if (allocateDedicatedMemory) { 33 | VkMemoryDedicatedAllocateInfoKHR dedicatedAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; 34 | dedicatedAllocateInfo.buffer = buffer; 35 | memoryAllocInfo.pNext = &dedicatedAllocateInfo; 36 | } 37 | if (vkAllocateMemory( 38 | logicalDevice, &memoryAllocInfo, nullptr, &bufferMemory 39 | ) != VK_SUCCESS 40 | ) { 41 | //qWarning("failed to allocate vertex buffer memory"); 42 | } 43 | 44 | // Assign memory 45 | vkBindBufferMemory(logicalDevice, buffer, bufferMemory, 0); 46 | } 47 | 48 | void VulkanUtil::copyBuffer( 49 | VkDevice logicalDevice, VkCommandPool commandPool, VkQueue queue, VkBuffer srcBuffer, 50 | VkBuffer dstBuffer, VkDeviceSize size, VkDeviceSize srcOffset, VkDeviceSize dstOffset 51 | ) { 52 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(logicalDevice, commandPool); 53 | 54 | // Copy command 55 | VkBufferCopy copyRegion = {}; 56 | copyRegion.size = size; 57 | copyRegion.srcOffset = srcOffset; 58 | copyRegion.dstOffset = dstOffset; 59 | vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); 60 | 61 | endSingleTimeCommands(logicalDevice, commandBuffer, commandPool, queue); 62 | } 63 | 64 | VkCommandBuffer VulkanUtil::beginSingleTimeCommands( 65 | VkDevice logicalDevice, VkCommandPool commandPool 66 | ) { 67 | // Allocate command buffer 68 | VkCommandBufferAllocateInfo allocInfo = {}; 69 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 70 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 71 | allocInfo.commandPool = commandPool; 72 | allocInfo.commandBufferCount = 1; 73 | 74 | VkCommandBuffer commandBuffer; 75 | vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer); 76 | 77 | // Begin recording commands 78 | VkCommandBufferBeginInfo beginInfo = {}; 79 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 80 | beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 81 | vkBeginCommandBuffer(commandBuffer, &beginInfo); 82 | 83 | return commandBuffer; 84 | } 85 | 86 | void VulkanUtil::endSingleTimeCommands( 87 | VkDevice logicalDevice, VkCommandBuffer commandBuffer, VkCommandPool commandPool, VkQueue queue 88 | ) { 89 | vkEndCommandBuffer(commandBuffer); 90 | 91 | // Execute command buffer 92 | VkSubmitInfo submitInfo = {}; 93 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 94 | submitInfo.commandBufferCount = 1; 95 | submitInfo.pCommandBuffers = &commandBuffer; 96 | 97 | VkFenceCreateInfo fenceInfo; 98 | fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 99 | fenceInfo.pNext = NULL; 100 | fenceInfo.flags = 0; 101 | VkFence fence; 102 | vkCreateFence(logicalDevice, &fenceInfo, NULL, &fence); 103 | 104 | vkQueueSubmit(queue, 1, &submitInfo, fence); 105 | 106 | VkResult result; 107 | // Wait for the command buffer to complete execution in a loop in case it takes longer to 108 | // complete than expected 109 | do { 110 | result = vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, UINT64_MAX); 111 | } while(result == VK_TIMEOUT); 112 | 113 | vkDestroyFence(logicalDevice, fence, nullptr); 114 | vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer); 115 | } 116 | 117 | uint32_t VulkanUtil::findMemoryType( 118 | VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties 119 | ) { 120 | VkPhysicalDeviceMemoryProperties memProperties; 121 | vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); 122 | 123 | for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { 124 | if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { 125 | return i; 126 | } 127 | } 128 | } 129 | 130 | uint32_t VulkanUtil::findQueueFamilies(VkPhysicalDevice device, VkQueueFlags queueFlags, int k) { 131 | uint32_t queueFamilyCount = 0; 132 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 133 | 134 | std::vector queueFamilies(queueFamilyCount); 135 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 136 | 137 | std::optional family; 138 | int i = 0; 139 | for (const auto& queueFamily : queueFamilies) { 140 | if (queueFamily.queueFlags & queueFlags) { 141 | family = i; 142 | } 143 | 144 | if (family.has_value() && int(family.value()) > k) { 145 | break; 146 | } 147 | 148 | i++; 149 | } 150 | 151 | return family.value(); 152 | } 153 | 154 | std::vector VulkanUtil::readBinaryFile(const std::string &filename) { 155 | // Open a binary file with the read position at the end 156 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 157 | if (!file.is_open()) { 158 | throw std::runtime_error("failed to open file " + filename); 159 | } 160 | 161 | // Get the file size from the current read position 162 | size_t fileSize = (size_t) file.tellg(); 163 | 164 | std::vector buffer(fileSize); 165 | file.seekg(0); 166 | file.read(buffer.data(), fileSize); 167 | 168 | file.close(); 169 | return buffer; 170 | } 171 | 172 | VkShaderModule VulkanUtil::createShader(VkDevice logicalDevice, const std::string &filename) { 173 | auto shaderCode = readBinaryFile(filename); 174 | 175 | VkShaderModuleCreateInfo shaderInfo = {}; 176 | shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 177 | shaderInfo.codeSize = shaderCode.size(); 178 | shaderInfo.pCode = reinterpret_cast(shaderCode.data()); 179 | 180 | VkShaderModule shaderModule; 181 | VkResult err = vkCreateShaderModule(logicalDevice, &shaderInfo, nullptr, &shaderModule); 182 | if (err != VK_SUCCESS) { 183 | throw std::runtime_error("failed to create shader module "); 184 | return VK_NULL_HANDLE; 185 | } 186 | 187 | return shaderModule; 188 | } 189 | 190 | void VulkanUtil::executeCommandBuffer( 191 | VkDevice logicalDevice, VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence 192 | ) { 193 | VkSubmitInfo submitInfo = {}; 194 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 195 | submitInfo.commandBufferCount = 1; 196 | submitInfo.pCommandBuffers = &commandBuffer; 197 | 198 | vkQueueSubmit(queue, 1, &submitInfo, fence); 199 | 200 | VkResult result; 201 | // Wait for the command buffer to complete execution in a loop in case it takes longer to 202 | // complete than expected 203 | do { 204 | result = vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, UINT64_MAX); 205 | } while(result == VK_TIMEOUT); 206 | // Free the command buffer 207 | vkResetFences(logicalDevice, 1, &fence); 208 | } 209 | 210 | void VulkanUtil::createImage( 211 | VkPhysicalDevice physicalDevice, VkDevice device, uint32_t width, uint32_t height, 212 | VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, 213 | VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, 214 | VkDeviceMemory &imageMemory, uint32_t arrayLayers 215 | ) { 216 | VkImageCreateInfo imageInfo = {}; 217 | imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; 218 | imageInfo.imageType = VK_IMAGE_TYPE_2D; 219 | imageInfo.extent.width = width; 220 | imageInfo.extent.height = height; 221 | imageInfo.extent.depth = 1; 222 | imageInfo.mipLevels = 1; 223 | imageInfo.arrayLayers = arrayLayers; 224 | imageInfo.format = format; 225 | imageInfo.tiling = tiling; 226 | imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 227 | imageInfo.usage = usage; 228 | imageInfo.samples = numSamples; 229 | imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 230 | 231 | if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { 232 | throw std::runtime_error("failed to create image!"); 233 | } 234 | 235 | VkMemoryRequirements memRequirements; 236 | vkGetImageMemoryRequirements(device, image, &memRequirements); 237 | 238 | VkMemoryAllocateInfo allocInfo{}; 239 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 240 | allocInfo.allocationSize = memRequirements.size; 241 | allocInfo.memoryTypeIndex = VulkanUtil::findMemoryType( 242 | physicalDevice, memRequirements.memoryTypeBits, properties 243 | ); 244 | 245 | if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) { 246 | throw std::runtime_error("failed to allocate image memory!"); 247 | } 248 | 249 | vkBindImageMemory(device, image, imageMemory, 0); 250 | } 251 | 252 | // https://vulkan-tutorial.com/Depth_buffering 253 | VkImageView VulkanUtil::createImageView( 254 | VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, 255 | uint32_t layerCount 256 | ) { 257 | VkImageViewCreateInfo viewInfo = {}; 258 | viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 259 | viewInfo.image = image; 260 | if (layerCount > 1) { 261 | viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; 262 | } else { 263 | viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 264 | } 265 | viewInfo.format = format; 266 | viewInfo.subresourceRange.aspectMask = aspectFlags; 267 | viewInfo.subresourceRange.baseMipLevel = 0; 268 | viewInfo.subresourceRange.levelCount = 1; 269 | viewInfo.subresourceRange.baseArrayLayer = 0; 270 | viewInfo.subresourceRange.layerCount = layerCount; 271 | 272 | VkImageView imageView; 273 | if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) { 274 | throw std::runtime_error("failed to create texture image view!"); 275 | } 276 | 277 | return imageView; 278 | } 279 | 280 | // https://vulkan-tutorial.com/Multisampling 281 | VkSampleCountFlagBits VulkanUtil::getMaxUsableMSAASampleCount(VkPhysicalDevice physicalDevice) { 282 | VkPhysicalDeviceProperties physicalDeviceProperties; 283 | vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties); 284 | 285 | VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts 286 | & physicalDeviceProperties.limits.framebufferDepthSampleCounts; 287 | if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; } 288 | if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; } 289 | if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; } 290 | if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; } 291 | if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; } 292 | if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; } 293 | 294 | return VK_SAMPLE_COUNT_1_BIT; 295 | } 296 | -------------------------------------------------------------------------------- /vulkanutil.h: -------------------------------------------------------------------------------- 1 | #ifndef VULKANUTIL_H 2 | #define VULKANUTIL_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | class VulkanUtil { 11 | public: 12 | static void createBuffer( 13 | VkPhysicalDevice physicalDevice, VkDevice logicalDevice, VkDeviceSize size, 14 | VkBufferUsageFlags usageFlags, VkBuffer &buffer, VkDeviceMemory &bufferMemory, 15 | VkMemoryPropertyFlags properties, bool allocateDedicatedMemory = false 16 | ); 17 | static void copyBuffer( 18 | VkDevice logicalDevice, VkCommandPool commandPool, VkQueue queue, VkBuffer srcBuffer, 19 | VkBuffer dstBuffer, VkDeviceSize size, VkDeviceSize srcOffset = 0, VkDeviceSize dstOffset = 0 20 | ); 21 | static VkCommandBuffer beginSingleTimeCommands( 22 | VkDevice logicalDevice, VkCommandPool commandPool 23 | ); 24 | static void endSingleTimeCommands( 25 | VkDevice logicalDevice, VkCommandBuffer commandBuffer, VkCommandPool commandPool, 26 | VkQueue queue 27 | ); 28 | static uint32_t findMemoryType( 29 | VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties 30 | ); 31 | static uint32_t findQueueFamilies(VkPhysicalDevice device, VkQueueFlags queueFlags, int k = -1); 32 | static VkShaderModule createShader(VkDevice logicalDevice, const std::string &filename); 33 | static void executeCommandBuffer( 34 | VkDevice logicalDevice, VkQueue queue, VkCommandBuffer commandBuffer, VkFence fence 35 | ); 36 | static void createImage( 37 | VkPhysicalDevice physicalDevice, VkDevice device, uint32_t width, uint32_t height, 38 | VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, 39 | VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, 40 | VkDeviceMemory &imageMemory, uint32_t arrayLayers = 1 41 | ); 42 | static VkImageView createImageView( 43 | VkDevice device, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, 44 | uint32_t layerCount = 1 45 | ); 46 | static VkSampleCountFlagBits getMaxUsableMSAASampleCount(VkPhysicalDevice physicalDevice); 47 | 48 | private: 49 | static std::vector readBinaryFile(const std::string &filename); 50 | }; 51 | 52 | #endif // VULKANUTIL_H 53 | --------------------------------------------------------------------------------