253 lines
11 KiB
C++
253 lines
11 KiB
C++
#include "slang-compile.h"
|
|
#include "Containers/Array.h"
|
|
#include "Graphics/Descriptor.h"
|
|
#include <filesystem>
|
|
#include <fmt/core.h>
|
|
#include <iostream>
|
|
#include <slang.h>
|
|
|
|
using namespace Seele;
|
|
|
|
#define CHECK_RESULT(x) \
|
|
{ \
|
|
SlangResult r = x; \
|
|
if (r != 0) { \
|
|
throw std::runtime_error(fmt::format("Error: {0}", r)); \
|
|
} \
|
|
}
|
|
#define CHECK_DIAGNOSTICS() \
|
|
{ \
|
|
if (diagnostics) { \
|
|
std::cout << (const char*)diagnostics->getBufferPointer() << std::endl; \
|
|
abort(); \
|
|
} \
|
|
}
|
|
|
|
thread_local Slang::ComPtr<slang::IGlobalSession> globalSession;
|
|
thread_local Slang::ComPtr<slang::IComponentType> specializedComponent;
|
|
thread_local Slang::ComPtr<slang::ISession> session;
|
|
thread_local Array<std::string> entryPoints;
|
|
|
|
namespace {
|
|
std::filesystem::path getExecutableDirectory() {
|
|
std::error_code error;
|
|
auto exePath = std::filesystem::read_symlink("/proc/self/exe", error);
|
|
if (!error) {
|
|
return exePath.parent_path();
|
|
}
|
|
return std::filesystem::current_path();
|
|
}
|
|
|
|
std::filesystem::path findExistingDirectory(std::initializer_list<std::filesystem::path> candidates) {
|
|
for (const auto& candidate : candidates) {
|
|
std::error_code error;
|
|
if (std::filesystem::exists(candidate, error) && std::filesystem::is_directory(candidate, error)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool containsDownstreamCompilerArtifacts(const std::filesystem::path& directory) {
|
|
std::error_code error;
|
|
if (!std::filesystem::exists(directory, error) || !std::filesystem::is_directory(directory, error)) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(directory, error)) {
|
|
if (error || !entry.is_regular_file(error)) {
|
|
continue;
|
|
}
|
|
|
|
const auto fileName = entry.path().filename().string();
|
|
if (fileName.rfind("slang-glslang", 0) == 0 || fileName.rfind("spirv-opt", 0) == 0 || fileName.rfind("spirv-dis", 0) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool hasArtifactWithPrefix(const std::filesystem::path& directory, const char* prefix) {
|
|
std::error_code error;
|
|
if (!std::filesystem::exists(directory, error) || !std::filesystem::is_directory(directory, error)) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(directory, error)) {
|
|
if (error || !entry.is_regular_file(error)) {
|
|
continue;
|
|
}
|
|
|
|
if (entry.path().filename().string().rfind(prefix, 0) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::filesystem::path findDownstreamCompilerDirectory(std::initializer_list<std::filesystem::path> candidates) {
|
|
for (const auto& candidate : candidates) {
|
|
if (containsDownstreamCompilerArtifacts(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return findExistingDirectory(candidates);
|
|
}
|
|
|
|
void configureDownstreamCompilers(slang::IGlobalSession* session) {
|
|
const auto executableDirectory = getExecutableDirectory();
|
|
const auto currentDirectory = std::filesystem::current_path();
|
|
const auto glslangDirectory = findDownstreamCompilerDirectory({
|
|
executableDirectory,
|
|
executableDirectory / "Seele",
|
|
executableDirectory / "vcpkg_installed" / "x64-linux" / "lib",
|
|
currentDirectory / "build",
|
|
currentDirectory / "build" / "Seele",
|
|
currentDirectory / "Seele",
|
|
currentDirectory / "vcpkg_installed" / "x64-linux" / "lib",
|
|
});
|
|
|
|
if (!glslangDirectory.empty()) {
|
|
const std::string path = glslangDirectory.string();
|
|
if (hasArtifactWithPrefix(glslangDirectory, "slang-glslang") || hasArtifactWithPrefix(glslangDirectory, "libslang-glslang")) {
|
|
session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_GLSLANG, path.c_str());
|
|
}
|
|
if (hasArtifactWithPrefix(glslangDirectory, "spirv-opt")) {
|
|
session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_SPIRV_OPT, path.c_str());
|
|
}
|
|
if (hasArtifactWithPrefix(glslangDirectory, "spirv-dis")) {
|
|
session->setDownstreamCompilerPath(SLANG_PASS_THROUGH_SPIRV_DIS, path.c_str());
|
|
}
|
|
}
|
|
|
|
// Avoid optional SPIR-V downstream tools that are often unavailable in RenderDoc launch environments.
|
|
session->setDefaultDownstreamCompiler(SLANG_SOURCE_LANGUAGE_SPIRV, SLANG_PASS_THROUGH_NONE);
|
|
session->setDownstreamCompilerForTransition(SLANG_SPIRV, SLANG_SPIRV, SLANG_PASS_THROUGH_NONE);
|
|
session->setDownstreamCompilerForTransition(SLANG_SPIRV, SLANG_SPIRV_ASM, SLANG_PASS_THROUGH_NONE);
|
|
}
|
|
}
|
|
|
|
void Seele::beginCompilation(const ShaderCompilationInfo& info, SlangCompileTarget target, Gfx::PPipelineLayout layout) {
|
|
if (!globalSession) {
|
|
SlangGlobalSessionDesc sessionDesc = {};
|
|
sessionDesc.enableGLSL = true;
|
|
slang::createGlobalSession(&sessionDesc, globalSession.writeRef());
|
|
configureDownstreamCompilers(globalSession);
|
|
}
|
|
slang::SessionDesc sessionDesc;
|
|
sessionDesc.flags = 0;
|
|
Array<slang::CompilerOptionEntry> option = {
|
|
{
|
|
.name = slang::CompilerOptionName::DumpIntermediates,
|
|
.value =
|
|
{
|
|
.kind = slang::CompilerOptionValueKind::Int,
|
|
.intValue0 = info.dumpIntermediate,
|
|
},
|
|
},
|
|
{
|
|
.name = slang::CompilerOptionName::Optimization,
|
|
.value =
|
|
{
|
|
.kind = slang::CompilerOptionValueKind::Int,
|
|
.intValue0 = SLANG_OPTIMIZATION_LEVEL_NONE,
|
|
},
|
|
},
|
|
{
|
|
.name = slang::CompilerOptionName::SkipSPIRVValidation,
|
|
.value =
|
|
{
|
|
.kind = slang::CompilerOptionValueKind::Int,
|
|
.intValue0 = 1,
|
|
},
|
|
},
|
|
{
|
|
.name = slang::CompilerOptionName::SkipDownstreamLinking,
|
|
.value =
|
|
{
|
|
.kind = slang::CompilerOptionValueKind::Int,
|
|
.intValue0 = 1,
|
|
},
|
|
},
|
|
};
|
|
|
|
sessionDesc.compilerOptionEntries = option.data();
|
|
sessionDesc.compilerOptionEntryCount = (uint32)option.size();
|
|
sessionDesc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
|
|
Array<slang::PreprocessorMacroDesc> macros;
|
|
for (const auto& [key, val] : info.defines) {
|
|
macros.add(slang::PreprocessorMacroDesc{
|
|
.name = key,
|
|
.value = val,
|
|
});
|
|
}
|
|
sessionDesc.preprocessorMacroCount = macros.size();
|
|
sessionDesc.preprocessorMacros = macros.data();
|
|
slang::TargetDesc targetDesc = {};
|
|
targetDesc.format = target;
|
|
sessionDesc.targetCount = 1;
|
|
sessionDesc.targets = &targetDesc;
|
|
StaticArray<const char*, 7> searchPaths = {"shaders/", "shaders/lib/", "shaders/raytracing/", "shaders/terrain", "shaders/game", "shaders/generated/", "shaders/fluid"};
|
|
sessionDesc.searchPaths = searchPaths.data();
|
|
sessionDesc.searchPathCount = searchPaths.size();
|
|
|
|
Slang::ComPtr<slang::IBlob> diagnostics;
|
|
|
|
CHECK_RESULT(globalSession->createSession(sessionDesc, session.writeRef()));
|
|
Array<slang::IComponentType*> components;
|
|
Map<std::string, slang::IModule*> moduleMap;
|
|
for (const auto& moduleName : info.modules) {
|
|
slang::IModule* loaded = session->loadModule(moduleName.c_str(), diagnostics.writeRef());
|
|
CHECK_DIAGNOSTICS();
|
|
components.add(loaded);
|
|
moduleMap[moduleName] = loaded;
|
|
}
|
|
entryPoints.clear();
|
|
for (const auto& [name, mod] : info.entryPoints) {
|
|
entryPoints.add(name);
|
|
slang::IEntryPoint* entry;
|
|
CHECK_RESULT(moduleMap[mod]->findEntryPointByName(name.c_str(), &entry));
|
|
components.add(entry);
|
|
}
|
|
|
|
Slang::ComPtr<slang::IComponentType> moduleComposition;
|
|
session->createCompositeComponentType(components.data(), components.size(), moduleComposition.writeRef(), diagnostics.writeRef());
|
|
|
|
CHECK_DIAGNOSTICS();
|
|
|
|
Slang::ComPtr<slang::IComponentType> linkedProgram;
|
|
moduleComposition->link(linkedProgram.writeRef(), diagnostics.writeRef());
|
|
|
|
CHECK_DIAGNOSTICS();
|
|
|
|
slang::ProgramLayout* reflection = linkedProgram->getLayout(0, diagnostics.writeRef());
|
|
|
|
CHECK_DIAGNOSTICS();
|
|
|
|
Array<slang::SpecializationArg> specialization;
|
|
for (const auto& [key, value] : info.typeParameter) {
|
|
specialization.add(slang::SpecializationArg::fromType(reflection->findTypeByName(value)));
|
|
}
|
|
linkedProgram->specialize(specialization.data(), specialization.size(), specializedComponent.writeRef(), diagnostics.writeRef());
|
|
CHECK_DIAGNOSTICS();
|
|
|
|
slang::ProgramLayout* signature = specializedComponent->getLayout(0, diagnostics.writeRef());
|
|
CHECK_DIAGNOSTICS();
|
|
for (uint32 i = 0; i < signature->getParameterCount(); ++i) {
|
|
auto param = signature->getParameterByIndex(i);
|
|
layout->addMapping(param->getName(), param->getBindingIndex());
|
|
}
|
|
|
|
}
|
|
|
|
Pair<Slang::ComPtr<slang::IBlob>, std::string> Seele::generateShader(const ShaderCreateInfo& createInfo) {
|
|
Slang::ComPtr<slang::IBlob> diagnostics;
|
|
Slang::ComPtr<slang::IBlob> kernelBlob;
|
|
specializedComponent->getEntryPointCode(createInfo.entryPointIndex, 0, kernelBlob.writeRef(), diagnostics.writeRef());
|
|
CHECK_DIAGNOSTICS();
|
|
return {kernelBlob, entryPoints[createInfo.entryPointIndex]};
|
|
}
|