librashader-capi 0.11.1

RetroArch shaders for all.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
//! librashader preset C API (`libra_preset_*`).
use crate::ctypes::{libra_preset_ctx_t, libra_shader_preset_t, LIBRA_COLOR_SPACE};
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
use crate::ffi::extern_fn;
use crate::LIBRASHADER_API_VERSION;
use librashader::presets::{PresetColorSpace, ShaderFeatures, ShaderPreset, WildcardContext};
use std::ffi::{c_char, CStr, CString};
use std::mem::MaybeUninit;
use std::ptr::NonNull;

const _: () = crate::assert_thread_safe::<ShaderPreset>();

/// A list of preset parameters.
#[repr(C)]
pub struct libra_preset_param_list_t {
    /// A pointer to the parameter
    pub parameters: *const libra_preset_param_t,
    /// The number of parameters in the list. This field
    /// is readonly, and changing it will lead to undefined
    /// behaviour on free.
    pub length: u64,
}

/// A preset parameter.
#[repr(C)]
pub struct libra_preset_param_t {
    /// The name of the parameter
    pub name: *const c_char,
    /// The description of the parameter.
    pub description: *const c_char,
    /// The initial value the parameter is set to.
    pub initial: f32,
    /// The minimum value that the parameter can be set to.
    pub minimum: f32,
    /// The maximum value that the parameter can be set to.
    pub maximum: f32,
    /// The step by which this parameter can be incremented or decremented.
    pub step: f32,
}

/// Options struct for loading shader presets.
///
/// Using this struct with `libra_preset_create_with_options` is the only way to
/// enable extended shader preset features.
#[repr(C)]
pub struct libra_preset_opt_t {
    /// The librashader API version.
    pub version: LIBRASHADER_API_VERSION,
    /// Enables `_HAS_ORIGINALASPECT_UNIFORMS` behaviour.
    ///
    /// If this is true, then `frame_options.aspect_ratio` must be set for correct behaviour of shaders
    /// that take the `AspectRatio` uniform.
    ///
    /// This is only supported on API 2 and above, otherwise this has no effect.
    pub original_aspect_uniforms: bool,
    /// Enables `_HAS_FRAMETIME_UNIFORMS` behaviour.
    ///
    /// If this is true, then `frame_options.frames_per_second` and `frame_options.frametime_delta`
    /// must be set for correct behaviour of shaders that take the `OriginalFPS` and
    /// `FrametimeDelta` uniforms.
    ///
    /// This is only supported on API 2 and above, otherwise this has no effect.
    pub frametime_uniforms: bool,
    /// Enables `_HAS_SENSOR_UNIFORMS` behaviour.
    ///
    /// If this is true, then the host should set `frame_options.gyroscope`, `frame_options.accelerometer`, and
    /// `frame_options.accelerometer_rest` must be set for correct behaviour of shaders that take the `Gyroscope`,
    /// `Accelerometer`, and `AccelerometerRest` uniforms.
    ///
    /// This is only supported on API 5 and above, otherwise this has no effect.
    pub sensor_uniforms: bool,
}

extern_fn! {
    /// Load a preset.
    ///
    /// This function is deprecated, and `libra_preset_create_with_options` should be used instead.
    /// ## 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`.
    fn libra_preset_create(
        filename: *const c_char,
        out: *mut MaybeUninit<libra_shader_preset_t>
    ) {
        assert_non_null!(filename);
        assert_non_null!(out);

        let filename = unsafe { CStr::from_ptr(filename) };
        let filename = filename.to_str()?;

        let preset = ShaderPreset::try_parse(filename, ShaderFeatures::NONE)?;
        unsafe {
            out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
                preset,
            )))))
        }
    }
}

extern_fn! {
    /// Load a preset with the given wildcard context.
    ///
    /// The wildcard context is immediately invalidated and must be recreated after
    /// the preset is created.
    ///
    /// Path information variables `PRESET_DIR` and `PRESET` will automatically be filled in.
    ///
    /// This function is deprecated, and `libra_preset_create_with_options` should be used instead.
    /// ## Safety
    ///  - `filename` must be either null or a valid, aligned pointer to a string path to the shader preset.
    ///  - `context` must be either null or a valid, aligned pointer to a initialized `libra_preset_ctx_t`.
    ///  - `context` is  invalidated after this function returns.
    ///  - `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`.
    fn libra_preset_create_with_context(
        filename: *const c_char,
        context: *mut libra_preset_ctx_t,
        out: *mut MaybeUninit<libra_shader_preset_t>
    ) {
        assert_non_null!(filename);
        assert_non_null!(context);
        assert_non_null!(out);

        let filename = unsafe { CStr::from_ptr(filename) };
        let filename = filename.to_str()?;

        let mut context = unsafe {
            let context_ptr = &mut *context;
            let context = context_ptr.take();
            Box::from_raw(context.unwrap().as_ptr())
        };

        context.add_path_defaults(filename);

        let preset = ShaderPreset::try_parse_with_context(filename, ShaderFeatures::NONE, *context)?;
        unsafe {
            out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
                preset,
            )))))
        }
    }
}

extern_fn! {
    /// Load a preset with optional options and an optional context.
    ///
    /// Both `context` and `options` may be null.
    ///
    /// If `context` is null, then a default context will be provided, and this function will not return `LIBRA_ERR_INVALID_PARAMETER`.
    /// If `options` is null, then default options will be chosen.
    ///
    /// If `context` is provided, it is immediately invalidated and must be recreated after
    /// the preset is created.
    ///
    /// ## Safety
    ///  - `filename` must be either null or a valid, aligned pointer to a string path to the shader preset.
    ///  - `context` must be either null or a valid, aligned pointer to an initialized `libra_preset_ctx_t`.
    ///  - `options` must be either null, or a valid, aligned pointer to a `libra_shader_opt_t`.
    ///    `LIBRASHADER_API_VERSION` should be set to `LIBRASHADER_CURRENT_VERSION`.
    ///  - `out` must be either null, or an aligned pointer to an uninitialized or invalid `libra_shader_preset_t`.
    ///
    /// ## Returns
    ///  - If `out` or `filename` is null, `out` is unchanged, and this function returns `LIBRA_ERR_INVALID_PARAMETER`.
    fn libra_preset_create_with_options(
        filename: *const c_char,
        context: *mut libra_preset_ctx_t,
        options: *mut MaybeUninit<libra_preset_opt_t>,
        out: *mut MaybeUninit<libra_shader_preset_t>
    ) {
        assert_non_null!(filename);
        assert_non_null!(out);

        let filename = unsafe { CStr::from_ptr(filename) };
        let filename = filename.to_str()?;

        // This control flow is like this because the wrapper makes it hard to return early..
        if options.is_null() {
            let preset = ShaderPreset::try_parse(filename, ShaderFeatures::NONE)?;
            unsafe {
                out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
                    preset,
                )))))
            }
        } else {
            // SAFETY: options is not null
            let mut options = unsafe { options.read() };
            let opt_ptr = options.as_mut_ptr();

            let api_version = unsafe { (&raw mut (*opt_ptr).version).read() };

            let mut context = if context.is_null() {
                Box::new(WildcardContext::new())
            } else {
                unsafe {
                    let context_ptr = &mut *context;
                    let context = context_ptr.take();
                    Box::from_raw(context.unwrap().as_ptr())
                }
            };

            context.add_path_defaults(filename);

            let mut flags = ShaderFeatures::NONE;

            // Original Aspect and Frametime Uniforms are an API 2 feature.
            if api_version >= 2 {
                let original_aspect_uniforms = unsafe { (&raw mut (*opt_ptr).original_aspect_uniforms).read() };
                let frametime_uniforms = unsafe { (&raw mut (*opt_ptr).frametime_uniforms).read() };

                if original_aspect_uniforms {
                    flags |= ShaderFeatures::ORIGINAL_ASPECT_UNIFORMS;
                }

                if frametime_uniforms {
                    flags |= ShaderFeatures::FRAMETIME_UNIFORMS;
                }
            }

            // Sensor Uniforms are an API 5 feature.
            if api_version >= 5 {
                let sensor_uniforms = unsafe { (&raw mut (*opt_ptr).sensor_uniforms).read() };

                if sensor_uniforms {
                    flags |= ShaderFeatures::SENSOR_UNIFORMS;
                }
            }

            let preset = ShaderPreset::try_parse(filename, flags)?;
            unsafe {
                out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
                    preset,
                )))))
            }
        }
    }
}

extern_fn! {
    /// Query the target color space of the preset's final pass.
    ///
    /// The target color space is derived from the last pass's declared output
    /// format. Use this before creating a filter chain to decide which
    /// swapchain color space to request to support HDR shaders.
    ///
    /// If `out` returns `LIBRA_COLOR_SPACE_SDR`, the preset does not have a
    /// final HDR pass and can be treated as an SDR shader.
    ///
    /// If the host intends PQ-scRGB, then the preset should return `LIBRA_COLOR_SPACE_SC_RGB`
    /// for successful promotion.
    ///
    /// ## Safety
    /// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
    /// - `out` must be aligned, but may be uninitialized.
    fn libra_preset_color_space(
        preset: *mut libra_shader_preset_t,
        out: *mut MaybeUninit<LIBRA_COLOR_SPACE>
    ) |preset| {
        assert_some_ptr!(preset);
        let cs: LIBRA_COLOR_SPACE = preset.color_space()?.into();
        unsafe {
            out.write(MaybeUninit::new(cs));
        }
    }
}

extern_fn! {
    /// Free the preset.
    ///
    /// If `preset` is null, this function does nothing. The resulting value in `preset` then becomes
    /// null.
    ///
    /// ## Safety
    /// - `preset` must be a valid and aligned pointer to a `libra_shader_preset_t`.
    fn libra_preset_free(preset: *mut libra_shader_preset_t) {
        assert_non_null!(preset);
        unsafe {
            let preset_ptr = &mut *preset;
            let preset = preset_ptr.take();
            drop(Box::from_raw(preset.unwrap().as_ptr()));
        }
    }
}

extern_fn! {
    /// Set the value of the parameter in the preset.
    ///
    /// ## Safety
    /// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
    /// - `name` must be null or a valid and aligned pointer to a string.
    fn libra_preset_set_param(
        preset: *mut libra_shader_preset_t,
        name: *const c_char,
        value: f32
    ) |name|; mut |preset| {
        let name = unsafe {
            CStr::from_ptr(name)
        };

        let name = name.to_str()?;
        assert_some_ptr!(mut preset);

        if let Some(param) = preset.parameters.iter_mut().find(|c| c.name == name) {
            param.value = value
        }
    }
}

extern_fn! {
    /// Get the value of the parameter as set in the preset.
    ///
    /// ## Safety
    /// - `preset` must be null or a valid and aligned pointer to a shader preset.
    /// - `name` must be null or a valid and aligned pointer to a string.
    /// - `value` may be a pointer to a uninitialized `float`.
    fn libra_preset_get_param(
        preset: *const libra_shader_preset_t,
        name: *const c_char,
        value: *mut MaybeUninit<f32>
    ) |name, preset| {
        let name = unsafe { CStr::from_ptr(name) };
        let name = name.to_str()?;
        assert_some_ptr!(preset);
        assert_non_null!(value);

        if let Some(param) = preset.parameters.iter().find(|c| c.name == name) {
            unsafe { value.write(MaybeUninit::new(param.value)) }
        }
    }
}

extern_fn! {
    /// Pretty print the shader preset.
    ///
    /// ## Safety
    /// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
    fn libra_preset_print(preset: *mut libra_shader_preset_t) |preset| {
        assert_some_ptr!(preset);
        println!("{preset:#?}");
    }
}

extern_fn! {
    /// Get a list of runtime parameters.
    ///
    /// ## Safety
    /// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
    /// - `out` must be an aligned pointer to a `libra_preset_parameter_list_t`.
    /// - The output struct should be treated as immutable. Mutating any struct fields
    ///   in the returned struct may at best cause memory leaks, and at worse
    ///   cause undefined behaviour when later freed.
    /// - It is safe to call `libra_preset_get_runtime_params` multiple times, however
    ///   the output struct must only be freed once per call.
    fn libra_preset_get_runtime_params(
        preset: *const libra_shader_preset_t,
        out: *mut MaybeUninit<libra_preset_param_list_t>
    ) |preset| {
        assert_some_ptr!(preset);
        assert_non_null!(out);

        let iter = librashader::presets::get_parameter_meta(preset)?;
        let mut values = Vec::new();
        for param in iter {
            let name = CString::new(param.id.to_string())
            .map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
            let description = CString::new(param.description)
            .map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
            values.push(libra_preset_param_t {
                name: name.into_raw().cast_const(),
                description: description.into_raw().cast_const(),
                initial: param.initial,
                minimum: param.minimum,
                maximum: param.maximum,
                step: param.step
            })
        }

        let values = values.into_boxed_slice();
        let (parts, len) = crate::ffi::boxed_slice_into_raw_parts(values);

        unsafe {
            out.write(MaybeUninit::new(libra_preset_param_list_t {
                parameters: parts,
                length: len as u64,
            }));
        }
    }
}

extern_fn! {
    /// Free the runtime parameters.
    ///
    /// Unlike the other `free` functions provided by librashader,
    /// `libra_preset_free_runtime_params` takes the struct directly.
    /// The caller must take care to maintain the lifetime of any pointers
    /// contained within the input `libra_preset_param_list_t`.
    ///
    /// ## Safety
    /// - Any pointers rooted at `parameters` becomes invalid after this function returns,
    ///   including any strings accessible via the input `libra_preset_param_list_t`.
    ///   The caller must ensure that there are no live pointers, aliased or unaliased,
    ///   to data accessible via the input `libra_preset_param_list_t`.
    ///
    /// - Accessing any data pointed to via the input `libra_preset_param_list_t` after it
    ///   has been freed is a use-after-free and is immediate undefined behaviour.
    ///
    /// - If any struct fields of the input `libra_preset_param_list_t` was modified from
    ///   their values given after `libra_preset_get_runtime_params`, this may result
    ///   in undefined behaviour.
    fn libra_preset_free_runtime_params(preset: libra_preset_param_list_t) {
        unsafe {
            let values =
                    crate::ffi::boxed_slice_from_raw_parts(preset.parameters.cast_mut(),
                preset.length as usize).into_vec();

            for value in values {
                let name = CString::from_raw(value.name.cast_mut());
                let description = CString::from_raw(value.description.cast_mut());

                drop(name);
                drop(description)
            }
        }
    }
}