Gets slang shaders mostly working

This commit is contained in:
2025-02-13 15:17:05 -05:00
parent 0c42361b6a
commit 289a074640
34 changed files with 695 additions and 617 deletions

3
.gitmodules vendored
View File

@@ -55,3 +55,6 @@
[submodule "fgl_cmake_modules"]
path = fgl_cmake_modules
url = https://github.com/KJNeko/fgl_cmake_modules.git
[submodule "dependencies/slang"]
path = dependencies/slang
url = https://github.com/shader-slang/slang.git

View File

@@ -31,7 +31,7 @@ include(dependencies/glm)
include(cmake_modules/dependencies/tracy.cmake)
include(dependencies/vulkan)
include(dependencies/catch2)
include(dependencies/shaderc)
includE(dependencies/slang)
add_subdirectory(src)

View File

@@ -1,8 +0,0 @@
#set(SPIRV-Headers_SOURCE_DIR ${CMAKE_SOURCE_DIR}/dependencies/SPIRV-Headers)
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/SPIRV-Headers)
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/SPIRV-Tools)
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/glslang)
set(SHADERC_SKIP_TESTS ON)
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/shaderc)

View File

@@ -0,0 +1,31 @@
set(SLANG_ENABLE_CUDA OFF)
set(SLANG_ENABLE_OPTIX OFF)
set(SLANG_ENABLE_NVAPI OFF)
set(SLANG_ENABLE_AFTERMATH OFF)
set(SLANG_ENABLE_DX_ON_VK OFF)
set(SLANG_EMBED_CORE_MODULE_SOURCE OFF)
set(SLANG_EMBED_CORE_MODULE ON)
set(SLANG_ENABLE_DXIL OFF)
set(SLANG_ENABLE_FULL_IR_VALIDATION ON)
set(SLANG_ENABLE_IR_BREAK_ALLOC ON)
set(SLANG_ENABLE_PREBUILD_BINARIES OFF)
set(SLANG_ENABLE_GFX OFF)
set(SLANG_ENABLE_SLANGD OFF)
set(SLANG_ENABLE_SHANGC OFF)
set(SLANG_ENABLE_TESTS OFF)
set(SLANG_ENABLE_EXAMPLES OFF)
set(SLANG_ENABLE_GFX OFF)
set(SLANG_USE_SYSTEM_VULKAN_HEADERS ON)
set(SLANG_ENABLE_SPLIT_DEBUG_INFO OFF)
set(SLANG_ENABLE_RELEASE_DEBUG_INFO ON)
set(SLANG_ENABLE_RELEASE_LTO OFF)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/dependencies/slang)

1
dependencies/slang vendored Submodule

Submodule dependencies/slang added at 79aebc18d5

View File

@@ -15,7 +15,7 @@ target_link_libraries(FGLEngine PUBLIC stdc++exp)
include(dependencies/spdlog)
include(dependencies/imgui)
target_link_libraries(FGLEngine PUBLIC Vulkan::Vulkan glm ImGui FGLLoader spdlog shaderc)
target_link_libraries(FGLEngine PUBLIC Vulkan::Vulkan glm ImGui FGLLoader spdlog slang)
target_link_libraries(FGLEngine PUBLIC glfw Tracy::TracyClient VMA)
target_include_directories(FGLEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
target_compile_features(FGLEngine PUBLIC cxx_std_23)
@@ -38,8 +38,11 @@ endif ()
if (DEFINED FGL_ENABLE_TESTS AND FGL_ENABLE_TESTS)
target_compile_definitions(FGLEngine PUBLIC FGL_TESTS=1)
target_compile_definitions(FGLEngine PUBLIC FGL_ENABLE_TEST_ASSERT=1)
target_link_libraries(FGLEngine PUBLIC Catch2::Catch2)
else ()
target_compile_definitions(FGLEngine PUBLIC FGL_TESTS=0)
target_compile_definitions(FGLEngine PUBLIC FGL_ENABLE_TEST_ASSERT=0)
endif ()
# Enable tracking for buffers, I need to find some way to disable this when trying to link

View File

@@ -265,7 +265,7 @@ namespace fgl::engine
{
const auto& [ pos, scale, rotation ] = m_transform;
const auto rotation_matrix { rotation.mat() };
const auto rotation_matrix { rotation.mat3() };
const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) };
@@ -346,6 +346,7 @@ namespace fgl::engine
auto set { camera_descriptor_set.create() };
set->bindUniformBuffer( 0, m_camera_frame_info[ i ] );
set->update();
set->setName( std::format( "Camera {} descriptor set {}", m_camera_idx, i ) );
m_camera_info_descriptors.emplace_back( std::move( set ) );
}

View File

@@ -63,8 +63,8 @@ namespace fgl::engine
builder.setPushConstant( vk::ShaderStageFlagBits::eFragment, sizeof( CompositionControl ) );
builder.setVertexShader( Shader::loadVertex( "shaders/fullscreen.vert" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/composition.frag" ) );
builder.setVertexShader( Shader::loadVertex( "shaders/composition.slang" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/composition.slang" ) );
builder.disableCulling();
builder.disableVertexInput();

View File

@@ -5,6 +5,7 @@
#include "Shader.hpp"
#include <fstream>
#include <utility>
#include "engine/debug/logging/logging.hpp"
#include "engine/rendering/devices/Device.hpp"
@@ -13,19 +14,35 @@
namespace fgl::engine
{
std::vector< std::byte > Shader::loadData( const std::filesystem::path& path )
std::string entrypointName( const ShaderType type, vk::PipelineShaderStageCreateInfo& info )
{
std::string str {};
switch ( type )
{
case Vertex:
str = "vertexMain";
break;
case Fragment:
str = "fragmentMain";
break;
case Compute:
str = "computeMain";
break;
default:
throw std::runtime_error( "Invalid shader stage flags" );
}
info.setPName( str.c_str() );
return str;
}
std::vector< std::byte > Shader::loadData( const std::filesystem::path& path, const ShaderType type )
{
if ( auto ifs = std::ifstream( path, std::ios::binary | std::ios::ate ); ifs )
{
std::vector< std::byte > data {};
data.resize( ifs.tellg() );
ifs.seekg( 0, std::ios::beg );
static_assert( sizeof( std::ifstream::char_type ) == sizeof( std::byte ) );
ifs.read( reinterpret_cast< std::ifstream::char_type* >( data.data() ), data.size() );
return compileShader( path.filename().string(), data );
return compileShader( path, type );
}
else
{
@@ -45,28 +62,32 @@ namespace fgl::engine
return module_info;
}
Shader::Shader( const std::filesystem::path& path, const vk::PipelineShaderStageCreateInfo& info ) :
m_path( path ),
shader_data( loadData( m_path ) ),
module_create_info( createModuleInfo() ),
Shader::Shader( std::filesystem::path path, const vk::PipelineShaderStageCreateInfo& info, const ShaderType type ) :
m_type( type ),
stage_info( info ),
m_entrypoint_name( entrypointName( type, stage_info ) ),
m_path( std::move( path ) ),
shader_data( loadData( m_path, type ) ),
module_create_info( createModuleInfo() ),
shader_module( Device::getInstance()->createShaderModule( module_create_info ) )
{
FGL_ASSERT( stage_info.pName == m_entrypoint_name.c_str(), "Entry point name mismatch" );
log::debug( "Created shader {}", stage_info.pName );
stage_info.module = shader_module;
}
std::shared_ptr< Shader > Shader::
loadShader( std::filesystem::path path, const vk::ShaderStageFlagBits stage_flags )
std::shared_ptr< Shader > Shader::loadShader(
const std::filesystem::path& path, const vk::ShaderStageFlagBits stage_flags, const ShaderType type )
{
std::filesystem::path full_path { std::filesystem::current_path() / path };
vk::PipelineShaderStageCreateInfo stage_info {};
stage_info.stage = stage_flags;
stage_info.flags = {};
stage_info.pName = "main";
stage_info.pName = nullptr;
stage_info.pSpecializationInfo = VK_NULL_HANDLE;
auto shader { std::make_shared< Shader >( path, stage_info ) };
auto shader { std::make_shared< Shader >( path, stage_info, type ) };
return shader;
}
@@ -74,7 +95,7 @@ namespace fgl::engine
void Shader::reload()
{
log::debug( "Reloading shader at {}", m_path.string() );
shader_data = loadData( m_path );
shader_data = loadData( m_path, m_type );
module_create_info = createModuleInfo();
shader_module = Device::getInstance()->createShaderModule( module_create_info );
stage_info.module = shader_module;

View File

@@ -8,22 +8,26 @@
#include <filesystem>
#include "shaders/Compiler.hpp"
namespace fgl::engine
{
struct Shader
{
ShaderType m_type;
vk::PipelineShaderStageCreateInfo stage_info;
std::string m_entrypoint_name;
std::filesystem::path m_path;
std::vector< std::byte > shader_data;
vk::ShaderModuleCreateInfo module_create_info;
vk::PipelineShaderStageCreateInfo stage_info;
vk::raii::ShaderModule shader_module;
static std::vector< std::byte > loadData( const std::filesystem::path& );
static std::vector< std::byte > loadData( const std::filesystem::path&, ShaderType type );
vk::ShaderModuleCreateInfo createModuleInfo() const;
Shader( const std::filesystem::path& path, const vk::PipelineShaderStageCreateInfo& info );
Shader( std::filesystem::path path, const vk::PipelineShaderStageCreateInfo& info, ShaderType type );
Shader( const Shader& other ) = delete;
@@ -33,16 +37,17 @@ namespace fgl::engine
Shader& operator=( Shader&& other ) = delete;
static std::shared_ptr< Shader > loadShader( std::filesystem::path path, vk::ShaderStageFlagBits stage_flags );
static std::shared_ptr< Shader >
loadShader( const std::filesystem::path& path, vk::ShaderStageFlagBits stage_flags, ShaderType type );
inline static std::shared_ptr< Shader > loadVertex( std::filesystem::path path )
static std::shared_ptr< Shader > loadVertex( const std::filesystem::path& path )
{
return loadShader( path, vk::ShaderStageFlagBits::eVertex );
return loadShader( path, vk::ShaderStageFlagBits::eVertex, ShaderType::Vertex );
}
inline static std::shared_ptr< Shader > loadFragment( std::filesystem::path path )
static std::shared_ptr< Shader > loadFragment( const std::filesystem::path& path )
{
return loadShader( path, vk::ShaderStageFlagBits::eFragment );
return loadShader( path, vk::ShaderStageFlagBits::eFragment, ShaderType::Fragment );
}
//! Reloads the shader from disk

View File

@@ -6,200 +6,161 @@
#include <cassert>
#include <fstream>
#include <slang-com-ptr.h>
#include "engine/FGL_DEFINES.hpp"
#include "engine/constants.hpp"
#include "engine/debug/logging/logging.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <shaderc/shaderc.hpp>
#pragma GCC diagnostic pop
#include "rendering/pipelines/Shader.hpp"
namespace fgl::engine
{
inline static std::unique_ptr< shaderc::Compiler > compiler;
shaderc::Compiler& getInstance()
void checkDiag( Slang::ComPtr< slang::IBlob >& diag )
{
if ( !compiler )
{
compiler = std::make_unique< shaderc::Compiler >();
}
if ( !diag ) return;
FGL_ASSERT( compiler, "Compiler invalid!" );
return *compiler;
log::error( static_cast< const char* >( diag->getBufferPointer() ) );
throw std::logic_error( "unexpected diagnostic" );
}
shaderc_shader_kind getShaderKindFromName( const std::string_view str )
{
if ( str.ends_with( ".frag" ) ) return shaderc_shader_kind::shaderc_fragment_shader;
if ( str.ends_with( ".vert" ) ) return shaderc_shader_kind::shaderc_vertex_shader;
struct ShaderVertexFlags
{};
throw std::runtime_error( "Unknown shader type!" );
}
struct ShaderFragmentFlags
{};
class Includer : public shaderc::CompileOptions::IncluderInterface
using ShaderLayoutFlags = std::variant< ShaderVertexFlags, ShaderFragmentFlags >;
std::vector< std::byte > compileShader( const std::filesystem::path& path, const ShaderType type )
{
struct DataHolder
{
std::filesystem::path source_path {};
std::vector< char > content {};
using namespace slang;
Slang::ComPtr< IGlobalSession > global_session {};
SlangGlobalSessionDesc global_desc {};
global_desc.enableGLSL = true;
createGlobalSession( &global_desc, global_session.writeRef() );
SessionDesc session_desc {};
std::array< CompilerOptionEntry, 1 > options {
{ { CompilerOptionName::VulkanUseEntryPointName,
{ .kind = CompilerOptionValueKind::Int, .intValue0 = 1 } } }
};
shaderc_include_result* GetInclude(
const char* requested_source,
shaderc_include_type type,
const char* requesting_source,
size_t include_depth ) override;
session_desc.compilerOptionEntries = options.data();
session_desc.compilerOptionEntryCount = options.size();
void ReleaseInclude( shaderc_include_result* data ) override;
};
TargetDesc target_desc {};
target_desc.format = SLANG_SPIRV;
target_desc.profile = global_session->findProfile( "glsl_450" );
shaderc_include_result* Includer::GetInclude(
const char* requested_source,
[[maybe_unused]] shaderc_include_type type,
const char* requesting_source,
[[maybe_unused]] size_t include_depth )
{
const std::string_view requsted { requested_source };
const std::string_view requster { requesting_source };
FGL_ASSERT( target_desc.profile != SLANG_PROFILE_UNKNOWN, "Invalid profile" );
std::vector< char > file_data {};
session_desc.targets = &target_desc;
session_desc.targetCount = 1;
const auto path { std::filesystem::current_path() / "shaders" / requsted };
const auto search_path { std::filesystem::path() / "shaders" };
const std::string search_path_str { search_path.string() };
const char* const search_pathr_str_ptr { search_path_str.data() };
session_desc.searchPaths = &search_pathr_str_ptr;
session_desc.searchPathCount = 1;
if ( std::ifstream ifs( path ); ifs )
Slang::ComPtr< ISession > session {};
global_session->createSession( session_desc, session.writeRef() );
const auto module_name { path.filename().string() };
Slang::ComPtr< IBlob > diagnostics;
IModule* module { session->loadModule( module_name.c_str(), diagnostics.writeRef() ) };
checkDiag( diagnostics );
FGL_ASSERT( module != nullptr, "Invalid module" );
std::string entry_point_name {};
switch ( type )
{
file_data.resize( std::filesystem::file_size( path ) );
ifs.read( file_data.data(), file_data.size() );
}
else
{
log::error( "Failed to find include {} for {}", path, requster );
//throw std::runtime_error( std::format( "Failed to open include file {} for file {}", requsted, requster ) );
auto error_return { new shaderc_include_result() };
error_return->user_data = nullptr;
error_return->source_name = "";
error_return->source_name_length = 0;
error_return->content = "Failed to find include for requsted file";
return error_return;
}
auto* data_holder { new DataHolder( path, std::move( file_data ) ) };
auto data { new shaderc_include_result() };
data->user_data = data_holder;
data->content = data_holder->content.data();
data->content_length = data_holder->content.size();
data->source_name = data_holder->source_path.c_str();
data->source_name_length = data_holder->source_path.string().size();
return data;
}
void Includer::ReleaseInclude( shaderc_include_result* data )
{
delete static_cast< DataHolder* >( data->user_data );
delete data;
}
std::vector< std::byte > compileShader( const std::string_view input_name, const std::vector< std::byte >& input )
{
shaderc::CompileOptions options {};
options.SetTargetEnvironment( shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_3 );
options.SetIncluder( std::make_unique< Includer >() );
#ifndef NDEBUG
options.SetOptimizationLevel( shaderc_optimization_level_zero );
#else
options.SetOptimizationLevel( shaderc_optimization_level_performance );
#endif
options.SetVulkanRulesRelaxed( false );
// Add macro defs to the shader
options.AddMacroDefinition( "INVALID_TEXTURE_ID", std::to_string( constants::INVALID_TEXTURE_ID ) );
options.AddMacroDefinition( "NEAR_PLANE", std::to_string( constants::NEAR_PLANE ) );
options.AddMacroDefinition( "FAR_PLANE", std::to_string( constants::FAR_PLANE ) );
// Helpful constants
options.AddMacroDefinition( "PI", std::to_string( std::numbers::pi_v< float > ) );
const shaderc_shader_kind kind { getShaderKindFromName( input_name ) };
const auto preprocessed_source { getInstance().PreprocessGlsl(
reinterpret_cast< const char* >( input.data() ), input.size(), kind, input_name.data(), options ) };
const auto result { getInstance().CompileGlslToSpv(
reinterpret_cast< const char* >( input.data() ), input.size(), kind, input_name.data(), options ) };
switch ( result.GetCompilationStatus() )
{
case shaderc_compilation_status_success:
case ShaderType::Compute:
entry_point_name = "computeMain";
break;
case Vertex:
entry_point_name = "vertexMain";
break;
case Fragment:
entry_point_name = "fragmentMain";
break;
case shaderc_compilation_status_compilation_error:
log::critical(
"Compilation error when compiling shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
default:
[[fallthrough]];
case shaderc_compilation_status_internal_error:
log::critical(
"internal error while compiling shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
[[fallthrough]];
case shaderc_compilation_status_null_result_object:
log::critical(
"null result object when compiling shader {} with error: {}",
input_name,
result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
[[fallthrough]];
case shaderc_compilation_status_invalid_assembly:
log::critical( "Failed to compile shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
[[fallthrough]];
case shaderc_compilation_status_validation_error:
log::critical( "Failed to compile shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
case shaderc_compilation_status_transformation_error:
log::critical( "Failed to compile shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
case shaderc_compilation_status_invalid_stage:
log::critical( "Failed to compile shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
case shaderc_compilation_status_configuration_error:
log::critical( "Failed to compile shader {} with error: {}", input_name, result.GetErrorMessage() );
throw std::runtime_error( "Failed to compile shader" );
break;
throw std::logic_error( "Invalid shader type" );
}
log::debug( "Compiled shader {}", input_name );
Slang::ComPtr< IEntryPoint > entry_point {};
module->findEntryPointByName( entry_point_name.c_str(), entry_point.writeRef() );
std::vector< std::byte > output {};
output.reserve( result.cend() - result.cbegin() );
std::array< IComponentType*, 2 > components { module, entry_point };
Slang::ComPtr< IComponentType > program;
session->createCompositeComponentType(
components.data(), components.size(), program.writeRef(), diagnostics.writeRef() );
checkDiag( diagnostics );
Slang::ComPtr< IComponentType > linked_program {};
program->link( linked_program.writeRef(), diagnostics.writeRef() );
checkDiag( diagnostics );
const auto parent_path { path.parent_path() };
for ( const auto& word : result )
{
output.insert(
output.end(),
reinterpret_cast< const std::byte* >( &word ),
reinterpret_cast< const std::byte* >( &word ) + sizeof( std::remove_reference_t< decltype( word ) > ) );
slang::ProgramLayout* layout { linked_program->getLayout() };
Slang::ComPtr< IBlob > json_glob {};
layout->toJson( json_glob.writeRef() );
log::debug(
"Shader layout: {}",
std::string_view(
static_cast< const char* >( json_glob->getBufferPointer() ), json_glob->getBufferSize() ) );
/*
if ( std::ofstream
ofs( path.parent_path()
/ std::format( "{}-{}.json", path.filename().string(), entry_point_name ) );
ofs )
{
ofs.write( static_cast< const char* >( json_glob->getBufferPointer() ), json_glob->getBufferSize() );
}
*/
FGL_ASSERT( layout != nullptr, "Layout must be valid" );
}
// Should be a multiple of 4
assert( output.size() % 4 == 0 );
assert( output.size() > 0 );
int entry_index { 0 };
int target_index { 0 };
return output;
Slang::ComPtr< IBlob > kernel_blob {};
linked_program->getEntryPointCode( entry_index, target_index, kernel_blob.writeRef(), diagnostics.writeRef() );
checkDiag( diagnostics );
FGL_ASSERT( kernel_blob != nullptr, "Kernel blob is not valid" );
log::debug( "Compiled shader {} with a length of {}", path.filename().string(), kernel_blob->getBufferSize() );
// if ( std::ofstream ofs( parent_path / std::format( "{}-{}.bin", path.filename().string(), entry_point_name ) );
// ofs )
// {
// ofs.write(
// static_cast< const std::ostream::char_type* >( kernel_blob->getBufferPointer() ),
// kernel_blob->getBufferSize() );
// }
std::vector< std::byte > compiled_code {};
compiled_code.resize( kernel_blob->getBufferSize() );
std::memcpy( compiled_code.data(), kernel_blob->getBufferPointer(), kernel_blob->getBufferSize() );
return compiled_code;
}
} // namespace fgl::engine

View File

@@ -3,19 +3,18 @@
//
#pragma once
#include <string_view>
#include <filesystem>
#include <vector>
namespace shaderc
{
class Compiler;
}
namespace fgl::engine
{
enum ShaderType
{
Vertex,
Fragment,
Compute
};
shaderc::Compiler& getInstance();
std::vector< std::byte > compileShader( const std::string_view input_name, const std::vector< std::byte >& input );
std::vector< std::byte > compileShader( const std::filesystem::path& input_name, ShaderType type );
} // namespace fgl::engine

View File

@@ -25,8 +25,8 @@ namespace fgl::engine
builder.setAttributeDescriptions( SimpleVertex::getAttributeDescriptions() );
builder.setBindingDescriptions( SimpleVertex::getBindingDescriptions() );
builder.setVertexShader( Shader::loadVertex( "shaders/fullscreen.vert" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/gui-compose.frag" ) );
builder.setVertexShader( Shader::loadVertex( "shaders/gui-compose.slang" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/gui-compose.slang" ) );
builder.addColorAttachment().setFormat( pickPresentFormat() ).finish();

View File

@@ -22,6 +22,7 @@ namespace fgl::engine
{
ZoneScoped;
/*
{
// PipelineConfigInfo standard_info { render_pass };
// PipelineConfigInfo::addGBufferAttachmentsConfig( standard_info );
@@ -42,6 +43,7 @@ namespace fgl::engine
m_standard_pipeline->setDebugName( "Standard entity pipeline" );
}
*/
{
// PipelineConfigInfo textured_info { render_pass };
@@ -55,8 +57,8 @@ namespace fgl::engine
builder.addDescriptorSet( Texture::getDescriptorLayout() );
builder.addDescriptorSet( Material::getDescriptorLayout() );
builder.setFragmentShader( Shader::loadFragment( "shaders/textured-gbuffer.frag" ) );
builder.setVertexShader( Shader::loadVertex( "shaders/textured-gbuffer.vert" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/textured.slang" ) );
builder.setVertexShader( Shader::loadVertex( "shaders/textured.slang" ) );
builder.setAttributeDescriptions( ModelVertex::getAttributeDescriptions() );
builder.setBindingDescriptions( ModelVertex::getBindingDescriptions() );

View File

@@ -37,8 +37,8 @@ namespace fgl::engine
builder.setTopology( vk::PrimitiveTopology::eLineList );
builder.setVertexShader( Shader::loadVertex( "shaders/line.vert" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/line.frag" ) );
builder.setVertexShader( Shader::loadVertex( "shaders/line.slang" ) );
builder.setFragmentShader( Shader::loadFragment( "shaders/line.slang" ) );
builder.addDynamicState( vk::DynamicState::eLineWidth );

16
src/shaders/camera.slang Normal file
View File

@@ -0,0 +1,16 @@
#version 450
module camera;
public struct CameraData
{
public mat4x4 projection : CAMERA_PROJECTION;
public mat4x4 view : CAMERA_VIEW;
public mat4x4 inverse_view : CAMERA_INVERSE_VIEW;
public vec3 getPos()
{
return vec3( inverse_view[ 3 ][ 0 ], inverse_view[ 3 ][ 1 ], inverse_view[ 3 ][ 2 ] );
}
}

View File

@@ -1,144 +0,0 @@
#version 450
layout (input_attachment_index = 0, binding = 0) uniform subpassInput i_color;
layout (input_attachment_index = 1, binding = 1) uniform subpassInput i_position;
layout (input_attachment_index = 2, binding = 2) uniform subpassInput i_normal;
layout (input_attachment_index = 3, binding = 3) uniform subpassInput i_metallic;
//layout (input_attachment_index = 4, binding = 4) uniform subpassInput i_specular;
layout (input_attachment_index = 4, binding = 4) uniform subpassInput i_emissive;
layout (location = 0) in vec2 in_uv;
layout (location = 0) out vec4 out_color;
layout (set = 1, binding = 0) uniform CameraInfo {
mat4 projection;
mat4 view;
mat4 inverse_view;
} camera_info;
layout(push_constant) uniform Constants {
uint flags;
} push;
//TODO: uniform binding with sun information
vec3 getCameraPosition()
{
return vec3(
camera_info.inverse_view[3][0],
camera_info.inverse_view[3][1],
camera_info.inverse_view[3][2]
);
}
vec3 sun_dir = normalize(vec3(-1.0, -1.0, -1.0));
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0;// Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
//TODO: Apparently this can be gotten from a texture instead?
vec3 schlick(vec3 F0, float cosTheta)
{
return F0 + (vec3(1.0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
void main()
{
const vec3 albedo = subpassLoad(i_color).xyz;
const vec3 position = subpassLoad(i_position).xyz;
const vec3 normal = subpassLoad(i_normal).xyz;
// metallic, roughness, occlusion
// r, g, b
const vec3 metallic_comb = subpassLoad(i_metallic).xyz;
// split the channels of ease of use
const float metallic_value = metallic_comb.r;
const float roughness_value = metallic_comb.g;
const float occlusion = metallic_comb.b;
const vec3 camera_pos = getCameraPosition();
// Calculate the vector from the world position to the camera
const vec3 Lo = normalize(camera_pos - position);
const vec3 N = normalize(normal);
float cosLo = max(dot(N, Lo), 0.0);
// Specular reflection
// vec3 Lr = 2.0 * cosLo * N - Lo;
vec3 Lr = reflect(-Lo, N);
const vec3 fresnel_factor = vec3(0.04);
// Fresnel
vec3 F0 = mix(fresnel_factor, albedo, metallic_value);
vec3 direct_lighting = vec3(0.0);
// Do this for each light
// {
vec3 Li = -sun_dir;
vec3 Lradiance = vec3(1.0);// color?
// half vector
vec3 Lh = normalize(Li + Lo);
float cosLi = max(dot(N, Li), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
//float cosTheta = max(dot(Lh, Lo), 0.0);
float cosTheta = dot(Li, N);
vec3 F = schlick(F0, cosTheta);
float D = ndfGGX(cosLh, roughness_value);
float G = gaSchlickGGX(cosLi, cosLo, roughness_value);
vec3 kb = mix(vec3(1.0) - F, vec3(0.0), metallic_value);
vec3 diffuse_BRDF = kb * albedo;
vec3 specular_BRDF = (F * D * G) / max(0.04, 4.0 * cosLi * cosLo);
direct_lighting = (diffuse_BRDF + specular_BRDF) * Lradiance * cosLi;
// }
vec3 ambient_lighting = albedo * 0.1;
switch (push.flags)
{
case 0:
out_color = vec4(direct_lighting + ambient_lighting, 1.0);
return;
case 1:
out_color = vec4(direct_lighting, 1.0);
return;
case 2:
out_color = vec4(ambient_lighting, 1.0);
return;
}
out_color = vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@@ -0,0 +1,151 @@
#version 450
import camera;
struct Vertex {
vec2 uv;
vec4 position : SV_Position;
};
[shader("vertex")]
Vertex vertexMain( uint index : SV_VertexID )
{
Vertex vertex;
vertex.uv = vec2( ( index << 1 ) & 2, index & 2 );
vertex.position = vec4( vertex.uv * 2.0 - 1.0f, 0.0f, 0.0f );
return vertex;
}
struct CompositeFragment {
vec4 color;
}
struct GBufferInput {
SubpassInput<vec4> color : COLOR;
SubpassInput<vec4> position : POSITION;
SubpassInput<vec4> normal : NORMAL;
SubpassInput<vec4> metallic : METALLIC;
SubpassInput<vec4> emissive : EMISSIVE;
};
// SubpassInput< vec4 > test : TEST;
ParameterBlock<GBufferInput> gbuffer : GBUFFER;
// SubpassInput<vec4> color : COLOR;
// SubpassInput<vec4> position : POSITION;
// SubpassInput<vec4> normal : NORMAL;
// SubpassInput<vec4> metallic : METALLIC;
// SubpassInput<vec4> emissive : EMISSIVE;
[[vk::binding(0,1)]]
ParameterBlock< CameraData > camera : CAMERA;
//TODO: constant flags
vec3 sun_dir = normalize(vec3(-1.0, -1.0, -1.0));
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2.
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (3.145 * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float cosLo, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0;// Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}
//TODO: Apparently this can be gotten from a texture instead?
vec3 schlick(vec3 F0, float cosTheta)
{
return F0 + (vec3(1.0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
[shader("fragment")]
CompositeFragment fragmentMain( Vertex vertex )
{
// const vec3 test_sample = test.SubpassLoad().xyz;
// const vec3 albedo = gbuffer.color.SubpassLoad().xyz * test_sample;
const vec3 albedo = gbuffer.color.SubpassLoad().xyz;
const vec3 position = gbuffer.position.SubpassLoad().xyz;
const vec3 normal = gbuffer.normal.SubpassLoad().xyz;
// metallic, roughness, occlusion
// r, g, b
const vec3 metallic_comb = gbuffer.metallic.SubpassLoad().xyz;
/*
const vec3 albedo = color.SubpassLoad().xyz;
const vec3 position = position.SubpassLoad().xyz;
const vec3 normal = normal.SubpassLoad().xyz;
// metallic, roughness, occlusion
// r, g, b
const vec3 metallic_comb = metallic.SubpassLoad().xyz;
*/
const float metallic = metallic_comb.r;
const float roughness = metallic_comb.g;
const float occlusion = metallic_comb.b;
const vec3 camera_pos = camera.getPos();
const vec3 Lo = normalize( camera_pos - position );
const vec3 N = normalize( normal );
float cosLo = max(dot(N, Lo), 0.0);
vec3 Lr = reflect( -Lo, N );
const vec3 fresnel_factor = vec3( 0.04 );
vec3 F0 = mix( fresnel_factor, albedo, metallic );
vec3 direct_lighting = vec3( 0.0 );
vec3 Li = -sun_dir;
vec3 Lradiance = vec3(1.0);
vec3 Lh = normalize( Li + Lo );
float cosLi = max( dot( N, Li ), 0.0 );
float cosLh = max( dot( N, Lh ), 0.0 );
float cosTheta = dot( Li, N );
vec3 F = schlick( F0, cosTheta );
float D = ndfGGX( cosLh, roughness );
float G = gaSchlickGGX( cosLi, cosLo, roughness );
vec3 kb = mix( vec3( 1.0 ) - F, vec3( 0.0 ), metallic );
vec3 diffuse_BRDF = kb * albedo;
vec3 specular_BRDF = ( F * D * G ) / max( 0.04, 4.0 * cosLi * cosLo );
direct_lighting = ( diffuse_BRDF + specular_BRDF ) * Lradiance * cosLi;
vec3 ambient_lighting = albedo * 0.1;
CompositeFragment frag;
frag.color = vec4( direct_lighting + ambient_lighting, 1.0 );
return frag;
}

View File

@@ -1,17 +0,0 @@
#version 450
// This vertex shader is meant to draw a single triangle on the entire screen butter so we can do full-screen effects.
// There *might* be a better way to do this. But it works for now.
layout (location = 0) out vec2 out_uv;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
out_uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(out_uv * 2.0f - 1.0f, 0.0f, 1.0f);
}

13
src/shaders/gbuffer.slang Normal file
View File

@@ -0,0 +1,13 @@
#version 450
module gbuffer;
public struct GBufferFragment
{
public vec4 color : COLOR;
public vec3 position : POSITION;
public vec4 normal : NORMAL;
public vec3 metallic : METALLIC;
public vec3 emissive : EMISSIVE;
};

View File

@@ -1,14 +0,0 @@
#version 450
//layout (input_attachment_index = 0, binding = 0) uniform subpassInput i_composite;
layout (location = 0) in vec2 in_uv;
layout (location = 0) out vec4 out_color;
void main() {
// vec4 composite = subpassLoad(i_composite).xyzw;
//
// out_color = composite;
out_color = vec4(0.0f, 0.0f, 0.0f, 0.0f);
}

View File

@@ -0,0 +1,39 @@
#version 450
struct Vertex {
vec2 uv;
vec4 position : SV_Position;
};
[shader("vertex")]
Vertex vertexMain( uint index : SV_VertexID )
{
Vertex vertex;
vertex.uv = vec2( ( index << 1 ) & 2, index & 2 );
vertex.position = vec4( vertex.uv * 2.0 - 1.0f, 0.0f, 0.0f );
return vertex;
}
struct FragOut {
vec4 color;
}
[shader("fragment")]
FragOut fragmentMain( Vertex vertex )
{
FragOut frag;
frag.color = vec4( 0.0f, 0.0f, 0.0f, 0.0f );
return frag;
}

View File

@@ -1,12 +0,0 @@
#version 450
layout(location = 0) in vec3 in_color;
#include "include/gbuffer_out.glsl"
void main() {
out_color = vec4(in_color, 1.0f);
out_normal = vec4(1.0f);
out_position = gl_FragCoord.xyz;
out_metallic = vec3(0.0, 0.0, 0.0);
}

42
src/shaders/line.slang Normal file
View File

@@ -0,0 +1,42 @@
#version 450
import camera;
import gbuffer;
struct LineVertex
{
float3 position : POSITION;
float3 color : COLOR;
};
struct VertexOut
{
float4 position : SV_Position;
float3 color;
};
[ [ vk::binding( 0, 1 ) ] ]
ParameterBlock< CameraData > camera : CAMERA;
[shader("vertex")]
VertexOut vertexMain( LineVertex vertex )
{
VertexOut out;
out.position = camera.projection * camera.view * vec4( vertex.position, 1.0 );
out.color = vertex.color;
return out;
}
[shader("fragment")]
GBufferFragment fragmentMain( VertexOut vertex )
{
GBufferFragment fragment;
fragment.color = vec4( vertex.color, 1.0f );
fragment.normal = vec4( 1.0f, 1.0f, 1.0f, 1.0f );
fragment.position = vertex.position.xyz;
fragment.metallic = vec3( 0.0f, 0.0f, 0.0f );
return fragment;
}

View File

@@ -1,14 +0,0 @@
#version 450
layout(location = 0) in vec3 in_pos;
layout(location = 1) in vec3 in_color;
#include "include/camera.glsl"
layout(location = 0) out vec3 out_color;
void main()
{
gl_Position = ubo.projection * ubo.view * vec4(in_pos, 1.0);
out_color = in_color;
}

View File

@@ -0,0 +1,71 @@
#version 450
module material;
public static const uint INVALID_TEXTURE_ID = 0;
public struct Albedo {
public uint texture_id;
public vec4 factors;
public bool isTexture()
{
return texture_id != INVALID_TEXTURE_ID;
}
};
public struct Metallic
{
public uint texture_id;
public float metallic_factor;
public float roughness_factor;
public bool isTexture()
{
return texture_id != INVALID_TEXTURE_ID;
}
}
public struct Normal
{
public uint texture_id;
public float scale;
public bool isTexture()
{
return texture_id != INVALID_TEXTURE_ID;
}
}
public struct Occlusion
{
public uint texture_id;
public float strength;
public bool isTexture()
{
return texture_id != INVALID_TEXTURE_ID;
}
}
public struct Emissive
{
public uint texture_id;
public vec3 factors;
public bool isTexture()
{
return texture_id != INVALID_TEXTURE_ID;
}
}
public struct Material
{
public Albedo color;
public Metallic metallic;
public Normal normal;
public Occlusion occlusion;
public Emissive emissive;
}

View File

@@ -0,0 +1,20 @@
#version 450
module vertex;
public struct SimpleVertex
{
public vec3 position;
public vec3 color;
public vec3 normal;
public vec2 uv;
};
public struct ModelVertex
{
public SimpleVertex simple;
public mat4x4 model_matrix;
public uint material_id;
};

View File

@@ -1,104 +0,0 @@
#version 450
#extension GL_EXT_nonuniform_qualifier: enable
#extension GL_EXT_debug_printf: enable
layout (location = 0) in vec3 in_normal;
layout (location = 1) in vec2 in_uv;
layout (location = 2) in vec3 in_world_pos;
layout (location = 3) in flat uint in_material_id;
#include "include/gbuffer_out.glsl"
layout (set = 1, binding = 0) uniform CameraInfo {
mat4 projection;
mat4 view;
mat4 inverse_view;
} camera_info;
layout (set = 2, binding = 0) uniform sampler2D tex[];
#include "include/material.glsl"
// Include all helper functions for glsl.
#include "include/pbr.glsl"
float linearDepth(float depth)
{
float z = depth * 2.0f - 1.0f;
return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE));
}
vec3 error_color = vec3(0.475, 0.0, 0.686);
float MIN_ROUGHNESS = 0.04;
void main()
{
out_position = in_world_pos;
vec3 diffuse_color;
vec4 base_color;
vec3 f0 = vec3(0.04);
uint mat_id = in_material_id;
if (mat_id == INVALID_TEXTURE_ID)
{
discard;
}
// The Vulkan-glTF-PBR example does some alpha mask stuff. I'm not exactly sure what it's used for
// So for now we will just set the base_color to be the texture color
uint color_texture_id = materials[mat_id].color_texture_id;
if (color_texture_id != INVALID_TEXTURE_ID)
{
// Load the texture and multiply it against the color factors
vec4 color = texture(tex[color_texture_id], in_uv).rgba;
vec4 factors = materials[mat_id].color_factors;
out_color = color * factors;
}
else
{
// If we have no color texture, Simply use the color factors as our color
out_color = materials[mat_id].color_factors;
}
float metallic_scalar = 0.0;
float roughness_scalar = 0.0;
uint metallic_id = materials[mat_id].metallic_texture_id;
if (metallic_id != INVALID_TEXTURE_ID)// If the metallic texture is assigned, Then we should use it. Otherwise we want to use the metallic factor
{
vec4 tex_sample = texture(tex[metallic_id], in_uv).rgba;
// r channel is for occlusion data (optional)
// g channel is for roughnes factor
// b channel is for metallic factor
metallic_scalar = tex_sample.b * materials[mat_id].metallic_factor;
roughness_scalar = tex_sample.g * materials[mat_id].roughness_factor;
}
else // Texture was not present, So we instead use the factor values as our values
{
metallic_scalar = clamp(materials[mat_id].metallic_factor, MIN_ROUGHNESS, 1.0);
roughness_scalar = clamp(materials[mat_id].roughness_factor, 0.0, 1.0);
}
// The example does some magic here that I don't understand?
// https://github.com/SaschaWillems/Vulkan-glTF-PBR/blob/master/data/shaders/material_pbr.frag#L242
float occlusion_scalar = 0.0;
out_metallic = vec3(metallic_scalar, roughness_scalar, occlusion_scalar);
const uint normal_texture_id = materials[mat_id].normal_texture_id;
if (normal_texture_id != INVALID_TEXTURE_ID)
{
vec3 tex_sample = texture(tex[normal_texture_id], in_uv).xyz;
out_normal = vec4(tex_sample * vec3(materials[mat_id].normal_scale), 1.0);
}
else
{
out_normal = vec4(in_normal, 1.0);
}
}

View File

@@ -1,27 +0,0 @@
#version 450
#extension GL_EXT_debug_printf: enable
#extension GL_GOOGLE_include_directive : enable
#include "include/vertex.glsl"
layout (location = 0) out vec3 out_normal;
layout (location = 1) out vec2 out_tex_coord;
layout (location = 2) out vec3 out_world_pos;
layout (location = 3) out flat uint out_material_id;
#include "include/camera.glsl"
void main() {
vec4 position_world = instance_model_matrix * vec4(position, 1.0);
gl_Position = ubo.projection * ubo.view * position_world;
out_world_pos = vec3(position_world);
mat3 normal_matrix = transpose(inverse(mat3(instance_model_matrix)));
out_normal = normalize(normal_matrix * normal);
out_tex_coord = uv;
out_material_id = in_material_id;
}

106
src/shaders/textured.slang Normal file
View File

@@ -0,0 +1,106 @@
#version 450
import model.vertex;
import camera;
import gbuffer;
import material;
struct Vertex {
vec3 normal;
vec2 tex_coord;
vec3 world_pos;
uint material_id;
}
[ [ vk::binding( 0, 1 ) ] ]
ParameterBlock< CameraData > camera : CAMERA;
[shader("vertex")]
Vertex vertexMain( ModelVertex in_vertex )
{
Vertex out_vertex;
vec4 world_pos = in_vertex.model_matrix * vec4(in_vertex.simple.position, 1.0);
out_vertex.world_pos = (camera.projection * camera.view * world_pos).xyz;
mat3 normal_matrix = transpose( inverse( mat3( in_vertex.model_matrix ) ) );
out_vertex.normal = normalize( normal_matrix * in_vertex.simple.normal );
out_vertex.tex_coord = in_vertex.simple.uv;
out_vertex.material_id = in_vertex.material_id;
return out_vertex;
}
[[vk::binding(0,3)]]
ParameterBlock<Material> materials[] : MATERIALS;
[[vk::binding(0,2)]]
Sampler2D[ ] tex : TEXTURES;
static const float MIN_ROUGHNESS = 0.04;
[shader("fragment")]
GBufferFragment fragmentMain( Vertex vertex : Vertex )
{
GBufferFragment frag;
frag.position = vertex.world_pos;
vec3 diffuse_color;
vec4 base_color;
vec3 f0 = vec3( 0.04 );
if ( vertex.material_id == INVALID_TEXTURE_ID )
{
discard;
}
Material material = materials[vertex.material_id];
if ( material.color.isTexture() )
{
vec4 color = texture( tex[ material.color.texture_id ], vertex.tex_coord );
vec4 factors = material.color.factors;
frag.color = color * factors;
}
else
{
frag.color = material.color.factors;
}
float metallic_scalar = 0.0;
float roughness_scalar = 0.0;
var metallic = material.metallic;
if ( metallic.isTexture() )
{
vec4 sample = texture( tex[ metallic.texture_id ], vertex.tex_coord );
metallic_scalar = sample.b * metallic.metallic_factor;
roughness_scalar = sample.g * metallic.roughness_factor;
}
else
{
metallic_scalar = clamp( metallic.roughness_factor, MIN_ROUGHNESS, 1.0 );
roughness_scalar = clamp( metallic.roughness_factor, 0.0, 1.0 );
}
float occlusion_scalar = 0.0;
frag.metallic = vec3( metallic_scalar, roughness_scalar, occlusion_scalar );
var normal = material.normal;
if ( normal.isTexture() )
{
vec3 sample = texture( tex[ normal.texture_id ], vertex.tex_coord ).rgb;
frag.normal = vec4( sample * vec3( normal.scale ), 1.0 );
}
else
{
frag.normal = vec4( vertex.normal, 1.0 );
}
return frag;
}

View File

@@ -1,37 +0,0 @@
#version 450
#extension GL_EXT_nonuniform_qualifier: enable
#extension GL_EXT_debug_printf: enable
layout (location = 0) in vec3 in_normal;
layout (location = 1) in vec2 in_tex_coord;
layout (location = 2) in vec3 in_world_pos;
layout (location = 3) in vec3 in_color;
layout (location = 0) out vec4 out_position;
layout (location = 1) out vec4 out_normal;
layout (location = 2) out vec4 out_albedo;
layout (set = 1, binding = 0) uniform CameraInfo {
mat4 projection;
mat4 view;
mat4 inverse_view;
} ubo;
float linearDepth(float depth)
{
float z = depth * 2.0f - 1.0f;
return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE));
}
void main()
{
out_position = vec4(in_world_pos, 1.0f);
vec3 N = normalize(in_normal);
out_normal = vec4(N, 1.0);
out_position.a = linearDepth(out_position.z);
out_albedo = vec4(in_color, 1.0);
}

View File

@@ -1,31 +0,0 @@
#version 450
#extension GL_EXT_debug_printf: enable
#extension GL_GOOGLE_include_directive : enable
#include "include/vertex.glsl"
layout (location = 0) out vec3 out_normal;
layout (location = 1) out vec2 out_tex_coord;
layout (location = 2) out vec3 out_world_pos;
layout (location = 3) out vec3 out_color;
layout (set = 1, binding = 0) uniform CameraInfo {
mat4 projection;
mat4 view;
mat4 inverse_view;
} ubo;
void main() {
vec4 position_world = instance_model_matrix * vec4(position, 1.0);
gl_Position = ubo.projection * ubo.view * position_world;
out_world_pos = vec3(gl_Position);
mat3 normal_matrix = transpose(inverse(mat3(instance_model_matrix)));
out_normal = normalize(normal_matrix * normal);
out_tex_coord = uv;
out_color = color;
}

1
vk.env Normal file
View File

@@ -0,0 +1 @@
VK_SDK_PATH=/home/kj16609/Desktop/Projects/sdks/1.3.304.1/x86_64