Latest chapter from vkguide but doesn't work yet.

This commit is contained in:
Zed A. Shaw 2025-11-28 14:11:28 -05:00
parent 40717cf8e4
commit a5c13d8654
10 changed files with 321 additions and 7 deletions

View file

@ -9,6 +9,9 @@ else
sh -x ./scripts/reset_build.sh sh -x ./scripts/reset_build.sh
endif endif
shaders:
glslangValidator -V gradient.comp -o gradient.comp.spv
%.cpp : %.rl %.cpp : %.rl
ragel -I $(ROOT_DIR) -G1 -o $@ $< ragel -I $(ROOT_DIR) -G1 -o $@ $<
@ -18,7 +21,7 @@ endif
%.png: %.dot %.png: %.dot
dot -Tpng $< -o $@ dot -Tpng $< -o $@
build: build:
meson compile -j 10 -C $(ROOT_DIR)/builddir meson compile -j 10 -C $(ROOT_DIR)/builddir
release_build: release_build:

28
gradient.comp Normal file
View file

@ -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);
}
}

BIN
gradient.comp.spv Normal file

Binary file not shown.

View file

@ -95,6 +95,8 @@ sources = [
'vk_initializers.cpp', 'vk_initializers.cpp',
'vk_engine.cpp', 'vk_engine.cpp',
'vk_images.cpp', 'vk_images.cpp',
'vk_descriptors.cpp',
'vk_pipelines.cpp',
'main.cpp', 'main.cpp',
] ]

92
vk_descriptors.cpp Normal file
View file

@ -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<PoolSizeRatio> poolRatios)
{
std::vector<VkDescriptorPoolSize> 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;
}

35
vk_descriptors.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "vk_types.h"
#include <vector>
struct DescriptorLayoutBuilder {
std::vector<VkDescriptorSetLayoutBinding> 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<PoolSizeRatio> poolRatios);
void clear_descriptors(VkDevice device);
void destroy_pool(VkDevice device);
VkDescriptorSet allocate(VkDevice device, VkDescriptorSetLayout layout);
};

View file

@ -1,5 +1,6 @@
#include "vk_engine.h" #include "vk_engine.h"
#include "vk_images.h" #include "vk_images.h"
#include "vk_pipelines.h"
#include <print> #include <print>
#include <SDL.h> #include <SDL.h>
@ -15,7 +16,7 @@
#define VMA_IMPLEMENTATION #define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h" #include "vk_mem_alloc.h"
constexpr bool bUseValidationLayers = false; constexpr bool bUseValidationLayers = true;
VulkanEngine* loadedEngine = nullptr; VulkanEngine* loadedEngine = nullptr;
@ -45,6 +46,8 @@ void VulkanEngine::init()
init_swapchain(); init_swapchain();
init_commands(); init_commands();
init_sync_structures(); init_sync_structures();
init_descriptors();
init_pipelines();
//everything went fine //everything went fine
_isInitialized = true; _isInitialized = true;
@ -358,11 +361,100 @@ void VulkanEngine::init_sync_structures() {
void VulkanEngine::draw_background(VkCommandBuffer cmd) void VulkanEngine::draw_background(VkCommandBuffer cmd)
{ {
VkClearColorValue clearValue; // bind the gradient drawing compute pipeline
float flash = std::abs(std::sin(float(_frameNumber) / 120.0f)); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline);
clearValue = { { 0.0f, 0.0f, flash, 1.0f} };
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<DescriptorAllocator::PoolSizeRatio> 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);
});
} }

View file

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <vk_types.h> #include <vk_types.h>
#include <vk_descriptors.h>
struct DeletionQueue { struct DeletionQueue {
std::deque<std::function<void()>> deletors; std::deque<std::function<void()>> deletors;
@ -35,6 +36,13 @@ constexpr unsigned int FRAME_OVERLAP=2;
class VulkanEngine { class VulkanEngine {
public: public:
VkPipeline _gradientPipeline;
VkPipelineLayout _gradientPipelineLayout;
DescriptorAllocator globalDescriptorAllocator;
VkDescriptorSet _drawImageDescriptors;
VkDescriptorSetLayout _drawImageDescriptorLayout;
// device selection // device selection
VkInstance _instance; VkInstance _instance;
VkDebugUtilsMessengerEXT _debug_messenger; VkDebugUtilsMessengerEXT _debug_messenger;
@ -93,6 +101,9 @@ private:
void init_swapchain(); void init_swapchain();
void init_commands(); void init_commands();
void init_sync_structures(); void init_sync_structures();
void init_descriptors();
void init_pipelines();
void init_background_pipelines();
void create_swapchain(uint32_t width, uint32_t height); void create_swapchain(uint32_t width, uint32_t height);
void destroy_swapchain(); void destroy_swapchain();

42
vk_pipelines.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "vk_pipelines.h"
#include <fstream>
#include "vk_initializers.h"
#include <print>
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<uint32_t> 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;
}

9
vk_pipelines.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include "vk_types.h"
namespace vkutil {
bool load_shader_module(const char* filePath,
VkDevice device,
VkShaderModule* outShaderModule);
}