Now draws a gradient with compute

This commit is contained in:
Alexandre 2026-05-28 23:32:38 +02:00
parent 921832a950
commit e9af67ad25
3 changed files with 294 additions and 17 deletions

View File

@ -27,29 +27,73 @@ find_package( VulkanMemoryAllocator CONFIG REQUIRED )
find_package( fmt REQUIRED ) find_package( fmt REQUIRED )
# Shader compilers # Shader compilers
# Todo # Todo
#find_program( GLSLANG_VALIDATOR "glslangValidator" HINTS $ENV{VULKAN_SDK}/bin REQUIRED )
add_executable (glslang::validator IMPORTED)
find_program (GLSLANG_VALIDATOR "glslangValidator" HINTS $ENV{VULKAN_SDK}/bin REQUIRED)
set_property (TARGET glslang::validator PROPERTY IMPORTED_LOCATION "${GLSLANG_VALIDATOR}")
find_program(SLANGC_EXECUTABLE slangc HINTS $ENV{VULKAN_SDK}/bin REQUIRED)
# fastgltf because it's not in nixpkgs # fastgltf because it's not in nixpkgs
FetchContent_Declare( FetchContent_Declare(
fastgltf fastgltf
GIT_REPOSITORY https://github.com/spnda/fastgltf.git GIT_REPOSITORY https://github.com/spnda/fastgltf.git
GIT_TAG v0.8.0 GIT_TAG v0.8.0
) )
FetchContent_MakeAvailable(fastgltf) FetchContent_MakeAvailable(fastgltf)
# vk-bootstrap because CMAKE can't find it on NixOS # vk-bootstrap because CMAKE can't find it on NixOS
FetchContent_Declare( FetchContent_Declare(
vk-bootstrap vk-bootstrap
GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap.git GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap.git
GIT_TAG v1.3.295 GIT_TAG v1.3.295
) )
FetchContent_MakeAvailable(vk-bootstrap) FetchContent_MakeAvailable(vk-bootstrap)
# Helper functions to build shaders
function(addGlslShader SOURCE OUTPUT_DIR)
get_filename_component(SHADER_NAME ${SOURCE} NAME_WE)
set(SPV_FILE "${OUTPUT_DIR}/${SHADER_NAME}.spv")
add_custom_command(
OUTPUT ${SPV_FILE}
COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR}
COMMAND glslang::validator
-V
--target-env vulkan1.3 # Change if needed
-o ${SPV_FILE}
${SOURCE}
DEPENDS ${SOURCE}
COMMENT "Compiling GLSL shader: ${SOURCE} -> ${SPV_FILE}"
VERBATIM
)
set(SPV_FILE ${SPV_FILE} PARENT_SCOPE)
endfunction()
function(addSlangShader SOURCES ENTRY_POINTS OUTPUT_DIR)
set(SPV_FILE "${OUTPUT_DIR}/slang.spv")
add_custom_command(
OUTPUT ${SPV_FILE}
COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR}
COMMAND ${SLANGC_EXECUTABLE}
${SOURCES}
-target spirv
-profile spirv_1_4+spvRayQueryKHR # Change if needed
-emit-spirv-directly
-fvk-use-entrypoint-name
${ENTRY_POINTS}
-o ${SPV_FILE}
DEPENDS ${SOURCES}
COMMENT "Compiling Slang shaders -> ${SPV_FILE}"
VERBATIM
)
set(SPV_FILE ${SPV_FILE} PARENT_SCOPE)
endfunction()
# A function to automate executables aditions # A function to automate executables aditions
function ( addBinary BINARY_NAME ) function ( addBinary BINARY_NAME )
cmake_parse_arguments ( BINARY "" "SOURCE" "SHADER;LIBS;TEXTURES;MODELS" ${ARGN} ) cmake_parse_arguments ( BINARY "" "SOURCE" "SHADERS;SLANG_SHADERS;LIBS;TEXTURES;MODELS" ${ARGN} )
if (DEFINED BINARY_SOURCE) if (DEFINED BINARY_SOURCE)
add_executable ( ${BINARY_NAME} src/${BINARY_SOURCE}.cpp ) add_executable ( ${BINARY_NAME} src/${BINARY_SOURCE}.cpp )
endif() endif()
@ -75,7 +119,30 @@ function ( addBinary BINARY_NAME )
BUILD_ID="${ENGINE_VERSION}" BUILD_ID="${ENGINE_VERSION}"
) )
if(DEFINED BINARY_SHADERS)
set(SHADER_OUTPUT_DIR ${CMAKE_BINARY_DIR}/${BINARY_NAME}/shaders)
set(ALL_SPV_FILES "")
foreach(SHADER_SRC ${BINARY_SHADERS})
if(NOT IS_ABSOLUTE ${SHADER_SRC})
set(SHADER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SRC})
endif()
addGlslShader(${SHADER_SRC} ${SHADER_OUTPUT_DIR})
list(APPEND ALL_SPV_FILES ${SPV_FILE})
endforeach()
add_custom_target(shaders_${BINARY_NAME} DEPENDS ${ALL_SPV_FILES})
add_dependencies(${BINARY_NAME} shaders_${BINARY_NAME})
endif()
if(DEFINED BINARY_SLANG_SHADERS)
set(SLANG_OUTPUT_DIR ${CMAKE_BINARY_DIR}/${BINARY_NAME}/shaders)
if(NOT DEFINED BINARY_SLANG_ENTRIES)
message(FATAL_ERROR "addBinary: SLANG_SHADERS nécessite SLANG_ENTRIES")
endif()
addSlangShader("${BINARY_SLANG_SHADERS}" "${BINARY_SLANG_ENTRIES}" ${SLANG_OUTPUT_DIR})
add_custom_target(slang_shaders_${BINARY_NAME} DEPENDS ${SPV_FILE})
add_dependencies(${BINARY_NAME} slang_shaders_${BINARY_NAME})
endif()
if (DEFINED BINARY_LIBS) if (DEFINED BINARY_LIBS)
target_link_libraries ( ${BINARY_NAME} ${BINARY_LIBS} ) target_link_libraries ( ${BINARY_NAME} ${BINARY_LIBS} )
@ -116,6 +183,7 @@ addBinary (
engine engine
SOURCE Engine/main SOURCE Engine/main
SHADERS shaders/gradient.comp
LIBS glfw fmt vk-bootstrap vulkan LIBS glfw fmt vk-bootstrap vulkan
) )

View File

@ -5,6 +5,8 @@
#include "descriptors.h" // Required for public interface custom allocator #include "descriptors.h" // Required for public interface custom allocator
#include "Common/Types.h" #include "Common/Types.h"
#include <GLFW/glfw3.h>
#include <fmt/base.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
@ -62,6 +64,10 @@ class Engine {
VkDescriptorSet _drawImageDescriptors; VkDescriptorSet _drawImageDescriptors;
VkDescriptorSetLayout _drawImageDescriptorLayout; VkDescriptorSetLayout _drawImageDescriptorLayout;
// Pipelines
VkPipeline _gradientPipeline;
VkPipelineLayout _gradientPipelineLayout;
void init(); void init();
@ -77,6 +83,8 @@ class Engine {
void initSwapchain(); void initSwapchain();
void initCommands(); void initCommands();
void initSyncStructures(); void initSyncStructures();
void initPipelines();
void initBackgroundPipelines();
void createSwapchain( void createSwapchain(
uint32_t width, uint32_t width,
@ -101,6 +109,7 @@ class Engine {
#define IMAGES_IMPL #define IMAGES_IMPL
#define INITIALIZERS_IMPL #define INITIALIZERS_IMPL
#define DESCRIPTORS_IMPL #define DESCRIPTORS_IMPL
#define PIPELINES_IMPL
#define CALLBACK_IMPL #define CALLBACK_IMPL
#define TIMER_IMPL #define TIMER_IMPL
#define TYPES_IMPL #define TYPES_IMPL
@ -111,6 +120,7 @@ class Engine {
#include "initializers.h" #include "initializers.h"
#include "callbacks.h" #include "callbacks.h"
#include "descriptors.h" #include "descriptors.h"
#include "pipelines.h"
#include "Common/Types.h" #include "Common/Types.h"
#include "Common/Timer.h" #include "Common/Timer.h"
@ -369,6 +379,7 @@ void Engine::initRender() {
initCommands(); initCommands();
initSyncStructures(); initSyncStructures();
initDescriptors(); initDescriptors();
initPipelines();
} }
void Engine::initVulkan() { void Engine::initVulkan() {
@ -656,6 +667,100 @@ void Engine::initDescriptors(){
#endif #endif
} }
void Engine::initPipelines() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing pipelines...");
#endif
initBackgroundPipelines();
#ifdef DEBUG
fmt::println("[Engine:Init] Pipelines initialized !");
#endif
}
void Engine::initBackgroundPipelines() {
#ifdef DEBUG
fmt::println("[Engine:Init] Initializing compute pipelines...");
#endif
VkPipelineLayoutCreateInfo computeLayout {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.setLayoutCount = 1,
.pSetLayouts = &_drawImageDescriptorLayout
};
VK_CHECK(
vkCreatePipelineLayout(
_device,
&computeLayout,
nullptr,
&_gradientPipelineLayout
);
);
// Create the actual shader
VkShaderModule computeDrawShader;
if (
!vkutil::loadShaderModule(
"shaders/gradient.spv",
_device,
&computeDrawShader
)
) {
fmt::println("[Engine:Error] Error when building the compute shader !");
}
VkPipelineShaderStageCreateInfo stageInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.module = computeDrawShader,
.pName = "main"
};
VkComputePipelineCreateInfo computePipelineCreateInfo {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.stage = stageInfo,
.layout = _gradientPipelineLayout
};
VK_CHECK(
vkCreateComputePipelines(
_device,
VK_NULL_HANDLE,
1,
&computePipelineCreateInfo,
nullptr,
&_gradientPipeline
);
);
// Do some cleanup
vkDestroyShaderModule(
_device,
computeDrawShader,
nullptr
);
_mainDeletionQueue.pushFunction([this]() {
vkDestroyPipelineLayout(
_device,
_gradientPipelineLayout,
nullptr
);
vkDestroyPipeline(
_device,
_gradientPipeline,
nullptr
);
});
#ifdef DEBUG
fmt::println("[Engine:Init] Compute pipelines initalized !");
#endif
}
//================================ //================================
//=== Render-related Functions === //=== Render-related Functions ===
//================================ //================================
@ -682,7 +787,9 @@ void Engine::createSwapchain(
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
} }
) )
.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR) // VSync .set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
// FIFO = VSync
// Mailbox = Unapped
.set_desired_extent(width, height) .set_desired_extent(width, height)
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT) .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.build() .build()
@ -710,7 +817,7 @@ void Engine::destroySwapchain() {
} }
#ifdef DEBUG #ifdef DEBUG
fmt::println("[Engine:Cleanup]Swapchain destroyed !"); fmt::println("[Engine:Cleanup] Swapchain destroyed !");
#endif #endif
} }
@ -725,15 +832,34 @@ void Engine::drawBackground(
VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
// Clear Image vkCmdBindPipeline(
vkCmdClearColorImage(
cmd, cmd,
_drawImage.image, VK_PIPELINE_BIND_POINT_COMPUTE,
VK_IMAGE_LAYOUT_GENERAL, _gradientPipeline
&clearValue,
1,
&clearRange
); );
vkCmdBindDescriptorSets(
cmd,
VK_PIPELINE_BIND_POINT_COMPUTE,
_gradientPipelineLayout,
0,
1,
&_drawImageDescriptors,
0,
nullptr
);
vkCmdDispatch(
cmd,
std::ceil(
_drawExtent.width / 16.0
),
std::ceil(
_drawExtent.height / 16.0
),
1
);
} }

View File

@ -2,12 +2,95 @@
#define PIPELINES_H #define PIPELINES_H
// Normal header // Normal header
#include "Common/Types.h"
#include <cstddef>
#include <cstdint>
#include <fmt/base.h>
#include <vector>
#include <vulkan/vulkan_core.h>
namespace vkutil {
bool loadShaderModule(
const char* filePath,
VkDevice device,
VkShaderModule* outShaderModule
);
};
#endif #endif
#ifdef PIPELINES_IMPL #ifdef PIPELINES_IMPL
#ifndef PIPELINES_IMPL_H #ifndef PIPELINES_IMPL_H
#define PIPELINES_IMPL_H #define PIPELINES_IMPL_H
// Implementation // Implementation
// Imports
#include <fstream>
// Custom imports
#include "initializers.h"
namespace vkutil {
bool loadShaderModule(
const char* filePath,
VkDevice device,
VkShaderModule* outShaderModule
) {
#ifdef DEBUG
fmt::println("[Engine:Utils] Loading shader located at {}...", filePath);
#endif
// Open the file, with the cursor at the end
std::ifstream file(
filePath,
std::ios::ate | std::ios::binary
);
if (!file.is_open()) {
return false;
}
// Find the size of the file by looking
// at the cursor
size_t fileSize = (size_t)file.tellg();
// Allocate a big enough vector to suit SPIR-V
std::vector<uint32_t> buffer(fileSize/ sizeof(uint32_t));
file.seekg(0); // Put the cursor at the begining
// Load the file in the buffer
file.read((char*)buffer.data(), fileSize);
file.close();
// Create the shader module
VkShaderModuleCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = nullptr,
.codeSize = buffer.size() * sizeof(uint32_t), // Convert to bytes
.pCode = buffer.data()
};
// Check that everything went well
VkShaderModule shaderModule;
if (vkCreateShaderModule(
device,
&createInfo,
nullptr,
&shaderModule
) != VK_SUCCESS){
return false;
}
*outShaderModule = shaderModule;
#ifdef DEBUG
fmt::println("[Engine:Utils] Shader loaded from {} and size {}", filePath, fileSize);
#endif
return true;
}
};
#endif #endif
#endif #endif