capi: basic capi for presets and gl

This commit is contained in:
chyyran 2022-12-03 18:32:10 -05:00
parent f92dc5cae6
commit b569de1522
30 changed files with 966 additions and 59 deletions

292
Cargo.lock generated
View file

@ -8,6 +8,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "auto_ops"
version = "0.3.0"
@ -71,6 +82,25 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cbindgen"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.0.73"
@ -86,6 +116,30 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"indexmap",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "cmake"
version = "0.1.48"
@ -159,6 +213,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "cty"
version = "0.2.2"
@ -186,6 +250,15 @@ dependencies = [
"threadpool",
]
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "fixedbitset"
version = "0.4.2"
@ -258,6 +331,17 @@ dependencies = [
"auto_ops",
]
[[package]]
name = "ghost"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb19fe8de3ea0920d282f7b77dd4227aea6b8b999b42cdf0ca41b2472b14443a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "gif"
version = "0.11.4"
@ -325,6 +409,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -363,6 +453,43 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "inventory"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb5160c60ba1e809707918ee329adb99d222888155835c6feedba19f6c3fd4"
dependencies = [
"ctor",
"ghost",
"inventory-impl",
]
[[package]]
name = "inventory-impl"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e41b53715c6f0c4be49510bb82dee2c1e51c8586d885abe65396e82ed518548"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "jobserver"
version = "0.1.25"
@ -421,6 +548,18 @@ dependencies = [
"librashader-runtime-gl",
]
[[package]]
name = "librashader-capi"
version = "0.1.0-alpha.1"
dependencies = [
"cbindgen",
"gl",
"librashader",
"paste",
"safer-ffi",
"thiserror",
]
[[package]]
name = "librashader-common"
version = "0.1.0-alpha.1"
@ -557,6 +696,21 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mini_paste"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2499b7bd9834270bf24cfc4dd96be59020ba6fd7f3276b772aee2de66e82b63"
dependencies = [
"mini_paste-proc_macro",
]
[[package]]
name = "mini_paste-proc_macro"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c5f1f52e39b728e73af4b454f1b29173d4544607bd395dafe1918fd149db67"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -684,6 +838,18 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "paste"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]]
name = "petgraph"
version = "0.6.2"
@ -786,6 +952,24 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "roxmltree"
version = "0.14.1"
@ -821,6 +1005,30 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "safer-ffi"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c710243617290d86a49a30564d94d0b646eacf6d7b67035e20d6e8a21f1193"
dependencies = [
"inventory",
"libc",
"mini_paste",
"safer_ffi-proc_macro",
]
[[package]]
name = "safer_ffi-proc_macro"
version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc02dc034daa0944eb133b448f261ef7422ccae768e30f30ce5cdeb4ae4e506c"
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
@ -833,6 +1041,37 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "shaderc"
version = "0.8.0"
@ -890,6 +1129,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.102"
@ -901,6 +1146,35 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.37"
@ -941,6 +1215,15 @@ dependencies = [
"weezl",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
@ -1035,6 +1318,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View file

@ -8,6 +8,7 @@ members = [
"librashader-runtime",
"librashader-runtime-d3d11",
"librashader-runtime-gl",
"librashader-capi",
]
[workspace.metadata.release]

View file

@ -0,0 +1,30 @@
[package]
name = "librashader-capi"
edition = "2021"
license = "MPL-2.0 OR GPL-3.0-only"
version = "0.1.0-alpha.1"
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
repository = "https://github.com/SnowflakePowered/librashader"
readme = "../README.md"
categories = ["emulators", "compilers", "graphics"]
keywords = ["shader", "retroarch", "SPIR-V"]
description = "RetroArch shaders for all."
[lib]
crate-type = [ "cdylib", "staticlib", "lib" ]
[features]
#default = ["runtime-opengl"]
#runtime-opengl = ["gl", "librashader/gl"]
headers = ["safer-ffi/headers"]
[dependencies]
librashader = { path = "../librashader", version = "0.1.0-alpha.1", features = ["gl"] }
thiserror = "1.0.37"
paste = "1.0.9"
gl = { version = "0.14.0" }
safer-ffi = { version = "0.0.10", repository = "https://github.com/getditto/safer_ffi" }
[build-dependencies]
cbindgen = "0.24.3"

19
librashader-capi/build.rs Normal file
View file

@ -0,0 +1,19 @@
use std::env;
use std::fs::File;
use std::io::{BufWriter, Write};
pub fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let mut buf = BufWriter::new(Vec::new());
cbindgen::generate(crate_dir)
.expect("Unable to generate bindings")
.write(&mut buf);
let bytes = buf.into_inner().expect("Unable to extract bytes");
let string = String::from_utf8(bytes).expect("Unable to create string");
// let string = string.replace("CHD_ERROR_", "CHDERR_");
File::create("librashader.h")
.expect("Unable to open file")
.write_all(string.as_bytes())
.expect("Unable to write bindings.")
}

View file

@ -0,0 +1,41 @@
language = "C"
cpp_compat = true
include_guard = "__LIBRASHADER_H__"
pragma_once = true
usize_is_size_t = true
[parse]
parse_deps = true
include = ["librashader",
"librashader-presets",
"librashader-preprocess",
"librashader-reflect",
"librashader-runtime-gl"
]
[struct]
[enum]
rename_variants = "ScreamingSnakeCase"
prefix_with_name = true
[export]
include = [
"PFN_lbr_load_preset",
"PFN_lbr_preset_free",
"PFN_lbr_preset_set_param",
"PFN_lbr_preset_get_param",
"PFN_lbr_preset_print",
"PFN_lbr_preset_get_runtime_param_names",
"GLFilterChain"
]
#exclude = ["LibrashaderError"]
#
[export.rename]
"LibrashaderError" = "_libra_error"
"ShaderPreset" = "_shader_preset"
"GLFilterChain" = "_filter_chain_gl"
"GLFilterChainOptions" = "filter_chain_gl_opt_t"

View file

@ -0,0 +1,3 @@
fn main() -> ::std::io::Result<()> {
::librashader_capi::generate_headers()
}

View file

@ -0,0 +1,116 @@
#ifndef __LIBRASHADER_H__
#define __LIBRASHADER_H__
#pragma once
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct FilterChain FilterChain;
typedef struct _libra_error _libra_error;
/**
* A shader preset including all specified parameters, textures, and paths to specified shaders.
*
* A shader preset can be used to create a filter chain runtime instance, or reflected to get
* parameter metadata.
*/
typedef struct _shader_preset _shader_preset;
typedef const struct _libra_error *libra_error_t;
typedef struct _shader_preset *libra_shader_preset_t;
typedef const void *(*gl_loader_t)(const char*);
typedef struct FilterChainOptions {
uint16_t gl_version;
bool use_dsa;
} FilterChainOptions;
typedef struct FilterChain *libra_gl_filter_chain_t;
/**
* Load a preset.
*/
typedef libra_error_t (*PFN_lbr_load_preset)(const char*, libra_shader_preset_t*);
typedef libra_error_t (*PFN_lbr_preset_free)(libra_shader_preset_t*);
typedef libra_error_t (*PFN_lbr_preset_set_param)(libra_shader_preset_t*, const char*, float);
typedef libra_error_t (*PFN_lbr_preset_get_param)(libra_shader_preset_t*, const char*, float*);
typedef libra_error_t (*PFN_lbr_preset_print)(libra_shader_preset_t*);
typedef libra_error_t (*PFN_lbr_preset_get_runtime_param_names)(libra_shader_preset_t*, float*);
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
libra_error_t libra_load_preset(const char *filename, libra_shader_preset_t *out);
/**
* Free the preset.
*/
libra_error_t libra_preset_free(libra_shader_preset_t *preset);
/**
* Set the value of the parameter in the preset.
*/
libra_error_t libra_preset_set_param(libra_shader_preset_t *preset, const char *name, float value);
/**
* Get the value of the parameter as set in the preset.
*/
libra_error_t libra_preset_get_param(libra_shader_preset_t *preset, const char *name, float *value);
/**
* Pretty print the shader preset.
*/
libra_error_t libra_preset_print(libra_shader_preset_t *preset);
/**
* Get a list of runtime parameter names.
*
* The returned value can not currently be freed.
*/
libra_error_t libra_preset_get_runtime_param_names(libra_shader_preset_t *preset,
const char **value);
/**
* Initialize the OpenGL Context for librashader.
*
* ## Safety
* Attempting to create a filter chain before initializing the GL context is undefined behaviour.
*
* Reinitializing the OpenGL context with a different loader immediately invalidates previous filter
* chain objects, and drawing with them causes immediate undefined behaviour.
*/
libra_error_t libra_gl_init_context(gl_loader_t loader);
/**
* Create the filter chain given the shader preset.
*
* The shader preset is immediately invalidated and must be recreated after
* the filter chain is created.
*
* ## Safety:
* - `preset` must be either null, or valid and aligned.
* - `options` must be either null, or valid and aligned.
* - `out` may be either null or uninitialized, but must be aligned.
*/
libra_error_t libra_gl_create_filter_chain(libra_shader_preset_t *preset,
const struct FilterChainOptions *options,
libra_gl_filter_chain_t *out);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __LIBRASHADER_H__ */

View file

@ -0,0 +1,9 @@
use std::mem::ManuallyDrop;
use librashader::presets::ShaderPreset;
use crate::error::LibrashaderError;
pub type libra_shader_preset_t = ManuallyDrop<Option<Box<ShaderPreset>>>;
pub type libra_error_t = *const LibrashaderError;
// #[cfg(feature = "runtime-opengl")]
pub type libra_gl_filter_chain_t = ManuallyDrop<Option<Box<librashader::runtime::gl::FilterChainGL>>>;

View file

@ -0,0 +1,54 @@
use std::any::Any;
use thiserror::Error;
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum LibrashaderError {
#[error("The parameter was null or invalid.")]
InvalidParameter(&'static str),
#[error("The path was invalid.")]
InvalidPath(#[from] std::str::Utf8Error),
#[error("There was an error parsing the preset.")]
PresetError(#[from] librashader::presets::ParsePresetError),
#[error("There was an error preprocessing the shader source.")]
PreprocessError(#[from] librashader::preprocess::PreprocessError),
// #[cfg(feature = "runtime-opengl")]
#[error("There was an error in the OpenGL filter chain.")]
OpenGlFilterError(#[from] librashader::runtime::gl::error::FilterChainError),
#[error("There was an unknown error.")]
UnknownError(Box<dyn Any + Send + 'static>)
}
impl LibrashaderError {
pub const fn ok() -> libra_error_t {
std::ptr::null()
}
pub fn panic(panic: Box<dyn Any + Send + 'static>) -> libra_error_t {
LibrashaderError::UnknownError(panic).export()
}
pub fn export(self) -> libra_error_t {
Box::into_raw(Box::new(self))
}
}
macro_rules! assert_non_null {
($value:ident) => {
if $value.is_null() {
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export()
}
}
}
macro_rules! assert_some {
($value:ident) => {
if $value.is_none() {
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export()
}
}
}
pub(crate) use assert_non_null;
pub(crate) use assert_some;
use crate::ctypes::libra_error_t;

View file

@ -0,0 +1,77 @@
macro_rules! ffi_body {
($body:block) => {
{
let result: Result<(), $crate::error::LibrashaderError> = try {
$body
};
let Err(e) = result else {
return $crate::error::LibrashaderError::ok()
};
e.export()
}
};
(|$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
{
$($crate::error::assert_non_null!($ref_capture);)*
$(let $ref_capture = unsafe { &*$ref_capture };)*
$($crate::error::assert_non_null!($mut_capture);)*
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
let result: Result<(), $crate::error::LibrashaderError> = try {
$body
};
let Err(e) = result else {
return $crate::error::LibrashaderError::ok()
};
e.export()
}
};
(mut |$($mut_capture:ident),*| $body:block) => {
{
$($crate::error::assert_non_null!($mut_capture);)*
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
let result: Result<(), $crate::error::LibrashaderError> = try {
$body
};
let Err(e) = result else {
return $crate::error::LibrashaderError::ok()
};
e.export()
}
};
(|$($ref_capture:ident),*| $body:block) => {
{
$($crate::error::assert_non_null!($ref_capture);)*
$(let $ref_capture = unsafe { &*$ref_capture };)*
let result: Result<(), $crate::error::LibrashaderError> = try {
$body
};
let Err(e) = result else {
return $crate::error::LibrashaderError::ok()
};
e.export()
}
}
}
macro_rules! extern_fn {
($(#[$($attrss:tt)*])* fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),*) $body:block) => {
paste::paste! {
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t;
}
#[no_mangle]
$(#[$($attrss)*])*
fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
$crate::ffi::ffi_body!($body)
}
}
}
pub(crate) use extern_fn;
pub(crate) use ffi_body;

View file

@ -0,0 +1,21 @@
#![allow(non_camel_case_types)]
#![feature(try_blocks)]
#![feature(vec_into_raw_parts)]
#![deny(unsafe_op_in_unsafe_fn)]
use std::os::raw::c_char;
pub mod presets;
pub mod runtime;
pub mod error;
pub mod ctypes;
mod ffi;
pub type PK_s = unsafe extern "C" fn(filename: *const c_char);
#[cfg(feature = "headers")] // c.f. the `Cargo.toml` section
pub fn generate_headers() -> ::std::io::Result<()> {
::safer_ffi::headers::builder()
.to_file("librashader.h")?
.generate()
}

View file

@ -0,0 +1,149 @@
use std::ffi::{c_char, CStr, CString, OsStr};
use std::mem::{ManuallyDrop, MaybeUninit};
use std::panic::catch_unwind;
use std::path::Path;
use librashader::presets::ShaderPreset;
use crate::ffi::ffi_body;
use crate::ctypes::{libra_error_t, libra_shader_preset_t};
use crate::error::{assert_non_null, assert_some, LibrashaderError};
use safer_ffi::prelude::*;
use safer_ffi::ffi_export;
use safer_ffi::char_p::char_p_ref as CStrRef;
// extern_fn! {
// /// SAFETY:
// /// - filename is aligned and valid for reads.
// fn load_preset(filename: *const c_char, out: *mut MaybeUninit<shader_preset_t>) {
// assert_non_null!(filename, "filename");
// assert_non_null!(out, "out");
//
// let filename = unsafe {
// CStr::from_ptr(filename)
// };
//
// let filename = filename.to_str()?;
//
// println!("loading {filename}");
// let preset = ShaderPreset::try_parse(filename)?;
//
// unsafe {
// out.write(MaybeUninit::new(ManuallyDrop::new(Box::new(preset))))
// }
// }
// }
/// Load a preset.
pub type PFN_lbr_load_preset = unsafe extern "C" fn (*const c_char, *mut MaybeUninit<libra_shader_preset_t>) -> libra_error_t;
#[no_mangle]
pub unsafe extern "C" fn libra_load_preset(filename: *const c_char, out: *mut MaybeUninit<libra_shader_preset_t>) -> libra_error_t {
ffi_body!({
assert_non_null!(filename);
assert_non_null!(out);
let filename = unsafe {
CStr::from_ptr(filename)
};
let filename = filename.to_str()?;
println!("loading {filename}");
let preset = ShaderPreset::try_parse(filename)?;
unsafe {
out.write(MaybeUninit::new(ManuallyDrop::new(Some(Box::new(preset)))))
}
})
}
pub type PFN_lbr_preset_free = unsafe extern "C" fn (*mut libra_shader_preset_t) -> libra_error_t;
/// Free the preset.
#[no_mangle]
pub unsafe extern "C" fn libra_preset_free(preset: *mut libra_shader_preset_t) -> libra_error_t {
ffi_body!({
assert_non_null!(preset);
unsafe {
let preset_ptr = &mut *preset;
ManuallyDrop::drop(preset_ptr);
preset.write(ManuallyDrop::new(None));
}
})
}
pub type PFN_lbr_preset_set_param = unsafe extern "C" fn (*mut libra_shader_preset_t, *const c_char, f32) -> libra_error_t;
/// Set the value of the parameter in the preset.
#[no_mangle]
pub unsafe extern "C" fn libra_preset_set_param(preset: *mut libra_shader_preset_t,
name: *const c_char, value: f32) -> libra_error_t {
ffi_body!(|name|; mut |preset| {
let name = unsafe {
CStr::from_ptr(name)
};
let name = name.to_str()?;
assert_some!(preset);
let preset = preset.as_mut().unwrap();
if let Some(param) = preset.parameters.iter_mut().find(|c| c.name == name) {
param.value = value
}
})
}
pub type PFN_lbr_preset_get_param = unsafe extern "C" fn (*mut libra_shader_preset_t, *const c_char, *mut MaybeUninit<f32>) -> libra_error_t;
/// Get the value of the parameter as set in the preset.
#[no_mangle]
pub unsafe extern "C" fn libra_preset_get_param(preset: *mut libra_shader_preset_t,
name: *const c_char, value: *mut MaybeUninit<f32>) -> libra_error_t {
ffi_body!(|name, preset | {
let name = unsafe {
CStr::from_ptr(name)
};
let name = name.to_str()?;
assert_some!(preset);
let preset = preset.as_ref().unwrap();
if let Some(param) = preset.parameters.iter().find(|c| c.name == name) {
unsafe {
value.write(MaybeUninit::new(param.value))
}
}
})
}
pub type PFN_lbr_preset_print = unsafe extern "C" fn (*mut libra_shader_preset_t) -> libra_error_t;
/// Pretty print the shader preset.
#[no_mangle]
pub unsafe extern "C" fn libra_preset_print(preset: *mut libra_shader_preset_t) -> libra_error_t {
ffi_body!(|preset| {
assert_some!(preset);
println!("{:#?}", preset.as_ref().unwrap());
})
}
pub type PFN_lbr_preset_get_runtime_param_names = unsafe extern "C" fn (*mut libra_shader_preset_t, *mut MaybeUninit<f32>) -> libra_error_t;
/// Get a list of runtime parameter names.
///
/// The returned value can not currently be freed.
#[no_mangle]
pub unsafe extern "C" fn libra_preset_get_runtime_param_names(preset: *mut libra_shader_preset_t, mut value: MaybeUninit<*mut *const c_char>) -> libra_error_t {
ffi_body!(|preset | {
assert_some!(preset);
let preset = preset.as_ref().unwrap();
let iter = librashader::presets::get_parameter_meta(preset)?;
let mut c_strings = Vec::new();
for param in iter {
let c_string = CString::new(param.id).map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
c_strings.push(c_string.into_raw().cast_const());
}
let (parts, _len, _cap) = c_strings.into_raw_parts();
value.write(parts);
})
}

View file

View file

@ -0,0 +1,61 @@
use std::ffi::{c_char, c_void, CString};
use std::mem::MaybeUninit;
use crate::ctypes::{libra_error_t, libra_gl_filter_chain_t, libra_shader_preset_t};
use crate::error::{assert_non_null, assert_some, LibrashaderError};
use crate::ffi::ffi_body;
use std::mem::ManuallyDrop;
pub use librashader::runtime::gl::options::FilterChainOptionsGL;
pub type gl_loader_t = unsafe extern "C" fn (*const c_char) -> *const c_void;
/// Initialize the OpenGL Context for librashader.
///
/// ## Safety
/// Attempting to create a filter chain before initializing the GL context is undefined behaviour.
///
/// Reinitializing the OpenGL context with a different loader immediately invalidates previous filter
/// chain objects, and drawing with them causes immediate undefined behaviour.
#[no_mangle]
pub unsafe extern "C" fn libra_gl_init_context(loader: gl_loader_t) -> libra_error_t {
gl::load_with(|s| {
unsafe {
let proc_name = CString::new(s).unwrap_unchecked();
loader(proc_name.as_ptr())
}
});
LibrashaderError::ok()
}
/// Create the filter chain given the shader preset.
///
/// The shader preset is immediately invalidated and must be recreated after
/// the filter chain is created.
///
/// ## Safety:
/// - `preset` must be either null, or valid and aligned.
/// - `options` must be either null, or valid and aligned.
/// - `out` may be either null or uninitialized, but must be aligned.
#[no_mangle]
pub unsafe extern "C" fn libra_gl_create_filter_chain(preset: *mut libra_shader_preset_t,
options: *const FilterChainOptionsGL,
out: *mut MaybeUninit<libra_gl_filter_chain_t>) -> libra_error_t {
ffi_body!({
assert_non_null!(preset);
let preset_ptr = unsafe {
&mut *preset
};
assert_some!(preset_ptr);
let preset = preset_ptr.take().unwrap();
let options = if options.is_null() {
None
} else {
Some(unsafe { &*options })
};
let chain = librashader::runtime::gl::FilterChainGL::load_from_preset(*preset, options)?;
unsafe {
out.write(MaybeUninit::new(ManuallyDrop::new(Some(Box::new(chain)))))
}
})
}

View file

@ -0,0 +1 @@
pub mod gl;

View file

@ -20,7 +20,7 @@ use std::path::Path;
use crate::error::FilterChainError;
use crate::filter_pass::{ConstantBufferBinding, FilterPass};
use crate::framebuffer::OwnedFramebuffer;
use crate::options::{FilterChainOptions, FrameOptions};
use crate::options::{FilterChainOptionsD3D11, FrameOptionsD3D11};
use crate::quad_render::DrawQuad;
use crate::render_target::RenderTarget;
use crate::samplers::SamplerSet;
@ -49,7 +49,7 @@ type ShaderPassMeta = (
>,
);
pub struct FilterChain {
pub struct FilterChainD3D11 {
pub(crate) common: FilterCommon,
pub(crate) passes: Vec<FilterPass>,
pub(crate) output_framebuffers: Box<[OwnedFramebuffer]>,
@ -75,13 +75,13 @@ pub(crate) struct FilterCommon {
pub config: FilterMutable,
}
impl FilterChain {
impl FilterChainD3D11 {
/// Load the shader preset at the given path into a filter chain.
pub fn load_from_path(
device: &ID3D11Device,
path: impl AsRef<Path>,
options: Option<&FilterChainOptions>,
) -> error::Result<FilterChain> {
options: Option<&FilterChainOptionsD3D11>,
) -> error::Result<FilterChainD3D11> {
// load passes from preset
let preset = ShaderPreset::try_parse(path)?;
Self::load_from_preset(device, preset, options)
@ -91,16 +91,16 @@ impl FilterChain {
pub fn load_from_preset(
device: &ID3D11Device,
preset: ShaderPreset,
options: Option<&FilterChainOptions>,
) -> error::Result<FilterChain> {
let (passes, semantics) = FilterChain::load_preset(preset.shaders, &preset.textures)?;
options: Option<&FilterChainOptionsD3D11>,
) -> error::Result<FilterChainD3D11> {
let (passes, semantics) = FilterChainD3D11::load_preset(preset.shaders, &preset.textures)?;
let use_deferred_context = options.map(|f| f.use_deferred_context).unwrap_or(false);
let samplers = SamplerSet::new(device)?;
// initialize passes
let filters = FilterChain::init_passes(device, passes, &semantics)?;
let filters = FilterChainD3D11::init_passes(device, passes, &semantics)?;
let mut immediate_context = None;
unsafe {
@ -155,17 +155,17 @@ impl FilterChain {
feedback_textures.resize_with(filters.len(), || None);
// load luts
let luts = FilterChain::load_luts(device, &current_context, &preset.textures)?;
let luts = FilterChainD3D11::load_luts(device, &current_context, &preset.textures)?;
let (history_framebuffers, history_textures) =
FilterChain::init_history(device,
&current_context,
&filters)?;
FilterChainD3D11::init_history(device,
&current_context,
&filters)?;
let draw_quad = DrawQuad::new(device, &current_context)?;
// todo: make vbo: d3d11.c 1376
Ok(FilterChain {
Ok(FilterChainD3D11 {
passes: filters,
output_framebuffers: output_framebuffers.into_boxed_slice(),
feedback_framebuffers: feedback_framebuffers.into_boxed_slice(),
@ -196,7 +196,7 @@ impl FilterChain {
}
}
impl FilterChain {
impl FilterChainD3D11 {
fn create_constant_buffer(device: &ID3D11Device, size: u32) -> error::Result<ID3D11Buffer> {
unsafe {
let buffer = device.CreateBuffer(
@ -249,7 +249,7 @@ impl FilterChain {
)?;
let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 {
let buffer = FilterChain::create_constant_buffer(device, ubo.size)?;
let buffer = FilterChainD3D11::create_constant_buffer(device, ubo.size)?;
Some(ConstantBufferBinding {
binding: ubo.binding,
size: ubo.size,
@ -261,7 +261,7 @@ impl FilterChain {
};
let push_cbuffer = if let Some(push) = &reflection.push_constant && push.size != 0 {
let buffer = FilterChain::create_constant_buffer(device, push.size)?;
let buffer = FilterChainD3D11::create_constant_buffer(device, push.size)?;
Some(ConstantBufferBinding {
binding: if ubo_cbuffer.is_some() { 1 } else { 0 },
size: push.size,
@ -470,7 +470,7 @@ impl FilterChain {
input: DxImageView,
viewport: &Viewport,
frame_count: usize,
options: Option<&FrameOptions>,
options: Option<&FrameOptionsD3D11>,
) -> error::Result<()> {
let passes = &mut self.passes[0..self.common.config.passes_enabled];
if let Some(options) = options {
@ -607,11 +607,11 @@ impl FilterChain {
}
}
impl librashader_runtime::filter_chain::FilterChain for FilterChain {
impl librashader_runtime::filter_chain::FilterChain for FilterChainD3D11 {
type Error = FilterChainError;
type Input<'a> = DxImageView;
type Viewport<'a> = Viewport<'a>;
type FrameOptions = FrameOptions;
type FrameOptions = FrameOptionsD3D11;
fn frame<'a>(&mut self, input: Self::Input<'a>, viewport: &Self::Viewport<'a>, frame_count: usize, options: Option<&Self::FrameOptions>) -> Result<(), Self::Error> {
self.frame(input, viewport, frame_count, options)

View file

@ -225,9 +225,9 @@ pub mod d3d11_hello_triangle {
use super::*;
use std::path::Path;
use crate::filter_chain::FilterChain;
use crate::filter_chain::FilterChainD3D11;
use crate::options::FilterChainOptions;
use crate::options::FilterChainOptionsD3D11;
use crate::texture::DxImageView;
use crate::viewport::Viewport;
use librashader_common::Size;
@ -239,7 +239,7 @@ pub mod d3d11_hello_triangle {
pub device: ID3D11Device,
pub context: ID3D11DeviceContext,
pub resources: Option<Resources>,
pub filter: FilterChain,
pub filter: FilterChainD3D11,
}
pub struct Resources {
@ -266,10 +266,10 @@ pub mod d3d11_hello_triangle {
impl Sample {
pub(crate) fn new(
filter: impl AsRef<Path>,
filter_options: Option<&FilterChainOptions>,
filter_options: Option<&FilterChainOptionsD3D11>,
) -> Result<Self> {
let (dxgi_factory, device, context) = create_device()?;
let filter = FilterChain::load_from_path(&device, filter, filter_options).unwrap();
let filter = FilterChainD3D11::load_from_path(&device, filter, filter_options).unwrap();
Ok(Sample {
filter,
dxgi_factory,

View file

@ -17,7 +17,7 @@ mod util;
mod viewport;
mod parameters;
pub use filter_chain::FilterChain;
pub use filter_chain::FilterChainD3D11;
pub use viewport::Viewport;
pub use texture::DxImageView;

View file

@ -1,11 +1,11 @@
#[repr(C)]
#[derive(Debug, Clone)]
pub struct FrameOptions {
pub struct FrameOptionsD3D11 {
pub clear_history: bool,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct FilterChainOptions {
pub struct FilterChainOptionsD3D11 {
pub use_deferred_context: bool,
}

View file

@ -1,8 +1,8 @@
use std::collections::hash_map::Iter;
use librashader_runtime::parameters::FilterChainParameters;
use crate::FilterChain;
use crate::FilterChainD3D11;
impl FilterChainParameters for FilterChain {
impl FilterChainParameters for FilterChainD3D11 {
fn get_enabled_pass_count(&self) -> usize {
self.common.config.passes_enabled
}

View file

@ -16,7 +16,7 @@ use crate::binding::{BufferStorage, UniformLocation, VariableLocation};
use crate::error::FilterChainError;
use crate::filter_pass::FilterPass;
use crate::gl::{DrawQuad, Framebuffer, FramebufferInterface, GLInterface, LoadLut, UboRing};
use crate::options::{FilterChainOptions, FrameOptions};
use crate::options::{FilterChainOptionsGL, FrameOptionsGL};
use crate::render_target::RenderTarget;
use crate::samplers::SamplerSet;
use crate::texture::Texture;
@ -87,7 +87,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
/// Load a filter chain from a pre-parsed `ShaderPreset`.
pub(crate) fn load_from_preset(
preset: ShaderPreset,
options: Option<&FilterChainOptions>,
options: Option<&FilterChainOptionsGL>,
) -> error::Result<Self> {
let (passes, semantics) = Self::load_preset(preset.shaders, &preset.textures)?;
@ -415,7 +415,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
count: usize,
viewport: &Viewport,
input: &GLImage,
options: Option<&FrameOptions>,
options: Option<&FrameOptionsGL>,
) -> error::Result<()> {
// limit number of passes to those enabled.
let passes = &mut self.passes[0..self.common.config.passes_enabled];

View file

@ -5,7 +5,7 @@ use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch;
use crate::{GLImage, Viewport};
use crate::error::{Result, FilterChainError};
use crate::options::{FilterChainOptions, FrameOptions};
use crate::options::{FilterChainOptionsGL, FrameOptionsGL};
mod filter_impl;
mod inner;
@ -13,14 +13,14 @@ mod parameters;
pub(crate) use filter_impl::FilterCommon;
pub struct FilterChain {
pub struct FilterChainGL {
pub(in crate::filter_chain) filter: FilterChainDispatch,
}
impl FilterChain {
impl FilterChainGL {
pub fn load_from_preset(
preset: ShaderPreset,
options: Option<&FilterChainOptions>,
options: Option<&FilterChainOptionsGL>,
) -> Result<Self> {
if let Some(options) = options && options.use_dsa {
return Ok(Self {
@ -37,7 +37,7 @@ impl FilterChain {
/// Load the shader preset at the given path into a filter chain.
pub fn load_from_path(
path: impl AsRef<Path>,
options: Option<&FilterChainOptions>,
options: Option<&FilterChainOptionsGL>,
) -> Result<Self> {
// load passes from preset
let preset = ShaderPreset::try_parse(path)?;
@ -52,7 +52,7 @@ impl FilterChain {
input: &GLImage,
viewport: &Viewport,
frame_count: usize,
options: Option<&FrameOptions>,
options: Option<&FrameOptionsGL>,
) -> Result<()> {
match &mut self.filter {
FilterChainDispatch::DirectStateAccess(p) => {
@ -63,11 +63,11 @@ impl FilterChain {
}
}
impl librashader_runtime::filter_chain::FilterChain for FilterChain {
impl librashader_runtime::filter_chain::FilterChain for FilterChainGL {
type Error = FilterChainError;
type Input<'a> = &'a GLImage;
type Viewport<'a> = Viewport<'a>;
type FrameOptions = FrameOptions;
type FrameOptions = FrameOptionsGL;
fn frame<'a>(
&mut self,

View file

@ -2,7 +2,7 @@ use std::collections::hash_map::Iter;
use librashader_runtime::parameters::FilterChainParameters;
use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch;
use crate::FilterChain;
use crate::FilterChainGL;
use crate::gl::GLInterface;
impl AsRef<dyn FilterChainParameters + 'static> for FilterChainDispatch {
@ -23,7 +23,7 @@ impl AsMut<dyn FilterChainParameters + 'static> for FilterChainDispatch {
}
}
impl FilterChainParameters for FilterChain {
impl FilterChainParameters for FilterChainGL {
fn get_enabled_pass_count(&self) -> usize {
self.filter.as_ref().get_enabled_pass_count()
}

View file

@ -7,7 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use librashader_common::Size;
use crate::filter_chain::FilterChain;
use crate::filter_chain::FilterChainGL;
use crate::framebuffer::GLImage;
use crate::gl::gl3::CompatibilityGL;
use crate::gl::{FramebufferInterface, GLInterface};
@ -268,7 +268,7 @@ pub fn do_loop(
events: Receiver<(f64, WindowEvent)>,
triangle_program: GLuint,
triangle_vao: GLuint,
filter: &mut FilterChain,
filter: &mut FilterChainGL,
) {
let mut framecount = 0;
let mut rendered_framebuffer = 0;

View file

@ -7,7 +7,7 @@ use glfw::{Context, Glfw, Window, WindowEvent};
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use librashader_common::Size;
use crate::filter_chain::FilterChain;
use crate::filter_chain::FilterChainGL;
use crate::framebuffer::GLImage;
use crate::gl::gl46::DirectStateAccessGL;
use crate::gl::{FramebufferInterface, GLInterface};
@ -259,7 +259,7 @@ pub fn do_loop(
events: Receiver<(f64, WindowEvent)>,
triangle_program: GLuint,
triangle_vao: GLuint,
filter: &mut FilterChain,
filter: &mut FilterChainGL,
) {
let mut framecount = 0;
let mut rendered_framebuffer = 0;

View file

@ -17,22 +17,22 @@ pub mod error;
pub mod options;
mod viewport;
pub use filter_chain::FilterChain;
pub use filter_chain::FilterChainGL;
pub use framebuffer::GLImage;
pub use viewport::Viewport;
#[cfg(test)]
mod tests {
use super::*;
use crate::filter_chain::FilterChain;
use crate::options::FilterChainOptions;
use crate::filter_chain::FilterChainGL;
use crate::options::FilterChainOptionsGL;
#[test]
fn triangle_gl() {
let (glfw, window, events, shader, vao) = gl::gl3::hello_triangle::setup();
let mut filter = FilterChain::load_from_path(
let mut filter = FilterChainGL::load_from_path(
"../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp",
Some(&FilterChainOptions {
Some(&FilterChainOptionsGL {
gl_version: 0,
use_dsa: false,
}),
@ -45,9 +45,9 @@ mod tests {
#[test]
fn triangle_gl46() {
let (glfw, window, events, shader, vao) = gl::gl46::hello_triangle::setup();
let mut filter = FilterChain::load_from_path(
let mut filter = FilterChainGL::load_from_path(
"../test/slang-shaders/vhs/VHSPro.slangp",
Some(&FilterChainOptions {
Some(&FilterChainOptionsGL {
gl_version: 0,
use_dsa: true,
}),

View file

@ -1,12 +1,12 @@
#[repr(C)]
#[derive(Debug, Clone)]
pub struct FrameOptions {
pub struct FrameOptionsGL {
pub clear_history: bool,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct FilterChainOptions {
pub struct FilterChainOptionsGL {
pub gl_version: u16,
pub use_dsa: bool,
}

22
librashader.h Normal file
View file

@ -0,0 +1,22 @@
/*! \file */
/*******************************************
* *
* File auto-generated by `::safer_ffi`. *
* *
* Do not manually edit this file. *
* *
*******************************************/
#ifndef __RUST_LIBRASHADER_CAPI__
#define __RUST_LIBRASHADER_CAPI__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __RUST_LIBRASHADER_CAPI__ */

View file

@ -23,5 +23,10 @@ librashader-runtime-gl = { path = "../librashader-runtime-gl", version = "0.1.0-
[features]
gl = [ "librashader-common/opengl" ]
d3d11 = [ "librashader-common/d3d11" ]
default = ["gl", "d3d11", "reflect", "preprocess", "presets" ]
gl = [ "runtime", "librashader-common/opengl" ]
d3d11 = [ "runtime", "librashader-common/d3d11" ]
runtime = []
reflect = []
preprocess = []
presets = []

View file

@ -3,6 +3,7 @@
//! Runtime implementations should depend directly on constituent crates.
#[cfg(feature = "presets")]
/// Parsing and usage of shader presets.
///
/// Shader presets contain shader and texture parameters, and the order in which to apply a set of shaders
@ -10,7 +11,6 @@
pub mod presets {
pub use librashader_presets::*;
use librashader_preprocess::{PreprocessError, ShaderParameter, ShaderSource};
use librashader_presets::ShaderPreset;
/// Get full parameter metadata from a shader preset.
pub fn get_parameter_meta(
preset: &ShaderPreset,
@ -26,6 +26,7 @@ pub mod presets {
}
}
#[cfg(feature = "preprocess")]
/// Loading and preprocessing of 'slang' shader source files.
///
/// Shader sources files must be loaded with imports resolved before being able to be compiled.
@ -34,6 +35,7 @@ pub mod preprocess {
pub use librashader_preprocess::*;
}
#[cfg(feature = "reflect")]
/// Shader compilation and reflection.
pub mod reflect {
/// Supported shader compiler targets.
@ -58,20 +60,24 @@ pub mod reflect {
}
/// Shader runtimes to execute a filter chain on a GPU surface.
#[cfg(feature = "runtime")]
pub mod runtime {
pub use librashader_runtime::parameters::FilterChainParameters;
pub use librashader_runtime::filter_chain::FilterChain;
#[cfg(feature = "gl")]
/// Shader runtime for OpenGL 3.3+.
pub mod gl {
pub use librashader_runtime_gl::*;
}
#[cfg(feature = "d3d11")]
/// Shader runtime for Direct3D11
pub mod d3d11 {
pub use librashader_runtime_d3d11::*;
}
#[cfg(feature = "vk")]
/// Shader compiler targets and runtime for Vulkan.
pub mod vk {