It's just not worth it.

This commit is contained in:
Bradlee Speice 2024-12-30 16:35:33 -05:00
parent 32e66fd29c
commit d4c3c0147c
7 changed files with 334 additions and 29 deletions

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
/.cache
/.idea /.idea
/target /build
/cmake-build-* /cmake-build-*
/target

View File

@ -1,39 +1,38 @@
# NOTE: This file is _not_ used as part of the main build process. # NOTE: This file is _not_ used as part of the main build process.
# It exists to help IDE tools provide auto-hinting while developing the C++ bridge # It exists to help IDE tools provide auto-hinting while developing the C++ bridge
cmake_minimum_required(VERSION 3.22) cmake_minimum_required(VERSION 3.22)
project(slang_compiler) project(slang-tools)
find_program(CXXBRIDGE cxxbridge) find_program(CXXBRIDGE cxxbridge)
set(SLANG_COMPILER_CXXBRIDGE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge) set(SLANG_TOOLS_CXXBRIDGE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge)
# Generate the Rust bridge stubs header # Generate the Rust bridge stubs header
# This is used by the C++ header to define an interface for Rust # This is used by the C++ header while defining the interface for Rust
file(MAKE_DIRECTORY ${SLANG_COMPILER_CXXBRIDGE_DIR}/rust) file(MAKE_DIRECTORY ${SLANG_TOOLS_CXXBRIDGE_DIR}/rust)
set(SLANG_COMPILER_CXXBRIDGE_RUST ${SLANG_COMPILER_CXXBRIDGE_DIR}/rust/cxx.h) set(SLANG_TOOLS_CXXBRIDGE_RUST ${SLANG_TOOLS_CXXBRIDGE_DIR}/rust/cxx.h)
add_custom_command( add_custom_command(
OUTPUT ${SLANG_COMPILER_CXXBRIDGE_RUST} OUTPUT ${SLANG_TOOLS_CXXBRIDGE_RUST}
COMMAND ${CXXBRIDGE} --header -o ${SLANG_COMPILER_CXXBRIDGE_RUST} COMMAND ${CXXBRIDGE} --header -o ${SLANG_TOOLS_CXXBRIDGE_RUST}
) )
# Generate the Rust bridge header # Generate the Rust bridge header
# This is used by the C++ implementation to interact with Rust # This is used by the C++ implementation to interact with Rust
file(MAKE_DIRECTORY ${SLANG_COMPILER_CXXBRIDGE_DIR}/slang-compiler-sys/src) file(MAKE_DIRECTORY ${SLANG_TOOLS_CXXBRIDGE_DIR}/slang-tools/src)
set(SLANG_COMPILER_CXXBRIDGE_INPUT ${CMAKE_CURRENT_LIST_DIR}/src/lib.rs) set(SLANG_TOOLS_CXXBRIDGE_INPUT ${CMAKE_CURRENT_LIST_DIR}/src/lib.rs)
set(SLANG_COMPILER_CXXBRIDGE_OUTPUT ${SLANG_COMPILER_CXXBRIDGE_DIR}/slang-compiler-sys/src/lib.rs.h) set(SLANG_TOOLS_CXXBRIDGE_OUTPUT ${SLANG_TOOLS_CXXBRIDGE_DIR}/slang-tools/src/lib.rs.h)
add_custom_command( add_custom_command(
OUTPUT ${SLANG_COMPILER_CXXBRIDGE_OUTPUT} OUTPUT ${SLANG_TOOLS_CXXBRIDGE_OUTPUT}
COMMAND ${CXXBRIDGE} ${SLANG_COMPILER_CXXBRIDGE_INPUT} --header -o ${SLANG_COMPILER_CXXBRIDGE_OUTPUT} COMMAND ${CXXBRIDGE} ${SLANG_TOOLS_CXXBRIDGE_INPUT} --header -o ${SLANG_TOOLS_CXXBRIDGE_OUTPUT}
DEPENDS ${SLANG_COMPILER_CXXBRIDGE_INPUT} DEPENDS ${SLANG_TOOLS_CXXBRIDGE_INPUT}
) )
add_custom_target(slang_compiler_bridge DEPENDS ${SLANG_COMPILER_CXXBRIDGE_RUST} ${SLANG_COMPILER_CXXBRIDGE_OUTPUT}) add_custom_target(slang_tools_bridge DEPENDS ${SLANG_TOOLS_CXXBRIDGE_RUST} ${SLANG_TOOLS_CXXBRIDGE_OUTPUT})
add_library(slang_tools "src/lib.cpp")
add_library(slang_compiler "src/lib.cpp")
# Find the bridge headers # Find the bridge headers
add_dependencies(slang_compiler slang_compiler_bridge) add_dependencies(slang_tools slang_tools_bridge)
target_include_directories(slang_compiler PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src ${SLANG_COMPILER_CXXBRIDGE_DIR}) target_include_directories(slang_tools PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src ${SLANG_TOOLS_CXXBRIDGE_DIR})
# Find the slang headers # Find the slang headers
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/slang" EXCLUDE_FROM_ALL) add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/slang" EXCLUDE_FROM_ALL)
target_link_libraries(slang_compiler PRIVATE slang) target_link_libraries(slang_tools PRIVATE slang)

2
Cargo.lock generated
View File

@ -173,7 +173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "slang-compiler-sys" name = "slang-tools"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cmake", "cmake",

View File

@ -1,5 +1,5 @@
[package] [package]
name = "slang-compiler-sys" name = "slang-tools"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -3,7 +3,33 @@
// //
#include "lib.h" #include "lib.h"
#include "slang-compiler-sys/src/lib.rs.h" #include <exception>
#include <utility>
#include "slang-tools/src/lib.rs.h"
namespace slang_compiler {
class slang_exception final : public std::exception {
public:
slang_exception(SlangResult result) : result_{result} {}
const char *what() const noexcept override { return what_.c_str(); }
private:
SlangResult result_;
std::string what_;
};
} // namespace slang_compiler
#define SLANG_TOOLS_CHECK(expr) \
[&]() { \
const SlangResult result = expr; \
if (SLANG_FAILED(result)) { \
throw slang_compiler::slang_exception(result); \
} \
}();
namespace slang_compiler { namespace slang_compiler {
@ -11,8 +37,110 @@ GlobalSession::GlobalSession() {
slang::createGlobalSession(global_session_.writeRef()); slang::createGlobalSession(global_session_.writeRef());
} }
std::unique_ptr<GlobalSession> create_global_session() { slang::IGlobalSession &GlobalSession::getSlangSession() {
return std::make_unique<GlobalSession>(); return *global_session_;
}
SlangResult GlobalSession::createSession(const slang::SessionDesc &desc,
slang::ISession **outSession) const {
return global_session_->createSession(desc, outSession);
}
std::shared_ptr<GlobalSession> create_global_session() {
return std::make_shared<GlobalSession>();
}
Session::Session(std::shared_ptr<GlobalSession> global_session,
Slang::ComPtr<slang::ISession> &&session)
: global_session_{global_session}, session_{session} {}
int64_t Session::get_loaded_module_count() const noexcept {
return session_->getLoadedModuleCount();
}
::SlangProfileID
get_slang_profile_id(slang::IGlobalSession &global_session,
const SlangProfileID_rs slang_profile_id) {
switch (slang_profile_id) {
case SlangProfileID_rs::spirv_1_0:
return global_session.findProfile("spirv_1_0");
}
return SLANG_PROFILE_UNKNOWN;
}
::SlangTargetFlags
get_slang_target_flags(const rust::Vec<SlangTargetFlags> &flags) noexcept {
uint32_t value = 0;
std::for_each(flags.cbegin(), flags.cend(), [&value](const auto &flag) {
value |= static_cast<std::underlying_type_t<SlangTargetFlags>>(flag);
});
return value;
}
std::unique_ptr<Session>
create_session(SessionDesc session_desc,
std::shared_ptr<GlobalSession> global_session) {
// The Slang session descriptor wants unowned pointers,
// so we copy the values from Rust into storage that
// survives at least through the call to `createSession`.
// Slang will maintain its own copy once as part of the session.
// I'm sure there's a better way to do this, but this works for now.
// First, convert the compiler options for each target descriptor
std::vector<std::string> targets_options_strings{};
std::vector<std::vector<slang::CompilerOptionEntry>> targets_options{};
for (const auto &target : session_desc.targets) {
std::vector<slang::CompilerOptionEntry> target_options;
for (const CompilerOptionEntry &target_option : target) {
auto& string_value_0 = targets_options_strings.emplace_back(target_option.value.string_value_0);
auto& string_value_1 = targets_options_strings.emplace_back(target_option.value.string_value_1);
auto slang_option = slang::CompilerOptionEntry{
.name = target_option.name,
.value = slang::CompilerOptionValue{
.kind = target_option.value.kind,
.intValue0 = target_option.value.int_value_0,
.intValue1 = target_option.value.int_value_1,
.stringValue0 = string_value_0.c_str(),
.stringValue1 = string_value_1.c_str()
}
};
target_options.push_back(slang_option);
}
targets_options.push_back(std::move(target_options));
}
// Second, convert the target descriptors
std::vector<slang::TargetDesc> target_descs;
for (size_t i = 0; i < session_desc.targets.size(); ++i) {
const auto& target = session_desc.targets[i];
auto& target_options = targets_options[i];
const auto target_desc = slang::TargetDesc{
.format = target.format,
.profile = get_slang_profile_id(global_session->getSlangSession(),
target.profile),
.floatingPointMode = target.floating_point_mode,
.lineDirectiveMode = target.line_directive_mode,
.forceGLSLScalarBufferLayout = target.force_glsl_scalar_buffer_layout,
.compilerOptionEntries = target_options.data(),
.compilerOptionEntryCount = static_cast<uint32_t>(target_options.size()),
};
target_descs.push_back(target_desc);
}
// Finally, build the session descriptor for slang.
const auto slang_session_desc = slang::SessionDesc{
.targets = target_descs.data(),
.targetCount = static_cast<int64_t>(target_descs.size()),
};
Slang::ComPtr<slang::ISession> session_ptr;
SLANG_TOOLS_CHECK(global_session->createSession(slang_session_desc,
session_ptr.writeRef()));
return std::make_unique<Session>(std::move(global_session),
std::move(session_ptr));
} }
} // namespace slang_compiler } // namespace slang_compiler

View File

@ -15,11 +15,27 @@ namespace slang_compiler {
class GlobalSession { class GlobalSession {
public: public:
explicit GlobalSession(); explicit GlobalSession();
[[nodiscard]] SlangResult createSession(const slang::SessionDesc& desc, slang::ISession** outSession) const;
slang::IGlobalSession& getSlangSession();
private: private:
Slang::ComPtr<slang::IGlobalSession> global_session_; Slang::ComPtr<slang::IGlobalSession> global_session_;
}; };
std::unique_ptr<GlobalSession> create_global_session(); std::shared_ptr<GlobalSession> create_global_session();
struct SessionDesc;
class Session {
public:
Session(std::shared_ptr<GlobalSession> global_session, Slang::ComPtr<slang::ISession>&& session);
[[nodiscard]] int64_t get_loaded_module_count() const noexcept;
private:
const std::shared_ptr<GlobalSession> global_session_;
Slang::ComPtr<slang::ISession> session_;
};
std::unique_ptr<Session> create_session(SessionDesc session_desc, std::shared_ptr<GlobalSession> global_session);
} // namespace slang_compiler } // namespace slang_compiler

View File

@ -1,19 +1,179 @@
use crate::ffi::{SessionDesc, SlangMatrixLayoutMode};
#[cxx::bridge(namespace = "slang_compiler")] #[cxx::bridge(namespace = "slang_compiler")]
mod ffi { mod ffi {
#[namespace = ""]
#[repr(i32)]
enum SlangCompileTarget {
SLANG_TARGET_UNKNOWN,
SLANG_TARGET_NONE,
SLANG_GLSL,
SLANG_HLSL = 5,
SLANG_SPIRV
}
// SlangProfileID is a combination of anonymous enum and preprocessor hack ultimately used
// to generate switch statements for the various supported profiles - see `slang-profile-defs.h`
// We'll define the names here and let C++ deal with mapping them to the ID
#[namespace = ""]
#[repr(u32)]
enum SlangProfileID_rs {
spirv_1_0,
}
// SlangTargetFlags is an anonymous enum in C++, use a shared enum (in the `slang_compiler` namespace)
// here as a nicer API. Also means we need to manage the values by hand instead of relying on cxx.
// https://stackoverflow.com/a/7147049
#[repr(u32)]
enum SlangTargetFlags {
SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES = 32,
SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM = 256,
SLANG_TARGET_FLAG_DUMP_IR = 512,
SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY = 1024,
}
#[namespace = ""]
#[repr(u32)]
enum SlangFloatingPointMode {
SLANG_FLOATING_POINT_MODE_DEFAULT,
SLANG_FLOATING_POINT_MODE_FAST,
SLANG_FLOATING_POINT_MODE_PRECISE,
}
#[namespace = ""]
#[repr(u32)]
enum SlangLineDirectiveMode {
SLANG_LINE_DIRECTIVE_MODE_DEFAULT,
SLANG_LINE_DIRECTIVE_MODE_NONE,
SLANG_LINE_DIRECTIVE_MODE_STANDARD,
SLANG_LINE_DIRECTIVE_MODE_GLSL,
SLANG_LINE_DIRECTIVE_MODE_SOURCE_MAP,
}
#[namespace = ""]
#[repr(u32)]
enum SlangMatrixLayoutMode {
SLANG_MATRIX_LAYOUT_MODE_UNKNOWN,
SLANG_MATRIX_LAYOUT_ROW_MAJOR,
SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
}
// Some slang enums are defined in the root namespace
#[namespace = ""]
unsafe extern "C++" {
include!("slang.h");
type SlangCompileTarget;
type SlangFloatingPointMode;
type SlangLineDirectiveMode;
type SlangMatrixLayoutMode;
}
#[namespace = "slang"]
#[repr(u32)]
enum CompilerOptionName {
MacroDefine
}
#[namespace = "slang"]
#[repr(u32)]
enum CompilerOptionValueKind {
Int,
String,
}
#[namespace = "slang"]
unsafe extern "C++" {
include!("slang.h");
type CompilerOptionName;
type CompilerOptionValueKind;
}
#[derive(Clone)]
struct CompilerOptionValue {
kind: CompilerOptionValueKind,
int_value_0: i32,
int_value_1: i32,
string_value_0: String,
string_value_1: String,
}
#[derive(Clone)]
struct CompilerOptionEntry {
name: CompilerOptionName,
value: CompilerOptionValue,
}
#[derive(Clone)]
struct TargetDesc {
format: SlangCompileTarget,
profile: SlangProfileID_rs,
flags: Vec<SlangTargetFlags>,
floating_point_mode: SlangFloatingPointMode,
line_directive_mode: SlangLineDirectiveMode,
force_glsl_scalar_buffer_layout: bool,
compiler_option_entries: Vec<CompilerOptionEntry>,
}
#[derive(Clone)]
struct PreprocessorMacroDesc {
name: String,
value: String,
}
#[derive(Clone)]
struct SessionDesc {
targets: Vec<TargetDesc>,
default_matrix_layout_mode: SlangMatrixLayoutMode,
search_paths: Vec<String>,
preprocessor_macros: Vec<PreprocessorMacroDesc>,
enable_effect_annotations: bool,
allow_glsl_syntax: bool,
compiler_option_entries: Vec<CompilerOptionEntry>,
}
unsafe extern "C++" { unsafe extern "C++" {
include!("lib.h"); include!("lib.h");
type GlobalSession; type GlobalSession;
fn create_global_session() -> UniquePtr<GlobalSession>; fn create_global_session() -> SharedPtr<GlobalSession>;
type Session;
#[must_use]
fn get_loaded_module_count(self: &Session) -> i64;
#[must_use]
fn create_session(session_desc: SessionDesc, global_session: SharedPtr<GlobalSession>) -> Result<UniquePtr<Session>>;
}
}
impl Default for SessionDesc {
fn default() -> Self {
SessionDesc {
targets: vec![],
default_matrix_layout_mode: SlangMatrixLayoutMode::SLANG_MATRIX_LAYOUT_ROW_MAJOR,
search_paths: vec![],
preprocessor_macros: vec![],
enable_effect_annotations: false,
allow_glsl_syntax: false,
compiler_option_entries: vec![],
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::ffi::create_global_session; use crate::ffi::{create_global_session, create_session};
#[test] #[test]
fn can_create_global_session() { fn can_create_global_session() {
let _global_session = create_global_session(); let _global_session = create_global_session();
} }
#[test]
fn can_create_session() {
let global_session = create_global_session();
let session = create_session(Default::default(), global_session).expect("unable to create session");
assert_eq!(session.get_loaded_module_count(), 0);
}
} }