diff --git a/Makefile b/Makefile index bd30a5e..3b0e75c 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,9 @@ else sh -x ./scripts/reset_build.sh endif +shaders: + glslangValidator -V gradient.comp -o gradient.comp.spv + %.cpp : %.rl ragel -I $(ROOT_DIR) -G1 -o $@ $< @@ -18,7 +21,7 @@ endif %.png: %.dot dot -Tpng $< -o $@ -build: +build: meson compile -j 10 -C $(ROOT_DIR)/builddir release_build: diff --git a/gradient.comp b/gradient.comp new file mode 100644 index 0000000..3c576f1 --- /dev/null +++ b/gradient.comp @@ -0,0 +1,28 @@ +//GLSL version to use +#version 460 + +//size of a workgroup for compute +layout (local_size_x = 16, local_size_y = 16) in; + +//descriptor bindings for the pipeline +layout(rgba16f,set = 0, binding = 0) uniform image2D image; + + +void main() +{ + ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(image); + + if(texelCoord.x < size.x && texelCoord.y < size.y) + { + vec4 color = vec4(0.0, 0.0, 0.0, 1.0); + + if(gl_LocalInvocationID.x != 0 && gl_LocalInvocationID.y != 0) + { + color.x = float(texelCoord.x)/(size.x); + color.y = float(texelCoord.y)/(size.y); + } + + imageStore(image, texelCoord, color); + } +} diff --git a/gradient.comp.spv b/gradient.comp.spv new file mode 100644 index 0000000..9e23671 Binary files /dev/null and b/gradient.comp.spv differ diff --git a/meson.build b/meson.build index c602331..a3e35ec 100644 --- a/meson.build +++ b/meson.build @@ -95,6 +95,8 @@ sources = [ 'vk_initializers.cpp', 'vk_engine.cpp', 'vk_images.cpp', + 'vk_descriptors.cpp', + 'vk_pipelines.cpp', 'main.cpp', ] diff --git a/vk_descriptors.cpp b/vk_descriptors.cpp new file mode 100644 index 0000000..bfad725 --- /dev/null +++ b/vk_descriptors.cpp @@ -0,0 +1,92 @@ +#include "vk_descriptors.h" + +void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type) +{ + + VkDescriptorSetLayoutBinding newbind {}; + newbind.binding = binding; + newbind.descriptorCount = 1; + newbind.descriptorType = type; + + bindings.push_back(newbind); +} + +void DescriptorLayoutBuilder::clear() +{ + bindings.clear(); +} + +VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device, + VkShaderStageFlags shaderStages, + void* pNext, + VkDescriptorSetLayoutCreateFlags flags) +{ + for(auto& b : bindings) { + b.stageFlags |= shaderStages; + } + + VkDescriptorSetLayoutCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO + }; + info.pNext = pNext; + + info.pBindings = bindings.data(); + info.bindingCount = (uint32_t)bindings.size(); + info.flags = flags; + + VkDescriptorSetLayout set; + VK_CHECK(vkCreateDescriptorSetLayout(device, + &info, nullptr, &set)); + + return set; +} + +void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets, + std::span poolRatios) +{ + std::vector poolSizes; + for(PoolSizeRatio ratio : poolRatios) { + poolSizes.push_back(VkDescriptorPoolSize{ + .type = ratio.type, + .descriptorCount = uint32_t(ratio.ratio * maxSets) + }); + } + + VkDescriptorPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO + }; + pool_info.flags = 0; + pool_info.maxSets = maxSets; + pool_info.poolSizeCount = (uint32_t)poolSizes.size(); + pool_info.pPoolSizes = poolSizes.data(); + + vkCreateDescriptorPool(device, &pool_info, nullptr, &pool); +} + +void DescriptorAllocator::clear_descriptors(VkDevice device) +{ + vkResetDescriptorPool(device, pool, 0); +} + +void DescriptorAllocator::destroy_pool(VkDevice device) +{ + vkDestroyDescriptorPool(device, pool, nullptr); +} + +VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout) +{ + VkDescriptorSetAllocateInfo allocInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO + }; + + allocInfo.pNext = nullptr; + allocInfo.descriptorPool = pool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &layout; + + VkDescriptorSet ds; + VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds)); + + return ds; +} + diff --git a/vk_descriptors.h b/vk_descriptors.h new file mode 100644 index 0000000..c0ea11d --- /dev/null +++ b/vk_descriptors.h @@ -0,0 +1,35 @@ +#pragma once + +#include "vk_types.h" +#include + +struct DescriptorLayoutBuilder { + std::vector bindings; + + void add_binding(uint32_t binding, VkDescriptorType type); + void clear(); + + VkDescriptorSetLayout build(VkDevice device, + VkShaderStageFlags shaderStages, + void* pNext = nullptr, + VkDescriptorSetLayoutCreateFlags flags = 0); +}; + +struct DescriptorAllocator { + + struct PoolSizeRatio { + VkDescriptorType type; + float ratio; + }; + + VkDescriptorPool pool; + + void init_pool(VkDevice device, uint32_t maxSets, + std::span poolRatios); + + void clear_descriptors(VkDevice device); + void destroy_pool(VkDevice device); + + VkDescriptorSet allocate(VkDevice device, VkDescriptorSetLayout layout); + +}; diff --git a/vk_engine.cpp b/vk_engine.cpp index 821ecb4..490d0de 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -1,5 +1,6 @@ #include "vk_engine.h" #include "vk_images.h" +#include "vk_pipelines.h" #include #include @@ -15,7 +16,7 @@ #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" -constexpr bool bUseValidationLayers = false; +constexpr bool bUseValidationLayers = true; VulkanEngine* loadedEngine = nullptr; @@ -45,6 +46,8 @@ void VulkanEngine::init() init_swapchain(); init_commands(); init_sync_structures(); + init_descriptors(); + init_pipelines(); //everything went fine _isInitialized = true; @@ -358,11 +361,100 @@ void VulkanEngine::init_sync_structures() { void VulkanEngine::draw_background(VkCommandBuffer cmd) { - VkClearColorValue clearValue; - float flash = std::abs(std::sin(float(_frameNumber) / 120.0f)); - clearValue = { { 0.0f, 0.0f, flash, 1.0f} }; +// bind the gradient drawing compute pipeline + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline); - VkImageSubresourceRange clearRange = vkinit::image_subresource_range(VK_IMAGE_ASPECT_COLOR_BIT); + // bind the descriptor set containing the draw image for the compute pipeline + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr); - vkCmdClearColorImage(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange); + // execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it + vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1); +} + + +void VulkanEngine::init_descriptors() { + std::vector sizes = + { + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 } + }; + + globalDescriptorAllocator.init_pool(_device, 10, sizes); + + // make the descriptor set layout for our compute draw + { + DescriptorLayoutBuilder builder; + builder.add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); + _drawImageDescriptorLayout = builder.build(_device, VK_SHADER_STAGE_COMPUTE_BIT); + } + + // other code + //allocate a descriptor set for our draw image + _drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); + + VkDescriptorImageInfo imgInfo{}; + imgInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imgInfo.imageView = _drawImage.imageView; + + VkWriteDescriptorSet drawImageWrite = {}; + drawImageWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + drawImageWrite.pNext = nullptr; + + drawImageWrite.dstBinding = 0; + drawImageWrite.dstSet = _drawImageDescriptors; + drawImageWrite.descriptorCount = 1; + drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + drawImageWrite.pImageInfo = &imgInfo; + + vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr); + + //make sure both the descriptor allocator and the new layout get cleaned up properly + _mainDeletionQueue.push_function([&]() { + globalDescriptorAllocator.destroy_pool(_device); + + vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr); + }); +} + +void VulkanEngine::init_pipelines() +{ + init_background_pipelines(); +} + +void VulkanEngine::init_background_pipelines() +{ + VkPipelineLayoutCreateInfo computeLayout{}; + computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + computeLayout.pNext = nullptr; + computeLayout.pSetLayouts = &_drawImageDescriptorLayout; + computeLayout.setLayoutCount = 1; + + VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); + + VkShaderModule computeDrawShader; + + if(!vkutil::load_shader_module("gradient.comp.spv", _device, &computeDrawShader)) { + std::println("Error when building compute shader"); + } + + VkPipelineShaderStageCreateInfo stageinfo{}; + stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageinfo.pNext = nullptr; + stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + stageinfo.module = computeDrawShader; + stageinfo.pName = "main"; + + VkComputePipelineCreateInfo computePipelineCreateInfo{}; + computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + computePipelineCreateInfo.pNext = nullptr; + computePipelineCreateInfo.layout = _gradientPipelineLayout; + computePipelineCreateInfo.stage = stageinfo; + + VK_CHECK(vkCreateComputePipelines(_device,VK_NULL_HANDLE,1, &computePipelineCreateInfo, nullptr, &_gradientPipeline)); + + vkDestroyShaderModule(_device, computeDrawShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); + vkDestroyPipeline(_device, _gradientPipeline, nullptr); + }); } diff --git a/vk_engine.h b/vk_engine.h index c9b120e..d029dcc 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -4,6 +4,7 @@ #pragma once #include +#include struct DeletionQueue { std::deque> deletors; @@ -35,6 +36,13 @@ constexpr unsigned int FRAME_OVERLAP=2; class VulkanEngine { public: + VkPipeline _gradientPipeline; + VkPipelineLayout _gradientPipelineLayout; + + DescriptorAllocator globalDescriptorAllocator; + VkDescriptorSet _drawImageDescriptors; + VkDescriptorSetLayout _drawImageDescriptorLayout; + // device selection VkInstance _instance; VkDebugUtilsMessengerEXT _debug_messenger; @@ -93,6 +101,9 @@ private: void init_swapchain(); void init_commands(); void init_sync_structures(); + void init_descriptors(); + void init_pipelines(); + void init_background_pipelines(); void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); diff --git a/vk_pipelines.cpp b/vk_pipelines.cpp new file mode 100644 index 0000000..4067f1d --- /dev/null +++ b/vk_pipelines.cpp @@ -0,0 +1,42 @@ +#include "vk_pipelines.h" +#include +#include "vk_initializers.h" +#include + +bool vkutil::load_shader_module(const char* filePath, + VkDevice device, + VkShaderModule* outShaderModule) +{ + std::ifstream file(filePath, std::ios::ate | std::ios::binary); + + if(!file.is_open()) { + std::println("failed to open file: {}", filePath); + return false; + } + + size_t fileSize = (size_t)file.tellg(); + + //spirv expects the buffer to uint32 + std::vector buffer(fileSize / sizeof(uint32_t)); + file.seekg(0); + file.read((char*)buffer.data(), fileSize); + + file.close(); + + VkShaderModuleCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.pNext = nullptr; + + // codeSize has to be in bytes + createInfo.codeSize = buffer.size() * sizeof(uint32_t); + createInfo.pCode = buffer.data(); + + VkShaderModule shaderModule; + if(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { + std::println("Error after vkCreateShaderModule"); + return false; + } + + *outShaderModule = shaderModule; + return true; +} diff --git a/vk_pipelines.h b/vk_pipelines.h new file mode 100644 index 0000000..df9c74d --- /dev/null +++ b/vk_pipelines.h @@ -0,0 +1,9 @@ +#pragma once + +#include "vk_types.h" + +namespace vkutil { + bool load_shader_module(const char* filePath, + VkDevice device, + VkShaderModule* outShaderModule); +}