diff --git a/Crafter.Graphics-VulkanDevice.cpp b/Crafter.Graphics-VulkanDevice.cpp index befe3e4..ef12aab 100644 --- a/Crafter.Graphics-VulkanDevice.cpp +++ b/Crafter.Graphics-VulkanDevice.cpp @@ -9,6 +9,8 @@ module; #include #include #include +#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 formatList = { + VK_FORMAT_D32_SFLOAT, + }; + + for (auto& format : formatList) { + VkFormatProperties formatProps; + vkGetPhysicalDeviceFormatProperties(physDevice, format, &formatProps); + if (formatProps.optimalTilingFeatures) + { + depthFormat = format; + break; + } + } + + vkCmdDrawMeshTasksEXTProc = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT")); + vkCmdBeginRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR")); + vkCmdEndRenderingKHRProc = reinterpret_cast(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()); } \ No newline at end of file diff --git a/Crafter.Graphics-VulkanDevice.cppm b/Crafter.Graphics-VulkanDevice.cppm index 66cab39..209bed1 100644 --- a/Crafter.Graphics-VulkanDevice.cppm +++ b/Crafter.Graphics-VulkanDevice.cppm @@ -3,14 +3,13 @@ module; #include #include #include +#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); }; } \ No newline at end of file diff --git a/Crafter.Graphics-VulkanPipeline.cppm b/Crafter.Graphics-VulkanPipeline.cppm index 54b0ad8..047bef0 100644 --- a/Crafter.Graphics-VulkanPipeline.cppm +++ b/Crafter.Graphics-VulkanPipeline.cppm @@ -3,6 +3,16 @@ module; #include #include #include +#include "VulkanInitializers.hpp" +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#define GLM_ENABLE_EXPERIMENTAL +#include +#include +#include +#include +#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 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(dynamics.size()); - - // Load our SPIR-V shaders. - std::array 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 poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(static_cast(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(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 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 modelWriteDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), + }; + + vkUpdateDescriptorSets(VulkanDevice::device, static_cast(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 dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array 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(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)); } }; } \ No newline at end of file diff --git a/Crafter.Graphics-WindowWaylandVulkan.cpp b/Crafter.Graphics-WindowWaylandVulkan.cpp index 8547c46..5ad9378 100644 --- a/Crafter.Graphics-WindowWaylandVulkan.cpp +++ b/Crafter.Graphics-WindowWaylandVulkan.cpp @@ -8,6 +8,10 @@ module; #include #include #include +#include +#include +#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 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 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 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 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 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 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(vkGetDeviceProcAddr(VulkanDevice::device, "vkCmdDrawMeshTasksEXT")); + + std::array 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 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(attachments.size()); + renderPassInfo.pAttachments = attachments.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpassDescription; + renderPassInfo.dependencyCount = static_cast(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(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, ¤tElement->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, ¤tElement->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(width), static_cast(height), 0, 1}; - vkCmdSetViewport(element->commandBuffer, 0, 1, &viewport); + VulkanDevice::vkCmdBeginRenderingKHRProc(drawCmdBuffers[i], &render_info); - VkRect2D scissor = {{static_cast(width), static_cast(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<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::layout, 0, 1, &VulkanPipeline, VulkanShader<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::descriptor_set, 0, nullptr); - vkCmdBindPipeline(element->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, VulkanPipeline, 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<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::pipelineLayout, 0, 1, &VulkanPipeline, 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<"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 = ¤tElement->startSemaphore; - submitInfo.pWaitDstStageMask = &waitStage; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &element->commandBuffer; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = ¤tElement->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 = ¤tElement->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])); } -} \ No newline at end of file + 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, ¤tBuffer)); + 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 = ¤tBuffer; + // 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)); + } +} + diff --git a/Crafter.Graphics-WindowWaylandVulkan.cppm b/Crafter.Graphics-WindowWaylandVulkan.cppm index 9859e19..52c4736 100644 --- a/Crafter.Graphics-WindowWaylandVulkan.cppm +++ b/Crafter.Graphics-WindowWaylandVulkan.cppm @@ -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 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 images; + std::vector imageViews; std::thread thread; + std::vector drawCmdBuffers; + std::vector 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; }; } \ No newline at end of file diff --git a/VulkanBuffer.cpp b/VulkanBuffer.cpp new file mode 100644 index 0000000..7d4bf24 --- /dev/null +++ b/VulkanBuffer.cpp @@ -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); + } + } +}; diff --git a/VulkanBuffer.h b/VulkanBuffer.h new file mode 100644 index 0000000..bd2a421 --- /dev/null +++ b/VulkanBuffer.h @@ -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 + +#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(); + }; +} \ No newline at end of file diff --git a/VulkanInitializers.hpp b/VulkanInitializers.hpp new file mode 100644 index 0000000..da08c7d --- /dev/null +++ b/VulkanInitializers.hpp @@ -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 +#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& poolSizes, + uint32_t maxSets) + { + VkDescriptorPoolCreateInfo descriptorPoolInfo{}; + descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolInfo.poolSizeCount = static_cast(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& bindings) + { + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{}; + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.pBindings = bindings.data(); + descriptorSetLayoutCreateInfo.bindingCount = static_cast(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 &vertexBindingDescriptions, + const std::vector &vertexAttributeDescriptions + ) + { + VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo{}; + pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + pipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = static_cast(vertexBindingDescriptions.size()); + pipelineVertexInputStateCreateInfo.pVertexBindingDescriptions = vertexBindingDescriptions.data(); + pipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = static_cast(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& pDynamicStates, + VkPipelineDynamicStateCreateFlags flags = 0) + { + VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo{}; + pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates.data(); + pipelineDynamicStateCreateInfo.dynamicStateCount = static_cast(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 &mapEntries, size_t dataSize, const void* data) + { + VkSpecializationInfo specializationInfo{}; + specializationInfo.mapEntryCount = static_cast(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; + } + + } +} \ No newline at end of file diff --git a/VulkanTools.cpp b/VulkanTools.cpp new file mode 100644 index 0000000..d2a4afc --- /dev/null +++ b/VulkanTools.cpp @@ -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 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 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 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); + } + + } +} diff --git a/VulkanTools.h b/VulkanTools.h new file mode 100644 index 0000000..83fde60 --- /dev/null +++ b/VulkanTools.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#include +#include +#elif defined(__ANDROID__) +#include "VulkanAndroid.h" +#include +#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); + } +} diff --git a/VulkanTransition.hpp b/VulkanTransition.hpp new file mode 100644 index 0000000..481ae4d --- /dev/null +++ b/VulkanTransition.hpp @@ -0,0 +1,163 @@ + #include + + + + 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> 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 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(image_memory_barriers.size()), + image_memory_barriers.data()); + } + \ No newline at end of file diff --git a/camera.hpp b/camera.hpp new file mode 100644 index 0000000..4838bde --- /dev/null +++ b/camera.hpp @@ -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 +#include +#include + +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; + } + +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index c1fde34..40515a5 100644 --- a/main.cpp +++ b/main.cpp @@ -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<"test2.spirv", "main", VK_SHADER_STAGE_FRAGMENT_BIT>>::CreatePipeline(); - window.Start(); while(true) { diff --git a/project.json b/project.json index 3cce0b5..0aff66f 100644 --- a/project.json +++ b/project.json @@ -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", diff --git a/test.mesh b/test.mesh index 5630a21..2e0a34c 100644 --- a/test.mesh +++ b/test.mesh @@ -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); -} \ No newline at end of file + 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); +} diff --git a/test.spirv b/test.spirv index e4ef206..d9dcb78 100644 Binary files a/test.spirv and b/test.spirv differ diff --git a/test2.frag b/test2.frag index ebc64ff..f425dd4 100644 --- a/test2.frag +++ b/test2.frag @@ -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; } \ No newline at end of file diff --git a/test2.spirv b/test2.spirv index eb0fdd2..fc2aac6 100644 Binary files a/test2.spirv and b/test2.spirv differ