From 5d6ddbb158e48ac4c31224a46c733a999da07cde Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Mon, 5 May 2025 23:52:15 +0200 Subject: [PATCH] Added slang for Shader code compilation. TODO: Use slang to create descriptors. --- .gitignore | 1 + CMakeLists.txt | 4 +- add_shader.cmake | 23 +- aster/CMakeLists.txt | 4 +- aster/include/aster/core/global.h | 4 +- aster/include/aster/core/pipeline.h | 23 ++ aster/include/aster/systems/device.h | 111 +++++- aster/src/aster/core/device.cpp | 4 +- aster/src/aster/core/global.cpp | 8 +- aster/src/aster/core/instance.cpp | 22 +- aster/src/aster/systems/commit_manager.cpp | 2 +- aster/src/aster/systems/device.cpp | 367 ++++++++++++++---- samples/01_triangle/CMakeLists.txt | 4 +- samples/01_triangle/shader/triangle.frag.glsl | 9 - samples/01_triangle/shader/triangle.slang | 35 ++ samples/01_triangle/shader/triangle.vert.glsl | 27 -- samples/01_triangle/shader/triangle.vs.hlsl | 28 -- samples/01_triangle/triangle.cpp | 80 ++-- samples/CMakeLists.txt | 4 +- vcpkg-configuration.json | 2 +- vcpkg.json | 3 +- 21 files changed, 525 insertions(+), 240 deletions(-) delete mode 100644 samples/01_triangle/shader/triangle.frag.glsl create mode 100644 samples/01_triangle/shader/triangle.slang delete mode 100644 samples/01_triangle/shader/triangle.vert.glsl delete mode 100644 samples/01_triangle/shader/triangle.vs.hlsl diff --git a/.gitignore b/.gitignore index dea1f32..72eb6a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/ .direnv/ .ccls-cache/ *.user +/vcpkg_installed diff --git a/CMakeLists.txt b/CMakeLists.txt index 055d78d..2067b31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,12 @@ cmake_minimum_required(VERSION 3.13) project(Aster VERSION 0.1.0) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) if (MSVC) - set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS}") + set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS} /utf-8") set(CMAKE_CXX_FLAGS_RELEASE "/O3") add_compile_definitions(_HAS_EXCEPTIONS=0) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) diff --git a/add_shader.cmake b/add_shader.cmake index d9c2ccb..50b61e0 100644 --- a/add_shader.cmake +++ b/add_shader.cmake @@ -1,10 +1,14 @@ function(add_shader TARGET SHADER) find_package(Vulkan REQUIRED COMPONENTS dxc) + get_filename_component(vulkan-bin-dir ${Vulkan_GLSLC_EXECUTABLE} DIRECTORY) + + find_program(slangc_exe NAMES "slangc") + if (NOT slangc_exe STREQUAL "slangc_exe-NOTFOUND") + set(slangc_exe_FOUND true) + endif() + get_filename_component(shader-ext ${SHADER} LAST_EXT) - get_filename_component(shader-inner ${SHADER} NAME_WLE) - get_filename_component(shader-type ${shader-inner} LAST_EXT) - string(REPLACE "." "" shader-type ${shader-type}) set(current-shader-path ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER}) set(current-output-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.spv) @@ -14,6 +18,11 @@ function(add_shader TARGET SHADER) if (Vulkan_dxc_exe_FOUND AND ${shader-ext} STREQUAL ".hlsl") message("Marked as hlsl file. ${current-output-path}") + + get_filename_component(shader-inner ${SHADER} NAME_WLE) + get_filename_component(shader-type ${shader-inner} LAST_EXT) + string(REPLACE "." "" shader-type ${shader-type}) + add_custom_command( OUTPUT ${current-output-path} COMMAND Vulkan::dxc_exe ${DXC_SHADER_FLAGS} -spirv -T "${shader-type}_6_0" -E main ${current-shader-path} -Fo ${current-output-path} @@ -28,6 +37,14 @@ function(add_shader TARGET SHADER) DEPENDS ${current-shader-path} IMPLICIT_DEPENDS CXX ${current-shader-path} VERBATIM) + elseif (${shader-ext} STREQUAL ".slang") + message("Marked as slang file. ${current-output-path}") + add_custom_command( + OUTPUT ${current-output-path} + COMMAND ${slangc_exe} -target spirv -o ${current-output-path} ${current-shader-path} + DEPENDS ${current-shader-path} + IMPLICIT_DEPENDS CXX ${current-shader-path} + VERBATIM) endif () # Make sure our build depends on this output. diff --git a/aster/CMakeLists.txt b/aster/CMakeLists.txt index 63762a6..5bc1e67 100644 --- a/aster/CMakeLists.txt +++ b/aster/CMakeLists.txt @@ -9,13 +9,14 @@ find_package(Vulkan REQUIRED) find_package(fmt CONFIG REQUIRED) find_package(VulkanMemoryAllocator CONFIG REQUIRED) find_package(EASTL CONFIG REQUIRED) +find_library(slang NAMES "slang" CONFIG REQUIRED) add_library(aster_core STATIC) add_subdirectory("include") add_subdirectory("src") -set_property(TARGET aster_core PROPERTY CXX_STANDARD 20) +target_compile_features(aster_core PUBLIC cxx_std_23) target_include_directories(aster_core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/aster") target_include_directories(aster_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") @@ -26,4 +27,5 @@ target_include_directories(aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS}) target_link_libraries(aster_core PRIVATE fmt::fmt) target_link_libraries(aster_core PRIVATE EASTL) target_link_libraries(aster_core PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator) +target_link_libraries(aster_core PUBLIC ${slang}) diff --git a/aster/include/aster/core/global.h b/aster/include/aster/core/global.h index f00d01b..c2665ba 100644 --- a/aster/include/aster/core/global.h +++ b/aster/include/aster/core/global.h @@ -229,7 +229,7 @@ struct fmt::formatter : nested_formatter format(vk::Result result, format_context &ctx) const { return write_padded(ctx, - [this, result](auto out) { return v10::format_to(out, "{}", nested(to_string(result))); }); + [this, result](auto out) { return v11::format_to(out, "{}", nested(to_string(result))); }); } }; @@ -240,7 +240,7 @@ struct fmt::formatter> : nested_fo // ReSharper disable once CppInconsistentNaming format(const eastl::fixed_string &str, format_context &ctx) const { - return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); }); + return write_padded(ctx, [this, str](auto out) { return v11::format_to(out, "{}", nested(str.c_str())); }); } }; diff --git a/aster/include/aster/core/pipeline.h b/aster/include/aster/core/pipeline.h index 8d845e6..bee1f23 100644 --- a/aster/include/aster/core/pipeline.h +++ b/aster/include/aster/core/pipeline.h @@ -18,7 +18,30 @@ struct Pipeline vk::Pipeline m_Pipeline; eastl::vector m_SetLayouts; + Pipeline() = default; Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline, eastl::vector &&setLayouts); ~Pipeline(); + + DISALLOW_COPY_AND_ASSIGN(Pipeline); + + Pipeline(Pipeline &&other) noexcept + : m_Device{other.m_Device}, + m_Layout{Take(other.m_Layout)}, + m_Pipeline{Take(other.m_Pipeline)}, + m_SetLayouts{std::move(other.m_SetLayouts)} + { + } + + Pipeline & + operator=(Pipeline &&other) noexcept + { + if (this == &other) + return *this; + m_Device = other.m_Device; + m_Layout = Take(other.m_Layout); + m_Pipeline = Take(other.m_Pipeline); + m_SetLayouts = std::move(other.m_SetLayouts); + return *this; + } }; \ No newline at end of file diff --git a/aster/include/aster/systems/device.h b/aster/include/aster/systems/device.h index 0b0d99b..7f56e0a 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -20,7 +20,10 @@ #include #include -#include +#include +#include + +#include constexpr static u32 MAX_FRAMES_IN_FLIGHT = 3; @@ -225,6 +228,59 @@ struct SamplerCreateInfo #pragma region Pipeline // ---------------------------------------------------------------------------------------------------- +struct PipelineCreationError +{ + enum class Kind + { + eNone, + eVulkan, + eSlang, + } m_Kind; + union { + u32 uNone; + vk::Result uVulkanResult; + SlangResult uSlangResult; + }; + + operator bool() const + { + return m_Kind != Kind::eNone; + } + + std::string + What() + { + switch (m_Kind) + { + case Kind::eNone: + return "No Error"; + case Kind::eVulkan: + return fmt::format("{}", uVulkanResult); + case Kind::eSlang: + return fmt::format("{}", uSlangResult); + } + return "No Error"; + } + + PipelineCreationError() + : m_Kind{Kind::eNone} + , uNone{} + { + } + + PipelineCreationError(vk::Result result) + : m_Kind{Kind::eVulkan} + , uVulkanResult{result} + { + } + + PipelineCreationError(SlangResult result) + : m_Kind{Kind::eSlang} + , uSlangResult{result} + { + } +}; + struct AttributeInfo { u32 m_Location; @@ -262,23 +318,34 @@ struct VertexInput bool m_IsPerInstance; }; -struct GraphicsShaderModuleInfo +enum class ShaderType +{ + eInvalid = 0, + eVertex = vk::ShaderStageFlagBits::eVertex, + eTesselationControl = vk::ShaderStageFlagBits::eTessellationControl, + eTesselationEvaluation = vk::ShaderStageFlagBits::eTessellationEvaluation, + eGeometry = vk::ShaderStageFlagBits::eGeometry, + eFragment = vk::ShaderStageFlagBits::eFragment, + eCompute = vk::ShaderStageFlagBits::eCompute, + eTask = vk::ShaderStageFlagBits::eTaskEXT, + eMesh = vk::ShaderStageFlagBits::eMeshEXT, + eMax, +}; + +constexpr static u32 ShaderTypeCount = 8; + +static_assert(Cast(ShaderType::eMax) == 1 + (1 << (ShaderTypeCount - 1))); + +struct ShaderInfo { std::string_view m_ShaderFile; - std::string_view m_EntryPoint; - enum class Type - { - eVertex = vk::ShaderStageFlagBits::eVertex, - eFragment = vk::ShaderStageFlagBits::eFragment, - eTesselationControl = vk::ShaderStageFlagBits::eTessellationControl, - eTesselationEvaluation = vk::ShaderStageFlagBits::eTessellationEvaluation, - } m_Type; + eastl::vector m_EntryPoints; }; struct GraphicsPipelineCreateInfo { eastl::fixed_vector m_VertexInputs; - eastl::fixed_vector m_ShaderModules; + eastl::fixed_vector m_Shaders; }; #pragma endregion @@ -294,13 +361,14 @@ static_assert(std::convertible_to m_Window; + Features m_Features; cstr m_AppName = "Aster App"; Version m_AppVersion = {0, 1, 0}; PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector; std::span m_PipelineCacheData = {}; - std::reference_wrapper m_Window; + eastl::vector m_ShaderSearchPaths; cstr m_Name = "Primary"; - Features m_Features; }; #pragma endregion @@ -472,10 +540,10 @@ class Device final // Sampler Management // ---------------------------------------------------------------------------------------------------- - using Handle = Ref; - using WeakHandle = WeakRef; - eastl::hash_map m_HashToSamplerIdx; + private: + eastl::hash_map> m_HashToSamplerIdx; + public: Ref CreateSampler(const SamplerCreateInfo &createInfo); // @@ -484,11 +552,18 @@ class Device final // TODO: Cache shader modules for reuse. Time to move to `slang` private: - vk::ShaderModule CreateShader(std::string_view shaderFile); + Slang::ComPtr m_GlobalSlangSession; + Slang::ComPtr m_SlangSession; + + PipelineCreationError + CreateShaders(eastl::fixed_vector &shadersOut, + Slang::ComPtr &program, const std::span &shaders); + PipelineCreationError + CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, const Slang::ComPtr &program); public: // Pipelines, unlike the other resources, are not ref-counted. - Pipeline CreatePipeline(const GraphicsPipelineCreateInfo &createInfo); + PipelineCreationError CreatePipeline(Pipeline &pipeline, const GraphicsPipelineCreateInfo &createInfo); // // Frames diff --git a/aster/src/aster/core/device.cpp b/aster/src/aster/core/device.cpp index 6de1741..4b99fe7 100644 --- a/aster/src/aster/core/device.cpp +++ b/aster/src/aster/core/device.cpp @@ -70,8 +70,8 @@ Device::Device(const Instance &context, PhysicalDevice &physicalDevice, Features SetName(m_Device, m_Name.data()); VmaVulkanFunctions vmaVulkanFunctions = { - .vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr, - .vkGetDeviceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr, + .vkGetInstanceProcAddr = vk::detail::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = vk::detail::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr, }; const VmaAllocatorCreateInfo allocatorCreateInfo = { diff --git a/aster/src/aster/core/global.cpp b/aster/src/aster/core/global.cpp index 6b31291..1009ea5 100644 --- a/aster/src/aster/core/global.cpp +++ b/aster/src/aster/core/global.cpp @@ -56,18 +56,18 @@ struct fmt::formatter // return format_to(ctx.out(), "({}, {})", foo.a, foo.b); // --== KEY LINE ==-- if (mem.m_Gigabytes > 0) { - return v10::format_to(ctx.out(), "{}.{} GB", mem.m_Gigabytes, Cast(mem.m_Megabytes / 1024.0)); + return v11::format_to(ctx.out(), "{}.{} GB", mem.m_Gigabytes, Cast(mem.m_Megabytes / 1024.0)); } if (mem.m_Megabytes > 0) { - return v10::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, Cast(mem.m_Kilobytes / 1024.0)); + return v11::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, Cast(mem.m_Kilobytes / 1024.0)); } if (mem.m_Kilobytes > 0) { - return v10::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, Cast(mem.m_Bytes / 1024.0)); + return v11::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, Cast(mem.m_Bytes / 1024.0)); } - return v10::format_to(ctx.out(), "{} Bytes", mem.m_Bytes); + return v11::format_to(ctx.out(), "{} Bytes", mem.m_Bytes); } }; diff --git a/aster/src/aster/core/instance.cpp b/aster/src/aster/core/instance.cpp index d8b0a22..5659d50 100644 --- a/aster/src/aster/core/instance.cpp +++ b/aster/src/aster/core/instance.cpp @@ -11,26 +11,22 @@ #include VKAPI_ATTR b32 VKAPI_CALL -DebugCallback(const VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - const VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT *callbackData, [[maybe_unused]] void *userData) +DebugCallback(const vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + const vk::DebugUtilsMessageTypeFlagsEXT messageType, + const vk::DebugUtilsMessengerCallbackDataEXT *callbackData, [[maybe_unused]] void *userData) { - using Severity = vk::DebugUtilsMessageSeverityFlagsEXT; using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT; - using MessageType = vk::DebugUtilsMessageTypeFlagsEXT; using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT; - const auto severity = Severity(messageSeverity); - - if (MessageType(messageType) & MessageTypeBits::eValidation) + if (messageType & MessageTypeBits::eValidation) { - if (severity & SeverityBits::eError) + if (messageSeverity & SeverityBits::eError) ERROR("{}", callbackData->pMessage); - if (severity & SeverityBits::eWarning) + if (messageSeverity & SeverityBits::eWarning) WARN("{}", callbackData->pMessage); - if (severity & SeverityBits::eInfo) + if (messageSeverity & SeverityBits::eInfo) INFO("{}", callbackData->pMessage); - if (severity & SeverityBits::eVerbose) + if (messageSeverity & SeverityBits::eVerbose) VERBOSE("{}", callbackData->pMessage); } @@ -71,7 +67,7 @@ Instance::Instance(const cstr appName, const Version version, bool enableValidat instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - const vk::DynamicLoader dl; + const vk::detail::DynamicLoader dl; // ReSharper disable once CppInconsistentNaming const auto vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); diff --git a/aster/src/aster/systems/commit_manager.cpp b/aster/src/aster/systems/commit_manager.cpp index 1c2f1c3..09d6421 100644 --- a/aster/src/aster/systems/commit_manager.cpp +++ b/aster/src/aster/systems/commit_manager.cpp @@ -93,7 +93,7 @@ CommitManager::CommitManager(const Device *device, const u32 maxBuffers, const u vk::DescriptorBindingFlags bindingFlags = vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind; - eastl::array layoutBindingFlags; + eastl::array layoutBindingFlags; layoutBindingFlags.fill(bindingFlags); vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = { diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 8cea14d..6a86250 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -11,6 +11,8 @@ #include "aster/util/files.h" +#include + static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer; @@ -421,55 +423,19 @@ systems::Device::CreateSampler(const SamplerCreateInfo &createInfo) // Pipelines // ---------------------------------------------------------------------------------------------------- -Pipeline -systems::Device::CreatePipeline(const GraphicsPipelineCreateInfo &createInfo) +systems::PipelineCreationError +systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCreateInfo &createInfo) { - auto findShader = [&shaderModules = createInfo.m_ShaderModules]( - GraphicsShaderModuleInfo::Type type) -> std::optional { - if (const auto res = - std::ranges::find_if(shaderModules, [type](const auto &v) { return v.m_Type == type; }); - res != std::ranges::end(shaderModules)) - { - return *res; - } - return std::nullopt; - }; + eastl::fixed_vector shaders; + Slang::ComPtr program; + if (auto shaderResult = CreateShaders(shaders, program, {createInfo.m_Shaders.begin(), createInfo.m_Shaders.end()}); + shaderResult) + { + return shaderResult; + } - auto vs = findShader(GraphicsShaderModuleInfo::Type::eVertex); - ERROR_IF(!vs, "Vertex Shader not found."); - GraphicsShaderModuleInfo vertexShader = *vs; - - auto fs = findShader(GraphicsShaderModuleInfo::Type::eFragment); - ERROR_IF(!fs, "Fragment Shader not found."); - GraphicsShaderModuleInfo fragmentShader = *fs; - - // Pipeline Setup - auto vertexShaderModule = CreateShader(vertexShader.m_ShaderFile); - auto fragmentShaderModule = CreateShader(fragmentShader.m_ShaderFile); - - eastl::array shaderStages = {{ - { - .stage = vk::ShaderStageFlagBits::eVertex, - .module = vertexShaderModule, - .pName = vertexShader.m_EntryPoint.data(), - }, - { - .stage = vk::ShaderStageFlagBits::eFragment, - .module = fragmentShaderModule, - .pName = fragmentShader.m_EntryPoint.data(), - }, - }}; - - vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { - .setLayoutCount = 0, - .pSetLayouts = nullptr, - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, - }; vk::PipelineLayout pipelineLayout; - vk::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); - ERROR_IF(Failed(result), "Could not create a pipeline layout. Cause: {}", result) THEN_ABORT(result); - SetName(pipelineLayout, "Triangle Layout"); + auto result = CreatePipelineLayout(pipelineLayout, program); eastl::fixed_vector inputBindingDescriptions; eastl::fixed_vector inputAttributeDescriptions; @@ -479,8 +445,7 @@ systems::Device::CreatePipeline(const GraphicsPipelineCreateInfo &createInfo) inputBindingDescriptions.push_back({ .binding = binding, .stride = vertexInput.m_Stride, - .inputRate = - vertexInput.m_IsPerInstance ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex, + .inputRate = vertexInput.m_IsPerInstance ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex, }); for (auto attrInput : vertexInput.m_Attribute) { @@ -562,8 +527,8 @@ systems::Device::CreatePipeline(const GraphicsPipelineCreateInfo &createInfo) vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { .pNext = &renderingCreateInfo, - .stageCount = Cast(shaderStages.size()), - .pStages = shaderStages.data(), + .stageCount = Cast(shaders.size()), + .pStages = shaders.data(), .pVertexInputState = &vertexInputStateCreateInfo, .pInputAssemblyState = &inputAssemblyStateCreateInfo, .pViewportState = &viewportStateCreateInfo, @@ -575,32 +540,271 @@ systems::Device::CreatePipeline(const GraphicsPipelineCreateInfo &createInfo) .layout = pipelineLayout, }; vk::Pipeline pipeline; - result = m_Device.m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); - ERROR_IF(Failed(result), "Could not create a graphics pipeline. Cause: {}", result) - THEN_ABORT(result); + auto vresult = m_Device.m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); + ERROR_IF(Failed(vresult), "Could not create a graphics pipeline. Cause: {}", vresult) + THEN_ABORT(vresult); SetName(pipeline, "Triangle Pipeline"); - m_Device.m_Device.destroy(vertexShaderModule, nullptr); - m_Device.m_Device.destroy(fragmentShaderModule, nullptr); + for (auto &shader : shaders) + { + m_Device.m_Device.destroy(shader.module, nullptr); + } - return {&m_Device, pipelineLayout, pipeline, {}}; + pipelineOut = {&m_Device, pipelineLayout, pipeline, {}}; + return {}; } -vk::ShaderModule -systems::Device::CreateShader(std::string_view shaderFile) +#define MARK_FOUND_AND_VALIDATE_UNIQUE(VAR, STAGE) \ + case SLANG_STAGE_##STAGE: \ + if (VAR) \ + { \ + ERROR("Multiple Entrypoints of Stage {}", #STAGE); \ + return SLANG_FAIL; \ + } \ + VAR = true; \ + break + +#define UNIMPLEMENTED_STAGE(STAGE) \ + case SLANG_STAGE_##STAGE: \ + ERROR("Unimplemented Stage " #STAGE); \ + return SLANG_FAIL + +static vk::ShaderStageFlagBits +SlangToVulkanShaderStage(const SlangStage stage) { - eastl::vector shaderCode = ReadFile(shaderFile); + switch (stage) + { + case SLANG_STAGE_VERTEX: + return vk::ShaderStageFlagBits::eVertex; + case SLANG_STAGE_HULL: + return vk::ShaderStageFlagBits::eTessellationControl; + case SLANG_STAGE_DOMAIN: + return vk::ShaderStageFlagBits::eTessellationEvaluation; + case SLANG_STAGE_GEOMETRY: + return vk::ShaderStageFlagBits::eGeometry; + case SLANG_STAGE_FRAGMENT: + return vk::ShaderStageFlagBits::eFragment; + case SLANG_STAGE_COMPUTE: + return vk::ShaderStageFlagBits::eCompute; + case SLANG_STAGE_RAY_GENERATION: + return vk::ShaderStageFlagBits::eRaygenKHR; + case SLANG_STAGE_INTERSECTION: + return vk::ShaderStageFlagBits::eIntersectionKHR; + case SLANG_STAGE_ANY_HIT: + return vk::ShaderStageFlagBits::eAnyHitKHR; + case SLANG_STAGE_CLOSEST_HIT: + return vk::ShaderStageFlagBits::eClosestHitKHR; + case SLANG_STAGE_MISS: + return vk::ShaderStageFlagBits::eMissKHR; + case SLANG_STAGE_CALLABLE: + return vk::ShaderStageFlagBits::eCallableKHR; + case SLANG_STAGE_MESH: + return vk::ShaderStageFlagBits::eMeshEXT; + case SLANG_STAGE_AMPLIFICATION: + return vk::ShaderStageFlagBits::eTaskEXT; + case SLANG_STAGE_NONE: + case SLANG_STAGE_COUNT: + ERROR("Invalid Shader Stage"); + return {}; + } + ERROR("Unreachable") THEN_ABORT(-1); +} - const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = { - .codeSize = shaderCode.size() * sizeof(u32), - .pCode = shaderCode.data(), +systems::PipelineCreationError +systems::Device::CreateShaders( + eastl::fixed_vector &shadersOut, + Slang::ComPtr &program, const std::span &shaders) +{ + using Slang::ComPtr; + + ComPtr shaderDiagnostics; + + eastl::fixed_vector components; + + bool vertexFound = false; + bool fragmentFound = false; + bool tesselationControlFound = false; + bool tesselationEvalFound = false; + bool geometryFound = false; + bool computeFound = false; + + u32 entryPointCount = 0; + + for (const auto &shaderInfo : shaders) + { + ComPtr shaderModule; + shaderModule = m_SlangSession->loadModule(shaderInfo.m_ShaderFile.data(), shaderDiagnostics.writeRef()); + if (shaderDiagnostics) + { + ERROR("{}", Cast(shaderDiagnostics->getBufferPointer())); + return SLANG_FAIL; + } + + components.push_back(shaderModule); + + for (auto entryPointName : shaderInfo.m_EntryPoints) + { + ComPtr actualEntryPoint; + auto slangResult = shaderModule->findEntryPointByName(entryPointName.data(), actualEntryPoint.writeRef()); + + slang::ProgramLayout *entryProgramLayout = actualEntryPoint->getLayout(0, shaderDiagnostics.writeRef()); + if (shaderDiagnostics) + { + ERROR("{}", Cast(shaderDiagnostics->getBufferPointer())); + return SLANG_FAIL; + } + switch (entryProgramLayout->getEntryPointByIndex(0)->getStage()) + { + MARK_FOUND_AND_VALIDATE_UNIQUE(vertexFound, VERTEX); + MARK_FOUND_AND_VALIDATE_UNIQUE(fragmentFound, FRAGMENT); + MARK_FOUND_AND_VALIDATE_UNIQUE(tesselationControlFound, HULL); + MARK_FOUND_AND_VALIDATE_UNIQUE(tesselationEvalFound, DOMAIN); + MARK_FOUND_AND_VALIDATE_UNIQUE(geometryFound, GEOMETRY); + MARK_FOUND_AND_VALIDATE_UNIQUE(computeFound, COMPUTE); + UNIMPLEMENTED_STAGE(RAY_GENERATION); + UNIMPLEMENTED_STAGE(INTERSECTION); + UNIMPLEMENTED_STAGE(ANY_HIT); + UNIMPLEMENTED_STAGE(CLOSEST_HIT); + UNIMPLEMENTED_STAGE(MISS); + UNIMPLEMENTED_STAGE(CALLABLE); + UNIMPLEMENTED_STAGE(MESH); + UNIMPLEMENTED_STAGE(AMPLIFICATION); + case SLANG_STAGE_COUNT: + case SLANG_STAGE_NONE: + ERROR("Invalid Stage.") THEN_ABORT(-1); + } + + if (slangResult < 0) + { + ERROR("Could not find entry point '{}' in '{}'. Cause: {}", entryPointName, shaderInfo.m_ShaderFile, + slangResult); + return slangResult; + } + + components.push_back(actualEntryPoint); + ++entryPointCount; + } + } + + bool validRasterShader = (vertexFound and fragmentFound) and not(tesselationControlFound xor tesselationEvalFound); + bool validComputeShader = computeFound; + bool validShaderSet = validRasterShader xor validComputeShader; + if (!validShaderSet) + { + eastl::fixed_vector shadersList; + if (vertexFound) + shadersList.push_back("vertex"); + if (fragmentFound) + shadersList.push_back("fragment"); + if (tesselationControlFound) + shadersList.push_back("tesselation control"); + if (tesselationEvalFound) + shadersList.push_back("tesselation eval"); + if (geometryFound) + shadersList.push_back("geometry"); + if (computeFound) + shadersList.push_back("compute"); + ERROR("Invalid combinations of shaders. [{}]", fmt::join(shadersList, " ")); + return SLANG_FAIL; + } + + ComPtr pipelineComposite; + auto slangResult = m_SlangSession->createCompositeComponentType(components.data(), Cast(components.size()), + pipelineComposite.writeRef()); + + if (slangResult < 0) + { + ERROR("Could not link program. Cause {}", slangResult); + } + + slangResult = pipelineComposite->link(program.writeRef(), shaderDiagnostics.writeRef()); + + if (slangResult < 0) + { + ERROR("{}", Cast(shaderDiagnostics->getBufferPointer())); + return slangResult; + } + + vk::Result result = vk::Result::eSuccess; + for (u32 entryPoint = 0; entryPoint < entryPointCount; ++entryPoint) + { + auto &outShader = shadersOut.push_back(); + + ComPtr kernelCode; + slangResult = program->getEntryPointCode(entryPoint, 0, kernelCode.writeRef(), shaderDiagnostics.writeRef()); + if (slangResult < 0) + { + ERROR("{}", Cast(shaderDiagnostics->getBufferPointer())); + return slangResult; + } + + if (auto progLayout = program->getLayout(0)) + { + if (auto entryPointReflection = progLayout->getEntryPointByIndex(entryPoint)) + { + outShader.pName = entryPointReflection->getName(); + outShader.stage = SlangToVulkanShaderStage(entryPointReflection->getStage()); + } + } + + const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = { + .codeSize = kernelCode->getBufferSize(), + .pCode = Cast(kernelCode->getBufferPointer()), + }; + + result = m_Device.m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &outShader.module); + if (Failed(result)) + { + ERROR("Shaders could not be created. Cause: {}", result); + break; + } + } + + if (Failed(result)) + { + for (auto &shader : shadersOut) + { + if (shader.module) + { + m_Device.m_Device.destroy(shader.module, nullptr); + } + } + return result; + } + + return {}; +} + +systems::PipelineCreationError +systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, + const Slang::ComPtr &program) +{ + using Slang::ComPtr; + + ComPtr layoutDiagnostics; + slang::ProgramLayout *layout = program->getLayout(0, layoutDiagnostics.writeRef()); + if (layoutDiagnostics) + { + ERROR_IF(!layout, "{}", Cast(layoutDiagnostics->getBufferPointer())); + return SLANG_FAIL; + } + + // TODO: Reflect to create the push constants and descriptor sets. + + const vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { + .setLayoutCount = 0, + .pSetLayouts = nullptr, + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, }; - vk::ShaderModule shaderModule; + vk::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); + if (Failed(result)) + { + ERROR("Could not create a pipeline layout. Cause: {}", result); + return result; + } - vk::Result result = m_Device.m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule); - ERROR_IF(Failed(result), "Shader {} could not be created. Cause: {}", shaderFile, result) - THEN_ABORT(result); - return shaderModule; + return {}; } #pragma endregion @@ -647,6 +851,29 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()}; + constexpr SlangGlobalSessionDesc globalSessionDesc = {}; + auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef()); + ERROR_IF(result < 0, "Could not create a slang global session.") THEN_ABORT(result); + + slang::CompilerOptionEntry useOriginalEntrypointNames = { + .name = slang::CompilerOptionName::VulkanUseEntryPointName, + .value = slang::CompilerOptionValue{.kind = slang::CompilerOptionValueKind::Int, .intValue0 = 1}, + }; + const slang::TargetDesc spirvTargetDesc = { + .format = SLANG_SPIRV, + .profile = m_GlobalSlangSession->findProfile("glsl_450"), + .compilerOptionEntries = &useOriginalEntrypointNames, + .compilerOptionEntryCount = 1, + }; + const slang::SessionDesc sessionDesc = { + .targets = &spirvTargetDesc, + .targetCount = 1, + .searchPaths = createInfo.m_ShaderSearchPaths.data(), + .searchPathCount = Cast(createInfo.m_ShaderSearchPaths.size()), + }; + result = m_GlobalSlangSession->createSession(sessionDesc, m_SlangSession.writeRef()); + ERROR_IF(result < 0, "Could not create a slang session.") THEN_ABORT(result); + u32 index = 0; for (auto &frame : m_Frames) { @@ -802,11 +1029,9 @@ systems::Device::Present(Frame &frame) void systems::Frame::Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView) { - AbortIfFailedMV(m_Device->m_Device.resetFences(1, &m_FrameAvailableFence), "Fence {} reset failed.", - m_FrameIdx); + AbortIfFailedMV(m_Device->m_Device.resetFences(1, &m_FrameAvailableFence), "Fence {} reset failed.", m_FrameIdx); - AbortIfFailedMV(m_Device->m_Device.resetCommandPool(m_Pool, {}), "Command pool {} reset failed.", - m_FrameIdx); + AbortIfFailedMV(m_Device->m_Device.resetCommandPool(m_Pool, {}), "Command pool {} reset failed.", m_FrameIdx); m_CommandBuffersAllocated = 0; m_ImageIdx = imageIdx; diff --git a/samples/01_triangle/CMakeLists.txt b/samples/01_triangle/CMakeLists.txt index 9489c0a..4000e58 100644 --- a/samples/01_triangle/CMakeLists.txt +++ b/samples/01_triangle/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.13) add_executable(triangle "triangle.cpp") -add_shader(triangle "shader/triangle.vert.glsl") -add_shader(triangle "shader/triangle.frag.glsl") +add_shader(triangle "shader/triangle.slang") +add_resource_dir(triangle "shader") target_link_libraries(triangle PRIVATE aster_core) target_link_libraries(triangle PRIVATE util_helper) diff --git a/samples/01_triangle/shader/triangle.frag.glsl b/samples/01_triangle/shader/triangle.frag.glsl deleted file mode 100644 index f85d053..0000000 --- a/samples/01_triangle/shader/triangle.frag.glsl +++ /dev/null @@ -1,9 +0,0 @@ -#version 450 -#pragma shader_stage(fragment) - -layout (location = 0) in vec3 inColor; -layout (location = 0) out vec4 outColor; - -void main() { - outColor = vec4(inColor, 1.0); -} \ No newline at end of file diff --git a/samples/01_triangle/shader/triangle.slang b/samples/01_triangle/shader/triangle.slang new file mode 100644 index 0000000..78b21c5 --- /dev/null +++ b/samples/01_triangle/shader/triangle.slang @@ -0,0 +1,35 @@ + +struct Vertex { + float3 point; + float3 color; +}; + +struct VSIn { + Vertex vertex; +}; + +struct VSOut +{ + float4 Pos : SV_POSITION; + float3 Color : COLOR0; +}; + +[shader("vertex")] +VSOut vsmain(VSIn input) { + VSOut output; + output.Pos = float4(input.vertex.point, 1.0f); + output.Color = input.vertex.color; + + return output; +} + +struct FSOut { + float4 Color; +}; + +[shader("fragment")] +FSOut fsmain(VSOut input) { + FSOut outp; + outp.Color = float4(input.Color, 1.0); + return outp; +} \ No newline at end of file diff --git a/samples/01_triangle/shader/triangle.vert.glsl b/samples/01_triangle/shader/triangle.vert.glsl deleted file mode 100644 index ea50725..0000000 --- a/samples/01_triangle/shader/triangle.vert.glsl +++ /dev/null @@ -1,27 +0,0 @@ -#version 450 -#pragma shader_stage(vertex) - -layout(location=0) in vec4 position; -layout(location=1) in vec4 color; - -layout(location=0) out vec3 outColor; - -void main() { - /* - vec3 points[] = { - vec3(-0.5f, -0.5f, 0.0f), - vec3(0.5f, -0.5f, 0.0f), - vec3(0.0f, 0.5f, 0.0f) - }; - vec3 colors[] = { - vec3( 1.0f, 0.0f, 0.0f ), - vec3( 0.0f, 1.0f, 0.0f ), - vec3( 0.0f, 0.0f, 1.0f ), - }; - - gl_Position = vec4(points[gl_VertexIndex], 1.0f); - outColor = vec3(colors[gl_VertexIndex]); //*/ - //* - gl_Position = vec4(position.xyz, 1.0f); - outColor = vec3(color.rgb); //*/ -} \ No newline at end of file diff --git a/samples/01_triangle/shader/triangle.vs.hlsl b/samples/01_triangle/shader/triangle.vs.hlsl deleted file mode 100644 index a2c7905..0000000 --- a/samples/01_triangle/shader/triangle.vs.hlsl +++ /dev/null @@ -1,28 +0,0 @@ -struct VSIn { - int idx : SV_VERTEXID; -}; - -struct VSOut -{ - float4 Pos : SV_POSITION; - [[vk::location(0)]] float3 Color : COLOR0; -}; - -VSOut main(VSIn input) { - float3 points[] = { - float3(-0.5f, -0.5f, 0.0f), - float3(0.5f, -0.5f, 0.0f), - float3(0.0f, 0.5f, 0.0f) - }; - float3 colors[] = { - float3( 1.0f, 0.0f, 0.0f ), - float3( 0.0f, 1.0f, 0.0f ), - float3( 0.0f, 0.0f, 1.0f ), - }; - - VSOut output; - output.Pos = float4(points[input.idx], 1.0f); - output.Color = colors[input.idx]; - - return output; -} \ No newline at end of file diff --git a/samples/01_triangle/triangle.cpp b/samples/01_triangle/triangle.cpp index a4f5dfc..f27f35d 100644 --- a/samples/01_triangle/triangle.cpp +++ b/samples/01_triangle/triangle.cpp @@ -20,35 +20,26 @@ #include -constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv"; -constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv"; +constexpr auto SHADER_MODULE = "triangle.slang"; struct Vertex { vec3 m_Position; vec3 m_Color; - constexpr static vk::VertexInputBindingDescription - GetBinding(const u32 binding) - { - return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex}; - } - - constexpr static eastl::array - GetAttributes(const u32 binding) + static eastl::vector + GetAttributes() { return { - vk::VertexInputAttributeDescription{ - .location = 0, - .binding = binding, - .format = vk::Format::eR32G32B32Sfloat, - .offset = offsetof(Vertex, m_Position), + { + .m_Location = 0, + .m_Offset = offsetof(Vertex, m_Position), + .m_Format = systems::AttributeInfo::Format::eFloat32X3, }, - vk::VertexInputAttributeDescription{ - .location = 1, - .binding = binding, - .format = vk::Format::eR32G32B32Sfloat, - .offset = offsetof(Vertex, m_Color), + { + .m_Location = 1, + .m_Offset = offsetof(Vertex, m_Color), + .m_Format = systems::AttributeInfo::Format::eFloat32X3, }, }; } @@ -61,44 +52,27 @@ main(int, char **) Window window = {"Triangle (Aster)", {640, 480}}; systems::Device device{{ - .m_AppName = "Triangle", .m_Window = window, - .m_Name = "Primary", .m_Features = {.m_Vulkan12Features = {.bufferDeviceAddress = true}, .m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}}, + .m_AppName = "Triangle", + .m_ShaderSearchPaths = {"shader/"}, + .m_Name = "Primary", }}; - Pipeline pipeline = device.CreatePipeline({ - .m_VertexInputs = {{ - .m_Attribute = - { - { - .m_Location = 0, - .m_Offset = offsetof(Vertex, m_Position), - .m_Format = systems::AttributeInfo::Format::eFloat32X3, - }, - { - .m_Location = 1, - .m_Offset = offsetof(Vertex, m_Color), - .m_Format = systems::AttributeInfo::Format::eFloat32X3, - }, - }, - .m_Stride = sizeof(Vertex), - }}, - .m_ShaderModules = - { - { - .m_ShaderFile = VERTEX_SHADER_FILE, - .m_EntryPoint = "main", - .m_Type = systems::GraphicsShaderModuleInfo::Type::eVertex, - }, - { - .m_ShaderFile = FRAGMENT_SHADER_FILE, - .m_EntryPoint = "main", - .m_Type = systems::GraphicsShaderModuleInfo::Type::eFragment, - }, - }, - }); + Pipeline pipeline; + auto pipelineError = device.CreatePipeline( + pipeline, { + .m_VertexInputs = {{ + .m_Attribute = Vertex::GetAttributes(), + .m_Stride = sizeof(Vertex), + }}, + .m_Shaders = {{ + .m_ShaderFile = SHADER_MODULE, + .m_EntryPoints = {"vsmain", "fsmain"}, + }}, + }); + ERROR_IF(pipelineError, "Error creating pipeline. Cause: {}", pipelineError.What()); // eastl::array vertices{}; eastl::array vertices = { diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index a20107f..47ec432 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -4,6 +4,6 @@ cmake_minimum_required(VERSION 3.13) add_subdirectory("00_util") add_subdirectory("01_triangle") -add_subdirectory("02_box") -add_subdirectory("03_model_render") +# add_subdirectory("02_box") +# add_subdirectory("03_model_render") # add_subdirectory("04_scenes") diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index 15af179..b01bcf0 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -1,7 +1,7 @@ { "default-registry": { "kind": "git", - "baseline": "b27651341123a59f7187b42ef2bc476284afb310", + "baseline": "41c447cc210dc39aa85d4a5f58b4a1b9e573b3dc", "repository": "https://github.com/microsoft/vcpkg" }, "registries": [ diff --git a/vcpkg.json b/vcpkg.json index fac4263..bb1b95a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -15,6 +15,7 @@ "scottt-debugbreak", "tinygltf", "vulkan-memory-allocator", - "entt" + "entt", + "shader-slang" ] }