Added slang for Shader code compilation.

TODO: Use slang to create descriptors.
This commit is contained in:
Anish Bhobe 2025-05-05 23:52:15 +02:00
parent 7507394af9
commit 5d6ddbb158
21 changed files with 525 additions and 240 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ build/
.direnv/
.ccls-cache/
*.user
/vcpkg_installed

View File

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

View File

@ -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.

View File

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

View File

@ -229,7 +229,7 @@ struct fmt::formatter<vk::Result> : nested_formatter<std::string>
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<eastl::fixed_string<TType, TCount, TOverflow>> : nested_fo
// ReSharper disable once CppInconsistentNaming
format(const eastl::fixed_string<TType, TCount, TOverflow> &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())); });
}
};

View File

@ -18,7 +18,30 @@ struct Pipeline
vk::Pipeline m_Pipeline;
eastl::vector<vk::DescriptorSetLayout> m_SetLayouts;
Pipeline() = default;
Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
eastl::vector<vk::DescriptorSetLayout> &&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;
}
};

View File

@ -20,7 +20,10 @@
#include <EASTL/optional.h>
#include <EASTL/vector_map.h>
#include <optional>
#include <slang-com-ptr.h>
#include <slang.h>
#include <variant>
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
{
std::string_view m_ShaderFile;
std::string_view m_EntryPoint;
enum class Type
{
eInvalid = 0,
eVertex = vk::ShaderStageFlagBits::eVertex,
eFragment = vk::ShaderStageFlagBits::eFragment,
eTesselationControl = vk::ShaderStageFlagBits::eTessellationControl,
eTesselationEvaluation = vk::ShaderStageFlagBits::eTessellationEvaluation,
} m_Type;
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<u32>(ShaderType::eMax) == 1 + (1 << (ShaderTypeCount - 1)));
struct ShaderInfo
{
std::string_view m_ShaderFile;
eastl::vector<std::string_view> m_EntryPoints;
};
struct GraphicsPipelineCreateInfo
{
eastl::fixed_vector<VertexInput, 4, false> m_VertexInputs;
eastl::fixed_vector<GraphicsShaderModuleInfo, 4, false> m_ShaderModules;
eastl::fixed_vector<ShaderInfo, 4, false> m_Shaders;
};
#pragma endregion
@ -294,13 +361,14 @@ static_assert(std::convertible_to<decltype(DefaultPhysicalDeviceSelector), Physi
struct DeviceCreateInfo
{
std::reference_wrapper<Window> m_Window;
Features m_Features;
cstr m_AppName = "Aster App";
Version m_AppVersion = {0, 1, 0};
PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector;
std::span<u8> m_PipelineCacheData = {};
std::reference_wrapper<Window> m_Window;
eastl::vector<cstr> m_ShaderSearchPaths;
cstr m_Name = "Primary";
Features m_Features;
};
#pragma endregion
@ -472,10 +540,10 @@ class Device final
// Sampler Management
// ----------------------------------------------------------------------------------------------------
using Handle = Ref<Sampler>;
using WeakHandle = WeakRef<Sampler>;
eastl::hash_map<vk::SamplerCreateInfo, WeakHandle> m_HashToSamplerIdx;
private:
eastl::hash_map<vk::SamplerCreateInfo, WeakRef<Sampler>> m_HashToSamplerIdx;
public:
Ref<Sampler> 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<slang::IGlobalSession> m_GlobalSlangSession;
Slang::ComPtr<slang::ISession> m_SlangSession;
PipelineCreationError
CreateShaders(eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut,
Slang::ComPtr<slang::IComponentType> &program, const std::span<const ShaderInfo> &shaders);
PipelineCreationError
CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, const Slang::ComPtr<slang::IComponentType> &program);
public:
// Pipelines, unlike the other resources, are not ref-counted.
Pipeline CreatePipeline(const GraphicsPipelineCreateInfo &createInfo);
PipelineCreationError CreatePipeline(Pipeline &pipeline, const GraphicsPipelineCreateInfo &createInfo);
//
// Frames

View File

@ -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 = {

View File

@ -56,18 +56,18 @@ struct fmt::formatter<MemorySize>
// 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<u16>(mem.m_Megabytes / 1024.0));
return v11::format_to(ctx.out(), "{}.{} GB", mem.m_Gigabytes, Cast<u16>(mem.m_Megabytes / 1024.0));
}
if (mem.m_Megabytes > 0)
{
return v10::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, Cast<u16>(mem.m_Kilobytes / 1024.0));
return v11::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, Cast<u16>(mem.m_Kilobytes / 1024.0));
}
if (mem.m_Kilobytes > 0)
{
return v10::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, Cast<u16>(mem.m_Bytes / 1024.0));
return v11::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, Cast<u16>(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);
}
};

View File

@ -11,26 +11,22 @@
#include <EASTL/fixed_vector.h>
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<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);

View File

@ -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<vk::DescriptorBindingFlags, decltype(descriptorLayoutBindings)::count> layoutBindingFlags;
eastl::array<vk::DescriptorBindingFlags, descriptorLayoutBindings.size()> layoutBindingFlags;
layoutBindingFlags.fill(bindingFlags);
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {

View File

@ -11,6 +11,8 @@
#include "aster/util/files.h"
#include <fmt/ranges.h>
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<GraphicsShaderModuleInfo> {
if (const auto res =
std::ranges::find_if(shaderModules, [type](const auto &v) { return v.m_Type == type; });
res != std::ranges::end(shaderModules))
eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> shaders;
Slang::ComPtr<slang::IComponentType> program;
if (auto shaderResult = CreateShaders(shaders, program, {createInfo.m_Shaders.begin(), createInfo.m_Shaders.end()});
shaderResult)
{
return *res;
return shaderResult;
}
return std::nullopt;
};
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<vk::PipelineShaderStageCreateInfo, 2> 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<vk::VertexInputBindingDescription, 4, false> inputBindingDescriptions;
eastl::fixed_vector<vk::VertexInputAttributeDescription, 4, false> 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<u32>(shaderStages.size()),
.pStages = shaderStages.data(),
.stageCount = Cast<u32>(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<u32> 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);
}
systems::PipelineCreationError
systems::Device::CreateShaders(
eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut,
Slang::ComPtr<slang::IComponentType> &program, const std::span<const ShaderInfo> &shaders)
{
using Slang::ComPtr;
ComPtr<slang::IBlob> shaderDiagnostics;
eastl::fixed_vector<slang::IComponentType *, ShaderTypeCount, false> 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<slang::IModule> shaderModule;
shaderModule = m_SlangSession->loadModule(shaderInfo.m_ShaderFile.data(), shaderDiagnostics.writeRef());
if (shaderDiagnostics)
{
ERROR("{}", Cast<cstr>(shaderDiagnostics->getBufferPointer()));
return SLANG_FAIL;
}
components.push_back(shaderModule);
for (auto entryPointName : shaderInfo.m_EntryPoints)
{
ComPtr<slang::IEntryPoint> actualEntryPoint;
auto slangResult = shaderModule->findEntryPointByName(entryPointName.data(), actualEntryPoint.writeRef());
slang::ProgramLayout *entryProgramLayout = actualEntryPoint->getLayout(0, shaderDiagnostics.writeRef());
if (shaderDiagnostics)
{
ERROR("{}", Cast<cstr>(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<cstr, 6> 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<slang::IComponentType> pipelineComposite;
auto slangResult = m_SlangSession->createCompositeComponentType(components.data(), Cast<u32>(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<cstr>(shaderDiagnostics->getBufferPointer()));
return slangResult;
}
vk::Result result = vk::Result::eSuccess;
for (u32 entryPoint = 0; entryPoint < entryPointCount; ++entryPoint)
{
auto &outShader = shadersOut.push_back();
ComPtr<slang::IBlob> kernelCode;
slangResult = program->getEntryPointCode(entryPoint, 0, kernelCode.writeRef(), shaderDiagnostics.writeRef());
if (slangResult < 0)
{
ERROR("{}", Cast<cstr>(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 = shaderCode.size() * sizeof(u32),
.pCode = shaderCode.data(),
.codeSize = kernelCode->getBufferSize(),
.pCode = Cast<const u32 *>(kernelCode->getBufferPointer()),
};
vk::ShaderModule shaderModule;
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;
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<slang::IComponentType> &program)
{
using Slang::ComPtr;
ComPtr<slang::IBlob> layoutDiagnostics;
slang::ProgramLayout *layout = program->getLayout(0, layoutDiagnostics.writeRef());
if (layoutDiagnostics)
{
ERROR_IF(!layout, "{}", Cast<cstr>(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::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout);
if (Failed(result))
{
ERROR("Could not create a pipeline layout. Cause: {}", result);
return result;
}
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<u32>(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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,35 +20,26 @@
#include <EASTL/array.h>
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<vk::VertexInputAttributeDescription, 2>
GetAttributes(const u32 binding)
static eastl::vector<systems::AttributeInfo>
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({
Pipeline pipeline;
auto pipelineError = device.CreatePipeline(
pipeline, {
.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_Attribute = Vertex::GetAttributes(),
.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,
},
},
.m_Shaders = {{
.m_ShaderFile = SHADER_MODULE,
.m_EntryPoints = {"vsmain", "fsmain"},
}},
});
ERROR_IF(pipelineError, "Error creating pipeline. Cause: {}", pipelineError.What());
// eastl::array<Vertex, 3> vertices{};
eastl::array vertices = {

View File

@ -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")

View File

@ -1,7 +1,7 @@
{
"default-registry": {
"kind": "git",
"baseline": "b27651341123a59f7187b42ef2bc476284afb310",
"baseline": "41c447cc210dc39aa85d4a5f58b4a1b9e573b3dc",
"repository": "https://github.com/microsoft/vcpkg"
},
"registries": [

View File

@ -15,6 +15,7 @@
"scottt-debugbreak",
"tinygltf",
"vulkan-memory-allocator",
"entt"
"entt",
"shader-slang"
]
}