working mesh shader

This commit is contained in:
Jorijn van der Graaf 2025-04-19 23:59:27 +02:00
commit 97ca634108
18 changed files with 2591 additions and 340 deletions

View file

@ -9,6 +9,8 @@ module;
#include <cstring>
#include <print>
#include <cstdio>
#include "VulkanInitializers.hpp"
#include "VulkanBuffer.h"
#define GET_EXTENSION_FUNCTION(_id) ((PFN_##_id)(vkGetInstanceProcAddr(instance, #_id)))
@ -23,7 +25,8 @@ const char* const deviceExtensionNames[] = {
"VK_KHR_swapchain",
"VK_KHR_spirv_1_4",
"VK_EXT_mesh_shader",
"VK_KHR_shader_float_controls"
"VK_KHR_shader_float_controls",
"VK_KHR_dynamic_rendering"
};
const char* const layerNames[] = {
"VK_LAYER_KHRONOS_validation"
@ -182,6 +185,7 @@ void VulkanDevice::CreateDevice() {
break;
}
}
float priority = 1;
@ -191,8 +195,13 @@ void VulkanDevice::CreateDevice() {
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority;
VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR};
dynamicRenderingFeature.dynamicRendering = VK_TRUE;
VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT};
ext_feature.meshShader = VK_TRUE;
ext_feature.pNext = &dynamicRenderingFeature;
VkPhysicalDeviceFeatures2 physical_features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
physical_features2.pNext = &ext_feature;
@ -237,4 +246,134 @@ void VulkanDevice::CreateDevice() {
commandPoolcreateInfo.queueFamilyIndex = queueFamilyIndex;
commandPoolcreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
CHECK_VK_RESULT(vkCreateCommandPool(device, &commandPoolcreateInfo, NULL, &commandPool));
vkGetPhysicalDeviceMemoryProperties(physDevice, &memoryProperties);
std::vector<VkFormat> formatList = {
VK_FORMAT_D32_SFLOAT,
};
for (auto& format : formatList) {
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(physDevice, format, &formatProps);
if (formatProps.optimalTilingFeatures)
{
depthFormat = format;
break;
}
}
vkCmdDrawMeshTasksEXTProc = reinterpret_cast<PFN_vkCmdDrawMeshTasksEXT>(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT"));
vkCmdBeginRenderingKHRProc = reinterpret_cast<PFN_vkCmdBeginRenderingKHR>(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR"));
vkCmdEndRenderingKHRProc = reinterpret_cast<PFN_vkCmdEndRenderingKHR>(vkGetInstanceProcAddr(instance, "vkCmdEndRenderingKHR"));
}
std::uint32_t VulkanDevice::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties) {
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++)
{
if ((typeBits & 1) == 1)
{
if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
return i;
}
}
typeBits >>= 1;
}
throw std::runtime_error("Could not find a matching memory type");
}
void VulkanDevice::CreateBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data)
{
// Create the buffer handle
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size);
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
CHECK_VK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, buffer));
// Create the memory backing up the buffer handle
VkMemoryRequirements memReqs;
VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();
vkGetBufferMemoryRequirements(device, *buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
// Find a memory type index that fits the properties of the buffer
memAlloc.memoryTypeIndex = GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);
// If the buffer has VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT set we also need to enable the appropriate flag during allocation
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo{};
if (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) {
allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR;
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
memAlloc.pNext = &allocFlagsInfo;
}
CHECK_VK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, memory));
// If a pointer to the buffer data has been passed, map the buffer and copy over the data
if (data != nullptr)
{
void *mapped;
CHECK_VK_RESULT(vkMapMemory(device, *memory, 0, size, 0, &mapped));
memcpy(mapped, data, size);
// If host coherency hasn't been requested, do a manual flush to make writes visible
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
{
VkMappedMemoryRange mappedRange = vks::initializers::mappedMemoryRange();
mappedRange.memory = *memory;
mappedRange.offset = 0;
mappedRange.size = size;
vkFlushMappedMemoryRanges(device, 1, &mappedRange);
}
vkUnmapMemory(device, *memory);
}
// Attach the memory to the buffer object
CHECK_VK_RESULT(vkBindBufferMemory(device, *buffer, *memory, 0));
}
void VulkanDevice::CreateBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vks::Buffer *buffer, VkDeviceSize size, void *data)
{
buffer->device = device;
// Create the buffer handle
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(usageFlags, size);
CHECK_VK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &buffer->buffer));
// Create the memory backing up the buffer handle
VkMemoryRequirements memReqs;
VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo();
vkGetBufferMemoryRequirements(device, buffer->buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
// Find a memory type index that fits the properties of the buffer
memAlloc.memoryTypeIndex = GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);
// If the buffer has VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT set we also need to enable the appropriate flag during allocation
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo{};
if (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) {
allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR;
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
memAlloc.pNext = &allocFlagsInfo;
}
CHECK_VK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &buffer->memory));
buffer->alignment = memReqs.alignment;
buffer->size = size;
buffer->usageFlags = usageFlags;
buffer->memoryPropertyFlags = memoryPropertyFlags;
// If a pointer to the buffer data has been passed, map the buffer and copy over the data
if (data != nullptr)
{
CHECK_VK_RESULT(buffer->map());
memcpy(buffer->mapped, data, size);
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) {
buffer->flush();
}
buffer->unmap();
}
// Initialize a default descriptor that covers the whole buffer size
buffer->setupDescriptor();
// Attach the memory to the buffer object
CHECK_VK_RESULT(buffer->bind());
}

View file

@ -3,14 +3,13 @@ module;
#include <cstdint>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_wayland.h>
#include "VulkanBuffer.h"
export module Crafter.Graphics:VulkanDevice;
export namespace Crafter {
class VulkanDevice {
public:
static void CreateDevice();
static void CHECK_VK_RESULT(VkResult result);
inline static VkInstance instance = VK_NULL_HANDLE;
inline static VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
inline static VkPhysicalDevice physDevice = VK_NULL_HANDLE;
@ -19,6 +18,15 @@ export namespace Crafter {
inline static VkQueue queue = VK_NULL_HANDLE;
inline static VkCommandPool commandPool = VK_NULL_HANDLE;
inline static VkSwapchainKHR swapchain = VK_NULL_HANDLE;
inline static VkRenderPass renderPass = VK_NULL_HANDLE;
inline static PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXTProc;
inline static PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHRProc;
inline static PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHRProc;
inline static VkPhysicalDeviceMemoryProperties memoryProperties;
inline static VkFormat depthFormat = VK_FORMAT_UNDEFINED;
static void CreateDevice();
static void CHECK_VK_RESULT(VkResult result);
static void CreateBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr);
static void CreateBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vks::Buffer *buffer, VkDeviceSize size, void *data = nullptr);
static std::uint32_t GetMemoryType(std::uint32_t typeBits, VkMemoryPropertyFlags properties);
};
}

View file

@ -3,6 +3,16 @@ module;
#include <cstdint>
#include <vulkan/vulkan.h>
#include <array>
#include "VulkanInitializers.hpp"
#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 "VulkanBuffer.h"
#include "camera.hpp"
export module Crafter.Graphics:VulkanPipeline;
import :VulkanDevice;
@ -14,97 +24,109 @@ namespace Crafter {
class VulkanPipeline {
public:
inline static VkPipeline pipeline;
inline static VkPipelineLayout layout;
inline static VkDescriptorPool descriptor_pool;
inline static VkDescriptorSetLayout descriptor_set_layout;
inline static VkDescriptorSet descriptor_set;
inline static VkPipelineLayout pipelineLayout;
inline static VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
inline static VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
inline static VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
inline static struct UniformData {
glm::mat4 projection;
glm::mat4 model;
glm::mat4 view;
} uniformData;
inline static vks::Buffer uniformBuffer;
static void CreatePipeline() {
VkDescriptorPoolCreateInfo descriptor_pool_create_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
descriptor_pool_create_info.maxSets = 2;
descriptor_pool_create_info.poolSizeCount = 0;
descriptor_pool_create_info.pPoolSizes = nullptr;
VulkanDevice::CHECK_VK_RESULT(vkCreateDescriptorPool(VulkanDevice::device, &descriptor_pool_create_info, nullptr, &descriptor_pool));
VkDescriptorSetLayoutCreateInfo descriptor_layout = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
descriptor_layout.bindingCount = 0;
descriptor_layout.pBindings = nullptr;
VulkanDevice::CHECK_VK_RESULT(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptor_layout, nullptr, &descriptor_set_layout));
VkDescriptorSetAllocateInfo alloc_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
alloc_info.descriptorPool = descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &descriptor_set_layout;
VulkanDevice::CHECK_VK_RESULT(vkAllocateDescriptorSets(VulkanDevice::device, &alloc_info, &descriptor_set));
Camera camera;
camera.type = Camera::CameraType::lookat;
camera.setPerspective(60.0f, 128 / 128, 0.1f, 512.0f);
camera.setRotation(glm::vec3(0.0f, 15.0f, 0.0f));
camera.setTranslation(glm::vec3(0.0f, 0.0f, -5.0f));
VulkanDevice::CreateBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData));
VulkanDevice::CHECK_VK_RESULT(uniformBuffer.map());
uniformData.projection = camera.matrices.perspective;
uniformData.view = camera.matrices.view;
uniformData.model = glm::mat4(1.0f);
memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData));
VkPipelineLayoutCreateInfo layout_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &descriptor_set_layout;
VulkanDevice::CHECK_VK_RESULT(vkCreatePipelineLayout(VulkanDevice::device, &layout_info, nullptr, &layout));
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
raster.cullMode = VK_CULL_MODE_BACK_BIT;
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
raster.lineWidth = 1.0f;
// Our attachment will write to all color channels, but no blending is enabled.
VkPipelineColorBlendAttachmentState blend_attachment{};
blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
blend.attachmentCount = 1;
blend.pAttachments = &blend_attachment;
// We will have one viewport and scissor box.
VkPipelineViewportStateCreateInfo viewport{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
viewport.viewportCount = 1;
viewport.scissorCount = 1;
// Disable all depth testing.
VkPipelineDepthStencilStateCreateInfo depth_stencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
// No multisampling.
VkPipelineMultisampleStateCreateInfo multisample{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// Specify that these states will be dynamic, i.e. not part of pipeline state object.
std::array<VkDynamicState, 2> dynamics{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
dynamic.pDynamicStates = dynamics.data();
dynamic.dynamicStateCount = static_cast<std::uint32_t>(dynamics.size());
// Load our SPIR-V shaders.
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{};
//Mesh stage of the pipeline
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = MeshShader::_stage;
shader_stages[0].module = MeshShader::shader;
shader_stages[0].pName = MeshShader::_entrypoint.value;
// Pool
std::vector<VkDescriptorPoolSize> poolSizes = {
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
};
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(static_cast<uint32_t>(poolSizes.size()), poolSizes.data(), 1);
VulkanDevice::CHECK_VK_RESULT(vkCreateDescriptorPool(VulkanDevice::device, &descriptorPoolInfo, nullptr, &descriptorPool));
// Fragment stage of the pipeline
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = FragmentShader::_stage;
shader_stages[1].module = FragmentShader::shader;
shader_stages[1].pName = FragmentShader::_entrypoint.value;
VkGraphicsPipelineCreateInfo pipe{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
pipe.stageCount = static_cast<std::uint32_t>(shader_stages.size());
pipe.pStages = shader_stages.data();
pipe.pVertexInputState = nullptr;
pipe.pInputAssemblyState = nullptr;
pipe.pRasterizationState = &raster;
pipe.pColorBlendState = &blend;
pipe.pMultisampleState = &multisample;
pipe.pViewportState = &viewport;
pipe.pDepthStencilState = &depth_stencil;
pipe.pDynamicState = &dynamic;
// We need to specify the pipeline layout and the render pass description up front as well.
pipe.renderPass = WindowWaylandVulkan::renderPass;
pipe.layout = layout;
// Layout
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_MESH_BIT_EXT, 0),
};
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VulkanDevice::CHECK_VK_RESULT(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptorLayoutInfo, nullptr, &descriptorSetLayout));
VulkanDevice::CHECK_VK_RESULT(vkCreateGraphicsPipelines(VulkanDevice::device, VK_NULL_HANDLE, 1, &pipe, nullptr, &pipeline));
// Set
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
VulkanDevice::CHECK_VK_RESULT(vkAllocateDescriptorSets(VulkanDevice::device, &allocInfo, &descriptorSet));
std::vector<VkWriteDescriptorSet> modelWriteDescriptorSets = {
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor),
};
vkUpdateDescriptorSets(VulkanDevice::device, static_cast<uint32_t>(modelWriteDescriptorSets.size()), modelWriteDescriptorSets.data(), 0, nullptr);
// Layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
VulkanDevice::CHECK_VK_RESULT(vkCreatePipelineLayout(VulkanDevice::device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
// Pipeline
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0);
VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
VkPipelineRenderingCreateInfoKHR pipeline_create{VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR};
pipeline_create.pNext = VK_NULL_HANDLE;
pipeline_create.colorAttachmentCount = 1;
pipeline_create.pColorAttachmentFormats = &format;
pipeline_create.depthAttachmentFormat = VulkanDevice::depthFormat;
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, VK_NULL_HANDLE, 0);
pipelineCI.pNext = &pipeline_create;
pipelineCI.pRasterizationState = &rasterizationState;
pipelineCI.pColorBlendState = &colorBlendState;
pipelineCI.pMultisampleState = &multisampleState;
pipelineCI.pViewportState = &viewportState;
pipelineCI.pDepthStencilState = &depthStencilState;
pipelineCI.pDynamicState = &dynamicState;
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCI.pStages = shaderStages.data();
// Not using a vertex shader, mesh shading doesn't require vertex input state
pipelineCI.pInputAssemblyState = nullptr;
pipelineCI.pVertexInputState = nullptr;
//Mesh stage of the pipeline
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[0].stage = MeshShader::_stage;
shaderStages[0].module = MeshShader::shader;
shaderStages[0].pName = MeshShader::_entrypoint.value;
shaderStages[0].flags = 0;
// Fragment stage of the pipeline
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[1].stage = FragmentShader::_stage;
shaderStages[1].module = FragmentShader::shader;
shaderStages[1].pName = FragmentShader::_entrypoint.value;
shaderStages[1].flags = 0;
VulkanDevice::CHECK_VK_RESULT(vkCreateGraphicsPipelines(VulkanDevice::device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &pipeline));
}
};
}

View file

@ -8,6 +8,10 @@ module;
#include <wayland-client.h>
#include <thread>
#include <iostream>
#include <cassert>
#include <exception>
#include "VulkanInitializers.hpp"
#include "VulkanTransition.hpp"
module Crafter.Graphics;
import Crafter.Event;
@ -15,254 +19,443 @@ using namespace Crafter;
void WindowWaylandVulkan::CreateSwapchain()
{
VkResult result;
VkSurfaceCapabilitiesKHR capabilities;
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VulkanDevice::physDevice, vulkanSurface, &capabilities));
// Store the current swap chain handle so we can use it later on to ease up recreation
VkSwapchainKHR oldSwapchain = swapChain;
uint32_t formatCount;
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, NULL));
// Get physical device surface properties and formats
VkSurfaceCapabilitiesKHR surfCaps;
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VulkanDevice::physDevice, vulkanSurface, &surfCaps));
std::vector<VkSurfaceFormatKHR> formats(formatCount);
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, formats.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;
}
VkSurfaceFormatKHR chosenFormat = formats[0];
for (uint32_t i = 0; i < formatCount; i++)
{
if (formats[i].format == VK_FORMAT_B8G8R8A8_UNORM)
{
chosenFormat = formats[i];
break;
}
}
// Select a present mode for the swapchain
uint32_t presentModeCount;
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, NULL));
assert(presentModeCount > 0);
format = chosenFormat.format;
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, presentModes.data()));
imageCount = capabilities.minImageCount + 1 < capabilities.maxImageCount ? capabilities.minImageCount + 1 : capabilities.minImageCount;
// 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;
VkSwapchainCreateInfoKHR swapchainCreateInfo = {};
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCreateInfo.surface = vulkanSurface;
swapchainCreateInfo.minImageCount = imageCount;
swapchainCreateInfo.imageFormat = chosenFormat.format;
swapchainCreateInfo.imageColorSpace = chosenFormat.colorSpace;
swapchainCreateInfo.imageExtent.width = width;
swapchainCreateInfo.imageExtent.height = height;
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchainCreateInfo.preTransform = capabilities.currentTransform;
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchainCreateInfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
swapchainCreateInfo.clipped = 1;
VulkanDevice::CHECK_VK_RESULT(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCreateInfo, NULL, &swapchain));
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Determine the number of images
uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
{
desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
}
VkAttachmentReference attachmentRef = {};
attachmentRef.attachment = 0;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// 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;
}
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &attachmentRef;
// 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;
};
}
VkRenderPassCreateInfo renderPasscreateInfo = {};
renderPasscreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPasscreateInfo.flags = 0;
renderPasscreateInfo.attachmentCount = 1;
renderPasscreateInfo.pAttachments = &attachment;
renderPasscreateInfo.subpassCount = 1;
renderPasscreateInfo.pSubpasses = &subpass;
VulkanDevice::CHECK_VK_RESULT(vkCreateRenderPass(VulkanDevice::device, &renderPasscreateInfo, NULL, &renderPass));
VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapchain, &imageCount, NULL));
VkSwapchainCreateInfoKHR swapchainCI = {};
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCI.surface = vulkanSurface;
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;
std::vector<VkImage> images(imageCount);
VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapchain, &imageCount, images.data()));
// 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;
}
swapchainElements.resize(imageCount);
// 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;
}
for (uint32_t i = 0; i < imageCount; i++)
{
VkCommandBufferAllocateInfo commandBufferAllocInfo = {};
commandBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAllocInfo.commandPool = VulkanDevice::commandPool;
commandBufferAllocInfo.commandBufferCount = 1;
commandBufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
vkAllocateCommandBuffers(VulkanDevice::device, &commandBufferAllocInfo, &swapchainElements[i].commandBuffer);
swapchainElements[i].image = images[i];
VulkanDevice::CHECK_VK_RESULT(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCI, nullptr, &swapChain));
VkImageViewCreateInfo imageViewcreateInfo = {};
imageViewcreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imageViewcreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageViewcreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewcreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewcreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewcreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
imageViewcreateInfo.subresourceRange.baseMipLevel = 0;
imageViewcreateInfo.subresourceRange.levelCount = 1;
imageViewcreateInfo.subresourceRange.baseArrayLayer = 0;
imageViewcreateInfo.subresourceRange.layerCount = 1;
imageViewcreateInfo.image = swapchainElements[i].image;
imageViewcreateInfo.format = format;
imageViewcreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VulkanDevice::CHECK_VK_RESULT(vkCreateImageView(VulkanDevice::device, &imageViewcreateInfo, NULL, &swapchainElements[i].imageView));
VkFramebufferCreateInfo framebufferCreateInfo = {};
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCreateInfo.renderPass = renderPass;
framebufferCreateInfo.attachmentCount = 1;
framebufferCreateInfo.pAttachments = &swapchainElements[i].imageView;
framebufferCreateInfo.width = width;
framebufferCreateInfo.height = height;
framebufferCreateInfo.layers = 1;
VulkanDevice::CHECK_VK_RESULT(vkCreateFramebuffer(VulkanDevice::device, &framebufferCreateInfo, NULL, &swapchainElements[i].framebuffer));
VkSemaphoreCreateInfo startSemaphoreCreateInfo = {};
startSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &startSemaphoreCreateInfo, NULL, &swapchainElements[i].startSemaphore));
VkSemaphoreCreateInfo endSemaphoreCreateInfo = {};
endSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &endSemaphoreCreateInfo, NULL, &swapchainElements[i].endSemaphore));
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VulkanDevice::CHECK_VK_RESULT(vkCreateFence(VulkanDevice::device, &fenceCreateInfo, NULL, &swapchainElements[i].fence));
// If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain)
if (oldSwapchain != VK_NULL_HANDLE) {
for (auto i = 0; i < images.size(); i++) {
vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(VulkanDevice::device, oldSwapchain, nullptr);
}
uint32_t imageCount{ 0 };
VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, nullptr));
swapchainElements[i].lastFence = VK_NULL_HANDLE;
}
// Get the swap chain images
images.resize(imageCount);
VulkanDevice::CHECK_VK_RESULT(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, images.data()));
// Get the swap chain buffers containing the image and imageview
imageViews.resize(imageCount);
for (auto i = 0; i < images.size(); 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;
colorAttachmentView.image = images[i];
VulkanDevice::CHECK_VK_RESULT(vkCreateImageView(VulkanDevice::device, &colorAttachmentView, nullptr, &imageViews[i]));
}
}
void WindowWaylandVulkan::DestroySwapchain()
{
for (uint32_t i = 0; i < imageCount; i++)
{
vkDestroyFence(VulkanDevice::device, swapchainElements[i].fence, NULL);
vkDestroySemaphore(VulkanDevice::device, swapchainElements[i].endSemaphore, NULL);
vkDestroySemaphore(VulkanDevice::device, swapchainElements[i].startSemaphore, NULL);
vkDestroyFramebuffer(VulkanDevice::device, swapchainElements[i].framebuffer, NULL);
vkDestroyImageView(VulkanDevice::device, swapchainElements[i].imageView, NULL);
vkFreeCommandBuffers(VulkanDevice::device, VulkanDevice::commandPool, 1, &swapchainElements[i].commandBuffer);
}
vkDestroyRenderPass(VulkanDevice::device, renderPass, NULL);
vkDestroySwapchainKHR(VulkanDevice::device, swapchain, NULL);
}
PFN_vkCmdDrawMeshTasksEXT command;
WindowWaylandVulkan::WindowWaylandVulkan(std::string name, std::uint32_t width, std::uint32_t height) : WindowWayland(name, width, height) {
VkWaylandSurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
createInfo.display = display;
createInfo.surface = surface;
VulkanDevice::CHECK_VK_RESULT(vkCreateWaylandSurfaceKHR(VulkanDevice::instance, &createInfo, NULL, &vulkanSurface));
// Get list of supported surface formats
std::uint32_t formatCount;
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, NULL));
assert(formatCount > 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
VulkanDevice::CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, surfaceFormats.data()));
// We want to get a format that best suits our needs, so we try to get one from a set of preferred formats
// Initialize the format to the first one returned by the implementation in case we can't find one of the preffered formats
VkSurfaceFormatKHR selectedFormat = surfaceFormats[0];
std::vector<VkFormat> preferredImageFormats = {
VK_FORMAT_R8G8B8A8_UNORM,
};
for (auto& availableFormat : surfaceFormats) {
if (std::find(preferredImageFormats.begin(), preferredImageFormats.end(), availableFormat.format) != preferredImageFormats.end()) {
selectedFormat = availableFormat;
break;
}
}
colorFormat = selectedFormat.format;
colorSpace = selectedFormat.colorSpace;
CreateSwapchain();
command = reinterpret_cast<PFN_vkCmdDrawMeshTasksEXT>(vkGetDeviceProcAddr(VulkanDevice::device, "vkCmdDrawMeshTasksEXT"));
std::array<VkAttachmentDescription, 2> attachments = {};
// Color attachment
attachments[0].format = colorFormat;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
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_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Depth attachment
attachments[1].format = VulkanDevice::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_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
dependencies[0].dependencyFlags = 0;
dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].dstSubpass = 0;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].srcAccessMask = 0;
dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
dependencies[1].dependencyFlags = 0;
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();
VulkanDevice::CHECK_VK_RESULT(vkCreateRenderPass(VulkanDevice::device, &renderPassInfo, nullptr, &renderPass));
VkImageCreateInfo imageCI{};
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCI.imageType = VK_IMAGE_TYPE_2D;
imageCI.format = VulkanDevice::depthFormat;
imageCI.extent = { width, height, 1 };
imageCI.mipLevels = 1;
imageCI.arrayLayers = 1;
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
VulkanDevice::CHECK_VK_RESULT(vkCreateImage(VulkanDevice::device, &imageCI, nullptr, &depthStencil.image));
VkMemoryRequirements memReqs{};
vkGetImageMemoryRequirements(VulkanDevice::device, depthStencil.image, &memReqs);
VkMemoryAllocateInfo memAllloc{};
memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllloc.allocationSize = memReqs.size;
memAllloc.memoryTypeIndex = VulkanDevice::GetMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VulkanDevice::CHECK_VK_RESULT(vkAllocateMemory(VulkanDevice::device, &memAllloc, nullptr, &depthStencil.memory));
VulkanDevice::CHECK_VK_RESULT(vkBindImageMemory(VulkanDevice::device, depthStencil.image, depthStencil.memory, 0));
VkImageViewCreateInfo imageViewCI{};
imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
imageViewCI.image = depthStencil.image;
imageViewCI.format = VulkanDevice::depthFormat;
imageViewCI.subresourceRange.baseMipLevel = 0;
imageViewCI.subresourceRange.levelCount = 1;
imageViewCI.subresourceRange.baseArrayLayer = 0;
imageViewCI.subresourceRange.layerCount = 1;
imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
VulkanDevice::CHECK_VK_RESULT(vkCreateImageView(VulkanDevice::device, &imageViewCI, nullptr, &depthStencil.view));
// Create frame buffers for every swap chain image
frameBuffers.resize(images.size());
for (uint32_t i = 0; i < frameBuffers.size(); i++)
{
const VkImageView attachments[2] = {
imageViews[i],
depthStencil.view
};
VkFramebufferCreateInfo frameBufferCreateInfo{};
frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frameBufferCreateInfo.renderPass = renderPass;
frameBufferCreateInfo.attachmentCount = 2;
frameBufferCreateInfo.pAttachments = attachments;
frameBufferCreateInfo.width = width;
frameBufferCreateInfo.height = height;
frameBufferCreateInfo.layers = 1;
VulkanDevice::CHECK_VK_RESULT(vkCreateFramebuffer(VulkanDevice::device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]));
}
drawCmdBuffers.resize(images.size());
VkCommandBufferAllocateInfo cmdBufAllocateInfo = vks::initializers::commandBufferAllocateInfo(VulkanDevice::commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast<uint32_t>(drawCmdBuffers.size()));
VulkanDevice::CHECK_VK_RESULT(vkAllocateCommandBuffers(VulkanDevice::device, &cmdBufAllocateInfo, drawCmdBuffers.data()));
VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete));
VulkanDevice::CHECK_VK_RESULT(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete));
// Set up submit info structure
// Semaphores will stay the same during application lifetime
// Command buffer submission info is set by each example
submitInfo = vks::initializers::submitInfo();
submitInfo.pWaitDstStageMask = &submitPipelineStages;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
}
WindowWaylandVulkan::~WindowWaylandVulkan() {
if (swapChain != VK_NULL_HANDLE) {
for (auto i = 0; i < images.size(); i++) {
vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(VulkanDevice::device, swapChain, nullptr);
}
if (vulkanSurface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(VulkanDevice::instance, vulkanSurface, nullptr);
}
}
void WindowWaylandVulkan::Start() {
thread = std::thread([this](){
while (open && wl_display_dispatch(display) != -1) {
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
}
});
while(true) {
SwapchainElement* currentElement = &swapchainElements[currentFrame];
VkClearValue clearValues[2];
clearValues[0].color = { };;
clearValues[1].depthStencil = { 1.0f, 0 };
VulkanDevice::CHECK_VK_RESULT(vkWaitForFences(VulkanDevice::device, 1, &currentElement->fence, 1, UINT64_MAX));
VulkanDevice::CHECK_VK_RESULT(vkAcquireNextImageKHR(VulkanDevice::device, swapchain, UINT64_MAX, currentElement->startSemaphore, NULL, &imageIndex));
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{
VulkanDevice::CHECK_VK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
SwapchainElement* element = &swapchainElements[imageIndex];
if (element->lastFence) {
VulkanDevice::CHECK_VK_RESULT(vkWaitForFences(VulkanDevice::device, 1, &element->lastFence, 1, UINT64_MAX));
}
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
element->lastFence = currentElement->fence;
VkImageSubresourceRange depth_range{range};
depth_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
VulkanDevice::CHECK_VK_RESULT(vkResetFences(VulkanDevice::device, 1, &currentElement->fence));
image_layout_transition(drawCmdBuffers[i],
images[i],
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
range);
VkCommandBufferBeginInfo commandBeginInfo = {};
commandBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
commandBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
image_layout_transition(drawCmdBuffers[i],
depthStencil.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
depth_range);
VulkanDevice::CHECK_VK_RESULT(vkBeginCommandBuffer(element->commandBuffer, &commandBeginInfo));
VkClearValue clearValue = {{
1.0f,
0.0f,
1.0f,
1.0f
}};
VkRenderingAttachmentInfoKHR color_attachment_info = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, VK_NULL_HANDLE};
color_attachment_info.imageView = imageViews[i];
color_attachment_info.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
color_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE;
color_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment_info.clearValue = { 0.0f, 0.0f, 0.2f, 1.0f };
VkRenderPassBeginInfo renderBeginInfo = {};
renderBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderBeginInfo.renderPass = renderPass;
renderBeginInfo.framebuffer = element->framebuffer;
renderBeginInfo.renderArea.offset.x = 0;
renderBeginInfo.renderArea.offset.y = 0;
renderBeginInfo.renderArea.extent.width = width;
renderBeginInfo.renderArea.extent.height = height;
renderBeginInfo.clearValueCount = 1;
renderBeginInfo.pClearValues = &clearValue;
VkRenderingAttachmentInfoKHR depth_attachment_info = {VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, VK_NULL_HANDLE};
depth_attachment_info.imageView = depthStencil.view;
depth_attachment_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE;
depth_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment_info.clearValue = { 1.0f, 0 };
vkCmdBeginRenderPass(element->commandBuffer, &renderBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
VkRenderingInfo render_info = {VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,VK_NULL_HANDLE,0};
render_info.renderArea = VkRect2D{VkOffset2D{}, VkExtent2D{width, height}};
render_info.viewMask = 0;
render_info.layerCount = 1;
render_info.colorAttachmentCount = 1;
render_info.pColorAttachments = &color_attachment_info;
render_info.pDepthAttachment = &depth_attachment_info;
render_info.pStencilAttachment = VK_NULL_HANDLE;
VkViewport viewport = {0, 0, static_cast<float>(width), static_cast<float>(height), 0, 1};
vkCmdSetViewport(element->commandBuffer, 0, 1, &viewport);
VulkanDevice::vkCmdBeginRenderingKHRProc(drawCmdBuffers[i], &render_info);
VkRect2D scissor = {{static_cast<std::int32_t>(width), static_cast<std::int32_t>(height)},{0,0}};
vkCmdSetScissor(element->commandBuffer, 0, 1, &scissor);
VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
vkCmdBindDescriptorSets(element->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::layout, 0, 1, &VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::descriptor_set, 0, nullptr);
vkCmdBindPipeline(element->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::pipeline);
command(element->commandBuffer, 1, 1, 1);
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdEndRenderPass(element->commandBuffer);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::pipelineLayout, 0, 1, &VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::descriptorSet, 0, NULL);
VulkanDevice::CHECK_VK_RESULT(vkEndCommandBuffer(element->commandBuffer));
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::pipeline);
const VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
// Use mesh and task shader to draw the scene
VulkanDevice::vkCmdDrawMeshTasksEXTProc(drawCmdBuffers[i], 3, 1, 1);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &currentElement->startSemaphore;
submitInfo.pWaitDstStageMask = &waitStage;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &element->commandBuffer;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &currentElement->endSemaphore;
VulkanDevice::vkCmdEndRenderingKHRProc(drawCmdBuffers[i]);
VulkanDevice::CHECK_VK_RESULT(vkQueueSubmit(VulkanDevice::queue, 1, &submitInfo, currentElement->fence));
image_layout_transition(drawCmdBuffers[i],
images[i],
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
range
);
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &currentElement->endSemaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapchain;
presentInfo.pImageIndices = &imageIndex;
VulkanDevice::CHECK_VK_RESULT(vkQueuePresentKHR(VulkanDevice::queue, &presentInfo));
currentFrame = (currentFrame + 1) % imageCount;
wl_display_roundtrip(display);
VulkanDevice::CHECK_VK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
}
}
while (open && wl_display_dispatch(display) != -1) {
// Acquire the next image from the swap chain
VulkanDevice::CHECK_VK_RESULT(vkAcquireNextImageKHR(VulkanDevice::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, &currentBuffer));
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VulkanDevice::CHECK_VK_RESULT(vkQueueSubmit(VulkanDevice::queue, 1, &submitInfo, VK_NULL_HANDLE));
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = NULL;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &currentBuffer;
// Check if a wait semaphore has been specified to wait for before presenting the image
if (semaphores.renderComplete != VK_NULL_HANDLE)
{
presentInfo.pWaitSemaphores = &semaphores.renderComplete;
presentInfo.waitSemaphoreCount = 1;
}
VulkanDevice::CHECK_VK_RESULT(vkQueuePresentKHR(VulkanDevice::queue, &presentInfo));
VulkanDevice::CHECK_VK_RESULT(vkQueueWaitIdle(VulkanDevice::queue));
}
}

View file

@ -13,33 +13,40 @@ import :WindowWayland;
import Crafter.Component;
namespace Crafter {
struct SwapchainElement
{
VkCommandBuffer commandBuffer;
struct DepthStencil {
VkImage image;
VkImageView imageView;
VkFramebuffer framebuffer;
VkSemaphore startSemaphore;
VkSemaphore endSemaphore;
VkFence fence;
VkFence lastFence;
VkDeviceMemory memory;
VkImageView view;
};
struct Semaphores {
// Swap chain image presentation
VkSemaphore presentComplete;
// Command buffer submission and execution
VkSemaphore renderComplete;
};
export class WindowWaylandVulkan : public WindowWayland {
public:
WindowWaylandVulkan(std::string name, std::uint32_t width, std::uint32_t height);
~WindowWaylandVulkan();
void Start();
inline static VkRenderPass renderPass = VK_NULL_HANDLE;
private:
void CreateSwapchain();
void DestroySwapchain();
VkSurfaceKHR vulkanSurface = VK_NULL_HANDLE;
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
std::vector<SwapchainElement> swapchainElements;
std::uint32_t imageCount;
std::uint32_t currentFrame = 0;
std::uint32_t imageIndex = 0;
VkFormat format = VK_FORMAT_UNDEFINED;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
VkFormat colorFormat;
VkColorSpaceKHR colorSpace;
std::vector<VkImage> images;
std::vector<VkImageView> imageViews;
std::thread thread;
std::vector<VkCommandBuffer> drawCmdBuffers;
std::vector<VkFramebuffer> frameBuffers;
VkSubmitInfo submitInfo;
DepthStencil depthStencil;
Semaphores semaphores;
uint32_t currentBuffer = 0;
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkRenderPass renderPass = VK_NULL_HANDLE;
};
}

135
VulkanBuffer.cpp Normal file
View file

@ -0,0 +1,135 @@
/*
* Vulkan buffer class
*
* Encapsulates a Vulkan buffer
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#include "VulkanBuffer.h"
namespace vks
{
/**
* Map a memory range of this buffer. If successful, mapped points to the specified buffer range.
*
* @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the buffer mapping call
*/
VkResult Buffer::map(VkDeviceSize size, VkDeviceSize offset)
{
return vkMapMemory(device, memory, offset, size, 0, &mapped);
}
/**
* Unmap a mapped memory range
*
* @note Does not return a result as vkUnmapMemory can't fail
*/
void Buffer::unmap()
{
if (mapped)
{
vkUnmapMemory(device, memory);
mapped = nullptr;
}
}
/**
* Attach the allocated memory block to the buffer
*
* @param offset (Optional) Byte offset (from the beginning) for the memory region to bind
*
* @return VkResult of the bindBufferMemory call
*/
VkResult Buffer::bind(VkDeviceSize offset)
{
return vkBindBufferMemory(device, buffer, memory, offset);
}
/**
* Setup the default descriptor for this buffer
*
* @param size (Optional) Size of the memory range of the descriptor
* @param offset (Optional) Byte offset from beginning
*
*/
void Buffer::setupDescriptor(VkDeviceSize size, VkDeviceSize offset)
{
descriptor.offset = offset;
descriptor.buffer = buffer;
descriptor.range = size;
}
/**
* Copies the specified data to the mapped buffer
*
* @param data Pointer to the data to copy
* @param size Size of the data to copy in machine units
*
*/
void Buffer::copyTo(void* data, VkDeviceSize size)
{
assert(mapped);
memcpy(mapped, data, size);
}
/**
* Flush a memory range of the buffer to make it visible to the device
*
* @note Only required for non-coherent memory
*
* @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the flush call
*/
VkResult Buffer::flush(VkDeviceSize size, VkDeviceSize offset)
{
VkMappedMemoryRange mappedRange = {};
mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return vkFlushMappedMemoryRanges(device, 1, &mappedRange);
}
/**
* Invalidate a memory range of the buffer to make it visible to the host
*
* @note Only required for non-coherent memory
*
* @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range.
* @param offset (Optional) Byte offset from beginning
*
* @return VkResult of the invalidate call
*/
VkResult Buffer::invalidate(VkDeviceSize size, VkDeviceSize offset)
{
VkMappedMemoryRange mappedRange = {};
mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
mappedRange.memory = memory;
mappedRange.offset = offset;
mappedRange.size = size;
return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange);
}
/**
* Release all Vulkan resources held by this buffer
*/
void Buffer::destroy()
{
if (buffer)
{
vkDestroyBuffer(device, buffer, nullptr);
}
if (memory)
{
vkFreeMemory(device, memory, nullptr);
}
}
};

46
VulkanBuffer.h Normal file
View file

@ -0,0 +1,46 @@
/*
* Vulkan buffer class
*
* Encapsulates a Vulkan buffer
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#pragma once
#include <vector>
#include "vulkan/vulkan.h"
#include "VulkanTools.h"
namespace vks
{
/**
* @brief Encapsulates access to a Vulkan buffer backed up by device memory
* @note To be filled by an external source like the VulkanDevice
*/
struct Buffer
{
VkDevice device;
VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkDescriptorBufferInfo descriptor;
VkDeviceSize size = 0;
VkDeviceSize alignment = 0;
void* mapped = nullptr;
/** @brief Usage flags to be filled by external source at buffer creation (to query at some later point) */
VkBufferUsageFlags usageFlags;
/** @brief Memory property flags to be filled by external source at buffer creation (to query at some later point) */
VkMemoryPropertyFlags memoryPropertyFlags;
VkResult map(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
void unmap();
VkResult bind(VkDeviceSize offset = 0);
void setupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
void copyTo(void* data, VkDeviceSize size);
VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
void destroy();
};
}

660
VulkanInitializers.hpp Normal file
View file

@ -0,0 +1,660 @@
/*
* Initializers for Vulkan structures and objects used by the examples
* Saves lot of VK_STRUCTURE_TYPE assignments
* Some initializers are parameterized for convenience
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#pragma once
#include <vector>
#include "vulkan/vulkan.h"
namespace vks
{
namespace initializers
{
inline VkMemoryAllocateInfo memoryAllocateInfo()
{
VkMemoryAllocateInfo memAllocInfo {};
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
return memAllocInfo;
}
inline VkMappedMemoryRange mappedMemoryRange()
{
VkMappedMemoryRange mappedMemoryRange {};
mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
return mappedMemoryRange;
}
inline VkCommandBufferAllocateInfo commandBufferAllocateInfo(
VkCommandPool commandPool,
VkCommandBufferLevel level,
uint32_t bufferCount)
{
VkCommandBufferAllocateInfo commandBufferAllocateInfo {};
commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
commandBufferAllocateInfo.commandPool = commandPool;
commandBufferAllocateInfo.level = level;
commandBufferAllocateInfo.commandBufferCount = bufferCount;
return commandBufferAllocateInfo;
}
inline VkCommandPoolCreateInfo commandPoolCreateInfo()
{
VkCommandPoolCreateInfo cmdPoolCreateInfo {};
cmdPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
return cmdPoolCreateInfo;
}
inline VkCommandBufferBeginInfo commandBufferBeginInfo()
{
VkCommandBufferBeginInfo cmdBufferBeginInfo {};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
return cmdBufferBeginInfo;
}
inline VkCommandBufferInheritanceInfo commandBufferInheritanceInfo()
{
VkCommandBufferInheritanceInfo cmdBufferInheritanceInfo {};
cmdBufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
return cmdBufferInheritanceInfo;
}
inline VkRenderPassBeginInfo renderPassBeginInfo()
{
VkRenderPassBeginInfo renderPassBeginInfo {};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
return renderPassBeginInfo;
}
inline VkRenderPassCreateInfo renderPassCreateInfo()
{
VkRenderPassCreateInfo renderPassCreateInfo {};
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
return renderPassCreateInfo;
}
/** @brief Initialize an image memory barrier with no image transfer ownership */
inline VkImageMemoryBarrier imageMemoryBarrier()
{
VkImageMemoryBarrier imageMemoryBarrier {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
return imageMemoryBarrier;
}
/** @brief Initialize a buffer memory barrier with no image transfer ownership */
inline VkBufferMemoryBarrier bufferMemoryBarrier()
{
VkBufferMemoryBarrier bufferMemoryBarrier {};
bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
return bufferMemoryBarrier;
}
inline VkMemoryBarrier memoryBarrier()
{
VkMemoryBarrier memoryBarrier {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
return memoryBarrier;
}
inline VkImageCreateInfo imageCreateInfo()
{
VkImageCreateInfo imageCreateInfo {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
return imageCreateInfo;
}
inline VkSamplerCreateInfo samplerCreateInfo()
{
VkSamplerCreateInfo samplerCreateInfo {};
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCreateInfo.maxAnisotropy = 1.0f;
return samplerCreateInfo;
}
inline VkImageViewCreateInfo imageViewCreateInfo()
{
VkImageViewCreateInfo imageViewCreateInfo {};
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
return imageViewCreateInfo;
}
inline VkFramebufferCreateInfo framebufferCreateInfo()
{
VkFramebufferCreateInfo framebufferCreateInfo {};
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
return framebufferCreateInfo;
}
inline VkSemaphoreCreateInfo semaphoreCreateInfo()
{
VkSemaphoreCreateInfo semaphoreCreateInfo {};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
return semaphoreCreateInfo;
}
inline VkFenceCreateInfo fenceCreateInfo(VkFenceCreateFlags flags = 0)
{
VkFenceCreateInfo fenceCreateInfo {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = flags;
return fenceCreateInfo;
}
inline VkEventCreateInfo eventCreateInfo()
{
VkEventCreateInfo eventCreateInfo {};
eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
return eventCreateInfo;
}
inline VkSubmitInfo submitInfo()
{
VkSubmitInfo submitInfo {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
return submitInfo;
}
inline VkViewport viewport(
float width,
float height,
float minDepth,
float maxDepth)
{
VkViewport viewport {};
viewport.width = width;
viewport.height = height;
viewport.minDepth = minDepth;
viewport.maxDepth = maxDepth;
return viewport;
}
inline VkRect2D rect2D(
int32_t width,
int32_t height,
int32_t offsetX,
int32_t offsetY)
{
VkRect2D rect2D {};
rect2D.extent.width = width;
rect2D.extent.height = height;
rect2D.offset.x = offsetX;
rect2D.offset.y = offsetY;
return rect2D;
}
inline VkBufferCreateInfo bufferCreateInfo()
{
VkBufferCreateInfo bufCreateInfo {};
bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
return bufCreateInfo;
}
inline VkBufferCreateInfo bufferCreateInfo(
VkBufferUsageFlags usage,
VkDeviceSize size)
{
VkBufferCreateInfo bufCreateInfo {};
bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufCreateInfo.usage = usage;
bufCreateInfo.size = size;
return bufCreateInfo;
}
inline VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
uint32_t poolSizeCount,
VkDescriptorPoolSize* pPoolSizes,
uint32_t maxSets)
{
VkDescriptorPoolCreateInfo descriptorPoolInfo {};
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolInfo.poolSizeCount = poolSizeCount;
descriptorPoolInfo.pPoolSizes = pPoolSizes;
descriptorPoolInfo.maxSets = maxSets;
return descriptorPoolInfo;
}
inline VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
const std::vector<VkDescriptorPoolSize>& poolSizes,
uint32_t maxSets)
{
VkDescriptorPoolCreateInfo descriptorPoolInfo{};
descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
descriptorPoolInfo.pPoolSizes = poolSizes.data();
descriptorPoolInfo.maxSets = maxSets;
return descriptorPoolInfo;
}
inline VkDescriptorPoolSize descriptorPoolSize(
VkDescriptorType type,
uint32_t descriptorCount)
{
VkDescriptorPoolSize descriptorPoolSize {};
descriptorPoolSize.type = type;
descriptorPoolSize.descriptorCount = descriptorCount;
return descriptorPoolSize;
}
inline VkDescriptorSetLayoutBinding descriptorSetLayoutBinding(
VkDescriptorType type,
VkShaderStageFlags stageFlags,
uint32_t binding,
uint32_t descriptorCount = 1)
{
VkDescriptorSetLayoutBinding setLayoutBinding {};
setLayoutBinding.descriptorType = type;
setLayoutBinding.stageFlags = stageFlags;
setLayoutBinding.binding = binding;
setLayoutBinding.descriptorCount = descriptorCount;
return setLayoutBinding;
}
inline VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
const VkDescriptorSetLayoutBinding* pBindings,
uint32_t bindingCount)
{
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo {};
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCreateInfo.pBindings = pBindings;
descriptorSetLayoutCreateInfo.bindingCount = bindingCount;
return descriptorSetLayoutCreateInfo;
}
inline VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
const std::vector<VkDescriptorSetLayoutBinding>& bindings)
{
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCreateInfo.pBindings = bindings.data();
descriptorSetLayoutCreateInfo.bindingCount = static_cast<uint32_t>(bindings.size());
return descriptorSetLayoutCreateInfo;
}
inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
const VkDescriptorSetLayout* pSetLayouts,
uint32_t setLayoutCount = 1)
{
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo {};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
pipelineLayoutCreateInfo.pSetLayouts = pSetLayouts;
return pipelineLayoutCreateInfo;
}
inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
uint32_t setLayoutCount = 1)
{
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
return pipelineLayoutCreateInfo;
}
inline VkDescriptorSetAllocateInfo descriptorSetAllocateInfo(
VkDescriptorPool descriptorPool,
const VkDescriptorSetLayout* pSetLayouts,
uint32_t descriptorSetCount)
{
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo {};
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocateInfo.descriptorPool = descriptorPool;
descriptorSetAllocateInfo.pSetLayouts = pSetLayouts;
descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
return descriptorSetAllocateInfo;
}
inline VkDescriptorImageInfo descriptorImageInfo(VkSampler sampler, VkImageView imageView, VkImageLayout imageLayout)
{
VkDescriptorImageInfo descriptorImageInfo {};
descriptorImageInfo.sampler = sampler;
descriptorImageInfo.imageView = imageView;
descriptorImageInfo.imageLayout = imageLayout;
return descriptorImageInfo;
}
inline VkWriteDescriptorSet writeDescriptorSet(
VkDescriptorSet dstSet,
VkDescriptorType type,
uint32_t binding,
VkDescriptorBufferInfo* bufferInfo,
uint32_t descriptorCount = 1)
{
VkWriteDescriptorSet writeDescriptorSet {};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.dstSet = dstSet;
writeDescriptorSet.descriptorType = type;
writeDescriptorSet.dstBinding = binding;
writeDescriptorSet.pBufferInfo = bufferInfo;
writeDescriptorSet.descriptorCount = descriptorCount;
return writeDescriptorSet;
}
inline VkWriteDescriptorSet writeDescriptorSet(
VkDescriptorSet dstSet,
VkDescriptorType type,
uint32_t binding,
VkDescriptorImageInfo *imageInfo,
uint32_t descriptorCount = 1)
{
VkWriteDescriptorSet writeDescriptorSet {};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.dstSet = dstSet;
writeDescriptorSet.descriptorType = type;
writeDescriptorSet.dstBinding = binding;
writeDescriptorSet.pImageInfo = imageInfo;
writeDescriptorSet.descriptorCount = descriptorCount;
return writeDescriptorSet;
}
inline VkVertexInputBindingDescription vertexInputBindingDescription(
uint32_t binding,
uint32_t stride,
VkVertexInputRate inputRate)
{
VkVertexInputBindingDescription vInputBindDescription {};
vInputBindDescription.binding = binding;
vInputBindDescription.stride = stride;
vInputBindDescription.inputRate = inputRate;
return vInputBindDescription;
}
inline VkVertexInputAttributeDescription vertexInputAttributeDescription(
uint32_t binding,
uint32_t location,
VkFormat format,
uint32_t offset)
{
VkVertexInputAttributeDescription vInputAttribDescription {};
vInputAttribDescription.location = location;
vInputAttribDescription.binding = binding;
vInputAttribDescription.format = format;
vInputAttribDescription.offset = offset;
return vInputAttribDescription;
}
inline VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo()
{
VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo {};
pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
return pipelineVertexInputStateCreateInfo;
}
inline VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo(
const std::vector<VkVertexInputBindingDescription> &vertexBindingDescriptions,
const std::vector<VkVertexInputAttributeDescription> &vertexAttributeDescriptions
)
{
VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo{};
pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
pipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexBindingDescriptions.size());
pipelineVertexInputStateCreateInfo.pVertexBindingDescriptions = vertexBindingDescriptions.data();
pipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexAttributeDescriptions.size());
pipelineVertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexAttributeDescriptions.data();
return pipelineVertexInputStateCreateInfo;
}
inline VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
VkPrimitiveTopology topology,
VkPipelineInputAssemblyStateCreateFlags flags,
VkBool32 primitiveRestartEnable)
{
VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo {};
pipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipelineInputAssemblyStateCreateInfo.topology = topology;
pipelineInputAssemblyStateCreateInfo.flags = flags;
pipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = primitiveRestartEnable;
return pipelineInputAssemblyStateCreateInfo;
}
inline VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
VkPolygonMode polygonMode,
VkCullModeFlags cullMode,
VkFrontFace frontFace,
VkPipelineRasterizationStateCreateFlags flags = 0)
{
VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo {};
pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
pipelineRasterizationStateCreateInfo.polygonMode = polygonMode;
pipelineRasterizationStateCreateInfo.cullMode = cullMode;
pipelineRasterizationStateCreateInfo.frontFace = frontFace;
pipelineRasterizationStateCreateInfo.flags = flags;
pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
return pipelineRasterizationStateCreateInfo;
}
inline VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
VkColorComponentFlags colorWriteMask,
VkBool32 blendEnable)
{
VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState {};
pipelineColorBlendAttachmentState.colorWriteMask = colorWriteMask;
pipelineColorBlendAttachmentState.blendEnable = blendEnable;
return pipelineColorBlendAttachmentState;
}
inline VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
uint32_t attachmentCount,
const VkPipelineColorBlendAttachmentState * pAttachments)
{
VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo {};
pipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
pipelineColorBlendStateCreateInfo.attachmentCount = attachmentCount;
pipelineColorBlendStateCreateInfo.pAttachments = pAttachments;
return pipelineColorBlendStateCreateInfo;
}
inline VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo(
VkBool32 depthTestEnable,
VkBool32 depthWriteEnable,
VkCompareOp depthCompareOp)
{
VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo {};
pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
pipelineDepthStencilStateCreateInfo.depthTestEnable = depthTestEnable;
pipelineDepthStencilStateCreateInfo.depthWriteEnable = depthWriteEnable;
pipelineDepthStencilStateCreateInfo.depthCompareOp = depthCompareOp;
pipelineDepthStencilStateCreateInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
pipelineDepthStencilStateCreateInfo.stencilTestEnable = VK_FALSE;
return pipelineDepthStencilStateCreateInfo;
}
inline VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
uint32_t viewportCount,
uint32_t scissorCount,
VkPipelineViewportStateCreateFlags flags = 0)
{
VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo {};
pipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
pipelineViewportStateCreateInfo.viewportCount = viewportCount;
pipelineViewportStateCreateInfo.scissorCount = scissorCount;
pipelineViewportStateCreateInfo.flags = flags;
return pipelineViewportStateCreateInfo;
}
inline VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
VkSampleCountFlagBits rasterizationSamples,
VkPipelineMultisampleStateCreateFlags flags = 0)
{
VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo {};
pipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipelineMultisampleStateCreateInfo.rasterizationSamples = rasterizationSamples;
pipelineMultisampleStateCreateInfo.flags = flags;
return pipelineMultisampleStateCreateInfo;
}
inline VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
const VkDynamicState * pDynamicStates,
uint32_t dynamicStateCount,
VkPipelineDynamicStateCreateFlags flags = 0)
{
VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo {};
pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates;
pipelineDynamicStateCreateInfo.dynamicStateCount = dynamicStateCount;
pipelineDynamicStateCreateInfo.flags = flags;
return pipelineDynamicStateCreateInfo;
}
inline VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
const std::vector<VkDynamicState>& pDynamicStates,
VkPipelineDynamicStateCreateFlags flags = 0)
{
VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo{};
pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates.data();
pipelineDynamicStateCreateInfo.dynamicStateCount = static_cast<uint32_t>(pDynamicStates.size());
pipelineDynamicStateCreateInfo.flags = flags;
return pipelineDynamicStateCreateInfo;
}
inline VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(uint32_t patchControlPoints)
{
VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo {};
pipelineTessellationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
pipelineTessellationStateCreateInfo.patchControlPoints = patchControlPoints;
return pipelineTessellationStateCreateInfo;
}
inline VkGraphicsPipelineCreateInfo pipelineCreateInfo(
VkPipelineLayout layout,
VkRenderPass renderPass,
VkPipelineCreateFlags flags = 0)
{
VkGraphicsPipelineCreateInfo pipelineCreateInfo {};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.layout = layout;
pipelineCreateInfo.renderPass = renderPass;
pipelineCreateInfo.flags = flags;
pipelineCreateInfo.basePipelineIndex = -1;
pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
return pipelineCreateInfo;
}
inline VkGraphicsPipelineCreateInfo pipelineCreateInfo()
{
VkGraphicsPipelineCreateInfo pipelineCreateInfo{};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.basePipelineIndex = -1;
pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
return pipelineCreateInfo;
}
inline VkComputePipelineCreateInfo computePipelineCreateInfo(
VkPipelineLayout layout,
VkPipelineCreateFlags flags = 0)
{
VkComputePipelineCreateInfo computePipelineCreateInfo {};
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
computePipelineCreateInfo.layout = layout;
computePipelineCreateInfo.flags = flags;
return computePipelineCreateInfo;
}
inline VkPushConstantRange pushConstantRange(
VkShaderStageFlags stageFlags,
uint32_t size,
uint32_t offset)
{
VkPushConstantRange pushConstantRange {};
pushConstantRange.stageFlags = stageFlags;
pushConstantRange.offset = offset;
pushConstantRange.size = size;
return pushConstantRange;
}
inline VkBindSparseInfo bindSparseInfo()
{
VkBindSparseInfo bindSparseInfo{};
bindSparseInfo.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
return bindSparseInfo;
}
/** @brief Initialize a map entry for a shader specialization constant */
inline VkSpecializationMapEntry specializationMapEntry(uint32_t constantID, uint32_t offset, size_t size)
{
VkSpecializationMapEntry specializationMapEntry{};
specializationMapEntry.constantID = constantID;
specializationMapEntry.offset = offset;
specializationMapEntry.size = size;
return specializationMapEntry;
}
/** @brief Initialize a specialization constant info structure to pass to a shader stage */
inline VkSpecializationInfo specializationInfo(uint32_t mapEntryCount, const VkSpecializationMapEntry* mapEntries, size_t dataSize, const void* data)
{
VkSpecializationInfo specializationInfo{};
specializationInfo.mapEntryCount = mapEntryCount;
specializationInfo.pMapEntries = mapEntries;
specializationInfo.dataSize = dataSize;
specializationInfo.pData = data;
return specializationInfo;
}
/** @brief Initialize a specialization constant info structure to pass to a shader stage */
inline VkSpecializationInfo specializationInfo(const std::vector<VkSpecializationMapEntry> &mapEntries, size_t dataSize, const void* data)
{
VkSpecializationInfo specializationInfo{};
specializationInfo.mapEntryCount = static_cast<uint32_t>(mapEntries.size());
specializationInfo.pMapEntries = mapEntries.data();
specializationInfo.dataSize = dataSize;
specializationInfo.pData = data;
return specializationInfo;
}
// Ray tracing related
inline VkAccelerationStructureGeometryKHR accelerationStructureGeometryKHR()
{
VkAccelerationStructureGeometryKHR accelerationStructureGeometryKHR{};
accelerationStructureGeometryKHR.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
return accelerationStructureGeometryKHR;
}
inline VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfoKHR()
{
VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfoKHR{};
accelerationStructureBuildGeometryInfoKHR.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
return accelerationStructureBuildGeometryInfoKHR;
}
inline VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfoKHR()
{
VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfoKHR{};
accelerationStructureBuildSizesInfoKHR.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
return accelerationStructureBuildSizesInfoKHR;
}
inline VkRayTracingShaderGroupCreateInfoKHR rayTracingShaderGroupCreateInfoKHR()
{
VkRayTracingShaderGroupCreateInfoKHR rayTracingShaderGroupCreateInfoKHR{};
rayTracingShaderGroupCreateInfoKHR.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
return rayTracingShaderGroupCreateInfoKHR;
}
inline VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCreateInfoKHR()
{
VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCreateInfoKHR{};
rayTracingPipelineCreateInfoKHR.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR;
return rayTracingPipelineCreateInfoKHR;
}
inline VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructureKHR()
{
VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructureKHR{};
writeDescriptorSetAccelerationStructureKHR.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
return writeDescriptorSetAccelerationStructureKHR;
}
}
}

455
VulkanTools.cpp Normal file
View file

@ -0,0 +1,455 @@
/*
* Assorted commonly used Vulkan helper functions
*
* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#include "VulkanTools.h"
#if !(defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT))
// iOS & macOS: getAssetPath() and getShaderBasePath() implemented externally for access to Obj-C++ path utilities
const std::string getAssetPath()
{
if (vks::tools::resourcePath != "") {
return vks::tools::resourcePath + "/assets/";
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
return "";
#elif defined(VK_EXAMPLE_ASSETS_DIR)
return VK_EXAMPLE_ASSETS_DIR;
#else
return "./../assets/";
#endif
}
const std::string getShaderBasePath()
{
if (vks::tools::resourcePath != "") {
return vks::tools::resourcePath + "/shaders/";
}
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
return "shaders/";
#elif defined(VK_EXAMPLE_SHADERS_DIR)
return VK_EXAMPLE_SHADERS_DIR;
#else
return "./../shaders/";
#endif
}
#endif
namespace vks
{
namespace tools
{
bool errorModeSilent = false;
std::string resourcePath = "";
std::string errorString(VkResult errorCode)
{
switch (errorCode)
{
#define STR(r) case VK_ ##r: return #r
STR(NOT_READY);
STR(TIMEOUT);
STR(EVENT_SET);
STR(EVENT_RESET);
STR(INCOMPLETE);
STR(ERROR_OUT_OF_HOST_MEMORY);
STR(ERROR_OUT_OF_DEVICE_MEMORY);
STR(ERROR_INITIALIZATION_FAILED);
STR(ERROR_DEVICE_LOST);
STR(ERROR_MEMORY_MAP_FAILED);
STR(ERROR_LAYER_NOT_PRESENT);
STR(ERROR_EXTENSION_NOT_PRESENT);
STR(ERROR_FEATURE_NOT_PRESENT);
STR(ERROR_INCOMPATIBLE_DRIVER);
STR(ERROR_TOO_MANY_OBJECTS);
STR(ERROR_FORMAT_NOT_SUPPORTED);
STR(ERROR_SURFACE_LOST_KHR);
STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
STR(SUBOPTIMAL_KHR);
STR(ERROR_OUT_OF_DATE_KHR);
STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
STR(ERROR_VALIDATION_FAILED_EXT);
STR(ERROR_INVALID_SHADER_NV);
STR(ERROR_INCOMPATIBLE_SHADER_BINARY_EXT);
#undef STR
default:
return "UNKNOWN_ERROR";
}
}
std::string physicalDeviceTypeString(VkPhysicalDeviceType type)
{
switch (type)
{
#define STR(r) case VK_PHYSICAL_DEVICE_TYPE_ ##r: return #r
STR(OTHER);
STR(INTEGRATED_GPU);
STR(DISCRETE_GPU);
STR(VIRTUAL_GPU);
STR(CPU);
#undef STR
default: return "UNKNOWN_DEVICE_TYPE";
}
}
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat)
{
// Since all depth formats may be optional, we need to find a suitable depth format to use
// Start with the highest precision packed format
std::vector<VkFormat> formatList = {
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM
};
for (auto& format : formatList)
{
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
*depthFormat = format;
return true;
}
}
return false;
}
VkBool32 getSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice, VkFormat* depthStencilFormat)
{
std::vector<VkFormat> formatList = {
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
};
for (auto& format : formatList)
{
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
*depthStencilFormat = format;
return true;
}
}
return false;
}
VkBool32 formatHasStencil(VkFormat format)
{
std::vector<VkFormat> stencilFormats = {
VK_FORMAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
};
return std::find(stencilFormats.begin(), stencilFormats.end(), format) != std::end(stencilFormats);
}
// Returns if a given format support LINEAR filtering
VkBool32 formatIsFilterable(VkPhysicalDevice physicalDevice, VkFormat format, VkImageTiling tiling)
{
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
if (tiling == VK_IMAGE_TILING_OPTIMAL)
return formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
if (tiling == VK_IMAGE_TILING_LINEAR)
return formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
return false;
}
// Create an image memory barrier for changing the layout of
// an image and put it into an active command buffer
// See chapter 11.4 "Image Layout" for details
void setImageLayout(
VkCommandBuffer cmdbuffer,
VkImage image,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkImageSubresourceRange subresourceRange,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask)
{
// Create an image barrier object
VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();
imageMemoryBarrier.oldLayout = oldImageLayout;
imageMemoryBarrier.newLayout = newImageLayout;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange = subresourceRange;
// Source layouts (old)
// Source access mask controls actions that have to be finished on the old layout
// before it will be transitioned to the new layout
switch (oldImageLayout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
// Image layout is undefined (or does not matter)
// Only valid as initial layout
// No flags required, listed only for completeness
imageMemoryBarrier.srcAccessMask = 0;
break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
// Image is preinitialized
// Only valid as initial layout for linear images, preserves memory contents
// Make sure host writes have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Image is a color attachment
// Make sure any writes to the color buffer have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Image is a depth/stencil attachment
// Make sure any writes to the depth/stencil buffer have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
// Image is a transfer source
// Make sure any reads from the image have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
// Image is a transfer destination
// Make sure any writes to the image have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
// Image is read by a shader
// Make sure any shader reads from the image have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
default:
// Other source layouts aren't handled (yet)
break;
}
// Target layouts (new)
// Destination access mask controls the dependency for the new image layout
switch (newImageLayout)
{
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
// Image will be used as a transfer destination
// Make sure any writes to the image have been finished
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
// Image will be used as a transfer source
// Make sure any reads from the image have been finished
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Image will be used as a color attachment
// Make sure any writes to the color buffer have been finished
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Image layout will be used as a depth/stencil attachment
// Make sure any writes to depth/stencil buffer have been finished
imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
// Image will be read in a shader (sampler, input attachment)
// Make sure any writes to the image have been finished
if (imageMemoryBarrier.srcAccessMask == 0)
{
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
}
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
default:
// Other source layouts aren't handled (yet)
break;
}
// Put barrier inside setup command buffer
vkCmdPipelineBarrier(
cmdbuffer,
srcStageMask,
dstStageMask,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
// Fixed sub resource on first mip level and layer
void setImageLayout(
VkCommandBuffer cmdbuffer,
VkImage image,
VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask)
{
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = aspectMask;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.layerCount = 1;
setImageLayout(cmdbuffer, image, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask);
}
void insertImageMemoryBarrier(
VkCommandBuffer cmdbuffer,
VkImage image,
VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkImageSubresourceRange subresourceRange)
{
VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();
imageMemoryBarrier.srcAccessMask = srcAccessMask;
imageMemoryBarrier.dstAccessMask = dstAccessMask;
imageMemoryBarrier.oldLayout = oldImageLayout;
imageMemoryBarrier.newLayout = newImageLayout;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange = subresourceRange;
vkCmdPipelineBarrier(
cmdbuffer,
srcStageMask,
dstStageMask,
0,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
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
}
void exitFatal(const std::string& message, VkResult resultCode)
{
exitFatal(message, (int32_t)resultCode);
}
#if defined(__ANDROID__)
// Android shaders are stored as assets in the apk
// So they need to be loaded via the asset manager
VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device)
{
// Load shader from compressed asset
AAsset* asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_STREAMING);
assert(asset);
size_t size = AAsset_getLength(asset);
assert(size > 0);
char *shaderCode = new char[size];
AAsset_read(asset, shaderCode, size);
AAsset_close(asset);
VkShaderModule shaderModule;
VkShaderModuleCreateInfo moduleCreateInfo;
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleCreateInfo.pNext = NULL;
moduleCreateInfo.codeSize = size;
moduleCreateInfo.pCode = (uint32_t*)shaderCode;
moduleCreateInfo.flags = 0;
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
delete[] shaderCode;
return shaderModule;
}
#else
VkShaderModule loadShader(const char *fileName, VkDevice device)
{
std::ifstream is(fileName, std::ios::binary | std::ios::in | std::ios::ate);
if (is.is_open())
{
size_t size = is.tellg();
is.seekg(0, std::ios::beg);
char* shaderCode = new char[size];
is.read(shaderCode, size);
is.close();
assert(size > 0);
VkShaderModule shaderModule;
VkShaderModuleCreateInfo moduleCreateInfo{};
moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleCreateInfo.codeSize = size;
moduleCreateInfo.pCode = (uint32_t*)shaderCode;
VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
delete[] shaderCode;
return shaderModule;
}
else
{
std::cerr << "Error: Could not open shader file \"" << fileName << "\"" << "\n";
return VK_NULL_HANDLE;
}
}
#endif
bool fileExists(const std::string &filename)
{
std::ifstream f(filename.c_str());
return !f.fail();
}
uint32_t alignedSize(uint32_t value, uint32_t alignment)
{
return (value + alignment - 1) & ~(alignment - 1);
}
size_t alignedSize(size_t value, size_t alignment)
{
return (value + alignment - 1) & ~(alignment - 1);
}
VkDeviceSize alignedVkSize(VkDeviceSize value, VkDeviceSize alignment)
{
return (value + alignment - 1) & ~(alignment - 1);
}
}
}

141
VulkanTools.h Normal file
View file

@ -0,0 +1,141 @@
/*
* Assorted Vulkan helper functions
*
* Copyright (C) 2016-2023 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 "VulkanInitializers.hpp"
#include <math.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <fstream>
#include <assert.h>
#include <stdio.h>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <fstream>
#include <algorithm>
#if defined(_WIN32)
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#elif defined(__ANDROID__)
#include "VulkanAndroid.h"
#include <android/asset_manager.h>
#endif
// Custom define for better code readability
#define VK_FLAGS_NONE 0
// Default fence timeout in nanoseconds
#define DEFAULT_FENCE_TIMEOUT 100000000000
// Macro to check and display Vulkan return results
#if defined(__ANDROID__)
#define VK_CHECK_RESULT(f) \
{ \
VkResult res = (f); \
if (res != VK_SUCCESS) \
{ \
LOGE("Fatal : VkResult is \" %s \" in %s at line %d", vks::tools::errorString(res).c_str(), __FILE__, __LINE__); \
assert(res == VK_SUCCESS); \
} \
}
#else
#define VK_CHECK_RESULT(f) \
{ \
VkResult res = (f); \
if (res != VK_SUCCESS) \
{ \
std::cout << "Fatal : VkResult is \"" << vks::tools::errorString(res) << "\" in " << __FILE__ << " at line " << __LINE__ << "\n"; \
assert(res == VK_SUCCESS); \
} \
}
#endif
const std::string getAssetPath();
const std::string getShaderBasePath();
namespace vks
{
namespace tools
{
/** @brief Setting this path chnanges the place where the samples looks for assets and shaders */
extern std::string resourcePath;
/** @brief Disable message boxes on fatal errors */
extern bool errorModeSilent;
/** @brief Returns an error code as a string */
std::string errorString(VkResult errorCode);
/** @brief Returns the device type as a string */
std::string physicalDeviceTypeString(VkPhysicalDeviceType type);
// Selected a suitable supported depth format starting with 32 bit down to 16 bit
// Returns false if none of the depth formats in the list is supported by the device
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat);
// Same as getSupportedDepthFormat but will only select formats that also have stencil
VkBool32 getSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice, VkFormat* depthStencilFormat);
// Returns true a given format support LINEAR filtering
VkBool32 formatIsFilterable(VkPhysicalDevice physicalDevice, VkFormat format, VkImageTiling tiling);
// Returns true if a given format has a stencil part
VkBool32 formatHasStencil(VkFormat format);
// Put an image memory barrier for setting an image layout on the sub resource into the given command buffer
void setImageLayout(
VkCommandBuffer cmdbuffer,
VkImage image,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkImageSubresourceRange subresourceRange,
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
// Uses a fixed sub resource layout with first mip level and layer
void setImageLayout(
VkCommandBuffer cmdbuffer,
VkImage image,
VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
/** @brief Insert an image memory barrier into the command buffer */
void insertImageMemoryBarrier(
VkCommandBuffer cmdbuffer,
VkImage image,
VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask,
VkImageLayout oldImageLayout,
VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkImageSubresourceRange subresourceRange);
// Display error message and exit on fatal error
void exitFatal(const std::string& message, int32_t exitCode);
void exitFatal(const std::string& message, VkResult resultCode);
// Load a SPIR-V shader (binary)
#if defined(__ANDROID__)
VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device);
#else
VkShaderModule loadShader(const char *fileName, VkDevice device);
#endif
/** @brief Checks if a file exists */
bool fileExists(const std::string &filename);
uint32_t alignedSize(uint32_t value, uint32_t alignment);
VkDeviceSize alignedVkSize(VkDeviceSize value, VkDeviceSize alignment);
}
}

163
VulkanTransition.hpp Normal file
View file

@ -0,0 +1,163 @@
#include <vulkan/vulkan.h>
VkAccessFlags getAccessFlags(VkImageLayout layout)
{
switch (layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
return VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
assert(false && "Don't know how to get a meaningful VkAccessFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
return 0;
default:
assert(false);
return 0;
}
}
VkPipelineStageFlags getPipelineStageFlags(VkImageLayout layout)
{
switch (layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_PIPELINE_STAGE_HOST_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
return VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
assert(false && "Don't know how to get a meaningful VkPipelineStageFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
return 0;
default:
assert(false);
return 0;
}
}
// Create an image memory barrier for changing the layout of
// an image and put it into an active command buffer
// See chapter 12.4 "Image Layout" for details
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkPipelineStageFlags src_stage_mask,
VkPipelineStageFlags dst_stage_mask,
VkAccessFlags src_access_mask,
VkAccessFlags dst_access_mask,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range)
{
// Create an image barrier object
VkImageMemoryBarrier image_memory_barrier{};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.srcAccessMask = src_access_mask;
image_memory_barrier.dstAccessMask = dst_access_mask;
image_memory_barrier.oldLayout = old_layout;
image_memory_barrier.newLayout = new_layout;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = image;
image_memory_barrier.subresourceRange = subresource_range;
// Put barrier inside setup command buffer
vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
}
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range)
{
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
}
// Fixed sub resource on first mip level and layer
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkImageSubresourceRange subresource_range = {};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
subresource_range.layerCount = 1;
image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
}
void image_layout_transition(VkCommandBuffer command_buffer,
std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
// Create image barrier objects
std::vector<VkImageMemoryBarrier> image_memory_barriers;
image_memory_barriers.reserve(imagesAndRanges.size());
for (size_t i = 0; i < imagesAndRanges.size(); i++)
{
image_memory_barriers.emplace_back(VkImageMemoryBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
nullptr,
src_access_mask,
dst_access_mask,
old_layout,
new_layout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
imagesAndRanges[i].first,
imagesAndRanges[i].second});
}
// Put barriers inside setup command buffer
vkCmdPipelineBarrier(command_buffer,
src_stage_mask,
dst_stage_mask,
0,
0,
nullptr,
0,
nullptr,
static_cast<uint32_t>(image_memory_barriers.size()),
image_memory_barriers.data());
}

253
camera.hpp Normal file
View file

@ -0,0 +1,253 @@
/*
* Basic camera class providing a look-at and first-person camera
*
* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
class Camera
{
private:
float fov;
float znear, zfar;
void updateViewMatrix()
{
glm::mat4 currentMatrix = matrices.view;
glm::mat4 rotM = glm::mat4(1.0f);
glm::mat4 transM;
rotM = glm::rotate(rotM, glm::radians(rotation.x * (flipY ? -1.0f : 1.0f)), glm::vec3(1.0f, 0.0f, 0.0f));
rotM = glm::rotate(rotM, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
rotM = glm::rotate(rotM, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
glm::vec3 translation = position;
if (flipY) {
translation.y *= -1.0f;
}
transM = glm::translate(glm::mat4(1.0f), translation);
if (type == CameraType::firstperson)
{
matrices.view = rotM * transM;
}
else
{
matrices.view = transM * rotM;
}
viewPos = glm::vec4(position, 0.0f) * glm::vec4(-1.0f, 1.0f, -1.0f, 1.0f);
if (matrices.view != currentMatrix) {
updated = true;
}
};
public:
enum CameraType { lookat, firstperson };
CameraType type = CameraType::lookat;
glm::vec3 rotation = glm::vec3();
glm::vec3 position = glm::vec3();
glm::vec4 viewPos = glm::vec4();
float rotationSpeed = 1.0f;
float movementSpeed = 1.0f;
bool updated = true;
bool flipY = false;
struct
{
glm::mat4 perspective;
glm::mat4 view;
} matrices;
struct
{
bool left = false;
bool right = false;
bool up = false;
bool down = false;
} keys;
bool moving() const
{
return keys.left || keys.right || keys.up || keys.down;
}
float getNearClip() const {
return znear;
}
float getFarClip() const {
return zfar;
}
void setPerspective(float fov, float aspect, float znear, float zfar)
{
glm::mat4 currentMatrix = matrices.perspective;
this->fov = fov;
this->znear = znear;
this->zfar = zfar;
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
if (flipY) {
matrices.perspective[1][1] *= -1.0f;
}
if (matrices.view != currentMatrix) {
updated = true;
}
};
void updateAspectRatio(float aspect)
{
glm::mat4 currentMatrix = matrices.perspective;
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
if (flipY) {
matrices.perspective[1][1] *= -1.0f;
}
if (matrices.view != currentMatrix) {
updated = true;
}
}
void setPosition(glm::vec3 position)
{
this->position = position;
updateViewMatrix();
}
void setRotation(glm::vec3 rotation)
{
this->rotation = rotation;
updateViewMatrix();
}
void rotate(glm::vec3 delta)
{
this->rotation += delta;
updateViewMatrix();
}
void setTranslation(glm::vec3 translation)
{
this->position = translation;
updateViewMatrix();
};
void translate(glm::vec3 delta)
{
this->position += delta;
updateViewMatrix();
}
void setRotationSpeed(float rotationSpeed)
{
this->rotationSpeed = rotationSpeed;
}
void setMovementSpeed(float movementSpeed)
{
this->movementSpeed = movementSpeed;
}
void update(float deltaTime)
{
updated = false;
if (type == CameraType::firstperson)
{
if (moving())
{
glm::vec3 camFront;
camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
camFront.y = sin(glm::radians(rotation.x));
camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
camFront = glm::normalize(camFront);
float moveSpeed = deltaTime * movementSpeed;
if (keys.up)
position += camFront * moveSpeed;
if (keys.down)
position -= camFront * moveSpeed;
if (keys.left)
position -= glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed;
if (keys.right)
position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed;
}
}
updateViewMatrix();
};
// Update camera passing separate axis data (gamepad)
// Returns true if view or position has been changed
bool updatePad(glm::vec2 axisLeft, glm::vec2 axisRight, float deltaTime)
{
bool retVal = false;
if (type == CameraType::firstperson)
{
// Use the common console thumbstick layout
// Left = view, right = move
const float deadZone = 0.0015f;
const float range = 1.0f - deadZone;
glm::vec3 camFront;
camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
camFront.y = sin(glm::radians(rotation.x));
camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
camFront = glm::normalize(camFront);
float moveSpeed = deltaTime * movementSpeed * 2.0f;
float rotSpeed = deltaTime * rotationSpeed * 50.0f;
// Move
if (fabsf(axisLeft.y) > deadZone)
{
float pos = (fabsf(axisLeft.y) - deadZone) / range;
position -= camFront * pos * ((axisLeft.y < 0.0f) ? -1.0f : 1.0f) * moveSpeed;
retVal = true;
}
if (fabsf(axisLeft.x) > deadZone)
{
float pos = (fabsf(axisLeft.x) - deadZone) / range;
position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * pos * ((axisLeft.x < 0.0f) ? -1.0f : 1.0f) * moveSpeed;
retVal = true;
}
// Rotate
if (fabsf(axisRight.x) > deadZone)
{
float pos = (fabsf(axisRight.x) - deadZone) / range;
rotation.y += pos * ((axisRight.x < 0.0f) ? -1.0f : 1.0f) * rotSpeed;
retVal = true;
}
if (fabsf(axisRight.y) > deadZone)
{
float pos = (fabsf(axisRight.y) - deadZone) / range;
rotation.x -= pos * ((axisRight.y < 0.0f) ? -1.0f : 1.0f) * rotSpeed;
retVal = true;
}
}
else
{
// todo: move code from example base class for look-at
}
if (retVal)
{
updateViewMatrix();
}
return retVal;
}
};

View file

@ -32,7 +32,6 @@ int main() {
VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>::CreateShader();
VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>::CreateShader();
VulkanPipeline<VulkanShader<"test.spirv", "main", VK_SHADER_STAGE_MESH_BIT_EXT>, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::CreatePipeline();
window.Start();
while(true) {

View file

@ -4,7 +4,7 @@
{
"name": "base",
"standard": "c++26",
"source_files": ["Crafter.Graphics-Window","Crafter.Graphics-WindowWayland","Crafter.Graphics-WindowWaylandWayland", "Crafter.Graphics-UiElement", "Crafter.Graphics-VulkanDevice", "Crafter.Graphics-WindowWaylandVulkan"],
"source_files": ["Crafter.Graphics-Window","Crafter.Graphics-WindowWayland","Crafter.Graphics-WindowWaylandWayland", "Crafter.Graphics-UiElement", "Crafter.Graphics-VulkanDevice", "Crafter.Graphics-WindowWaylandVulkan", "VulkanBuffer", "VulkanTools"],
"c_files": ["wayland-xdg-decoration-unstable-v1-client-protocol", "xdg-shell-protocol", "shm"],
"module_files": ["Crafter.Graphics-Window","Crafter.Graphics-WindowWayland","Crafter.Graphics-WindowWaylandWayland", "Crafter.Graphics", "Crafter.Graphics-UiElement", "Crafter.Graphics-Types", "Crafter.Graphics-VulkanDevice", "Crafter.Graphics-VulkanPipeline", "Crafter.Graphics-VulkanShader", "Crafter.Graphics-WindowWaylandVulkan"],
"build_dir": "./build",

View file

@ -1,16 +1,52 @@
/* Copyright (c) 2021, Sascha Willems
*
* SPDX-License-Identifier: MIT
*
*/
#version 450
#extension GL_EXT_mesh_shader : require
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
mat4 view;
} ubo;
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(triangles, max_vertices = 3, max_primitives = 1) out;
layout(location = 0) out VertexOutput
{
vec4 color;
} vertexOutput[];
const vec4[3] positions = {
vec4( 0.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0)
};
const vec4[3] colors = {
vec4(0.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0),
vec4(1.0, 0.0, 0.0, 1.0)
};
void main()
{
uint vertexCount = 3;
uint triangleCount = 1;
SetMeshOutputsEXT(vertexCount, triangleCount);
gl_MeshVerticesEXT[0].gl_Position = vec4(0.5,-0.5, 0, 1);
gl_MeshVerticesEXT[1].gl_Position = vec4(0.5, 0.5, 0, 1);
gl_MeshVerticesEXT[2].gl_Position = vec4(-0.5, 0.5, 0, 1);
gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);
}
uint iid = gl_LocalInvocationID.x;
vec4 offset = vec4(0.0, 0.0, gl_GlobalInvocationID.x, 0.0);
SetMeshOutputsEXT(3, 1);
mat4 mvp = ubo.projection * ubo.view * ubo.model;
gl_MeshVerticesEXT[0].gl_Position = mvp * (positions[0] + offset);
gl_MeshVerticesEXT[1].gl_Position = mvp * (positions[1] + offset);
gl_MeshVerticesEXT[2].gl_Position = mvp * (positions[2] + offset);
vertexOutput[0].color = colors[0];
vertexOutput[1].color = colors[1];
vertexOutput[2].color = colors[2];
gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);
}

Binary file not shown.

View file

@ -1,25 +1,19 @@
#version 450
/* Copyright (c) 2023-2024 Holochip Corporation
/* Copyright (c) 2021, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
* SPDX-License-Identifier: MIT
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
layout (location = 0) out vec4 color;
#version 450
layout (location = 0) in VertexInput {
vec4 color;
} vertexInput;
layout(location = 0) out vec4 outFragColor;
void main()
{
color = vec4(1.0,0.0,0.0,1.0);
outFragColor = vertexInput.color;
}

Binary file not shown.