diff --git a/librashader-capi/Cargo.toml b/librashader-capi/Cargo.toml index 24f1c0f..6bc76b7 100644 --- a/librashader-capi/Cargo.toml +++ b/librashader-capi/Cargo.toml @@ -17,14 +17,14 @@ crate-type = [ "cdylib", "staticlib", "lib" ] [features] #default = ["runtime-opengl"] -#runtime-opengl = ["gl", "librashader/gl"] +runtime-opengl = ["gl", "librashader/gl"] headers = ["safer-ffi/headers"] [dependencies] -librashader = { path = "../librashader", version = "0.1.0-alpha.2", features = ["gl"] } +librashader = { path = "../librashader", version = "0.1.0-alpha.2" } thiserror = "1.0.37" paste = "1.0.9" -gl = { version = "0.14.0" } +gl = { version = "0.14.0", optional = true } safer-ffi = { version = "0.0.10", repository = "https://github.com/getditto/safer_ffi" } [build-dependencies] diff --git a/librashader-capi/cbindgen.toml b/librashader-capi/cbindgen.toml index 8096adc..5a13671 100644 --- a/librashader-capi/cbindgen.toml +++ b/librashader-capi/cbindgen.toml @@ -3,7 +3,7 @@ cpp_compat = true include_guard = "__LIBRASHADER_H__" pragma_once = true usize_is_size_t = true - +documentation_style = "c++" [parse] parse_deps = true include = ["librashader", @@ -22,13 +22,16 @@ prefix_with_name = true [export] include = [ + # preset "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", - "FilterChain" + + # error + "PFN_lbr_error_code", ] @@ -39,3 +42,4 @@ include = [ "ShaderPreset" = "_shader_preset" "FilterChainGL" = "_filter_chain_gl" "FilterChainOptionsGL" = "filter_chain_gl_opt_t" +"FrameOptionsGL" = "frame_gl_opt_t" diff --git a/librashader-capi/librashader.h b/librashader-capi/librashader.h index ba8dcb2..beaa6e2 100644 --- a/librashader-capi/librashader.h +++ b/librashader-capi/librashader.h @@ -9,22 +9,39 @@ #include #include +/// Error codes for librashader error types. +enum LIBRA_ERRNO +#ifdef __cplusplus + : int32_t +#endif // __cplusplus + { + LIBRA_ERRNO_UNKNOWN_ERROR = 0, + LIBRA_ERRNO_INVALID_PARAMETER = 1, + LIBRA_ERRNO_INVALID_PATH = 2, + LIBRA_ERRNO_PRESET_ERROR = 3, + LIBRA_ERRNO_PREPROCESS_ERROR = 4, + LIBRA_ERRNO_RUNTIME_ERROR = 5, +}; +#ifndef __cplusplus +typedef int32_t LIBRA_ERRNO; +#endif // __cplusplus + typedef struct _filter_chain_gl _filter_chain_gl; +/// The error type for librashader. 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. - */ +/// 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 _libra_error *libra_error_t; typedef struct _shader_preset *libra_shader_preset_t; +/// A GL function loader that librashader needs to be initialized with. typedef const void *(*gl_loader_t)(const char*); typedef struct filter_chain_gl_opt_t { @@ -34,13 +51,19 @@ typedef struct filter_chain_gl_opt_t { typedef struct _filter_chain_gl *libra_gl_filter_chain_t; +/// OpenGL parameters for the source image. typedef struct libra_source_image_gl_t { + /// A texture GLuint to the source image. uint32_t handle; + /// The format of the source image. uint32_t format; + /// The width of the source image. uint32_t width; + /// The height of the source image. uint32_t height; } libra_source_image_gl_t; +/// Parameters for the output viewport. typedef struct libra_viewport_t { float x; float y; @@ -48,17 +71,21 @@ typedef struct libra_viewport_t { uint32_t height; } libra_viewport_t; +/// OpenGL parameters for the output framebuffer. typedef struct libra_draw_framebuffer_gl_t { + /// A framebuffer GLuint to the output framebuffer. uint32_t handle; + /// A texture GLuint to the logical buffer of the output framebuffer. uint32_t texture; + /// The format of the output framebuffer. uint32_t format; - uint32_t width; - uint32_t height; } libra_draw_framebuffer_gl_t; -/** - * Load a preset. - */ +typedef struct frame_gl_opt_t { + bool clear_history; + int32_t frame_direction; +} frame_gl_opt_t; + 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*); @@ -75,71 +102,122 @@ typedef libra_error_t (*PFN_lbr_preset_get_runtime_param_names)(libra_shader_pre extern "C" { #endif // __cplusplus -libra_error_t libra_load_preset(const char *filename, libra_shader_preset_t *out); +/// Load a preset. +/// +/// ## Safety +/// - `filename` must be either null or a valid, aligned pointer to a string path to the shader preset. +/// - `out` must be either null, or an aligned pointer to an uninitialized or invalid `libra_shader_preset_t`. +/// ## Returns +/// - If any parameters are null, `out` is unchanged, and this function returns `LIBRA_ERR_INVALID_PARAMETER`. +libra_error_t libra_load_preset(const char *filename, + libra_shader_preset_t *out); -/** - * Free the preset. - */ +/// Free the preset. +/// +/// If `preset` is null, this function does nothing. The resulting value in `preset` then becomes +/// null. libra_error_t libra_preset_free(libra_shader_preset_t *preset); -/** - * Set the value of the parameter in the 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. - */ +/// 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. - */ +/// 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. - */ +/// 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 will fail. - * - * Reinitializing the OpenGL context with a different loader immediately invalidates previous filter - * chain objects, and drawing with them causes immediate undefined behaviour. - */ +/// Initialize the OpenGL Context for librashader. +/// +/// ## Safety +/// Attempting to create a filter chain will fail. +/// +/// 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. - */ +/// 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` must be aligned, but may be null, invalid, or uninitialized. libra_error_t libra_gl_filter_chain_create(libra_shader_preset_t *preset, const struct filter_chain_gl_opt_t *options, libra_gl_filter_chain_t *out); +/// Draw a frame with the given parameters for the given filter chain. +/// +/// ## Safety +/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this +/// function will return an error. +/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float` +/// values for the model view projection matrix. +/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t` +/// struct. libra_error_t libra_gl_filter_chain_frame(libra_gl_filter_chain_t *chain, size_t frame_count, struct libra_source_image_gl_t image, struct libra_viewport_t viewport, struct libra_draw_framebuffer_gl_t out, - const float *mvp); + const float *mvp, + const struct frame_gl_opt_t *opt); +/// Free a GL filter chain. +/// +/// The resulting value in `chain` then becomes null. +/// ## Safety +/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`. libra_error_t libra_gl_filter_chain_free(libra_gl_filter_chain_t *chain); +/// Get the error code corresponding to this error object. +/// +/// ## Safety +/// - `error` must be valid and initialized. +LIBRA_ERRNO libra_error_errno(libra_error_t error); + +/// Print the error message. +/// +/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0. +/// ## Safety +/// - `error` must be a valid and initialized instance of `libra_error_t`. +int32_t libra_error_print(libra_error_t error); + +/// Frees any internal state kept by the error. +/// +/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0. +/// The resulting error object becomes null. +/// ## Safety +/// - `error` must be null or a pointer to a valid and initialized instance of `libra_error_t`. +int32_t libra_error_free(libra_error_t *error); + +/// Writes the error message into `out` +/// +/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0. +/// ## Safety +/// - `error` must be a valid and initialized instance of `libra_error_t`. +/// - `out` must be a non-null pointer. The resulting string must not be modified. +int32_t libra_error_write(libra_error_t error, + char **out); + +/// Frees an error string previously allocated by `libra_error_write`. +/// +/// After freeing, the pointer will be set to null. +/// ## Safety +/// - If `libra_error_write` is not null, it must point to a string previously returned by `libra_error_write`. +/// Attempting to free anything else, including strings or objects from other librashader functions, is immediate +/// Undefined Behaviour. +int32_t libra_error_free_string(char **out); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/librashader-capi/src/ctypes.rs b/librashader-capi/src/ctypes.rs index de49a19..ae85cbc 100644 --- a/librashader-capi/src/ctypes.rs +++ b/librashader-capi/src/ctypes.rs @@ -3,11 +3,12 @@ use librashader::presets::ShaderPreset; use crate::error::LibrashaderError; pub type libra_shader_preset_t = Option>; -pub type libra_error_t = *const LibrashaderError; +pub type libra_error_t = Option>; -// #[cfg(feature = "runtime-opengl")] +#[cfg(feature = "runtime-opengl")] pub type libra_gl_filter_chain_t = Option>; +/// Parameters for the output viewport. #[repr(C)] pub struct libra_viewport_t { pub x: f32, diff --git a/librashader-capi/src/lib.rs b/librashader-capi/src/lib.rs index d48eb6d..b96bb24 100644 --- a/librashader-capi/src/lib.rs +++ b/librashader-capi/src/lib.rs @@ -1,3 +1,29 @@ +//! The C API for [librashader](https://docs.rs/librashader/). +//! +//! The librashader C API is designed to be loaded dynamically via `librashader_ld.h`, but static usage is also +//! possible by linking against `librashader.h` as well as any static libraries used by `librashader`. +//! +//! ## Usage +//! ⚠ Rust consumers should take a look at [librashader](https://docs.rs/librashader/) ⚠ +//! +//! The C API is designed to be easy to use and safe. Most objects are only accessible behind an opaque pointer. +//! Every allocated object can be freed with a corresponding `free` function **for that specific object type**. +//! +//! Once an object is freed, the input pointer is always set to null. Attempting to free an object that was not +//! allocated from `librashader` or trying to free an object with a wrong `free` function results in +//! immediate **undefined behaviour**. +//! +//! In general, all functions will accept null pointers for all parameters. However, passing a null pointer +//! into any function that requires a non-null pointer will result in the function returning an error with code `INVALID_PARAMETER`. +//! +//! All types that begin with an underscore, such as `_libra_error` or `_shader_preset` are handles that +//! can not be constructed validly, and should always be used with pointer indirection via the corresponding `_t` types. +//! +//! ## Errors +//! The librashader C API provides a robust, reflective error system. Every function returns a `libra_error_t`, which is either +//! a null pointer, or a handle to an opaque allocated error object. If the returned error is null, then the function was successful. +//! Otherwise, error information can be accessed via the `libra_error_` set of APIs. If an error indeed occurs, it may be freed by +//! `libra_error_free`. #![allow(non_camel_case_types)] #![feature(try_blocks)] #![feature(vec_into_raw_parts)] @@ -11,8 +37,7 @@ pub mod error; pub mod ctypes; mod ffi; -pub type PK_s = unsafe extern "C" fn(filename: *const c_char); - +#[doc(hide)] #[cfg(feature = "headers")] // c.f. the `Cargo.toml` section pub fn generate_headers() -> ::std::io::Result<()> { ::safer_ffi::headers::builder() diff --git a/librashader-capi/src/presets.rs b/librashader-capi/src/presets.rs index 4901ccf..c1c65d2 100644 --- a/librashader-capi/src/presets.rs +++ b/librashader-capi/src/presets.rs @@ -32,8 +32,15 @@ use std::ptr::NonNull; // } // } -/// Load a preset. pub type PFN_lbr_load_preset = unsafe extern "C" fn (*const c_char, *mut MaybeUninit) -> libra_error_t; + +/// Load a preset. +/// +/// ## Safety +/// - `filename` must be either null or a valid, aligned pointer to a string path to the shader preset. +/// - `out` must be either null, or an aligned pointer to an uninitialized or invalid `libra_shader_preset_t`. +/// ## Returns +/// - If any parameters are null, `out` is unchanged, and this function returns `LIBRA_ERR_INVALID_PARAMETER`. #[no_mangle] pub unsafe extern "C" fn libra_load_preset(filename: *const c_char, out: *mut MaybeUninit) -> libra_error_t { ffi_body!({ @@ -58,6 +65,9 @@ pub unsafe extern "C" fn libra_load_preset(filename: *const c_char, out: *mut Ma pub type PFN_lbr_preset_free = unsafe extern "C" fn (*mut libra_shader_preset_t) -> libra_error_t; /// Free the preset. +/// +/// If `preset` is null, this function does nothing. The resulting value in `preset` then becomes +/// null. #[no_mangle] pub unsafe extern "C" fn libra_preset_free(preset: *mut libra_shader_preset_t) -> libra_error_t { ffi_body!({ @@ -117,8 +127,8 @@ pub type PFN_lbr_preset_print = unsafe extern "C" fn (*mut libra_shader_preset_t #[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()); + assert_some_ptr!(preset); + println!("{:#?}", preset); }) } diff --git a/librashader-capi/src/runtime/gl.rs b/librashader-capi/src/runtime/gl/filter_chain.rs similarity index 68% rename from librashader-capi/src/runtime/gl.rs rename to librashader-capi/src/runtime/gl/filter_chain.rs index 40db655..6b5d69c 100644 --- a/librashader-capi/src/runtime/gl.rs +++ b/librashader-capi/src/runtime/gl/filter_chain.rs @@ -4,13 +4,17 @@ use crate::ctypes::{libra_error_t, libra_gl_filter_chain_t, libra_shader_preset_ use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError}; use crate::ffi::ffi_body; use std::ptr::NonNull; +use std::slice; use librashader::runtime::FilterChain; use librashader::runtime::gl::{GLImage, Viewport}; pub use librashader::runtime::gl::options::FilterChainOptionsGL; +pub use librashader::runtime::gl::options::FrameOptionsGL; use librashader::Size; +/// A GL function loader that librashader needs to be initialized with. pub type gl_loader_t = unsafe extern "C" fn (*const c_char) -> *const c_void; + /// Initialize the OpenGL Context for librashader. /// /// ## Safety @@ -38,7 +42,7 @@ pub unsafe extern "C" fn libra_gl_init_context(loader: gl_loader_t) -> libra_err /// ## 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. +/// - `out` must be aligned, but may be null, invalid, or uninitialized. #[no_mangle] pub unsafe extern "C" fn libra_gl_filter_chain_create(preset: *mut libra_shader_preset_t, options: *const FilterChainOptionsGL, @@ -66,21 +70,28 @@ pub unsafe extern "C" fn libra_gl_filter_chain_create(preset: *mut libra_shader_ }) } +/// OpenGL parameters for the source image. #[repr(C)] pub struct libra_source_image_gl_t { + /// A texture GLuint to the source image. pub handle: u32, + /// The format of the source image. pub format: u32, + /// The width of the source image. pub width: u32, + /// The height of the source image. pub height: u32 } +/// OpenGL parameters for the output framebuffer. #[repr(C)] pub struct libra_draw_framebuffer_gl_t { + /// A framebuffer GLuint to the output framebuffer. pub handle: u32, + /// A texture GLuint to the logical buffer of the output framebuffer. pub texture: u32, + /// The format of the output framebuffer. pub format: u32, - pub width: u32, - pub height: u32 } impl From for GLImage { @@ -94,6 +105,15 @@ impl From for GLImage { } } +/// Draw a frame with the given parameters for the given filter chain. +/// +/// ## Safety +/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this +/// function will return an error. +/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float` +/// values for the model view projection matrix. +/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t` +/// struct. #[no_mangle] pub unsafe extern "C" fn libra_gl_filter_chain_frame(chain: *mut libra_gl_filter_chain_t, frame_count: usize, @@ -101,23 +121,41 @@ pub unsafe extern "C" fn libra_gl_filter_chain_frame(chain: *mut libra_gl_filter viewport: libra_viewport_t, out: libra_draw_framebuffer_gl_t, mvp: *const f32, + opt: *const FrameOptionsGL, ) -> libra_error_t { ffi_body!(mut |chain| { assert_some_ptr!(mut chain); let image: GLImage = image.into(); + let mvp = if mvp.is_null() { + None + } else { + Some(<&[f32; 16]>::try_from(unsafe { slice::from_raw_parts(mvp, 16) }).unwrap()) + }; + + let opt = if opt.is_null() { + None + } else { + Some(unsafe { opt.read() }) + }; + let viewport = Viewport { x: viewport.x, y: viewport.y, - output: &chain.create_framebuffer_raw(out.texture, out.handle, out.format, Size::new(out.width, out.height), 1), - mvp: None, + output: &chain.create_framebuffer_raw(out.texture, out.handle, out.format, Size::new(viewport.width, viewport.height), 1), + mvp, }; - chain.frame(&image, &viewport, frame_count, None)?; + chain.frame(&image, &viewport, frame_count, opt.as_ref())?; }) } +/// Free a GL filter chain. +/// +/// The resulting value in `chain` then becomes null. +/// ## Safety +/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`. #[no_mangle] pub unsafe extern "C" fn libra_gl_filter_chain_free(chain: *mut libra_gl_filter_chain_t) -> libra_error_t { ffi_body!({ diff --git a/librashader-capi/src/runtime/gl/mod.rs b/librashader-capi/src/runtime/gl/mod.rs new file mode 100644 index 0000000..d28780f --- /dev/null +++ b/librashader-capi/src/runtime/gl/mod.rs @@ -0,0 +1 @@ +pub mod filter_chain; diff --git a/librashader-capi/src/runtime/mod.rs b/librashader-capi/src/runtime/mod.rs index 7eea004..2f89d9f 100644 --- a/librashader-capi/src/runtime/mod.rs +++ b/librashader-capi/src/runtime/mod.rs @@ -1 +1,2 @@ -pub mod gl; \ No newline at end of file +#[cfg(feature = "runtime-opengl")] +mod gl; \ No newline at end of file diff --git a/librashader/src/lib.rs b/librashader/src/lib.rs index bab228c..2fe4c4e 100644 --- a/librashader/src/lib.rs +++ b/librashader/src/lib.rs @@ -1,7 +1,33 @@ -//! Re-exports for usage of librashader in consuming libraries. +#![forbid(missing_docs)] +//! RetroArch shader preset compiler and runtime. +//! +//! librashader provides convenient and safe access to RetroArch ['slang' shaders](https://github.com/libretro/slang-shaders). +//! The preset parser, shader preprocessor, and shader runtimes have all been reimplemented in Rust to provide easy access to +//! the rich library of shaders. +//! +//! ## Usage +//! The core objects in librashader are the [`ShaderPreset`](crate::presets::ShaderPreset) and the +//! [`FilterChain`](crate::runtime::FilterChain), the implementation of which is runtime dependent. +//! +//! The basic workflow involves parsing a `ShaderPreset`, which can then be used to construct +//! a `FilterChain`. All shaders will then be compiled, after which `FilterChain::frame` can be +//! called with appropriate input and output parameters to draw a frame with the shader effect applied. +//! +//! ## Runtimes +//! Currently available runtimes are OpenGL 3.3+ and 4.6 (with DSA), and Direct3D 11. +//! Work on the Vulkan and Direct3D 12 runtimes are in progress. +//! +//! | **API** | **Status** | **`librashader` feature** | +//! |-------------|------------|---------------------------| +//! | OpenGL 3.3+ | ✔ | `gl` | +//! | OpenGL 4.6 | ✔ | `gl` | +//! | Vulkan | 🚧 | `vk` | +//! | Direct3D 11 | ✔ | `d3d11` | +//! | Direct3D 12 | 🚧 | `d3d12` | +//! | OpenGL 2 | ❌ | | +//! | DirectX 9 | ❌ | | +//! | Metal | ❌ | | //! -//! Runtime implementations should depend directly on constituent crates. - #[cfg(feature = "presets")] /// Parsing and usage of shader presets. @@ -67,6 +93,9 @@ pub mod runtime { #[cfg(feature = "gl")] /// Shader runtime for OpenGL 3.3+. + /// + /// Note that the OpenGL runtime requires `gl` to be + /// initialized with [`gl::load_with`](https://docs.rs/gl/0.14.0/gl/fn.load_with.html). pub mod gl { pub use librashader_runtime_gl::*; } diff --git a/test/capi-tests/librashader-capi-tests/librashader-capi-tests/librashader-capi-tests.cpp b/test/capi-tests/librashader-capi-tests/librashader-capi-tests/librashader-capi-tests.cpp index 8dc210a..caec15e 100644 --- a/test/capi-tests/librashader-capi-tests/librashader-capi-tests/librashader-capi-tests.cpp +++ b/test/capi-tests/librashader-capi-tests/librashader-capi-tests/librashader-capi-tests.cpp @@ -16,9 +16,15 @@ int main() } libra_preset_print(&preset); libra_gl_filter_chain_t chain; - error = libra_gl_create_filter_chain(&preset, NULL, &chain); + + error = libra_gl_filter_chain_create(NULL, NULL, &chain); if (error != NULL) { - std::cout << "error happened\n"; + libra_error_print(error); + char* error_str; + libra_error_write(error, &error_str); + printf("%s", error_str); + libra_error_free_string(&error_str); + printf("%s", error_str); } return 0; }