Browse Source
Effectively: - Move the previous bindgen script into build.rs - In lib.rs, include the file generated at build time - Compile inline statics with devkitARM toolchain - Remove now-unneeded reimplementations of inline statics - Use doxygen_rs to clean up the docs of the generated bindings. Nice! This cleans up the repo a lot and also ensures we are using the canonical implementations of those inline functions, which should help prevent mistakes as well.pull/26/head
Ian Chamberlain
1 year ago
14 changed files with 142 additions and 2054 deletions
@ -1,9 +0,0 @@ |
|||||||
[package] |
|
||||||
name = "bindgen-citro3d" |
|
||||||
version = "0.1.0" |
|
||||||
edition = "2021" |
|
||||||
description = "Helper tool to generate citro3d-sys bindings." |
|
||||||
publish = false |
|
||||||
|
|
||||||
[dependencies] |
|
||||||
bindgen = "0.62.0" |
|
@ -1,92 +0,0 @@ |
|||||||
//! This is meant to be run as a "script" to generate bindings to `citro3d`.
|
|
||||||
//! We use this instead of `bindgen-cli` to enable the use of [`CustomCallbacks`]
|
|
||||||
//! with [`bindgen`] as a library for finer grained control of the bindings.
|
|
||||||
|
|
||||||
use std::iter::FromIterator; |
|
||||||
use std::path::PathBuf; |
|
||||||
|
|
||||||
use bindgen::callbacks::{DeriveTrait, ImplementsTrait, ParseCallbacks}; |
|
||||||
use bindgen::{Builder, RustTarget}; |
|
||||||
|
|
||||||
fn main() { |
|
||||||
let devkitpro = std::env::var("DEVKITPRO").expect("DEVKITPRO not set in environment"); |
|
||||||
let devkitarm = std::env::var("DEVKITARM").expect("DEVKITARM not set in environment"); |
|
||||||
|
|
||||||
let include_path = PathBuf::from_iter([devkitpro.as_str(), "libctru", "include"]); |
|
||||||
let header = include_path.join("tex3ds.h"); |
|
||||||
|
|
||||||
let sysroot = PathBuf::from(devkitarm).join("arm-none-eabi"); |
|
||||||
let system_include = sysroot.join("include"); |
|
||||||
|
|
||||||
let bindings = Builder::default() |
|
||||||
.header(header.to_str().unwrap()) |
|
||||||
.rust_target(RustTarget::Nightly) |
|
||||||
.use_core() |
|
||||||
.trust_clang_mangling(false) |
|
||||||
.layout_tests(false) |
|
||||||
.ctypes_prefix("::libc") |
|
||||||
.prepend_enum_name(false) |
|
||||||
.fit_macro_constants(true) |
|
||||||
.raw_line("use ctru_sys::*;") |
|
||||||
.must_use_type("Result") |
|
||||||
.blocklist_type("u(8|16|32|64)") |
|
||||||
.opaque_type("(GPU|GFX)_.*") |
|
||||||
.opaque_type("float24Uniform_s") |
|
||||||
.allowlist_file(".*/c3d/.*[.]h") |
|
||||||
.allowlist_file(".*/tex3ds[.]h") |
|
||||||
.blocklist_file(".*/3ds/.*[.]h") |
|
||||||
.blocklist_file(".*/sys/.*[.]h") |
|
||||||
.clang_args([ |
|
||||||
"--target=arm-none-eabi", |
|
||||||
"--sysroot", |
|
||||||
sysroot.to_str().unwrap(), |
|
||||||
"-isystem", |
|
||||||
system_include.to_str().unwrap(), |
|
||||||
"-I", |
|
||||||
include_path.to_str().unwrap(), |
|
||||||
"-mfloat-abi=hard", |
|
||||||
"-march=armv6k", |
|
||||||
"-mtune=mpcore", |
|
||||||
"-mfpu=vfp", |
|
||||||
"-DARM11 ", |
|
||||||
"-D_3DS ", |
|
||||||
"-D__3DS__ ", |
|
||||||
]) |
|
||||||
.parse_callbacks(Box::new(CustomCallbacks)) |
|
||||||
.generate() |
|
||||||
.expect("Unable to generate bindings"); |
|
||||||
|
|
||||||
bindings |
|
||||||
.write(Box::new(std::io::stdout())) |
|
||||||
.expect("failed to write bindings"); |
|
||||||
} |
|
||||||
|
|
||||||
/// Custom callback struct to allow us to mark some "known good types" as
|
|
||||||
/// [`Copy`], which in turn allows using Rust `union` instead of bindgen union
|
|
||||||
/// types. See
|
|
||||||
/// <https://rust-lang.github.io/rust-bindgen/using-unions.html#which-union-type-will-bindgen-generate>
|
|
||||||
/// for more info.
|
|
||||||
///
|
|
||||||
/// We do the same for [`Debug`] just for the convenience of derived Debug impls
|
|
||||||
/// on some `citro3d` types.
|
|
||||||
#[derive(Debug)] |
|
||||||
struct CustomCallbacks; |
|
||||||
|
|
||||||
impl ParseCallbacks for CustomCallbacks { |
|
||||||
fn blocklisted_type_implements_trait( |
|
||||||
&self, |
|
||||||
name: &str, |
|
||||||
derive_trait: DeriveTrait, |
|
||||||
) -> Option<ImplementsTrait> { |
|
||||||
if let DeriveTrait::Copy | DeriveTrait::Debug = derive_trait { |
|
||||||
match name { |
|
||||||
"u64_" | "u32_" | "u16_" | "u8_" | "u64" | "u32" | "u16" | "u8" | "gfxScreen_t" |
|
||||||
| "gfx3dSide_t" => Some(ImplementsTrait::Yes), |
|
||||||
_ if name.starts_with("GPU_") => Some(ImplementsTrait::Yes), |
|
||||||
_ => None, |
|
||||||
} |
|
||||||
} else { |
|
||||||
None |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,19 +1,143 @@ |
|||||||
|
//! This build script generates bindings from `citro3d` on the fly at compilation
|
||||||
|
//! time into `OUT_DIR`, from which they can be included into `lib.rs`.
|
||||||
|
|
||||||
use std::env; |
use std::env; |
||||||
|
use std::iter::FromIterator; |
||||||
|
use std::path::{Path, PathBuf}; |
||||||
|
|
||||||
|
use bindgen::callbacks::{DeriveTrait, ImplementsTrait, ParseCallbacks}; |
||||||
|
use bindgen::{Builder, RustTarget}; |
||||||
|
|
||||||
fn main() { |
fn main() { |
||||||
let dkp_path = env::var("DEVKITPRO").unwrap(); |
let devkitpro = env::var("DEVKITPRO").expect("DEVKITPRO not set in environment"); |
||||||
|
println!("cargo:rerun-if-env-changed=DEVKITPRO"); |
||||||
|
|
||||||
|
let devkitarm = std::env::var("DEVKITARM").expect("DEVKITARM not set in environment"); |
||||||
|
println!("cargo:rerun-if-env-changed=DEVKITARM"); |
||||||
|
|
||||||
let debug_symbols = env::var("DEBUG").unwrap(); |
let debug_symbols = env::var("DEBUG").unwrap(); |
||||||
|
println!("cargo:rerun-if-env-changed=DEBUG"); |
||||||
|
|
||||||
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
||||||
|
println!("cargo:rerun-if-env-changed=OUT_DIR"); |
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs"); |
println!("cargo:rerun-if-changed=build.rs"); |
||||||
println!("cargo:rerun-if-env-changed=DEVKITPRO"); |
println!("cargo:rustc-link-search=native={devkitpro}/libctru/lib"); |
||||||
println!("cargo:rustc-link-search=native={dkp_path}/libctru/lib"); |
|
||||||
println!( |
println!( |
||||||
"cargo:rustc-link-lib=static={}", |
"cargo:rustc-link-lib=static={}", |
||||||
match debug_symbols.as_str() { |
match debug_symbols.as_str() { |
||||||
// Based on valid values described in
|
// Based on valid values described in
|
||||||
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
|
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
|
||||||
"0" | "false" => "citro3d", |
"0" | "false" | "none" => "citro3d", |
||||||
_ => "citro3dd", |
_ => "citro3dd", |
||||||
} |
} |
||||||
); |
); |
||||||
|
|
||||||
|
let include_path = PathBuf::from_iter([devkitpro.as_str(), "libctru", "include"]); |
||||||
|
let tex3ds_h = include_path.join("tex3ds.h"); |
||||||
|
let citro3d_h = include_path.join("citro3d.h"); |
||||||
|
let three_ds_h = include_path.join("3ds.h"); |
||||||
|
|
||||||
|
let sysroot = Path::new(devkitarm.as_str()).join("arm-none-eabi"); |
||||||
|
let system_include = sysroot.join("include"); |
||||||
|
let static_fns_path = Path::new("citro3d_statics_wrapper"); |
||||||
|
|
||||||
|
let bindings = Builder::default() |
||||||
|
.header(three_ds_h.to_str().unwrap()) |
||||||
|
.header(citro3d_h.to_str().unwrap()) |
||||||
|
.header(tex3ds_h.to_str().unwrap()) |
||||||
|
.rust_target(RustTarget::Nightly) |
||||||
|
.use_core() |
||||||
|
.trust_clang_mangling(false) |
||||||
|
.layout_tests(false) |
||||||
|
.ctypes_prefix("::libc") |
||||||
|
.prepend_enum_name(false) |
||||||
|
.fit_macro_constants(true) |
||||||
|
.raw_line("use ctru_sys::*;") |
||||||
|
.must_use_type("Result") |
||||||
|
.blocklist_type("u(8|16|32|64)") |
||||||
|
.opaque_type("(GPU|GFX)_.*") |
||||||
|
.opaque_type("float24Uniform_s") |
||||||
|
.allowlist_file(".*/c3d/.*[.]h") |
||||||
|
.allowlist_file(".*/tex3ds[.]h") |
||||||
|
.blocklist_file(".*/3ds/.*[.]h") |
||||||
|
.blocklist_file(".*/sys/.*[.]h") |
||||||
|
.wrap_static_fns(true) |
||||||
|
.wrap_static_fns_path(out_dir.join(static_fns_path)) |
||||||
|
.clang_args([ |
||||||
|
"--target=arm-none-eabi", |
||||||
|
"--sysroot", |
||||||
|
sysroot.to_str().unwrap(), |
||||||
|
"-isystem", |
||||||
|
system_include.to_str().unwrap(), |
||||||
|
"-I", |
||||||
|
include_path.to_str().unwrap(), |
||||||
|
"-mfloat-abi=hard", |
||||||
|
"-march=armv6k", |
||||||
|
"-mtune=mpcore", |
||||||
|
"-mfpu=vfp", |
||||||
|
"-DARM11 ", |
||||||
|
"-D_3DS ", |
||||||
|
"-D__3DS__ ", |
||||||
|
]) |
||||||
|
.parse_callbacks(Box::new(CustomCallbacks)) |
||||||
|
.generate() |
||||||
|
.expect("Unable to generate bindings"); |
||||||
|
|
||||||
|
bindings |
||||||
|
.write_to_file(out_dir.join("bindings.rs")) |
||||||
|
.expect("failed to write bindings"); |
||||||
|
|
||||||
|
// Compile static inline fns wrapper
|
||||||
|
let cc = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-gcc"); |
||||||
|
let ar = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-ar"); |
||||||
|
|
||||||
|
cc::Build::new() |
||||||
|
.compiler(cc) |
||||||
|
.archiver(ar) |
||||||
|
.include(&include_path) |
||||||
|
.file(out_dir.join(static_fns_path.with_extension("c"))) |
||||||
|
.flag("-march=armv6k") |
||||||
|
.flag("-mtune=mpcore") |
||||||
|
.flag("-mfloat-abi=hard") |
||||||
|
.flag("-mfpu=vfp") |
||||||
|
.flag("-mtp=soft") |
||||||
|
.flag("-Wno-deprecated-declarations") |
||||||
|
.compile("citro3d_statics_wrapper"); |
||||||
|
} |
||||||
|
|
||||||
|
/// Custom callback struct to allow us to mark some "known good types" as
|
||||||
|
/// [`Copy`], which in turn allows using Rust `union` instead of bindgen union types. See
|
||||||
|
/// <https://rust-lang.github.io/rust-bindgen/using-unions.html#which-union-type-will-bindgen-generate>
|
||||||
|
/// for more info.
|
||||||
|
///
|
||||||
|
/// We do the same for [`Debug`] just for the convenience of derived Debug impls
|
||||||
|
/// on some `citro3d` types.
|
||||||
|
///
|
||||||
|
/// Finally, we use [`doxygen_rs`] to transform the doc comments into something
|
||||||
|
/// easier to read in the generated documentation / hover documentation.
|
||||||
|
#[derive(Debug)] |
||||||
|
struct CustomCallbacks; |
||||||
|
|
||||||
|
impl ParseCallbacks for CustomCallbacks { |
||||||
|
fn process_comment(&self, comment: &str) -> Option<String> { |
||||||
|
Some(doxygen_rs::transform(comment)) |
||||||
|
} |
||||||
|
|
||||||
|
fn blocklisted_type_implements_trait( |
||||||
|
&self, |
||||||
|
name: &str, |
||||||
|
derive_trait: DeriveTrait, |
||||||
|
) -> Option<ImplementsTrait> { |
||||||
|
if let DeriveTrait::Copy | DeriveTrait::Debug = derive_trait { |
||||||
|
match name { |
||||||
|
"u64_" | "u32_" | "u16_" | "u8_" | "u64" | "u32" | "u16" | "u8" | "gfxScreen_t" |
||||||
|
| "gfx3dSide_t" => Some(ImplementsTrait::Yes), |
||||||
|
_ if name.starts_with("GPU_") => Some(ImplementsTrait::Yes), |
||||||
|
_ => None, |
||||||
|
} |
||||||
|
} else { |
||||||
|
None |
||||||
|
} |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,9 +0,0 @@ |
|||||||
//! Definitions from `<c3d/base.h>`
|
|
||||||
|
|
||||||
use crate::C3D_FixedAttribGetWritePtr; |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_FixedAttribSet(id: libc::c_int, x: f32, y: f32, z: f32, w: f32) { |
|
||||||
let ptr = C3D_FixedAttribGetWritePtr(id); |
|
||||||
(*ptr).c.copy_from_slice(&[x, y, z, w]); |
|
||||||
} |
|
@ -1,13 +0,0 @@ |
|||||||
// TODO: move this to ctru-sys, maybe?
|
|
||||||
// would probably be auto-generated via https://github.com/rust3ds/ctru-rs/issues/123
|
|
||||||
|
|
||||||
use ctru_sys::{osSharedConfig_s, OS_SHAREDCFG_VADDR}; |
|
||||||
|
|
||||||
fn OS_SharedConfig() -> *mut osSharedConfig_s { |
|
||||||
OS_SHAREDCFG_VADDR as _ |
|
||||||
} |
|
||||||
|
|
||||||
/// Gets the state of the 3D slider as a value from 0.0 to 1.0
|
|
||||||
pub unsafe fn osGet3DSliderState() -> f32 { |
|
||||||
(*OS_SharedConfig()).slider_3d |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
//! Definitions from `<c3d/renderqueue.h>`
|
|
||||||
|
|
||||||
use crate::*; |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_RenderTargetDetachOutput(target: *mut C3D_RenderTarget) { |
|
||||||
C3D_RenderTargetSetOutput(core::ptr::null_mut(), (*target).screen, (*target).side, 0); |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_RenderTargetClear( |
|
||||||
target: *mut C3D_RenderTarget, |
|
||||||
clearBits: C3D_ClearBits, |
|
||||||
clearColor: u32, |
|
||||||
clearDepth: u32, |
|
||||||
) { |
|
||||||
C3D_FrameBufClear(&mut (*target).frameBuf, clearBits, clearColor, clearDepth); |
|
||||||
} |
|
@ -1,100 +0,0 @@ |
|||||||
//! Definitions from `<c3d/texenv.h>`.
|
|
||||||
//!
|
|
||||||
//! Most of these functions are `static inline` so they don't get generated by `bindgen`.
|
|
||||||
//! See <https://github.com/rust-lang/rust-bindgen/issues/1090>.
|
|
||||||
|
|
||||||
use core::ops::{BitOr, Shl}; |
|
||||||
|
|
||||||
use ctru_sys::{GPU_COMBINEFUNC, GPU_PREVIOUS, GPU_REPLACE, GPU_TEVSCALE_1, GPU_TEVSRC}; |
|
||||||
use libc::c_int; |
|
||||||
|
|
||||||
use super::*; |
|
||||||
|
|
||||||
// TODO: why are these two different macros in C?
|
|
||||||
|
|
||||||
/// Creates a texture combiner source parameter from three sources.
|
|
||||||
#[inline] |
|
||||||
fn GPU_TEVSOURCES<T>(a: T, b: T, c: T) -> T |
|
||||||
where |
|
||||||
T: BitOr<Output = T> + Shl<u8, Output = T>, |
|
||||||
{ |
|
||||||
a | b << 4 | c << 8 |
|
||||||
} |
|
||||||
|
|
||||||
/// Creates a texture combiner operand parameter from three operands.
|
|
||||||
#[inline] |
|
||||||
fn GPU_TEVOPERANDS<T>(a: T, b: T, c: T) -> T |
|
||||||
where |
|
||||||
T: BitOr<Output = T> + Shl<u8, Output = T>, |
|
||||||
{ |
|
||||||
a | b << 4 | c << 8 |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_TexEnvInit(env: *mut C3D_TexEnv) { |
|
||||||
(*env).srcRgb = GPU_TEVSOURCES(GPU_PREVIOUS, 0, 0) as u16; |
|
||||||
(*env).srcAlpha = (*env).srcRgb; |
|
||||||
(*env).__bindgen_anon_1.opAll = 0; |
|
||||||
(*env).funcRgb = GPU_REPLACE as u16; |
|
||||||
(*env).funcAlpha = (*env).funcRgb; |
|
||||||
(*env).color = 0xFFFFFFFF; |
|
||||||
(*env).scaleRgb = GPU_TEVSCALE_1 as u16; |
|
||||||
(*env).scaleAlpha = GPU_TEVSCALE_1 as u16; |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_TexEnvSrc( |
|
||||||
env: *mut C3D_TexEnv, |
|
||||||
mode: C3D_TexEnvMode, |
|
||||||
s1: GPU_TEVSRC, |
|
||||||
s2: GPU_TEVSRC, // default GPU_PRIMARY_COLOR
|
|
||||||
s3: GPU_TEVSRC, // default GPU_PRIMARY_COLOR
|
|
||||||
) { |
|
||||||
let param = GPU_TEVSOURCES(s1, s2, s3); |
|
||||||
|
|
||||||
if mode & C3D_RGB != 0 { |
|
||||||
(*env).srcRgb = param as u16; |
|
||||||
} |
|
||||||
|
|
||||||
if mode & C3D_Alpha != 0 { |
|
||||||
(*env).srcAlpha = param as u16; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: match API of texenv.h
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_TexEnvOp( |
|
||||||
env: *mut C3D_TexEnv, |
|
||||||
mode: C3D_TexEnvMode, |
|
||||||
o1: c_int, |
|
||||||
o2: c_int, |
|
||||||
o3: c_int, |
|
||||||
) { |
|
||||||
let param = GPU_TEVOPERANDS(o1, o2, o3); |
|
||||||
|
|
||||||
if mode & C3D_RGB != 0 { |
|
||||||
// (*env).opRgb = param as u16;
|
|
||||||
(*env) |
|
||||||
.__bindgen_anon_1 |
|
||||||
.__bindgen_anon_1 |
|
||||||
.set_opRgb(param as u32); |
|
||||||
} |
|
||||||
|
|
||||||
if mode & C3D_Alpha != 0 { |
|
||||||
(*env) |
|
||||||
.__bindgen_anon_1 |
|
||||||
.__bindgen_anon_1 |
|
||||||
.set_opAlpha(param as u32); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_TexEnvFunc(env: *mut C3D_TexEnv, mode: C3D_TexEnvMode, param: GPU_COMBINEFUNC) { |
|
||||||
if mode & C3D_RGB != 0 { |
|
||||||
(*env).funcRgb = param as u16; |
|
||||||
} |
|
||||||
|
|
||||||
if mode & C3D_Alpha != 0 { |
|
||||||
(*env).funcAlpha = param as u16; |
|
||||||
} |
|
||||||
} |
|
@ -1,50 +0,0 @@ |
|||||||
//! Definitions from`<c3d/uniforms.h>`
|
|
||||||
|
|
||||||
use ctru_sys::GPU_SHADER_TYPE; |
|
||||||
|
|
||||||
use super::{C3D_FVUnif, C3D_FVUnifDirty, C3D_FVec, C3D_Mtx}; |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_FVUnifWritePtr( |
|
||||||
type_: GPU_SHADER_TYPE, |
|
||||||
id: libc::c_int, |
|
||||||
size: libc::c_int, |
|
||||||
) -> *mut C3D_FVec { |
|
||||||
for i in 0..size { |
|
||||||
C3D_FVUnifDirty[type_ as usize][(id + i) as usize] = true; |
|
||||||
} |
|
||||||
|
|
||||||
return &mut C3D_FVUnif[type_ as usize][id as usize]; |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_FVUnifMtxNx4( |
|
||||||
type_: GPU_SHADER_TYPE, |
|
||||||
id: libc::c_int, |
|
||||||
mtx: *const C3D_Mtx, |
|
||||||
num: libc::c_int, |
|
||||||
) { |
|
||||||
let ptr = C3D_FVUnifWritePtr(type_, id, num); |
|
||||||
|
|
||||||
for i in 0..num { |
|
||||||
*ptr.offset(i as isize) = (*mtx).r.as_ref()[i as usize]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_FVUnifMtx4x4(type_: GPU_SHADER_TYPE, id: libc::c_int, mtx: *const C3D_Mtx) { |
|
||||||
C3D_FVUnifMtxNx4(type_, id, mtx, 4); |
|
||||||
} |
|
||||||
|
|
||||||
#[inline] |
|
||||||
pub unsafe fn C3D_FVUnifSet( |
|
||||||
type_: GPU_SHADER_TYPE, |
|
||||||
id: libc::c_int, |
|
||||||
x: f32, |
|
||||||
y: f32, |
|
||||||
z: f32, |
|
||||||
w: f32, |
|
||||||
) { |
|
||||||
let ptr = C3D_FVUnifWritePtr(type_, id, 1); |
|
||||||
(*ptr).c.copy_from_slice(&[x, y, z, w]); |
|
||||||
} |
|
Loading…
Reference in new issue