From d780cc2c80a433e8b3abfc4a4e618aea2b89360d Mon Sep 17 00:00:00 2001 From: Sergey Kosarevsky Date: Sat, 3 Aug 2024 02:23:15 -0600 Subject: [PATCH] Added Slang example `001_HelloTriangle_Slang.cpp` --- CMakeLists.txt | 6 +- lvk/vulkan/CMakeLists.txt | 30 +++++ samples/001_HelloTriangle_Slang.cpp | 164 ++++++++++++++++++++++++---- samples/CMakeLists.txt | 2 + 4 files changed, 176 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 941b9fe026..3b6fff4689 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ option(LVK_WITH_WAYLAND "Enable Wayland" OFF option(LVK_WITH_IMPLOT "Enable ImPlot" ON) option(LVK_WITH_OPENXR "Enable OpenXR" OFF) option(LVK_WITH_ANDROID_VALIDATION "Enable validation layers on Android" ON) +option(LVK_WITH_SLANG "Enable Slang compiler" OFF) cmake_dependent_option(LVK_WITH_VULKAN_PORTABILITY "Enable portability extension" ON "APPLE" OFF) @@ -34,8 +35,9 @@ if(LVK_WITH_SAMPLES AND NOT LVK_WITH_GLFW) endif() if(ANDROID) - message(STATUS "WARNING: LVK_WITH_GLFW and LVK_WITH_SAMPLES were set to OFF for Android") + message(STATUS "WARNING: LVK_WITH_GLFW and LVK_WITH_SLANG were set to OFF for Android") set(LVK_WITH_GLFW OFF) + set(LVK_WITH_SLANG OFF) endif() if(LVK_WITH_WAYLAND AND (ANDROID OR APPLE OR WIN32)) @@ -152,7 +154,7 @@ if(LVK_WITH_OPENXR) endif() # temporary -if(NOT LVK_USE_CUSTOM_MOLTENVK) +if(NOT LVK_USE_CUSTOM_MOLTENVK AND NOT LVK_WITH_SLANG) find_package(Vulkan REQUIRED) endif() diff --git a/lvk/vulkan/CMakeLists.txt b/lvk/vulkan/CMakeLists.txt index 6484bbc007..72fcce7962 100644 --- a/lvk/vulkan/CMakeLists.txt +++ b/lvk/vulkan/CMakeLists.txt @@ -35,6 +35,32 @@ lvk_set_folder(SPIRV "third-party/glslang") lvk_set_folder(glslang-default-resource-limits "third-party/glslang") # cmake-format: on +# slang +# cmake-format: off +if(LVK_WITH_SLANG) + set(SLANG_ENABLE_CUDA OFF CACHE BOOL "") + set(SLANG_ENABLE_OPTIX OFF CACHE BOOL "") + set(SLANG_ENABLE_NVAPI OFF CACHE BOOL "") + set(SLANG_ENABLE_XLIB OFF CACHE BOOL "") + set(SLANG_ENABLE_AFTERMATH OFF CACHE BOOL "") + set(SLANG_ENABLE_DX_ON_VK OFF CACHE BOOL "") + set(SLANG_ENABLE_GFX OFF CACHE BOOL "") + set(SLANG_ENABLE_SLANGC OFF CACHE BOOL "") + set(SLANG_ENABLE_SLANGRT ON CACHE BOOL "") + set(SLANG_ENABLE_SLANG_GLSLANG OFF CACHE BOOL "") + set(SLANG_ENABLE_TESTS OFF CACHE BOOL "") + set(SLANG_ENABLE_EXAMPLES OFF CACHE BOOL "") + set(SLANG_ENABLE_REPLAYER OFF CACHE BOOL "") + set(SLANG_ENABLE_PREBUILT_BINARIES OFF CACHE BOOL "") + add_subdirectory(${LVK_ROOT_DIR}/third-party/deps/src/slang "slang") + lvk_set_folder(compiler-core "third-party/slang") + lvk_set_folder(core "third-party/slang") + lvk_set_folder(slang "third-party/slang") + lvk_set_folder(slangd "third-party/slang") + lvk_set_folder(slang-rt "third-party/slang") +endif() +# cmake-format: on + # SPIRV-Reflect set(SPIRV_REFLECT_EXECUTABLE OFF CACHE BOOL "") set(SPIRV_REFLECT_STATIC_LIB ON CACHE BOOL "") @@ -52,6 +78,10 @@ endif() target_link_libraries(LVKVulkan PRIVATE LVKLibrary) target_link_libraries(LVKVulkan PRIVATE glslang SPIRV glslang-default-resource-limits) target_link_libraries(LVKVulkan PRIVATE spirv-reflect-static) +if(LVK_WITH_SLANG) + target_link_libraries(LVKVulkan PRIVATE slang) + target_link_libraries(LVKVulkan PRIVATE slang-rt) +endif() if(LVK_USE_CUSTOM_MOLTENVK) target_include_directories(LVKVulkan PUBLIC "${LVK_CUSTOM_MOLTENVK_PATH}/include") diff --git a/samples/001_HelloTriangle_Slang.cpp b/samples/001_HelloTriangle_Slang.cpp index df40da33c7..35b669aa9b 100644 --- a/samples/001_HelloTriangle_Slang.cpp +++ b/samples/001_HelloTriangle_Slang.cpp @@ -16,33 +16,55 @@ #include #endif -const char* codeVS = R"( -#version 460 -layout (location=0) out vec3 color; -const vec2 pos[3] = vec2[3]( - vec2(-0.6, -0.4), - vec2( 0.6, -0.4), - vec2( 0.0, 0.6) +#include +#include +#include +#include + +#include +#include +#include +#include + +const char* codeSlang = R"( +static const float2 pos[3] = float2[3]( + float2(-0.6, -0.4), + float2( 0.6, -0.4), + float2( 0.0, 0.6) ); -const vec3 col[3] = vec3[3]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) +static const float3 col[3] = float3[3]( + float3(1.0, 0.0, 0.0), + float3(0.0, 1.0, 0.0), + float3(0.0, 0.0, 1.0) ); -void main() { - gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); - color = col[gl_VertexIndex]; -} -)"; -const char* codeFS = R"( -#version 460 -layout (location=0) in vec3 color; -layout (location=0) out vec4 out_FragColor; +struct OutVertex { + float3 color; +}; -void main() { - out_FragColor = vec4(color, 1.0); +struct Fragment { + float4 color; }; + +struct VertexStageOutput { + OutVertex vertex : OutVertex; + float4 sv_position : SV_Position; +}; + +[shader("vertex")] +VertexStageOutput vertexMain(uint vertexID : SV_VertexID) { + VertexStageOutput output; + + output.vertex.color = col[vertexID]; + output.sv_position = float4(pos[vertexID], 0.0, 1.0); + + return output; +} + +[shader("fragment")] +float4 fragmentMain(OutVertex vertex : OutVertex) : SV_Target { + return float4(vertex.color, 1.0); +} )"; int width_ = 800; @@ -54,9 +76,103 @@ std::unique_ptr ctx_; lvk::Holder vert_; lvk::Holder frag_; +std::vector compileSlangToSPIRV(const char* code, lvk::ShaderStage stage) { + using namespace Slang; + + ComPtr slangGlobalSession; + if (SLANG_FAILED(slang::createGlobalSession(slangGlobalSession.writeRef()))) { + return {}; + } + + const slang::TargetDesc targetDesc = { + .format = SLANG_SPIRV, + .profile = slangGlobalSession->findProfile("spirv_1_6"), + .flags = SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY, + }; + + const slang::SessionDesc sessionDesc = { + .targets = &targetDesc, + .targetCount = 1, + }; + + ComPtr session; + if (SLANG_FAILED(slangGlobalSession->createSession(sessionDesc, session.writeRef()))) { + return {}; + } + + slang::IModule* slangModule = nullptr; + { + ComPtr diagnosticBlob; + slangModule = session->loadModuleFromSourceString("", "", code, diagnosticBlob.writeRef()); + if (diagnosticBlob) { + LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); + } + if (!slangModule) { + return {}; + } + } + + ComPtr entryPointVert; + ComPtr entryPointFrag; + slangModule->findEntryPointByName("vertexMain", entryPointVert.writeRef()); + slangModule->findEntryPointByName("fragmentMain", entryPointFrag.writeRef()); + + Slang::List componentTypes; + componentTypes.add(slangModule); + int entryPointCount = 0; + int vertexEntryPointIndex = entryPointCount++; + componentTypes.add(entryPointVert); + int fragmentEntryPointIndex = entryPointCount++; + componentTypes.add(entryPointFrag); + + ComPtr composedProgram; + { + ComPtr diagnosticBlob; + SlangResult result = session->createCompositeComponentType( + componentTypes.getBuffer(), componentTypes.getCount(), composedProgram.writeRef(), diagnosticBlob.writeRef()); + if (diagnosticBlob) { + LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); + } + if (SLANG_FAILED(result)) { + return {}; + } + } + + ComPtr spirvCode; + { + ComPtr diagnosticBlob; + const int entryPoint = stage == lvk::Stage_Vert ? vertexEntryPointIndex : fragmentEntryPointIndex; + SlangResult result = composedProgram->getEntryPointCode(entryPoint, 0, spirvCode.writeRef(), diagnosticBlob.writeRef()); + if (diagnosticBlob) { + LLOGW("%s", (const char*)diagnosticBlob->getBufferPointer()); + } + if (SLANG_FAILED(result)) { + return {}; + } + } + + const uint8_t* ptr = reinterpret_cast(spirvCode->getBufferPointer()); + + return std::vector(ptr, ptr + spirvCode->getBufferSize()); +} + +lvk::Holder slangCreateShaderModule(const char* code, + lvk::ShaderStage stage, + const char* debugName, + const bool dumpSPIRV = false) { + const std::vector spirv = compileSlangToSPIRV(code, stage); + + if (dumpSPIRV) { + std::ofstream fout("dump." + std::to_string(stage), std::ios::out | std::ios::binary); + fout.write(reinterpret_cast(spirv.data()), spirv.size()); + } + + return ctx_->createShaderModule({spirv.data(), spirv.size(), stage, debugName}); +} + void init() { - vert_ = ctx_->createShaderModule({codeVS, lvk::Stage_Vert, "Shader Module: main (vert)"}); - frag_ = ctx_->createShaderModule({codeFS, lvk::Stage_Frag, "Shader Module: main (frag)"}); + vert_ = slangCreateShaderModule(codeSlang, lvk::Stage_Vert, "Shader Module: main (vert)"); + frag_ = slangCreateShaderModule(codeSlang, lvk::Stage_Frag, "Shader Module: main (frag)"); renderPipelineState_Triangle_ = ctx_->createRenderPipeline( { diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 874a7488fd..e749090587 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -140,4 +140,6 @@ ADD_DEMO_LINK_LIBRARIES("Tiny_MeshLarge" ktx) # Slang if(LVK_WITH_SLANG) ADD_DEMO("001_HelloTriangle_Slang") + target_link_libraries(001_HelloTriangle_Slang PRIVATE slang-rt) + target_link_libraries(001_HelloTriangle_Slang PRIVATE core) endif()