parent
14f0b51c75
commit
aceb49e0be
|
@ -1,329 +0,0 @@
|
|||
/*
|
||||
* Extended sample base class for ray tracing based samples
|
||||
*
|
||||
* Copyright (C) 2020-2021 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#include "VulkanRaytracingSample.h"
|
||||
|
||||
void VulkanRaytracingSample::updateRenderPass()
|
||||
{
|
||||
// Update the default render pass with different color attachment load ops to keep attachment contents
|
||||
// With this change, we can e.g. draw an UI on top of the ray traced scene
|
||||
|
||||
vkDestroyRenderPass(device, renderPass, nullptr);
|
||||
|
||||
std::array<VkAttachmentDescription, 2> attachments = {};
|
||||
// Color attachment
|
||||
attachments[0].format = swapChain.colorFormat;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
// Depth attachment
|
||||
attachments[1].format = depthFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorReference = {};
|
||||
colorReference.attachment = 0;
|
||||
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depthReference = {};
|
||||
depthReference.attachment = 1;
|
||||
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpassDescription = {};
|
||||
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpassDescription.colorAttachmentCount = 1;
|
||||
subpassDescription.pColorAttachments = &colorReference;
|
||||
subpassDescription.pDepthStencilAttachment = &depthReference;
|
||||
subpassDescription.inputAttachmentCount = 0;
|
||||
subpassDescription.pInputAttachments = nullptr;
|
||||
subpassDescription.preserveAttachmentCount = 0;
|
||||
subpassDescription.pPreserveAttachments = nullptr;
|
||||
subpassDescription.pResolveAttachments = nullptr;
|
||||
|
||||
// Subpass dependencies for layout transitions
|
||||
std::array<VkSubpassDependency, 2> dependencies;
|
||||
|
||||
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependencies[0].dstSubpass = 0;
|
||||
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
dependencies[1].srcSubpass = 0;
|
||||
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo = {};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassInfo.pAttachments = attachments.data();
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpassDescription;
|
||||
renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
|
||||
renderPassInfo.pDependencies = dependencies.data();
|
||||
VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass));
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::enableExtensions()
|
||||
{
|
||||
// Require Vulkan 1.1
|
||||
apiVersion = VK_API_VERSION_1_1;
|
||||
|
||||
// Ray tracing related extensions required by this sample
|
||||
enabledDeviceExtensions.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
|
||||
if (!rayQueryOnly) {
|
||||
enabledDeviceExtensions.push_back(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
// Required by VK_KHR_acceleration_structure
|
||||
enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
|
||||
enabledDeviceExtensions.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
|
||||
enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
|
||||
|
||||
// Required for VK_KHR_ray_tracing_pipeline
|
||||
enabledDeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME);
|
||||
|
||||
// Required by VK_KHR_spirv_1_4
|
||||
enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
VulkanRaytracingSample::ScratchBuffer VulkanRaytracingSample::createScratchBuffer(VkDeviceSize size)
|
||||
{
|
||||
ScratchBuffer scratchBuffer{};
|
||||
// Buffer and memory
|
||||
VkBufferCreateInfo bufferCreateInfo{};
|
||||
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferCreateInfo.size = size;
|
||||
bufferCreateInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||
VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &scratchBuffer.handle));
|
||||
VkMemoryRequirements memoryRequirements{};
|
||||
vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, scratchBuffer.handle, &memoryRequirements);
|
||||
VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{};
|
||||
memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
|
||||
memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
|
||||
VkMemoryAllocateInfo memoryAllocateInfo = {};
|
||||
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo;
|
||||
memoryAllocateInfo.allocationSize = memoryRequirements.size;
|
||||
memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &scratchBuffer.memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, scratchBuffer.handle, scratchBuffer.memory, 0));
|
||||
// Buffer device address
|
||||
VkBufferDeviceAddressInfoKHR bufferDeviceAddresInfo{};
|
||||
bufferDeviceAddresInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
|
||||
bufferDeviceAddresInfo.buffer = scratchBuffer.handle;
|
||||
scratchBuffer.deviceAddress = vkGetBufferDeviceAddressKHR(vulkanDevice->logicalDevice, &bufferDeviceAddresInfo);
|
||||
return scratchBuffer;
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::deleteScratchBuffer(ScratchBuffer& scratchBuffer)
|
||||
{
|
||||
if (scratchBuffer.memory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(vulkanDevice->logicalDevice, scratchBuffer.memory, nullptr);
|
||||
}
|
||||
if (scratchBuffer.handle != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(vulkanDevice->logicalDevice, scratchBuffer.handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::createAccelerationStructure(AccelerationStructure& accelerationStructure, VkAccelerationStructureTypeKHR type, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo)
|
||||
{
|
||||
// Buffer and memory
|
||||
VkBufferCreateInfo bufferCreateInfo{};
|
||||
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferCreateInfo.size = buildSizeInfo.accelerationStructureSize;
|
||||
bufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||
VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &accelerationStructure.buffer));
|
||||
VkMemoryRequirements memoryRequirements{};
|
||||
vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, accelerationStructure.buffer, &memoryRequirements);
|
||||
VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{};
|
||||
memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
|
||||
memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
|
||||
VkMemoryAllocateInfo memoryAllocateInfo{};
|
||||
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo;
|
||||
memoryAllocateInfo.allocationSize = memoryRequirements.size;
|
||||
memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &accelerationStructure.memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, accelerationStructure.buffer, accelerationStructure.memory, 0));
|
||||
// Acceleration structure
|
||||
VkAccelerationStructureCreateInfoKHR accelerationStructureCreate_info{};
|
||||
accelerationStructureCreate_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
|
||||
accelerationStructureCreate_info.buffer = accelerationStructure.buffer;
|
||||
accelerationStructureCreate_info.size = buildSizeInfo.accelerationStructureSize;
|
||||
accelerationStructureCreate_info.type = type;
|
||||
vkCreateAccelerationStructureKHR(vulkanDevice->logicalDevice, &accelerationStructureCreate_info, nullptr, &accelerationStructure.handle);
|
||||
// AS device address
|
||||
VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{};
|
||||
accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
|
||||
accelerationDeviceAddressInfo.accelerationStructure = accelerationStructure.handle;
|
||||
accelerationStructure.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(vulkanDevice->logicalDevice, &accelerationDeviceAddressInfo);
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::deleteAccelerationStructure(AccelerationStructure& accelerationStructure)
|
||||
{
|
||||
vkFreeMemory(device, accelerationStructure.memory, nullptr);
|
||||
vkDestroyBuffer(device, accelerationStructure.buffer, nullptr);
|
||||
vkDestroyAccelerationStructureKHR(device, accelerationStructure.handle, nullptr);
|
||||
}
|
||||
|
||||
uint64_t VulkanRaytracingSample::getBufferDeviceAddress(VkBuffer buffer)
|
||||
{
|
||||
VkBufferDeviceAddressInfoKHR bufferDeviceAI{};
|
||||
bufferDeviceAI.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
|
||||
bufferDeviceAI.buffer = buffer;
|
||||
return vkGetBufferDeviceAddressKHR(vulkanDevice->logicalDevice, &bufferDeviceAI);
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::createStorageImage(VkFormat format, VkExtent3D extent)
|
||||
{
|
||||
// Release ressources if image is to be recreated
|
||||
if (storageImage.image != VK_NULL_HANDLE) {
|
||||
vkDestroyImageView(device, storageImage.view, nullptr);
|
||||
vkDestroyImage(device, storageImage.image, nullptr);
|
||||
vkFreeMemory(device, storageImage.memory, nullptr);
|
||||
storageImage = {};
|
||||
}
|
||||
|
||||
VkImageCreateInfo image = vks::initializers::imageCreateInfo();
|
||||
image.imageType = VK_IMAGE_TYPE_2D;
|
||||
image.format = format;
|
||||
image.extent = extent;
|
||||
image.mipLevels = 1;
|
||||
image.arrayLayers = 1;
|
||||
image.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &image, nullptr, &storageImage.image));
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, storageImage.image, &memReqs);
|
||||
VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo();
|
||||
memoryAllocateInfo.allocationSize = memReqs.size;
|
||||
memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memoryAllocateInfo, nullptr, &storageImage.memory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, storageImage.image, storageImage.memory, 0));
|
||||
|
||||
VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo();
|
||||
colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorImageView.format = format;
|
||||
colorImageView.subresourceRange = {};
|
||||
colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorImageView.subresourceRange.baseMipLevel = 0;
|
||||
colorImageView.subresourceRange.levelCount = 1;
|
||||
colorImageView.subresourceRange.baseArrayLayer = 0;
|
||||
colorImageView.subresourceRange.layerCount = 1;
|
||||
colorImageView.image = storageImage.image;
|
||||
VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &colorImageView, nullptr, &storageImage.view));
|
||||
|
||||
VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||
vks::tools::setImageLayout(cmdBuffer, storageImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 });
|
||||
vulkanDevice->flushCommandBuffer(cmdBuffer, queue);
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::deleteStorageImage()
|
||||
{
|
||||
vkDestroyImageView(vulkanDevice->logicalDevice, storageImage.view, nullptr);
|
||||
vkDestroyImage(vulkanDevice->logicalDevice, storageImage.image, nullptr);
|
||||
vkFreeMemory(vulkanDevice->logicalDevice, storageImage.memory, nullptr);
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::prepare()
|
||||
{
|
||||
VulkanExampleBase::prepare();
|
||||
// Get properties and features
|
||||
rayTracingPipelineProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
|
||||
VkPhysicalDeviceProperties2 deviceProperties2{};
|
||||
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
deviceProperties2.pNext = &rayTracingPipelineProperties;
|
||||
vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
|
||||
accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
|
||||
VkPhysicalDeviceFeatures2 deviceFeatures2{};
|
||||
deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
deviceFeatures2.pNext = &accelerationStructureFeatures;
|
||||
vkGetPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2);
|
||||
// Get the function pointers required for ray tracing
|
||||
vkGetBufferDeviceAddressKHR = reinterpret_cast<PFN_vkGetBufferDeviceAddressKHR>(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR"));
|
||||
vkCmdBuildAccelerationStructuresKHR = reinterpret_cast<PFN_vkCmdBuildAccelerationStructuresKHR>(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR"));
|
||||
vkBuildAccelerationStructuresKHR = reinterpret_cast<PFN_vkBuildAccelerationStructuresKHR>(vkGetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR"));
|
||||
vkCreateAccelerationStructureKHR = reinterpret_cast<PFN_vkCreateAccelerationStructureKHR>(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureKHR"));
|
||||
vkDestroyAccelerationStructureKHR = reinterpret_cast<PFN_vkDestroyAccelerationStructureKHR>(vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureKHR"));
|
||||
vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast<PFN_vkGetAccelerationStructureBuildSizesKHR>(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureBuildSizesKHR"));
|
||||
vkGetAccelerationStructureDeviceAddressKHR = reinterpret_cast<PFN_vkGetAccelerationStructureDeviceAddressKHR>(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureDeviceAddressKHR"));
|
||||
vkCmdTraceRaysKHR = reinterpret_cast<PFN_vkCmdTraceRaysKHR>(vkGetDeviceProcAddr(device, "vkCmdTraceRaysKHR"));
|
||||
vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesKHR>(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesKHR"));
|
||||
vkCreateRayTracingPipelinesKHR = reinterpret_cast<PFN_vkCreateRayTracingPipelinesKHR>(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesKHR"));
|
||||
// Update the render pass to keep the color attachment contents, so we can draw the UI on top of the ray traced output
|
||||
if (!rayQueryOnly) {
|
||||
updateRenderPass();
|
||||
}
|
||||
}
|
||||
|
||||
VkStridedDeviceAddressRegionKHR VulkanRaytracingSample::getSbtEntryStridedDeviceAddressRegion(VkBuffer buffer, uint32_t handleCount)
|
||||
{
|
||||
const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment);
|
||||
VkStridedDeviceAddressRegionKHR stridedDeviceAddressRegionKHR{};
|
||||
stridedDeviceAddressRegionKHR.deviceAddress = getBufferDeviceAddress(buffer);
|
||||
stridedDeviceAddressRegionKHR.stride = handleSizeAligned;
|
||||
stridedDeviceAddressRegionKHR.size = handleCount * handleSizeAligned;
|
||||
return stridedDeviceAddressRegionKHR;
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::createShaderBindingTable(ShaderBindingTable& shaderBindingTable, uint32_t handleCount)
|
||||
{
|
||||
// Create buffer to hold all shader handles for the SBT
|
||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
||||
VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&shaderBindingTable,
|
||||
rayTracingPipelineProperties.shaderGroupHandleSize * handleCount));
|
||||
// Get the strided address to be used when dispatching the rays
|
||||
shaderBindingTable.stridedDeviceAddressRegion = getSbtEntryStridedDeviceAddressRegion(shaderBindingTable.buffer, handleCount);
|
||||
// Map persistent
|
||||
shaderBindingTable.map();
|
||||
}
|
||||
|
||||
void VulkanRaytracingSample::drawUI(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer)
|
||||
{
|
||||
VkClearValue clearValues[2];
|
||||
clearValues[0].color = defaultClearColor;
|
||||
clearValues[1].depthStencil = { 1.0f, 0 };
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
|
||||
renderPassBeginInfo.renderPass = renderPass;
|
||||
renderPassBeginInfo.renderArea.offset.x = 0;
|
||||
renderPassBeginInfo.renderArea.offset.y = 0;
|
||||
renderPassBeginInfo.renderArea.extent.width = width;
|
||||
renderPassBeginInfo.renderArea.extent.height = height;
|
||||
renderPassBeginInfo.clearValueCount = 2;
|
||||
renderPassBeginInfo.pClearValues = clearValues;
|
||||
renderPassBeginInfo.framebuffer = framebuffer;
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
VulkanExampleBase::drawUI(commandBuffer);
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Extended sample base class for ray tracing based samples
|
||||
*
|
||||
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
#include "vulkanexamplebase.h"
|
||||
#include "VulkanTools.h"
|
||||
#include "VulkanDevice.hpp"
|
||||
|
||||
class VulkanRaytracingSample : public VulkanExampleBase
|
||||
{
|
||||
protected:
|
||||
// Update the default render pass with different color attachment load ops
|
||||
virtual void updateRenderPass();
|
||||
public:
|
||||
// Function pointers for ray tracing related stuff
|
||||
PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR;
|
||||
PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR;
|
||||
PFN_vkDestroyAccelerationStructureKHR vkDestroyAccelerationStructureKHR;
|
||||
PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR;
|
||||
PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR;
|
||||
PFN_vkBuildAccelerationStructuresKHR vkBuildAccelerationStructuresKHR;
|
||||
PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR;
|
||||
PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR;
|
||||
PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR;
|
||||
PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR;
|
||||
|
||||
// Available features and properties
|
||||
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties{};
|
||||
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures{};
|
||||
|
||||
// Enabled features and properties
|
||||
VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{};
|
||||
VkPhysicalDeviceRayTracingPipelineFeaturesKHR enabledRayTracingPipelineFeatures{};
|
||||
VkPhysicalDeviceAccelerationStructureFeaturesKHR enabledAccelerationStructureFeatures{};
|
||||
|
||||
// Holds information for a ray tracing scratch buffer that is used as a temporary storage
|
||||
struct ScratchBuffer
|
||||
{
|
||||
uint64_t deviceAddress = 0;
|
||||
VkBuffer handle = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
// Holds information for a ray tracing acceleration structure
|
||||
struct AccelerationStructure {
|
||||
VkAccelerationStructureKHR handle;
|
||||
uint64_t deviceAddress = 0;
|
||||
VkDeviceMemory memory;
|
||||
VkBuffer buffer;
|
||||
};
|
||||
|
||||
// Holds information for a storage image that the ray tracing shaders output to
|
||||
struct StorageImage {
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
VkFormat format;
|
||||
} storageImage;
|
||||
|
||||
// Extends the buffer class and holds information for a shader binding table
|
||||
class ShaderBindingTable : public vks::Buffer {
|
||||
public:
|
||||
VkStridedDeviceAddressRegionKHR stridedDeviceAddressRegion{};
|
||||
};
|
||||
|
||||
// Set to true, to denote that the sample only uses ray queries (changes extension and render pass handling)
|
||||
bool rayQueryOnly = false;
|
||||
|
||||
void enableExtensions();
|
||||
ScratchBuffer createScratchBuffer(VkDeviceSize size);
|
||||
void deleteScratchBuffer(ScratchBuffer& scratchBuffer);
|
||||
void createAccelerationStructure(AccelerationStructure& accelerationStructure, VkAccelerationStructureTypeKHR type, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo);
|
||||
void deleteAccelerationStructure(AccelerationStructure& accelerationStructure);
|
||||
uint64_t getBufferDeviceAddress(VkBuffer buffer);
|
||||
void createStorageImage(VkFormat format, VkExtent3D extent);
|
||||
void deleteStorageImage();
|
||||
VkStridedDeviceAddressRegionKHR getSbtEntryStridedDeviceAddressRegion(VkBuffer buffer, uint32_t handleCount);
|
||||
void createShaderBindingTable(ShaderBindingTable& shaderBindingTable, uint32_t handleCount);
|
||||
// Draw the ImGUI UI overlay using a render pass
|
||||
void drawUI(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer);
|
||||
|
||||
virtual void prepare();
|
||||
};
|
|
@ -1,616 +0,0 @@
|
|||
/*
|
||||
* Class wrapping access to the swap chain
|
||||
*
|
||||
* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system
|
||||
*
|
||||
* Copyright (C) 2016-2021 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#include "VulkanSwapChain.h"
|
||||
|
||||
/** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
void VulkanSwapChain::initSurface(void* platformHandle, void* platformWindow)
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
void VulkanSwapChain::initSurface(ANativeWindow* window)
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
void VulkanSwapChain::initSurface(IDirectFB* dfb, IDirectFBSurface* window)
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
void VulkanSwapChain::initSurface(wl_display *display, wl_surface *window)
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
void VulkanSwapChain::initSurface(xcb_connection_t* connection, xcb_window_t window)
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
void VulkanSwapChain::initSurface(void* view)
|
||||
#elif (defined(_DIRECT2DISPLAY) || defined(VK_USE_PLATFORM_HEADLESS_EXT))
|
||||
void VulkanSwapChain::initSurface(uint32_t width, uint32_t height)
|
||||
#endif
|
||||
{
|
||||
VkResult err = VK_SUCCESS;
|
||||
|
||||
// Create the os-specific surface
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle;
|
||||
surfaceCreateInfo.hwnd = (HWND)platformWindow;
|
||||
err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.window = window;
|
||||
err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_IOS_MVK)
|
||||
VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
||||
surfaceCreateInfo.pNext = NULL;
|
||||
surfaceCreateInfo.flags = 0;
|
||||
surfaceCreateInfo.pView = view;
|
||||
err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
surfaceCreateInfo.pNext = NULL;
|
||||
surfaceCreateInfo.flags = 0;
|
||||
surfaceCreateInfo.pView = view;
|
||||
err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface);
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
createDirect2DisplaySurface(width, height);
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
VkDirectFBSurfaceCreateInfoEXT surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT;
|
||||
surfaceCreateInfo.dfb = dfb;
|
||||
surfaceCreateInfo.surface = window;
|
||||
err = vkCreateDirectFBSurfaceEXT(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.display = display;
|
||||
surfaceCreateInfo.surface = window;
|
||||
err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.connection = connection;
|
||||
surfaceCreateInfo.window = window;
|
||||
err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_HEADLESS_EXT)
|
||||
VkHeadlessSurfaceCreateInfoEXT surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT;
|
||||
PFN_vkCreateHeadlessSurfaceEXT fpCreateHeadlessSurfaceEXT = (PFN_vkCreateHeadlessSurfaceEXT)vkGetInstanceProcAddr(instance, "vkCreateHeadlessSurfaceEXT");
|
||||
if (!fpCreateHeadlessSurfaceEXT){
|
||||
vks::tools::exitFatal("Could not fetch function pointer for the headless extension!", -1);
|
||||
}
|
||||
err = fpCreateHeadlessSurfaceEXT(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#endif
|
||||
|
||||
if (err != VK_SUCCESS) {
|
||||
vks::tools::exitFatal("Could not create surface!", err);
|
||||
}
|
||||
|
||||
// Get available queue family properties
|
||||
uint32_t queueCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
|
||||
assert(queueCount >= 1);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueProps(queueCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
|
||||
|
||||
// Iterate over each queue to learn whether it supports presenting:
|
||||
// Find a queue with present support
|
||||
// Will be used to present the swap chain images to the windowing system
|
||||
std::vector<VkBool32> supportsPresent(queueCount);
|
||||
for (uint32_t i = 0; i < queueCount; i++)
|
||||
{
|
||||
fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]);
|
||||
}
|
||||
|
||||
// Search for a graphics and a present queue in the array of queue
|
||||
// families, try to find one that supports both
|
||||
uint32_t graphicsQueueNodeIndex = UINT32_MAX;
|
||||
uint32_t presentQueueNodeIndex = UINT32_MAX;
|
||||
for (uint32_t i = 0; i < queueCount; i++)
|
||||
{
|
||||
if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
||||
{
|
||||
if (graphicsQueueNodeIndex == UINT32_MAX)
|
||||
{
|
||||
graphicsQueueNodeIndex = i;
|
||||
}
|
||||
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
graphicsQueueNodeIndex = i;
|
||||
presentQueueNodeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (presentQueueNodeIndex == UINT32_MAX)
|
||||
{
|
||||
// If there's no queue that supports both present and graphics
|
||||
// try to find a separate present queue
|
||||
for (uint32_t i = 0; i < queueCount; ++i)
|
||||
{
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
presentQueueNodeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if either a graphics or a presenting queue hasn't been found
|
||||
if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX)
|
||||
{
|
||||
vks::tools::exitFatal("Could not find a graphics and/or presenting queue!", -1);
|
||||
}
|
||||
|
||||
// todo : Add support for separate graphics and presenting queue
|
||||
if (graphicsQueueNodeIndex != presentQueueNodeIndex)
|
||||
{
|
||||
vks::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", -1);
|
||||
}
|
||||
|
||||
queueNodeIndex = graphicsQueueNodeIndex;
|
||||
|
||||
// Get list of supported surface formats
|
||||
uint32_t formatCount;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL));
|
||||
assert(formatCount > 0);
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data()));
|
||||
|
||||
// If the surface format list only includes one entry with VK_FORMAT_UNDEFINED,
|
||||
// there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM
|
||||
if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
|
||||
{
|
||||
colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
colorSpace = surfaceFormats[0].colorSpace;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate over the list of available surface format and
|
||||
// check for the presence of VK_FORMAT_B8G8R8A8_UNORM
|
||||
bool found_B8G8R8A8_UNORM = false;
|
||||
for (auto&& surfaceFormat : surfaceFormats)
|
||||
{
|
||||
if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM)
|
||||
{
|
||||
colorFormat = surfaceFormat.format;
|
||||
colorSpace = surfaceFormat.colorSpace;
|
||||
found_B8G8R8A8_UNORM = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// in case VK_FORMAT_B8G8R8A8_UNORM is not available
|
||||
// select the first available color format
|
||||
if (!found_B8G8R8A8_UNORM)
|
||||
{
|
||||
colorFormat = surfaceFormats[0].format;
|
||||
colorSpace = surfaceFormats[0].colorSpace;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance, physical and logical device to use for the swapchain and get all required function pointers
|
||||
*
|
||||
* @param instance Vulkan instance to use
|
||||
* @param physicalDevice Physical device used to query properties and formats relevant to the swapchain
|
||||
* @param device Logical representation of the device to create the swapchain for
|
||||
*
|
||||
*/
|
||||
void VulkanSwapChain::connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device)
|
||||
{
|
||||
this->instance = instance;
|
||||
this->physicalDevice = physicalDevice;
|
||||
this->device = device;
|
||||
fpGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
|
||||
fpGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
|
||||
fpGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
|
||||
fpGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
|
||||
|
||||
fpCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(device, "vkCreateSwapchainKHR"));
|
||||
fpDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(device, "vkDestroySwapchainKHR"));
|
||||
fpGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(device, "vkGetSwapchainImagesKHR"));
|
||||
fpAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(device, "vkAcquireNextImageKHR"));
|
||||
fpQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(vkGetDeviceProcAddr(device, "vkQueuePresentKHR"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the swapchain and get its images with given width and height
|
||||
*
|
||||
* @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
||||
* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
||||
* @param vsync (Optional) Can be used to force vsync-ed rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode)
|
||||
*/
|
||||
void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync, bool fullscreen)
|
||||
{
|
||||
// Store the current swap chain handle so we can use it later on to ease up recreation
|
||||
VkSwapchainKHR oldSwapchain = swapChain;
|
||||
|
||||
// Get physical device surface properties and formats
|
||||
VkSurfaceCapabilitiesKHR surfCaps;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps));
|
||||
|
||||
// Get available present modes
|
||||
uint32_t presentModeCount;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL));
|
||||
assert(presentModeCount > 0);
|
||||
|
||||
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()));
|
||||
|
||||
VkExtent2D swapchainExtent = {};
|
||||
// If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
|
||||
if (surfCaps.currentExtent.width == (uint32_t)-1)
|
||||
{
|
||||
// If the surface size is undefined, the size is set to
|
||||
// the size of the images requested.
|
||||
swapchainExtent.width = *width;
|
||||
swapchainExtent.height = *height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the surface size is defined, the swap chain size must match
|
||||
swapchainExtent = surfCaps.currentExtent;
|
||||
*width = surfCaps.currentExtent.width;
|
||||
*height = surfCaps.currentExtent.height;
|
||||
}
|
||||
|
||||
|
||||
// Select a present mode for the swapchain
|
||||
|
||||
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
|
||||
// This mode waits for the vertical blank ("v-sync")
|
||||
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
// If v-sync is not requested, try to find a mailbox mode
|
||||
// It's the lowest latency non-tearing present mode available
|
||||
if (!vsync)
|
||||
{
|
||||
for (size_t i = 0; i < presentModeCount; i++)
|
||||
{
|
||||
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
break;
|
||||
}
|
||||
if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the number of images
|
||||
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
|
||||
#if (defined(VK_USE_PLATFORM_MACOS_MVK) && defined(VK_EXAMPLE_XCODE_GENERATED))
|
||||
// SRS - Work around known MoltenVK issue re 2x frame rate when vsync (VK_PRESENT_MODE_FIFO_KHR) enabled
|
||||
struct utsname sysInfo;
|
||||
uname(&sysInfo);
|
||||
// SRS - When vsync is on, use minImageCount when not in fullscreen or when running on Apple Silcon
|
||||
// This forces swapchain image acquire frame rate to match display vsync frame rate
|
||||
if (vsync && (!fullscreen || strcmp(sysInfo.machine, "arm64") == 0))
|
||||
{
|
||||
desiredNumberOfSwapchainImages = surfCaps.minImageCount;
|
||||
}
|
||||
#endif
|
||||
if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
|
||||
{
|
||||
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
|
||||
}
|
||||
|
||||
// Find the transformation of the surface
|
||||
VkSurfaceTransformFlagsKHR preTransform;
|
||||
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
|
||||
{
|
||||
// We prefer a non-rotated transform
|
||||
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
}
|
||||
else
|
||||
{
|
||||
preTransform = surfCaps.currentTransform;
|
||||
}
|
||||
|
||||
// Find a supported composite alpha format (not all devices support alpha opaque)
|
||||
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
// Simply select the first composite alpha format available
|
||||
std::vector<VkCompositeAlphaFlagBitsKHR> compositeAlphaFlags = {
|
||||
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
|
||||
};
|
||||
for (auto& compositeAlphaFlag : compositeAlphaFlags) {
|
||||
if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) {
|
||||
compositeAlpha = compositeAlphaFlag;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainCI = {};
|
||||
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainCI.surface = surface;
|
||||
swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
|
||||
swapchainCI.imageFormat = colorFormat;
|
||||
swapchainCI.imageColorSpace = colorSpace;
|
||||
swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
|
||||
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
|
||||
swapchainCI.imageArrayLayers = 1;
|
||||
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapchainCI.queueFamilyIndexCount = 0;
|
||||
swapchainCI.presentMode = swapchainPresentMode;
|
||||
// Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images
|
||||
swapchainCI.oldSwapchain = oldSwapchain;
|
||||
// Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
|
||||
swapchainCI.clipped = VK_TRUE;
|
||||
swapchainCI.compositeAlpha = compositeAlpha;
|
||||
|
||||
// Enable transfer source on swap chain images if supported
|
||||
if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) {
|
||||
swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
// Enable transfer destination on swap chain images if supported
|
||||
if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) {
|
||||
swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain));
|
||||
|
||||
// If an existing swap chain is re-created, destroy the old swap chain
|
||||
// This also cleans up all the presentable images
|
||||
if (oldSwapchain != VK_NULL_HANDLE)
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
fpDestroySwapchainKHR(device, oldSwapchain, nullptr);
|
||||
}
|
||||
VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL));
|
||||
|
||||
// Get the swap chain images
|
||||
images.resize(imageCount);
|
||||
VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data()));
|
||||
|
||||
// Get the swap chain buffers containing the image and imageview
|
||||
buffers.resize(imageCount);
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
VkImageViewCreateInfo colorAttachmentView = {};
|
||||
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
colorAttachmentView.pNext = NULL;
|
||||
colorAttachmentView.format = colorFormat;
|
||||
colorAttachmentView.components = {
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A
|
||||
};
|
||||
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorAttachmentView.subresourceRange.baseMipLevel = 0;
|
||||
colorAttachmentView.subresourceRange.levelCount = 1;
|
||||
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
|
||||
colorAttachmentView.subresourceRange.layerCount = 1;
|
||||
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorAttachmentView.flags = 0;
|
||||
|
||||
buffers[i].image = images[i];
|
||||
|
||||
colorAttachmentView.image = buffers[i].image;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the next image in the swap chain
|
||||
*
|
||||
* @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use
|
||||
* @param imageIndex Pointer to the image index that will be increased if the next image could be acquired
|
||||
*
|
||||
* @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX
|
||||
*
|
||||
* @return VkResult of the image acquisition
|
||||
*/
|
||||
VkResult VulkanSwapChain::acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex)
|
||||
{
|
||||
// By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown
|
||||
// With that we don't have to handle VK_NOT_READY
|
||||
return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an image for presentation
|
||||
*
|
||||
* @param queue Presentation queue for presenting the image
|
||||
* @param imageIndex Index of the swapchain image to queue for presentation
|
||||
* @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE)
|
||||
*
|
||||
* @return VkResult of the queue presentation
|
||||
*/
|
||||
VkResult VulkanSwapChain::queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore)
|
||||
{
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.pNext = NULL;
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = &swapChain;
|
||||
presentInfo.pImageIndices = &imageIndex;
|
||||
// Check if a wait semaphore has been specified to wait for before presenting the image
|
||||
if (waitSemaphore != VK_NULL_HANDLE)
|
||||
{
|
||||
presentInfo.pWaitSemaphores = &waitSemaphore;
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
}
|
||||
return fpQueuePresentKHR(queue, &presentInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy and free Vulkan resources used for the swapchain
|
||||
*/
|
||||
void VulkanSwapChain::cleanup()
|
||||
{
|
||||
if (swapChain != VK_NULL_HANDLE)
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
}
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
fpDestroySwapchainKHR(device, swapChain, nullptr);
|
||||
vkDestroySurfaceKHR(instance, surface, nullptr);
|
||||
}
|
||||
surface = VK_NULL_HANDLE;
|
||||
swapChain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
#if defined(_DIRECT2DISPLAY)
|
||||
/**
|
||||
* Create direct to display surface
|
||||
*/
|
||||
void VulkanSwapChain::createDirect2DisplaySurface(uint32_t width, uint32_t height)
|
||||
{
|
||||
uint32_t displayPropertyCount;
|
||||
|
||||
// Get display property
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL);
|
||||
VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount];
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties);
|
||||
|
||||
// Get plane property
|
||||
uint32_t planePropertyCount;
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL);
|
||||
VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount];
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties);
|
||||
|
||||
VkDisplayKHR display = VK_NULL_HANDLE;
|
||||
VkDisplayModeKHR displayMode;
|
||||
VkDisplayModePropertiesKHR* pModeProperties;
|
||||
bool foundMode = false;
|
||||
|
||||
for(uint32_t i = 0; i < displayPropertyCount;++i)
|
||||
{
|
||||
display = pDisplayProperties[i].display;
|
||||
uint32_t modeCount;
|
||||
vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL);
|
||||
pModeProperties = new VkDisplayModePropertiesKHR[modeCount];
|
||||
vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties);
|
||||
|
||||
for (uint32_t j = 0; j < modeCount; ++j)
|
||||
{
|
||||
const VkDisplayModePropertiesKHR* mode = &pModeProperties[j];
|
||||
|
||||
if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height)
|
||||
{
|
||||
displayMode = mode->displayMode;
|
||||
foundMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundMode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
delete [] pModeProperties;
|
||||
}
|
||||
|
||||
if(!foundMode)
|
||||
{
|
||||
vks::tools::exitFatal("Can't find a display and a display mode!", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for a best plane we can use
|
||||
uint32_t bestPlaneIndex = UINT32_MAX;
|
||||
VkDisplayKHR* pDisplays = NULL;
|
||||
for(uint32_t i = 0; i < planePropertyCount; i++)
|
||||
{
|
||||
uint32_t planeIndex=i;
|
||||
uint32_t displayCount;
|
||||
vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL);
|
||||
if (pDisplays)
|
||||
{
|
||||
delete [] pDisplays;
|
||||
}
|
||||
pDisplays = new VkDisplayKHR[displayCount];
|
||||
vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays);
|
||||
|
||||
// Find a display that matches the current plane
|
||||
bestPlaneIndex = UINT32_MAX;
|
||||
for(uint32_t j = 0; j < displayCount; j++)
|
||||
{
|
||||
if(display == pDisplays[j])
|
||||
{
|
||||
bestPlaneIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(bestPlaneIndex != UINT32_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestPlaneIndex == UINT32_MAX)
|
||||
{
|
||||
vks::tools::exitFatal("Can't find a plane for displaying!", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
VkDisplayPlaneCapabilitiesKHR planeCap;
|
||||
vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap);
|
||||
VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0;
|
||||
|
||||
if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
}
|
||||
|
||||
VkDisplaySurfaceCreateInfoKHR surfaceInfo{};
|
||||
surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceInfo.pNext = NULL;
|
||||
surfaceInfo.flags = 0;
|
||||
surfaceInfo.displayMode = displayMode;
|
||||
surfaceInfo.planeIndex = bestPlaneIndex;
|
||||
surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex;
|
||||
surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
surfaceInfo.globalAlpha = 1.0;
|
||||
surfaceInfo.alphaMode = alphaMode;
|
||||
surfaceInfo.imageExtent.width = width;
|
||||
surfaceInfo.imageExtent.height = height;
|
||||
|
||||
VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface);
|
||||
if (result !=VK_SUCCESS) {
|
||||
vks::tools::exitFatal("Failed to create surface!", result);
|
||||
}
|
||||
|
||||
delete[] pDisplays;
|
||||
delete[] pModeProperties;
|
||||
delete[] pDisplayProperties;
|
||||
delete[] pPlaneProperties;
|
||||
}
|
||||
#endif
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Class wrapping access to the swap chain
|
||||
*
|
||||
* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system
|
||||
*
|
||||
* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "VulkanTools.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "VulkanAndroid.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
typedef struct _SwapChainBuffers {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
} SwapChainBuffer;
|
||||
|
||||
class VulkanSwapChain
|
||||
{
|
||||
private:
|
||||
VkInstance instance;
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkSurfaceKHR surface;
|
||||
// Function pointers
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
|
||||
PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
|
||||
PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
|
||||
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
|
||||
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
|
||||
PFN_vkQueuePresentKHR fpQueuePresentKHR;
|
||||
public:
|
||||
VkFormat colorFormat;
|
||||
VkColorSpaceKHR colorSpace;
|
||||
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
|
||||
uint32_t imageCount;
|
||||
std::vector<VkImage> images;
|
||||
std::vector<SwapChainBuffer> buffers;
|
||||
uint32_t queueNodeIndex = UINT32_MAX;
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
void initSurface(void* platformHandle, void* platformWindow);
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
void initSurface(ANativeWindow* window);
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
void initSurface(IDirectFB* dfb, IDirectFBSurface* window);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
void initSurface(wl_display* display, wl_surface* window);
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
void initSurface(xcb_connection_t* connection, xcb_window_t window);
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
void initSurface(void* view);
|
||||
#elif (defined(_DIRECT2DISPLAY) || defined(VK_USE_PLATFORM_HEADLESS_EXT))
|
||||
void initSurface(uint32_t width, uint32_t height);
|
||||
#if defined(_DIRECT2DISPLAY)
|
||||
void createDirect2DisplaySurface(uint32_t width, uint32_t height);
|
||||
#endif
|
||||
#endif
|
||||
void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device);
|
||||
void create(uint32_t* width, uint32_t* height, bool vsync = false, bool fullscreen = false);
|
||||
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t* imageIndex);
|
||||
VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE);
|
||||
void cleanup();
|
||||
};
|
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* Class wrapping access to the swap chain
|
||||
*
|
||||
* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system
|
||||
*
|
||||
* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "macros.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "VulkanAndroid.h"
|
||||
#endif
|
||||
|
||||
typedef struct _SwapChainBuffers {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
} SwapChainBuffer;
|
||||
|
||||
class VulkanSwapChain
|
||||
{
|
||||
private:
|
||||
VkInstance instance;
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
// Function pointers
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
|
||||
PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
|
||||
PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
|
||||
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
|
||||
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
|
||||
PFN_vkQueuePresentKHR fpQueuePresentKHR;
|
||||
public:
|
||||
VkFormat colorFormat;
|
||||
VkColorSpaceKHR colorSpace;
|
||||
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
|
||||
uint32_t imageCount;
|
||||
std::vector<VkImage> images;
|
||||
std::vector<SwapChainBuffer> buffers;
|
||||
VkExtent2D extent = {};
|
||||
uint32_t queueNodeIndex = UINT32_MAX;
|
||||
|
||||
/** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
void initSurface(void* platformHandle, void* platformWindow)
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
void initSurface(ANativeWindow* window)
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
void initSurface(wl_display *display, wl_surface *window)
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
void initSurface(xcb_connection_t* connection, xcb_window_t window)
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
void initSurface(void* view)
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
void initSurface(uint32_t width, uint32_t height)
|
||||
#endif
|
||||
{
|
||||
VkResult err = VK_SUCCESS;
|
||||
|
||||
// Create the os-specific surface
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle;
|
||||
surfaceCreateInfo.hwnd = (HWND)platformWindow;
|
||||
err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.window = window;
|
||||
err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_IOS_MVK)
|
||||
VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
||||
surfaceCreateInfo.pNext = NULL;
|
||||
surfaceCreateInfo.flags = 0;
|
||||
surfaceCreateInfo.pView = view;
|
||||
err = vkCreateIOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||
surfaceCreateInfo.pNext = NULL;
|
||||
surfaceCreateInfo.flags = 0;
|
||||
surfaceCreateInfo.pView = view;
|
||||
err = vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, NULL, &surface);
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
createDirect2DisplaySurface(width, height);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.display = display;
|
||||
surfaceCreateInfo.surface = window;
|
||||
err = vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {};
|
||||
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceCreateInfo.connection = connection;
|
||||
surfaceCreateInfo.window = window;
|
||||
err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
|
||||
#endif
|
||||
|
||||
if (err != VK_SUCCESS) {
|
||||
std::cerr << "Could not create surface!" << std::endl;
|
||||
exit(err);
|
||||
}
|
||||
|
||||
// Get available queue family properties
|
||||
uint32_t queueCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
|
||||
assert(queueCount >= 1);
|
||||
|
||||
std::vector<VkQueueFamilyProperties> queueProps(queueCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
|
||||
|
||||
// Iterate over each queue to learn whether it supports presenting:
|
||||
// Find a queue with present support
|
||||
// Will be used to present the swap chain images to the windowing system
|
||||
std::vector<VkBool32> supportsPresent(queueCount);
|
||||
for (uint32_t i = 0; i < queueCount; i++)
|
||||
{
|
||||
fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]);
|
||||
}
|
||||
|
||||
// Search for a graphics and a present queue in the array of queue
|
||||
// families, try to find one that supports both
|
||||
uint32_t graphicsQueueNodeIndex = UINT32_MAX;
|
||||
uint32_t presentQueueNodeIndex = UINT32_MAX;
|
||||
for (uint32_t i = 0; i < queueCount; i++)
|
||||
{
|
||||
if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
||||
{
|
||||
if (graphicsQueueNodeIndex == UINT32_MAX)
|
||||
{
|
||||
graphicsQueueNodeIndex = i;
|
||||
}
|
||||
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
graphicsQueueNodeIndex = i;
|
||||
presentQueueNodeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (presentQueueNodeIndex == UINT32_MAX)
|
||||
{
|
||||
// If there's no queue that supports both present and graphics
|
||||
// try to find a separate present queue
|
||||
for (uint32_t i = 0; i < queueCount; ++i)
|
||||
{
|
||||
if (supportsPresent[i] == VK_TRUE)
|
||||
{
|
||||
presentQueueNodeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if either a graphics or a presenting queue hasn't been found
|
||||
if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) {
|
||||
std::cerr << "Could not find a graphics and/or presenting queue!" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// todo : Add support for separate graphics and presenting queue
|
||||
if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
|
||||
std::cerr << "Separate graphics and presenting queues are not supported yet!" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
queueNodeIndex = graphicsQueueNodeIndex;
|
||||
|
||||
// Get list of supported surface formats
|
||||
uint32_t formatCount;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL));
|
||||
assert(formatCount > 0);
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data()));
|
||||
|
||||
// If the surface format list only includes one entry with VK_FORMAT_UNDEFINED,
|
||||
// there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM
|
||||
if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
|
||||
{
|
||||
colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
colorSpace = surfaceFormats[0].colorSpace;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate over the list of available surface format and
|
||||
// check for the presence of VK_FORMAT_B8G8R8A8_UNORM
|
||||
bool found_B8G8R8A8_UNORM = false;
|
||||
for (auto&& surfaceFormat : surfaceFormats)
|
||||
{
|
||||
// Prefer SRGB
|
||||
if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB)
|
||||
{
|
||||
colorFormat = surfaceFormat.format;
|
||||
colorSpace = surfaceFormat.colorSpace;
|
||||
found_B8G8R8A8_UNORM = true;
|
||||
break;
|
||||
}
|
||||
//if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM)
|
||||
//{
|
||||
// colorFormat = surfaceFormat.format;
|
||||
// colorSpace = surfaceFormat.colorSpace;
|
||||
// found_B8G8R8A8_UNORM = true;
|
||||
// break;
|
||||
//}
|
||||
}
|
||||
|
||||
// in case VK_FORMAT_B8G8R8A8_UNORM is not available
|
||||
// select the first available color format
|
||||
if (!found_B8G8R8A8_UNORM)
|
||||
{
|
||||
colorFormat = surfaceFormats[0].format;
|
||||
colorSpace = surfaceFormats[0].colorSpace;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance, physical and logical device to use for the swapchain and get all required function pointers
|
||||
*
|
||||
* @param instance Vulkan instance to use
|
||||
* @param physicalDevice Physical device used to query properties and formats relevant to the swapchain
|
||||
* @param device Logical representation of the device to create the swapchain for
|
||||
*
|
||||
*/
|
||||
void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device)
|
||||
{
|
||||
this->instance = instance;
|
||||
this->physicalDevice = physicalDevice;
|
||||
this->device = device;
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR);
|
||||
GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR);
|
||||
GET_DEVICE_PROC_ADDR(device, QueuePresentKHR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the swapchain and get it's images with given width and height
|
||||
*
|
||||
* @param width Pointer to the width of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
||||
* @param height Pointer to the height of the swapchain (may be adjusted to fit the requirements of the swapchain)
|
||||
* @param vsync (Optional) Can be used to force vsync'd rendering (by using VK_PRESENT_MODE_FIFO_KHR as presentation mode)
|
||||
*/
|
||||
void create(uint32_t *width, uint32_t *height, bool vsync = false)
|
||||
{
|
||||
VkSwapchainKHR oldSwapchain = swapChain;
|
||||
|
||||
// Get physical device surface properties and formats
|
||||
VkSurfaceCapabilitiesKHR surfCaps;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps));
|
||||
|
||||
// Get available present modes
|
||||
uint32_t presentModeCount;
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL));
|
||||
assert(presentModeCount > 0);
|
||||
|
||||
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
||||
VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()));
|
||||
|
||||
// If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
|
||||
if (surfCaps.currentExtent.width == (uint32_t)-1)
|
||||
{
|
||||
// If the surface size is undefined, the size is set to
|
||||
// the size of the images requested.
|
||||
extent.width = *width;
|
||||
extent.height = *height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the surface size is defined, the swap chain size must match
|
||||
extent = surfCaps.currentExtent;
|
||||
*width = surfCaps.currentExtent.width;
|
||||
*height = surfCaps.currentExtent.height;
|
||||
}
|
||||
|
||||
|
||||
// Select a present mode for the swapchain
|
||||
|
||||
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
|
||||
// This mode waits for the vertical blank ("v-sync")
|
||||
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
// If v-sync is not requested, try to find a mailbox mode
|
||||
// It's the lowest latency non-tearing present mode available
|
||||
if (!vsync)
|
||||
{
|
||||
for (size_t i = 0; i < presentModeCount; i++)
|
||||
{
|
||||
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
break;
|
||||
}
|
||||
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
|
||||
{
|
||||
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the number of images
|
||||
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
|
||||
if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
|
||||
{
|
||||
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
|
||||
}
|
||||
|
||||
// Find the transformation of the surface
|
||||
VkSurfaceTransformFlagsKHR preTransform;
|
||||
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
|
||||
{
|
||||
// We prefer a non-rotated transform
|
||||
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
}
|
||||
else
|
||||
{
|
||||
preTransform = surfCaps.currentTransform;
|
||||
}
|
||||
|
||||
// Find a supported composite alpha format (not all devices support alpha opaque)
|
||||
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
// Simply select the first composite alpha format available
|
||||
std::vector<VkCompositeAlphaFlagBitsKHR> compositeAlphaFlags = {
|
||||
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
|
||||
};
|
||||
for (auto& compositeAlphaFlag : compositeAlphaFlags) {
|
||||
if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) {
|
||||
compositeAlpha = compositeAlphaFlag;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainCI = {};
|
||||
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainCI.pNext = NULL;
|
||||
swapchainCI.surface = surface;
|
||||
swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
|
||||
swapchainCI.imageFormat = colorFormat;
|
||||
swapchainCI.imageColorSpace = colorSpace;
|
||||
swapchainCI.imageExtent = { extent.width, extent.height };
|
||||
swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
|
||||
swapchainCI.imageArrayLayers = 1;
|
||||
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapchainCI.queueFamilyIndexCount = 0;
|
||||
swapchainCI.pQueueFamilyIndices = NULL;
|
||||
swapchainCI.presentMode = swapchainPresentMode;
|
||||
swapchainCI.oldSwapchain = oldSwapchain;
|
||||
// Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
|
||||
swapchainCI.clipped = VK_TRUE;
|
||||
swapchainCI.compositeAlpha = compositeAlpha;
|
||||
|
||||
// Set additional usage flag for blitting from the swapchain images if supported
|
||||
VkFormatProperties formatProps;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, colorFormat, &formatProps);
|
||||
if ((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR) || (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
|
||||
swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain));
|
||||
|
||||
// If an existing swap chain is re-created, destroy the old swap chain
|
||||
// This also cleans up all the presentable images
|
||||
if (oldSwapchain != VK_NULL_HANDLE)
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
fpDestroySwapchainKHR(device, oldSwapchain, nullptr);
|
||||
}
|
||||
VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL));
|
||||
|
||||
// Get the swap chain images
|
||||
images.resize(imageCount);
|
||||
VK_CHECK_RESULT(fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data()));
|
||||
|
||||
// Get the swap chain buffers containing the image and imageview
|
||||
buffers.resize(imageCount);
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
VkImageViewCreateInfo colorAttachmentView = {};
|
||||
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
colorAttachmentView.pNext = NULL;
|
||||
colorAttachmentView.format = colorFormat;
|
||||
colorAttachmentView.components = {
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A
|
||||
};
|
||||
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
colorAttachmentView.subresourceRange.baseMipLevel = 0;
|
||||
colorAttachmentView.subresourceRange.levelCount = 1;
|
||||
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
|
||||
colorAttachmentView.subresourceRange.layerCount = 1;
|
||||
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
colorAttachmentView.flags = 0;
|
||||
|
||||
buffers[i].image = images[i];
|
||||
|
||||
colorAttachmentView.image = buffers[i].image;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the next image in the swap chain
|
||||
*
|
||||
* @param presentCompleteSemaphore (Optional) Semaphore that is signaled when the image is ready for use
|
||||
* @param imageIndex Pointer to the image index that will be increased if the next image could be acquired
|
||||
*
|
||||
* @note The function will always wait until the next image has been acquired by setting timeout to UINT64_MAX
|
||||
*
|
||||
* @return VkResult of the image acquisition
|
||||
*/
|
||||
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex)
|
||||
{
|
||||
if (swapChain == VK_NULL_HANDLE) {
|
||||
// Probably acquireNextImage() is called just after cleanup() (e.g. window has been terminated on Android).
|
||||
// todo : Use a dedicated error code.
|
||||
return VK_ERROR_OUT_OF_DATE_KHR;
|
||||
}
|
||||
|
||||
// By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown
|
||||
// With that we don't have to handle VK_NOT_READY
|
||||
return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an image for presentation
|
||||
*
|
||||
* @param queue Presentation queue for presenting the image
|
||||
* @param imageIndex Index of the swapchain image to queue for presentation
|
||||
* @param waitSemaphore (Optional) Semaphore that is waited on before the image is presented (only used if != VK_NULL_HANDLE)
|
||||
*
|
||||
* @return VkResult of the queue presentation
|
||||
*/
|
||||
VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE)
|
||||
{
|
||||
VkPresentInfoKHR presentInfo = {};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.pNext = NULL;
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = &swapChain;
|
||||
presentInfo.pImageIndices = &imageIndex;
|
||||
// Check if a wait semaphore has been specified to wait for before presenting the image
|
||||
if (waitSemaphore != VK_NULL_HANDLE)
|
||||
{
|
||||
presentInfo.pWaitSemaphores = &waitSemaphore;
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
}
|
||||
return fpQueuePresentKHR(queue, &presentInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy and free Vulkan resources used for the swapchain
|
||||
*/
|
||||
void cleanup()
|
||||
{
|
||||
if (swapChain != VK_NULL_HANDLE)
|
||||
{
|
||||
for (uint32_t i = 0; i < imageCount; i++)
|
||||
{
|
||||
vkDestroyImageView(device, buffers[i].view, nullptr);
|
||||
}
|
||||
}
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
fpDestroySwapchainKHR(device, swapChain, nullptr);
|
||||
vkDestroySurfaceKHR(instance, surface, nullptr);
|
||||
}
|
||||
surface = VK_NULL_HANDLE;
|
||||
swapChain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
#if defined(_DIRECT2DISPLAY)
|
||||
|
||||
void exitFatal(const std::string& message, int32_t exitCode)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
if (!errorModeSilent) {
|
||||
MessageBox(NULL, message.c_str(), NULL, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
LOGE("Fatal error: %s", message.c_str());
|
||||
vks::android::showAlert(message.c_str());
|
||||
#endif
|
||||
std::cerr << message << "\n";
|
||||
#if !defined(__ANDROID__)
|
||||
exit(exitCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Create direct to display surface
|
||||
*/
|
||||
void createDirect2DisplaySurface(uint32_t width, uint32_t height)
|
||||
{
|
||||
uint32_t displayPropertyCount;
|
||||
|
||||
// Get display property
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, NULL);
|
||||
VkDisplayPropertiesKHR* pDisplayProperties = new VkDisplayPropertiesKHR[displayPropertyCount];
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertyCount, pDisplayProperties);
|
||||
|
||||
// Get plane property
|
||||
uint32_t planePropertyCount;
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, NULL);
|
||||
VkDisplayPlanePropertiesKHR* pPlaneProperties = new VkDisplayPlanePropertiesKHR[planePropertyCount];
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &planePropertyCount, pPlaneProperties);
|
||||
|
||||
VkDisplayKHR display = VK_NULL_HANDLE;
|
||||
VkDisplayModeKHR displayMode;
|
||||
VkDisplayModePropertiesKHR* pModeProperties;
|
||||
bool foundMode = false;
|
||||
|
||||
for(uint32_t i = 0; i < displayPropertyCount;++i)
|
||||
{
|
||||
display = pDisplayProperties[i].display;
|
||||
uint32_t modeCount;
|
||||
vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, NULL);
|
||||
pModeProperties = new VkDisplayModePropertiesKHR[modeCount];
|
||||
vkGetDisplayModePropertiesKHR(physicalDevice, display, &modeCount, pModeProperties);
|
||||
|
||||
for (uint32_t j = 0; j < modeCount; ++j)
|
||||
{
|
||||
const VkDisplayModePropertiesKHR* mode = &pModeProperties[j];
|
||||
|
||||
if (mode->parameters.visibleRegion.width == width && mode->parameters.visibleRegion.height == height)
|
||||
{
|
||||
displayMode = mode->displayMode;
|
||||
foundMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundMode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
delete [] pModeProperties;
|
||||
}
|
||||
|
||||
if(!foundMode)
|
||||
{
|
||||
exitFatal("Can't find a display and a display mode!", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for a best plane we can use
|
||||
uint32_t bestPlaneIndex = UINT32_MAX;
|
||||
VkDisplayKHR* pDisplays = NULL;
|
||||
for(uint32_t i = 0; i < planePropertyCount; i++)
|
||||
{
|
||||
uint32_t planeIndex=i;
|
||||
uint32_t displayCount;
|
||||
vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, NULL);
|
||||
if (pDisplays)
|
||||
{
|
||||
delete [] pDisplays;
|
||||
}
|
||||
pDisplays = new VkDisplayKHR[displayCount];
|
||||
vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, planeIndex, &displayCount, pDisplays);
|
||||
|
||||
// Find a display that matches the current plane
|
||||
bestPlaneIndex = UINT32_MAX;
|
||||
for(uint32_t j = 0; j < displayCount; j++)
|
||||
{
|
||||
if(display == pDisplays[j])
|
||||
{
|
||||
bestPlaneIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(bestPlaneIndex != UINT32_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestPlaneIndex == UINT32_MAX)
|
||||
{
|
||||
exitFatal("Can't find a plane for displaying!", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
VkDisplayPlaneCapabilitiesKHR planeCap;
|
||||
vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, displayMode, bestPlaneIndex, &planeCap);
|
||||
VkDisplayPlaneAlphaFlagBitsKHR alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)0;
|
||||
|
||||
if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR;
|
||||
}
|
||||
else if (planeCap.supportedAlpha & VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR)
|
||||
{
|
||||
alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
}
|
||||
|
||||
VkDisplaySurfaceCreateInfoKHR surfaceInfo{};
|
||||
surfaceInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
surfaceInfo.pNext = NULL;
|
||||
surfaceInfo.flags = 0;
|
||||
surfaceInfo.displayMode = displayMode;
|
||||
surfaceInfo.planeIndex = bestPlaneIndex;
|
||||
surfaceInfo.planeStackIndex = pPlaneProperties[bestPlaneIndex].currentStackIndex;
|
||||
surfaceInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
surfaceInfo.globalAlpha = 1.0;
|
||||
surfaceInfo.alphaMode = alphaMode;
|
||||
surfaceInfo.imageExtent.width = width;
|
||||
surfaceInfo.imageExtent.height = height;
|
||||
|
||||
VkResult result = vkCreateDisplayPlaneSurfaceKHR(instance, &surfaceInfo, NULL, &surface);
|
||||
if (result !=VK_SUCCESS) {
|
||||
exitFatal("Failed to create surface!", result);
|
||||
}
|
||||
|
||||
delete[] pDisplays;
|
||||
delete[] pModeProperties;
|
||||
delete[] pDisplayProperties;
|
||||
delete[] pPlaneProperties;
|
||||
}
|
||||
#endif
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Vulkan Example base class
|
||||
*
|
||||
* Copyright (C) by Sascha Willems - www.saschawillems.de
|
||||
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
|
||||
*
|
||||
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
|
@ -13,208 +13,124 @@
|
|||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
#include <android/native_activity.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include "VulkanAndroid.h"
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
#include <directfb.h>
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
//
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
#include <xcb/xcb.h>
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <QuartzCore/CAMetalLayer.h>
|
||||
#include <CoreVideo/CVDisplayLink.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <numeric>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <string>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include "CommandLineParser.hpp"
|
||||
#include "keycodes.hpp"
|
||||
#include "VulkanTools.h"
|
||||
#include "VulkanDebug.h"
|
||||
#include "VulkanUIOverlay.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanDevice.hpp"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include "VulkanInitializers.hpp"
|
||||
#include "macros.h"
|
||||
#include "camera.hpp"
|
||||
#include "benchmark.hpp"
|
||||
#include "keycodes.hpp"
|
||||
|
||||
#include "VulkanDevice.hpp"
|
||||
#include "VulkanSwapChain.hpp"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
class VulkanExampleBase
|
||||
{
|
||||
private:
|
||||
std::string getWindowTitle();
|
||||
float fpsTimer = 0.0f;
|
||||
uint32_t frameCounter = 0;
|
||||
uint32_t destWidth;
|
||||
uint32_t destHeight;
|
||||
bool resizing = false;
|
||||
void windowResize();
|
||||
void handleMouseMove(int32_t x, int32_t y);
|
||||
void nextFrame();
|
||||
void updateOverlay();
|
||||
void createPipelineCache();
|
||||
void createCommandPool();
|
||||
void createSynchronizationPrimitives();
|
||||
void initSwapchain();
|
||||
void setupSwapChain();
|
||||
void createCommandBuffers();
|
||||
void destroyCommandBuffers();
|
||||
std::string shaderDir = "glsl";
|
||||
PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallback;
|
||||
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallback;
|
||||
VkDebugReportCallbackEXT debugReportCallback;
|
||||
struct MultisampleTarget {
|
||||
struct {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
VkDeviceMemory memory;
|
||||
} color;
|
||||
struct {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
VkDeviceMemory memory;
|
||||
} depth;
|
||||
} multisampleTarget;
|
||||
protected:
|
||||
// Returns the path to the root of the glsl or hlsl shader directory.
|
||||
std::string getShadersPath() const;
|
||||
// Returns the path to the root of the homework glsl or hlsl shader directory.
|
||||
std::string getHomeworkShadersPath() const;
|
||||
// Frame counter to display fps
|
||||
uint32_t frameCounter = 0;
|
||||
uint32_t lastFPS = 0;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> lastTimestamp, tPrevEnd;
|
||||
// Vulkan instance, stores all per-application states
|
||||
VkInstance instance;
|
||||
std::vector<std::string> supportedInstanceExtensions;
|
||||
// Physical device (GPU) that Vulkan will use
|
||||
VkPhysicalDevice physicalDevice;
|
||||
// Stores physical device properties (for e.g. checking device limits)
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
// Stores the features available on the selected physical device (for e.g. checking if a feature is available)
|
||||
VkPhysicalDeviceFeatures deviceFeatures;
|
||||
// Stores all available memory (type) properties for the physical device
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
|
||||
/** @brief Set of physical device features to be enabled for this example (must be set in the derived constructor) */
|
||||
VkPhysicalDeviceFeatures enabledFeatures{};
|
||||
/** @brief Set of device extensions to be enabled for this example (must be set in the derived constructor) */
|
||||
std::vector<const char*> enabledDeviceExtensions;
|
||||
std::vector<const char*> enabledInstanceExtensions;
|
||||
/** @brief Optional pNext structure for passing extension structures to device creation */
|
||||
void* deviceCreatepNextChain = nullptr;
|
||||
/** @brief Logical device, application's view of the physical device (GPU) */
|
||||
VkDevice device;
|
||||
// Handle to the device graphics queue that command buffers are submitted to
|
||||
VkQueue queue;
|
||||
// Depth buffer format (selected during Vulkan initialization)
|
||||
VkFormat depthFormat;
|
||||
// Command buffer pool
|
||||
VkCommandPool cmdPool;
|
||||
/** @brief Pipeline stages used to wait at for graphics queue submissions */
|
||||
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
// Contains command buffers and semaphores to be presented to the queue
|
||||
VkSubmitInfo submitInfo;
|
||||
// Command buffers used for rendering
|
||||
std::vector<VkCommandBuffer> drawCmdBuffers;
|
||||
// Global render pass for frame buffer writes
|
||||
VkRenderPass renderPass = VK_NULL_HANDLE;
|
||||
// List of available frame buffers (same as number of swap chain images)
|
||||
std::vector<VkFramebuffer>frameBuffers;
|
||||
// Active frame buffer index
|
||||
uint32_t currentBuffer = 0;
|
||||
// Descriptor set pool
|
||||
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||
// List of shader modules created (stored for cleanup)
|
||||
std::vector<VkShaderModule> shaderModules;
|
||||
// Pipeline cache object
|
||||
VkPipelineCache pipelineCache;
|
||||
// Wraps the swap chain to present images (framebuffers) to the windowing system
|
||||
VulkanSwapChain swapChain;
|
||||
// Synchronization semaphores
|
||||
struct {
|
||||
// Swap chain image presentation
|
||||
VkSemaphore presentComplete;
|
||||
// Command buffer submission and execution
|
||||
VkSemaphore renderComplete;
|
||||
} semaphores;
|
||||
std::vector<VkFence> waitFences;
|
||||
public:
|
||||
bool prepared = false;
|
||||
bool resized = false;
|
||||
bool viewUpdated = false;
|
||||
uint32_t width = 1280;
|
||||
uint32_t height = 720;
|
||||
|
||||
vks::UIOverlay UIOverlay;
|
||||
CommandLineParser commandLineParser;
|
||||
|
||||
/** @brief Last frame time measured using a high performance timer (if available) */
|
||||
float frameTimer = 1.0f;
|
||||
|
||||
vks::Benchmark benchmark;
|
||||
|
||||
/** @brief Encapsulated physical and logical vulkan device */
|
||||
vks::VulkanDevice *vulkanDevice;
|
||||
|
||||
/** @brief Example settings that can be changed e.g. by command line arguments */
|
||||
struct Settings {
|
||||
/** @brief Activates validation layers (and message output) when set to true */
|
||||
bool validation = false;
|
||||
/** @brief Set to true if fullscreen mode has been requested via command line */
|
||||
bool fullscreen = false;
|
||||
/** @brief Set to true if v-sync will be forced for the swapchain */
|
||||
bool vsync = false;
|
||||
/** @brief Enable UI overlay */
|
||||
bool overlay = true;
|
||||
} settings;
|
||||
|
||||
VkClearColorValue defaultClearColor = { { 0.025f, 0.025f, 0.025f, 1.0f } };
|
||||
|
||||
static std::vector<const char*> args;
|
||||
|
||||
// Defines a frame rate independent timer value clamped from -1.0...1.0
|
||||
// For use in animations, rotations, etc.
|
||||
float timer = 0.0f;
|
||||
// Multiplier for speeding up (or slowing down) the global timer
|
||||
float timerSpeed = 0.25f;
|
||||
bool paused = false;
|
||||
|
||||
Camera camera;
|
||||
glm::vec2 mousePos;
|
||||
|
||||
VkQueue queue;
|
||||
VkFormat depthFormat;
|
||||
VkCommandPool cmdPool;
|
||||
VkRenderPass renderPass;
|
||||
std::vector<VkFramebuffer>frameBuffers;
|
||||
uint32_t currentBuffer = 0;
|
||||
VkDescriptorPool descriptorPool;
|
||||
VkPipelineCache pipelineCache;
|
||||
VulkanSwapChain swapChain;
|
||||
std::string title = "Vulkan Example";
|
||||
std::string name = "vulkanExample";
|
||||
uint32_t apiVersion = VK_API_VERSION_1_0;
|
||||
void windowResize();
|
||||
public:
|
||||
static std::vector<const char*> args;
|
||||
bool prepared = false;
|
||||
uint32_t width = 1280;
|
||||
uint32_t height = 720;
|
||||
float frameTimer = 1.0f;
|
||||
Camera camera;
|
||||
glm::vec2 mousePos;
|
||||
bool paused = false;
|
||||
uint32_t lastFPS = 0;
|
||||
|
||||
struct {
|
||||
struct Settings {
|
||||
bool validation = false;
|
||||
bool fullscreen = false;
|
||||
bool vsync = false;
|
||||
bool multiSampling = true;
|
||||
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT;
|
||||
} settings;
|
||||
|
||||
struct DepthStencil {
|
||||
VkImage image;
|
||||
VkDeviceMemory mem;
|
||||
VkImageView view;
|
||||
} depthStencil;
|
||||
|
||||
struct {
|
||||
struct GamePadState {
|
||||
glm::vec2 axisLeft = glm::vec2(0.0f);
|
||||
glm::vec2 axisRight = glm::vec2(0.0f);
|
||||
} gamePadState;
|
||||
|
||||
struct {
|
||||
struct MouseButtons {
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool middle = false;
|
||||
|
@ -227,38 +143,26 @@ public:
|
|||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
// true if application has focused, false if moved to background
|
||||
bool focused = false;
|
||||
struct TouchPos {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} touchPos;
|
||||
bool touchDown = false;
|
||||
double touchTimer = 0.0;
|
||||
int64_t lastTapTime = 0;
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
void* view;
|
||||
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||
bool quit = false;
|
||||
#endif
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
bool quit = false;
|
||||
IDirectFB *dfb = nullptr;
|
||||
IDirectFBDisplayLayer *layer = nullptr;
|
||||
IDirectFBWindow *window = nullptr;
|
||||
IDirectFBSurface *surface = nullptr;
|
||||
IDirectFBEventBuffer *event_buffer = nullptr;
|
||||
std::string androidProduct;
|
||||
struct TouchPoint {
|
||||
int32_t id;
|
||||
float x;
|
||||
float y;
|
||||
bool down = false;
|
||||
};
|
||||
float pinchDist = 0.0f;
|
||||
std::array<TouchPoint, 2> touchPoints;
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
wl_display *display = nullptr;
|
||||
wl_registry *registry = nullptr;
|
||||
wl_compositor *compositor = nullptr;
|
||||
struct xdg_wm_base *shell = nullptr;
|
||||
wl_shell *shell = nullptr;
|
||||
wl_seat *seat = nullptr;
|
||||
wl_pointer *pointer = nullptr;
|
||||
wl_keyboard *keyboard = nullptr;
|
||||
wl_surface *surface = nullptr;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
wl_shell_surface *shell_surface = nullptr;
|
||||
bool quit = false;
|
||||
bool configured = false;
|
||||
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
bool quit = false;
|
||||
|
@ -268,36 +172,19 @@ public:
|
|||
xcb_screen_t *screen;
|
||||
xcb_window_t window;
|
||||
xcb_intern_atom_reply_t *atom_wm_delete_window;
|
||||
#elif defined(VK_USE_PLATFORM_HEADLESS_EXT)
|
||||
bool quit = false;
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
NSWindow* window;
|
||||
#endif
|
||||
|
||||
VulkanExampleBase(bool enableValidation = false);
|
||||
virtual ~VulkanExampleBase();
|
||||
/** @brief Setup the vulkan instance, enable required extensions and connect to the physical device (GPU) */
|
||||
bool initVulkan();
|
||||
|
||||
#if defined(_WIN32)
|
||||
void setupConsole(std::string title);
|
||||
void setupDPIAwareness();
|
||||
HWND setupWindow(HINSTANCE hinstance, WNDPROC wndproc);
|
||||
void handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
static int32_t handleAppInput(struct android_app* app, AInputEvent* event);
|
||||
static void handleAppCommand(android_app* app, int32_t cmd);
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
void* setupWindow(void* view);
|
||||
void displayLinkOutputCb();
|
||||
void mouseDragged(float x, float y);
|
||||
void windowWillResize(float x, float y);
|
||||
void windowDidResize();
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
IDirectFBSurface *setupWindow();
|
||||
void handleEvent(const DFBWindowEvent *event);
|
||||
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
struct xdg_surface *setupWindow();
|
||||
wl_shell_surface *setupWindow();
|
||||
void initWaylandConnection();
|
||||
void setSize(int width, int height);
|
||||
static void registryGlobalCb(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version);
|
||||
void registryGlobal(struct wl_registry *registry, uint32_t name,
|
||||
|
@ -343,192 +230,28 @@ public:
|
|||
xcb_window_t setupWindow();
|
||||
void initxcbConnection();
|
||||
void handleEvent(const xcb_generic_event_t *event);
|
||||
#else
|
||||
void setupWindow();
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
NSWindow* setupWindow();
|
||||
void mouseDragged(float x, float y);
|
||||
void windowWillResize(float x, float y);
|
||||
void windowDidResize();
|
||||
#endif
|
||||
/** @brief (Virtual) Creates the application wide Vulkan instance */
|
||||
|
||||
VulkanExampleBase();
|
||||
virtual ~VulkanExampleBase();
|
||||
|
||||
void initVulkan();
|
||||
|
||||
virtual VkResult createInstance(bool enableValidation);
|
||||
/** @brief (Pure virtual) Render function to be implemented by the sample application */
|
||||
virtual void render() = 0;
|
||||
/** @brief (Virtual) Called when the camera view has changed */
|
||||
virtual void viewChanged();
|
||||
/** @brief (Virtual) Called after a key was pressed, can be used to do custom key handling */
|
||||
virtual void keyPressed(uint32_t);
|
||||
/** @brief (Virtual) Called after the mouse cursor moved and before internal events (like camera rotation) is handled */
|
||||
virtual void mouseMoved(double x, double y, bool &handled);
|
||||
/** @brief (Virtual) Called when the window has been resized, can be used by the sample application to recreate resources */
|
||||
virtual void windowResized();
|
||||
/** @brief (Virtual) Called when resources have been recreated that require a rebuild of the command buffers (e.g. frame buffer), to be implemented by the sample application */
|
||||
virtual void buildCommandBuffers();
|
||||
/** @brief (Virtual) Setup default depth and stencil views */
|
||||
virtual void setupDepthStencil();
|
||||
/** @brief (Virtual) Setup default framebuffers for all requested swapchain images */
|
||||
virtual void setupFrameBuffer();
|
||||
/** @brief (Virtual) Setup a default renderpass */
|
||||
virtual void setupRenderPass();
|
||||
/** @brief (Virtual) Called after the physical device features have been read, can be used to set features to enable on the device */
|
||||
virtual void getEnabledFeatures();
|
||||
/** @brief (Virtual) Called after the physical device extensions have been read, can be used to enable extensions based on the supported extension listing*/
|
||||
virtual void getEnabledExtensions();
|
||||
|
||||
/** @brief Prepares all Vulkan resources and functions required to run the sample */
|
||||
virtual void prepare();
|
||||
virtual void fileDropped(std::string filename);
|
||||
|
||||
/** @brief Loads a SPIR-V shader file for the given shader stage */
|
||||
VkPipelineShaderStageCreateInfo loadShader(std::string fileName, VkShaderStageFlagBits stage);
|
||||
void initSwapchain();
|
||||
void setupSwapChain();
|
||||
|
||||
/** @brief Entry point for the main render loop */
|
||||
void renderLoop();
|
||||
|
||||
/** @brief Adds the drawing commands for the ImGui overlay to the given command buffer */
|
||||
void drawUI(const VkCommandBuffer commandBuffer);
|
||||
|
||||
/** Prepare the next frame for workload submission by acquiring the next swap chain image */
|
||||
void prepareFrame();
|
||||
/** @brief Presents the current image to the swap chain */
|
||||
void submitFrame();
|
||||
/** @brief (Virtual) Default image acquire + submission and command buffer submission function */
|
||||
virtual void renderFrame();
|
||||
|
||||
/** @brief (Virtual) Called when the UI overlay is updating, can be used to add custom elements to the overlay */
|
||||
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay);
|
||||
|
||||
#if defined(_WIN32)
|
||||
virtual void OnHandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
void renderFrame();
|
||||
};
|
||||
|
||||
// OS specific macros for the example main entry points
|
||||
#if defined(_WIN32)
|
||||
// Windows entry point
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) \
|
||||
{ \
|
||||
if (plumageRender != NULL) \
|
||||
{ \
|
||||
plumageRender->handleMessages(hWnd, uMsg, wParam, lParam); \
|
||||
} \
|
||||
return (DefWindowProc(hWnd, uMsg, wParam, lParam)); \
|
||||
} \
|
||||
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) \
|
||||
{ \
|
||||
for (int32_t i = 0; i < __argc; i++) { PlumageRender::args.push_back(__argv[i]); }; \
|
||||
plumageRender = new PlumageRender(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->setupWindow(hInstance, WndProc); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
return 0; \
|
||||
}
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
// Android entry point
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
void android_main(android_app* state) \
|
||||
{ \
|
||||
plumageRender = new PlumageRender(); \
|
||||
state->userData = plumageRender; \
|
||||
state->onAppCmd = PlumageRender::handleAppCommand; \
|
||||
state->onInputEvent = PlumageRender::handleAppInput; \
|
||||
androidApp = state; \
|
||||
vks::android::getDeviceConfig(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
}
|
||||
#elif defined(_DIRECT2DISPLAY)
|
||||
// Linux entry point with direct to display wsi
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
static void handleEvent() \
|
||||
{ \
|
||||
} \
|
||||
int main(const int argc, const char *argv[]) \
|
||||
{ \
|
||||
for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \
|
||||
plumageRender = new PlumageRender(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
return 0; \
|
||||
}
|
||||
#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT)
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
static void handleEvent(const DFBWindowEvent *event) \
|
||||
{ \
|
||||
if (plumageRender != NULL) \
|
||||
{ \
|
||||
plumageRender->handleEvent(event); \
|
||||
} \
|
||||
} \
|
||||
int main(const int argc, const char *argv[]) \
|
||||
{ \
|
||||
for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \
|
||||
plumageRender = new PlumageRender(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->setupWindow(); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
return 0; \
|
||||
}
|
||||
#elif (defined(VK_USE_PLATFORM_WAYLAND_KHR) || defined(VK_USE_PLATFORM_HEADLESS_EXT))
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
int main(const int argc, const char *argv[]) \
|
||||
{ \
|
||||
for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \
|
||||
plumageRender = new PlumageRender(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->setupWindow(); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
return 0; \
|
||||
}
|
||||
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
static void handleEvent(const xcb_generic_event_t *event) \
|
||||
{ \
|
||||
if (plumageRender != NULL) \
|
||||
{ \
|
||||
plumageRender->handleEvent(event); \
|
||||
} \
|
||||
} \
|
||||
int main(const int argc, const char *argv[]) \
|
||||
{ \
|
||||
for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \
|
||||
plumageRender = new VulkanExample(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->setupWindow(); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
return 0; \
|
||||
}
|
||||
#elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK))
|
||||
#if defined(VK_EXAMPLE_XCODE_GENERATED)
|
||||
#define PLUMAGE_RENDER_MAIN() \
|
||||
PlumageRender *plumageRender; \
|
||||
int main(const int argc, const char *argv[]) \
|
||||
{ \
|
||||
@autoreleasepool \
|
||||
{ \
|
||||
for (size_t i = 0; i < argc; i++) { PlumageRender::args.push_back(argv[i]); }; \
|
||||
plumageRender = new PlumageRender(); \
|
||||
plumageRender->initVulkan(); \
|
||||
plumageRender->setupWindow(nullptr); \
|
||||
plumageRender->prepare(); \
|
||||
plumageRender->renderLoop(); \
|
||||
delete(plumageRender); \
|
||||
} \
|
||||
return 0; \
|
||||
}
|
||||
#else
|
||||
#define PLUMAGE_RENDER_MAIN()
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "tiny_gltf.h"
|
||||
#include "VulkanDevice.hpp"
|
||||
//#include "VulkanUtils.hpp"
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#define ENABLE_VALIDATION false
|
||||
|
|
|
@ -722,10 +722,9 @@ PlumageRender::PlumageRender():
|
|||
}
|
||||
|
||||
|
||||
//----------------------------Prepare precompute Lighting or BRDF LUT-----------------------------------------------//
|
||||
//Irradiance map for diffuse lighting
|
||||
|
||||
|
||||
// generate two cube maps
|
||||
// irradiance cube map
|
||||
// prefileter environment cube map
|
||||
void PlumageRender::generateCubemaps()
|
||||
{
|
||||
enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
|
||||
|
@ -1138,12 +1137,12 @@ PlumageRender::PlumageRender():
|
|||
switch (target) {
|
||||
case IRRADIANCE:
|
||||
irradiancePushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
|
||||
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockIrradiance), &pushBlockIrradiance);
|
||||
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(IrradiancePushBlock), &irradiancePushBlock);
|
||||
break;
|
||||
case PREFILTEREDENV:
|
||||
prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
|
||||
prefilterPushBlock.roughness = (float)m / (float)(numMips - 1);
|
||||
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockPrefilterEnv), &pushBlockPrefilterEnv);
|
||||
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock);
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -1263,8 +1262,8 @@ PlumageRender::PlumageRender():
|
|||
std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PlumageRender::GenerateBRDFLUT()
|
||||
// generate BRDF integration map for roughness/NdotV
|
||||
void PlumageRender::generateBRDFLUT()
|
||||
{
|
||||
auto tStart = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
@ -1461,8 +1460,8 @@ PlumageRender::PlumageRender():
|
|||
|
||||
// Look-up-table (from BRDF) pipeline
|
||||
shaderStages = {
|
||||
loadShader(device, "genbrdflut.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
|
||||
loadShader(device, "genbrdflut.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
loadShader(filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT),
|
||||
loadShader(filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
};
|
||||
VkPipeline pipeline;
|
||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
|
||||
|
@ -1520,136 +1519,196 @@ PlumageRender::PlumageRender():
|
|||
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
|
||||
std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl;
|
||||
}
|
||||
//----------------------------End Precompute brick------------------------------------------------------------------//
|
||||
#pragma region pbr render pass setting
|
||||
|
||||
void PlumageRender::createAttachment(
|
||||
VkFormat format,
|
||||
VkImageUsageFlagBits usage,
|
||||
FrameBufferAttachment* attachment,
|
||||
uint32_t width,
|
||||
uint32_t height)
|
||||
{
|
||||
VkImageAspectFlags aspectMask = 0;
|
||||
VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
attachment->format = format;
|
||||
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
{
|
||||
aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
imageUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||
{
|
||||
aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (format >= VK_FORMAT_D16_UNORM_S8_UINT)
|
||||
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
|
||||
assert(aspectMask > 0);
|
||||
|
||||
VkImageCreateInfo image = vks::initializers::imageCreateInfo();
|
||||
image.imageType = VK_IMAGE_TYPE_2D;
|
||||
image.format = format;
|
||||
image.extent.width = width;
|
||||
image.extent.height = height;
|
||||
image.extent.depth = 1;
|
||||
image.mipLevels = 1;
|
||||
image.arrayLayers = 1;
|
||||
image.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image.usage = imageUsage | usage;
|
||||
|
||||
VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();
|
||||
VkMemoryRequirements memReqs;
|
||||
|
||||
VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &attachment->image));
|
||||
vkGetImageMemoryRequirements(device, attachment->image, &memReqs);
|
||||
memAlloc.allocationSize = memReqs.size;
|
||||
memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &attachment->deviceMemory));
|
||||
VK_CHECK_RESULT(vkBindImageMemory(device, attachment->image, attachment->deviceMemory, 0));
|
||||
|
||||
VkImageViewCreateInfo imageView = vks::initializers::imageViewCreateInfo();
|
||||
imageView.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
imageView.format = format;
|
||||
imageView.subresourceRange = {};
|
||||
imageView.subresourceRange.aspectMask = aspectMask;
|
||||
imageView.subresourceRange.baseMipLevel = 0;
|
||||
imageView.subresourceRange.levelCount = 1;
|
||||
imageView.subresourceRange.baseArrayLayer = 0;
|
||||
imageView.subresourceRange.layerCount = 1;
|
||||
imageView.image = attachment->image;
|
||||
VK_CHECK_RESULT(vkCreateImageView(device, &imageView, nullptr, &attachment->imageView));
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void PlumageRender::prepareUniformBuffers()
|
||||
{
|
||||
// Vertex shader uniform buffer block
|
||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&shaderData.buffer,
|
||||
sizeof(shaderData.values)));
|
||||
|
||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
&shaderData.skinSSBO,
|
||||
sizeof(glm::mat4) * glTFModel.nodeCount));
|
||||
|
||||
// Map persistent
|
||||
VK_CHECK_RESULT(shaderData.buffer.map());
|
||||
VK_CHECK_RESULT(shaderData.skinSSBO.map());
|
||||
|
||||
for (auto& material : glTFModel.materials)
|
||||
{
|
||||
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||
&material.materialData.buffer,
|
||||
sizeof(VulkanglTFModel::MaterialData::Values),
|
||||
&material.materialData.values));
|
||||
for (auto& uniformBuffer : uniformBuffers) {
|
||||
uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene));
|
||||
uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox));
|
||||
uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData));
|
||||
}
|
||||
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void PlumageRender::updateUniformBuffers()
|
||||
{
|
||||
shaderData.values.projection = camera.matrices.perspective;
|
||||
shaderData.values.model = camera.matrices.view;
|
||||
shaderData.values.viewPos = camera.viewPos;
|
||||
shaderData.values.bFlagSet.x = normalMapping;
|
||||
shaderData.values.bFlagSet.y = pbrEnabled;
|
||||
memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values));
|
||||
// Scene
|
||||
shaderDataScene.projection = camera.matrices.perspective;
|
||||
shaderDataScene.view = camera.matrices.view;
|
||||
|
||||
// Center and scale model
|
||||
float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f;
|
||||
glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]);
|
||||
translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]);
|
||||
|
||||
shaderDataScene.model = glm::mat4(1.0f);
|
||||
shaderDataScene.model[0][0] = scale;
|
||||
shaderDataScene.model[1][1] = scale;
|
||||
shaderDataScene.model[2][2] = scale;
|
||||
shaderDataScene.model = glm::translate(shaderDataScene.model, translate);
|
||||
|
||||
shaderDataScene.camPos = glm::vec3(
|
||||
-camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)),
|
||||
-camera.position.z * sin(glm::radians(camera.rotation.x)),
|
||||
camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x))
|
||||
);
|
||||
|
||||
// Skybox
|
||||
shaderDataSkybox.projection = camera.matrices.perspective;
|
||||
shaderDataSkybox.view = camera.matrices.view;
|
||||
shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view));
|
||||
}
|
||||
|
||||
void PlumageRender::updateShaderData()
|
||||
{
|
||||
shaderData.lightDir = glm::vec4(
|
||||
sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
|
||||
sin(glm::radians(lightSource.rotation.y)),
|
||||
cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
|
||||
0.0f);
|
||||
}
|
||||
|
||||
void PlumageRender::windowResized()
|
||||
{
|
||||
buildCommandBuffers();
|
||||
vkDeviceWaitIdle(device);
|
||||
updateUniformBuffers();
|
||||
// update UI
|
||||
//updateUIOverlay();
|
||||
}
|
||||
|
||||
void PlumageRender::prepare()
|
||||
{
|
||||
VulkanExampleBase::prepare();
|
||||
|
||||
camera.type = Camera::CameraType::lookat;
|
||||
|
||||
camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f);
|
||||
camera.rotationSpeed = 0.25f;
|
||||
camera.movementSpeed = 0.1f;
|
||||
camera.setPosition({ 0.0f, 0.0f, 1.0f });
|
||||
camera.setRotation({ 0.0f, 0.0f, 0.0f });
|
||||
|
||||
waitFences.resize(renderAhead);
|
||||
presentCompleteSemaphores.resize(renderAhead);
|
||||
renderCompleteSemaphores.resize(renderAhead);
|
||||
commandBuffers.resize(swapChain.imageCount);
|
||||
uniformBuffers.resize(swapChain.imageCount);
|
||||
descriptorSets.resize(swapChain.imageCount);
|
||||
// Command buffer execution fences
|
||||
for (auto& waitFence : waitFences) {
|
||||
VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
|
||||
VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence));
|
||||
}
|
||||
// Queue ordering semaphores
|
||||
for (auto& semaphore : presentCompleteSemaphores) {
|
||||
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
||||
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
|
||||
}
|
||||
for (auto& semaphore : renderCompleteSemaphores) {
|
||||
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
||||
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
|
||||
}
|
||||
// Command buffers
|
||||
{
|
||||
VkCommandBufferAllocateInfo cmdBufAllocateInfo{};
|
||||
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
cmdBufAllocateInfo.commandPool = cmdPool;
|
||||
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
cmdBufAllocateInfo.commandBufferCount = static_cast<uint32_t>(commandBuffers.size());
|
||||
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandBuffers.data()));
|
||||
}
|
||||
|
||||
loadAssets();
|
||||
GenerateBRDFLUT();
|
||||
GenerateIrradianceCubemap();
|
||||
GeneratePrefilteredCubemap();
|
||||
generateBRDFLUT();
|
||||
generateCubemaps();
|
||||
prepareUniformBuffers();
|
||||
setupDescriptors();
|
||||
preparePipelines();
|
||||
|
||||
//ui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount);
|
||||
//updateOverlay();
|
||||
|
||||
buildCommandBuffers();
|
||||
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
void PlumageRender::render()
|
||||
{
|
||||
renderFrame();
|
||||
if (!prepared) {
|
||||
return;
|
||||
}
|
||||
|
||||
//updateOverlay();
|
||||
|
||||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
|
||||
VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex]));
|
||||
|
||||
VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer);
|
||||
if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) {
|
||||
windowResize();
|
||||
}
|
||||
else {
|
||||
VK_CHECK_RESULT(acquire);
|
||||
}
|
||||
|
||||
// Update UBOs
|
||||
updateUniformBuffers();
|
||||
UniformBufferSet currentUB = uniformBuffers[currentBuffer];
|
||||
memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene));
|
||||
memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData));
|
||||
memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox));
|
||||
|
||||
const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.pWaitDstStageMask = &waitDstStageMask;
|
||||
submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex];
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex];
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffers[currentBuffer];
|
||||
submitInfo.commandBufferCount = 1;
|
||||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex]));
|
||||
|
||||
VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]);
|
||||
if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) {
|
||||
if (present == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||
windowResize();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
VK_CHECK_RESULT(present);
|
||||
}
|
||||
}
|
||||
|
||||
frameIndex += 1;
|
||||
frameIndex %= renderAhead;
|
||||
|
||||
if (!paused) {
|
||||
if (rotateModel) {
|
||||
modelrot.y += frameTimer * 35.0f;
|
||||
if (modelrot.y > 360.0f) {
|
||||
modelrot.y -= 360.0f;
|
||||
}
|
||||
}
|
||||
if ((animate) && (models.scene.animations.size() > 0)) {
|
||||
animationTimer += frameTimer;
|
||||
if (animationTimer > models.scene.animations[animationIndex].end) {
|
||||
animationTimer -= models.scene.animations[animationIndex].end;
|
||||
}
|
||||
models.scene.updateAnimation(animationIndex, animationTimer);
|
||||
}
|
||||
updateShaderData();
|
||||
if (rotateModel) {
|
||||
updateUniformBuffers();
|
||||
}
|
||||
}
|
||||
if (camera.updated) {
|
||||
updateUniformBuffers();
|
||||
}
|
||||
if (!paused && glTFModel.animations.size() > 0)
|
||||
{
|
||||
glTFModel.updateAnimation(frameTimer, shaderData.skinSSBO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <vulkan/vulkan.h>
|
||||
#include "VulkanExampleBase.h"
|
||||
#include "glTFModel.h"
|
||||
#include "VulkanUtils.hpp"
|
||||
//#include "VulkanDevice.hpp"
|
||||
|
||||
|
||||
|
@ -60,9 +61,9 @@ public:
|
|||
} shaderData;
|
||||
|
||||
struct UniformBufferSet {
|
||||
vks::Buffer scene;
|
||||
vks::Buffer skybox;
|
||||
vks::Buffer params;
|
||||
Buffer scene;
|
||||
Buffer skybox;
|
||||
Buffer params;
|
||||
};
|
||||
|
||||
struct UBOMatrices {
|
||||
|
@ -70,7 +71,7 @@ public:
|
|||
glm::mat4 model;
|
||||
glm::mat4 view;
|
||||
glm::vec3 camPos;
|
||||
} shaderValuesScene, shaderValuesSkybox;
|
||||
} shaderDataScene, shaderDataSkybox;
|
||||
|
||||
struct PushConstBlockMaterial {
|
||||
glm::vec4 baseColorFactor;
|
||||
|
@ -112,8 +113,8 @@ public:
|
|||
std::string filterVertShaderPath = getAssetPath() + "shaders/filtercube.vert.spv";
|
||||
std::string prefilterEnvmapFragShaderPath = getAssetPath() + "shaders/prefilterenvmap.frag.spv";
|
||||
//brdf cube map
|
||||
std::string brdfVertShaderPath = getAssetPath() + "buster_drone/shaders/glsl/genbrdflut.vert.spv";
|
||||
std::string brdfFragShaderPath = getAssetPath() + "buster_drone/shaders/glsl/genbrdflut.frag.spv";
|
||||
std::string brdfVertShaderPath = getAssetPath() + "shaders/genbrdflut.vert.spv";
|
||||
std::string brdfFragShaderPath = getAssetPath() + "shaders/genbrdflut.frag.spv";
|
||||
// environment map texture
|
||||
std::string envMapFilePath = getAssetPath() + "environments/papermill.ktx";
|
||||
|
||||
|
@ -284,9 +285,11 @@ public:
|
|||
void preparePipelines();
|
||||
void CreateToneMappingPipeline();
|
||||
void generateCubemaps();
|
||||
void GenerateBRDFLUT();
|
||||
void generateBRDFLUT();
|
||||
void prepareUniformBuffers();
|
||||
void updateUniformBuffers();
|
||||
void updateShaderData();
|
||||
void windowResized();
|
||||
void prepare();
|
||||
virtual void render();
|
||||
virtual void viewChanged();
|
||||
|
|
Loading…
Reference in New Issue