diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d34409..a468201 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,29 +27,73 @@ find_package( VulkanMemoryAllocator CONFIG REQUIRED ) find_package( fmt REQUIRED ) # Shader compilers # 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 FetchContent_Declare( - fastgltf - GIT_REPOSITORY https://github.com/spnda/fastgltf.git - GIT_TAG v0.8.0 + fastgltf + GIT_REPOSITORY https://github.com/spnda/fastgltf.git + GIT_TAG v0.8.0 ) FetchContent_MakeAvailable(fastgltf) # vk-bootstrap because CMAKE can't find it on NixOS FetchContent_Declare( - vk-bootstrap - GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap.git - GIT_TAG v1.3.295 + vk-bootstrap + GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap.git + GIT_TAG v1.3.295 ) 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 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) add_executable ( ${BINARY_NAME} src/${BINARY_SOURCE}.cpp ) endif() @@ -75,7 +119,30 @@ function ( addBinary BINARY_NAME ) 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) target_link_libraries ( ${BINARY_NAME} ${BINARY_LIBS} ) @@ -116,6 +183,7 @@ addBinary ( engine SOURCE Engine/main + SHADERS shaders/gradient.comp LIBS glfw fmt vk-bootstrap vulkan ) diff --git a/src/Engine/engine/engine.h b/src/Engine/engine/engine.h index 6f588f2..5855817 100644 --- a/src/Engine/engine/engine.h +++ b/src/Engine/engine/engine.h @@ -5,6 +5,8 @@ #include "descriptors.h" // Required for public interface custom allocator #include "Common/Types.h" +#include +#include #include @@ -62,6 +64,10 @@ class Engine { VkDescriptorSet _drawImageDescriptors; VkDescriptorSetLayout _drawImageDescriptorLayout; + // Pipelines + VkPipeline _gradientPipeline; + VkPipelineLayout _gradientPipelineLayout; + void init(); @@ -77,6 +83,8 @@ class Engine { void initSwapchain(); void initCommands(); void initSyncStructures(); + void initPipelines(); + void initBackgroundPipelines(); void createSwapchain( uint32_t width, @@ -101,6 +109,7 @@ class Engine { #define IMAGES_IMPL #define INITIALIZERS_IMPL #define DESCRIPTORS_IMPL +#define PIPELINES_IMPL #define CALLBACK_IMPL #define TIMER_IMPL #define TYPES_IMPL @@ -111,6 +120,7 @@ class Engine { #include "initializers.h" #include "callbacks.h" #include "descriptors.h" +#include "pipelines.h" #include "Common/Types.h" #include "Common/Timer.h" @@ -369,6 +379,7 @@ void Engine::initRender() { initCommands(); initSyncStructures(); initDescriptors(); + initPipelines(); } void Engine::initVulkan() { @@ -656,6 +667,100 @@ void Engine::initDescriptors(){ #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 === //================================ @@ -682,7 +787,9 @@ void Engine::createSwapchain( .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) .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT) .build() @@ -710,7 +817,7 @@ void Engine::destroySwapchain() { } #ifdef DEBUG - fmt::println("[Engine:Cleanup]Swapchain destroyed !"); + fmt::println("[Engine:Cleanup] Swapchain destroyed !"); #endif } @@ -725,15 +832,34 @@ void Engine::drawBackground( VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); - // Clear Image - vkCmdClearColorImage( + vkCmdBindPipeline( cmd, - _drawImage.image, - VK_IMAGE_LAYOUT_GENERAL, - &clearValue, - 1, - &clearRange + VK_PIPELINE_BIND_POINT_COMPUTE, + _gradientPipeline ); + + 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 + ); + } diff --git a/src/Engine/engine/pipelines.h b/src/Engine/engine/pipelines.h index d662f5d..33566f6 100644 --- a/src/Engine/engine/pipelines.h +++ b/src/Engine/engine/pipelines.h @@ -2,12 +2,95 @@ #define PIPELINES_H // Normal header +#include "Common/Types.h" +#include +#include +#include +#include +#include + +namespace vkutil { + bool loadShaderModule( + const char* filePath, + VkDevice device, + VkShaderModule* outShaderModule + ); +}; + #endif #ifdef PIPELINES_IMPL #ifndef PIPELINES_IMPL_H #define PIPELINES_IMPL_H // Implementation +// Imports +#include + +// 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 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