use std::ptr;
pub mod runtime;
use thiserror::Error;
use rps_sys as ffi;
#[macro_export]
macro_rules! call {
($f:expr) => {{
let code = unsafe { $f };
match code {
0 => Ok(()),
_ => Err(Error::from(code)),
}
}};
}
#[repr(i32)]
#[derive(Debug, Error, Copy, Clone)]
pub enum Error {
#[error("Unspecified error")]
Unspecified = ffi::RpsResult_RPS_ERROR_UNSPECIFIED,
#[error("Unrecognized command")]
UnrecognizedCommand = ffi::RpsResult_RPS_ERROR_UNRECOGNIZED_COMMAND,
#[error("Invalid arguments")]
InvalidArguments = ffi::RpsResult_RPS_ERROR_INVALID_ARGUMENTS,
#[error("Invalid data")]
InvalidData = ffi::RpsResult_RPS_ERROR_INVALID_DATA,
#[error("Invalid operation")]
InvalidOperation = ffi::RpsResult_RPS_ERROR_INVALID_OPERATION,
#[error("Out of memory")]
OutOfMemory = ffi::RpsResult_RPS_ERROR_OUT_OF_MEMORY,
#[error("File not found")]
FileNotFound = ffi::RpsResult_RPS_ERROR_FILE_NOT_FOUND,
#[error("Invalid file format")]
InvalidFileFormat = ffi::RpsResult_RPS_ERROR_INVALID_FILE_FORMAT,
#[error("File format version too old")]
UnsupportedVersionTooOld = ffi::RpsResult_RPS_ERROR_UNSUPPORTED_VERSION_TOO_OLD,
#[error("File format version too new")]
UnsupportedVersionTooNew = ffi::RpsResult_RPS_ERROR_UNSUPPORTED_VERSION_TOO_NEW,
#[error("Unknown node")]
UnknownNode = ffi::RpsResult_RPS_ERROR_UNKNOWN_NODE,
#[error("Index out of bounds")]
IndexOutOfBounds = ffi::RpsResult_RPS_ERROR_INDEX_OUT_OF_BOUNDS,
#[error("Command already finalized")]
CommandAlreadyFinal = ffi::RpsResult_RPS_ERROR_COMMAND_ALREADY_FINAL,
#[error("Data layout mismatch between runtime and shader")]
InteropDataLayoutMismatch = ffi::RpsResult_RPS_ERROR_INTEROP_DATA_LAYOUT_MISMATCH,
#[error("Key not found")]
KeyNotFound = ffi::RpsResult_RPS_ERROR_KEY_NOT_FOUND,
#[error("Key duplicated")]
KeyDuplicated = ffi::RpsResult_RPS_ERROR_KEY_DUPLICATED,
#[error("Not implemented")]
NotImplemented = ffi::RpsResult_RPS_ERROR_NOT_IMPLEMENTED,
#[error("Integer overflow")]
IntegerOverflow = ffi::RpsResult_RPS_ERROR_INTEGER_OVERFLOW,
#[error("Exclusive ranges overlapping")]
RangeOverlapping = ffi::RpsResult_RPS_ERROR_RANGE_OVERLAPPING,
#[error("Invalid pipeline configuration")]
ValidationFailed = ffi::RpsResult_RPS_ERROR_VALIDATION_FAILED,
#[error("Compiler error")]
InvalidProgram = ffi::RpsResult_RPS_ERROR_INVALID_PROGRAM,
#[error("RPSL module is incompatible with the current runtime")]
UnsupportedModuleVersion = ffi::RpsResult_RPS_ERROR_UNSUPPORTED_MODULE_VERSION,
#[error("Type safety check failed")]
TypeMismatch = ffi::RpsResult_RPS_ERROR_TYPE_MISMATCH,
#[error("Not supported")]
NotSupported = ffi::RpsResult_RPS_ERROR_NOT_SUPPORTED,
#[error("Runtime API error")]
RuntimeApiError = ffi::RpsResult_RPS_ERROR_RUNTIME_API_ERROR,
#[error("RPS library internal error")]
InternalError = ffi::RpsResult_RPS_ERROR_INTERNAL_ERROR,
#[error("Unmapped error code: {0}")]
Unknown(ffi::RpsResult) = i32::MAX,
}
impl From<Error> for ffi::RpsResult {
fn from(value: Error) -> Self {
match value {
Error::Unknown(code) => code,
_ => i32::from(value),
}
}
}
impl From<ffi::RpsResult> for Error {
fn from(value: ffi::RpsResult) -> Self {
match value {
ffi::RpsResult_RPS_ERROR_UNSPECIFIED => Error::Unspecified,
ffi::RpsResult_RPS_ERROR_UNRECOGNIZED_COMMAND => Error::UnrecognizedCommand,
ffi::RpsResult_RPS_ERROR_INVALID_ARGUMENTS => Error::InvalidArguments,
ffi::RpsResult_RPS_ERROR_INVALID_DATA => Error::InvalidData,
ffi::RpsResult_RPS_ERROR_INVALID_OPERATION => Error::InvalidOperation,
ffi::RpsResult_RPS_ERROR_OUT_OF_MEMORY => Error::OutOfMemory,
ffi::RpsResult_RPS_ERROR_FILE_NOT_FOUND => Error::FileNotFound,
ffi::RpsResult_RPS_ERROR_INVALID_FILE_FORMAT => Error::InvalidFileFormat,
ffi::RpsResult_RPS_ERROR_UNSUPPORTED_VERSION_TOO_OLD => Error::UnsupportedVersionTooOld,
ffi::RpsResult_RPS_ERROR_UNSUPPORTED_VERSION_TOO_NEW => Error::UnsupportedVersionTooNew,
ffi::RpsResult_RPS_ERROR_UNKNOWN_NODE => Error::UnknownNode,
ffi::RpsResult_RPS_ERROR_INDEX_OUT_OF_BOUNDS => Error::IndexOutOfBounds,
ffi::RpsResult_RPS_ERROR_COMMAND_ALREADY_FINAL => Error::CommandAlreadyFinal,
ffi::RpsResult_RPS_ERROR_INTEROP_DATA_LAYOUT_MISMATCH => {
Error::InteropDataLayoutMismatch
}
ffi::RpsResult_RPS_ERROR_KEY_NOT_FOUND => Error::KeyNotFound,
ffi::RpsResult_RPS_ERROR_KEY_DUPLICATED => Error::KeyDuplicated,
ffi::RpsResult_RPS_ERROR_NOT_IMPLEMENTED => Error::NotImplemented,
ffi::RpsResult_RPS_ERROR_INTEGER_OVERFLOW => Error::IntegerOverflow,
ffi::RpsResult_RPS_ERROR_RANGE_OVERLAPPING => Error::RangeOverlapping,
ffi::RpsResult_RPS_ERROR_VALIDATION_FAILED => Error::ValidationFailed,
ffi::RpsResult_RPS_ERROR_INVALID_PROGRAM => Error::InvalidProgram,
ffi::RpsResult_RPS_ERROR_UNSUPPORTED_MODULE_VERSION => Error::UnsupportedModuleVersion,
ffi::RpsResult_RPS_ERROR_TYPE_MISMATCH => Error::TypeMismatch,
ffi::RpsResult_RPS_ERROR_NOT_SUPPORTED => Error::NotSupported,
ffi::RpsResult_RPS_ERROR_RUNTIME_API_ERROR => Error::RuntimeApiError,
ffi::RpsResult_RPS_ERROR_INTERNAL_ERROR => Error::InternalError,
_ => Error::Unknown(value),
}
}
}
#[derive(Default)]
pub struct DeviceBuilder {
create_info: ffi::RpsDeviceCreateInfo,
}
impl DeviceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn data_alloc_info(&mut self, size: usize, alignment: usize) -> &mut Self {
self.create_info.privateDataAllocInfo = ffi::RpsAllocInfo { size, alignment };
self
}
pub fn build(&self) -> Result<Device, Error> {
let mut handle = ptr::null_mut();
call!(ffi::rpsDeviceCreate(&self.create_info, &mut handle))?;
Ok(Device {
handle,
callbacks: None,
})
}
pub fn build_null(&self, callbacks: Box<dyn runtime::Callbacks>) -> Result<Device, Error> {
let mut device = self.build()?;
let runtime_info = ffi::RpsRuntimeDeviceCreateInfo {
pUserContext: callbacks.as_ref() as *const _ as *mut _,
callbacks: runtime::CALLBACKS,
};
let create_info = ffi::RpsNullRuntimeDeviceCreateInfo {
pDeviceCreateInfo: &self.create_info,
pRuntimeCreateInfo: &runtime_info,
};
call!(ffi::rpsNullRuntimeDeviceCreate(
&create_info,
&mut device.handle
))?;
device.callbacks = Some(callbacks);
Ok(device)
}
}
pub struct Device {
handle: ffi::RpsDevice,
callbacks: Option<Box<dyn runtime::Callbacks>>,
}
impl Drop for Device {
fn drop(&mut self) {
unsafe { ffi::rpsDeviceDestroy(self.handle) };
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Dummy;
impl runtime::Callbacks for Dummy {}
#[test]
fn default_device() {
let device = DeviceBuilder::default().build().unwrap();
drop(device);
}
#[test]
fn null_runtime() {
let device = DeviceBuilder::new().build_null(Box::new(Dummy)).unwrap();
drop(device);
}
}