use crate::{
callback::ErasedFn, sys::*, Buffer, BufferSize, Bvh, DeviceProperty, Error, Geometry,
GeometryBuilder, GeometryKind, Scene, SceneFlags,
};
use std::{
ffi::CString,
fmt::{self, Display, Formatter},
ptr,
sync::{Arc, Mutex},
};
#[derive(Debug)]
pub struct Device {
pub(crate) handle: RTCDevice,
callbacks: Arc<Mutex<DeviceCallbacks>>,
}
#[derive(Debug, Default)]
struct DeviceCallbacks {
error_fn: Option<ErasedFn>,
memory_monitor_fn: Option<ErasedFn>,
}
impl Clone for Device {
fn clone(&self) -> Self {
unsafe { rtcRetainDevice(self.handle) }
Self {
handle: self.handle,
callbacks: self.callbacks.clone(),
}
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
rtcReleaseDevice(self.handle);
}
}
}
unsafe impl Send for Device {}
unsafe impl Sync for Device {}
impl Device {
pub fn new() -> Result<Device, Error> { create_device(None, true) }
pub fn debug() -> Result<Device, Error> {
let cfg = CString::new("verbose=4").unwrap();
create_device(Some(cfg), true)
}
pub fn with_config(config: Config) -> Result<Device, Error> {
let cfg = config.to_c_string();
create_device(Some(cfg), config.report_errors)
}
pub fn set_error_function<F>(&self, error_fn: F)
where
F: Fn(RTCError, &'static str) + Send + Sync + 'static,
{
let mut cbs = self.callbacks.lock().unwrap();
let erased = ErasedFn::new(error_fn);
unsafe {
rtcSetDeviceErrorFunction(self.handle, error_function::<F>(), erased.as_ptr());
}
cbs.error_fn = Some(erased);
}
pub fn unset_error_function(&self) {
let mut cbs = self.callbacks.lock().unwrap();
unsafe {
rtcSetDeviceErrorFunction(self.handle, None, ptr::null_mut());
}
cbs.error_fn = None;
}
pub fn set_memory_monitor_function<F>(&self, monitor_fn: F)
where
F: Fn(isize, bool) -> bool + Send + Sync + 'static,
{
let mut cbs = self.callbacks.lock().unwrap();
let erased = ErasedFn::new(monitor_fn);
unsafe {
rtcSetDeviceMemoryMonitorFunction(
self.handle,
memory_monitor_function::<F>(),
erased.as_ptr(),
);
}
cbs.memory_monitor_fn = Some(erased);
}
pub fn unset_memory_monitor_function(&self) {
let mut cbs = self.callbacks.lock().unwrap();
unsafe {
rtcSetDeviceMemoryMonitorFunction(self.handle, None, ptr::null_mut());
}
cbs.memory_monitor_fn = None;
}
pub fn get_property(&self, prop: DeviceProperty) -> Result<isize, Error> {
let _ = self.get_error();
let value = unsafe { rtcGetDeviceProperty(self.handle, prop) };
match self.get_error() {
Error::NONE => Ok(value),
error => Err(error),
}
}
pub fn get_error(&self) -> RTCError { unsafe { rtcGetDeviceError(self.handle) } }
pub fn create_scene<'s>(&self) -> Result<Scene<'s>, Error> { Scene::new(self.clone()) }
pub fn create_scene_with_flags(&self, flags: SceneFlags) -> Result<Scene<'_>, Error> {
Scene::new_with_flags(self.clone(), flags)
}
pub fn create_bvh(&self) -> Result<Bvh, Error> { Bvh::new(self) }
pub fn create_buffer(&self, size: usize) -> Result<Buffer, Error> {
Buffer::new(self, BufferSize::new(size).unwrap())
}
pub fn create_geometry<'a>(&self, kind: GeometryKind) -> Result<GeometryBuilder<'a>, Error> {
Ok(Geometry::new(self, kind))
}
}
#[derive(Debug, Clone, Copy)]
pub enum Isa {
Sse2,
Sse4_2,
Avx,
Avx2,
Avx512,
}
impl Display for Isa {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
Isa::Sse2 => write!(f, "sse2"),
Isa::Sse4_2 => write!(f, "sse4.2"),
Isa::Avx => write!(f, "avx"),
Isa::Avx2 => write!(f, "avx2"),
Isa::Avx512 => write!(f, "avx512"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum FrequencyLevel {
Simd128,
Simd256,
Simd512,
}
impl Display for FrequencyLevel {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
FrequencyLevel::Simd128 => write!(f, "simd128"),
FrequencyLevel::Simd256 => write!(f, "simd256"),
FrequencyLevel::Simd512 => write!(f, "simd512"),
}
}
}
pub struct Config {
pub threads: u32,
pub user_threads: u32,
pub set_affinity: bool,
pub start_threads: bool,
pub isa: Option<Isa>,
pub max_isa: Isa,
pub hugepages: bool,
pub enable_selockmemoryprivilege: bool,
pub verbose: u32,
pub frequency_level: Option<FrequencyLevel>,
pub report_errors: bool,
}
impl Config {
pub fn to_c_string(&self) -> CString {
let isa = self
.isa
.map(|isa| format!("isa={},", isa))
.unwrap_or_default();
let frequency_level = self
.frequency_level
.map(|frequency_level| format!("frequency_level={}", frequency_level))
.unwrap_or_default();
let formated = format!(
"threads={},verbose={},set_affinity={},start_threads={},max_isa={},hugepages={},\
enable_selockmemoryprivilege={},{}{}",
self.threads,
self.verbose,
self.set_affinity as u32,
self.start_threads as u32,
self.max_isa,
self.hugepages as u32,
self.enable_selockmemoryprivilege as u32,
isa,
frequency_level
)
.into_bytes();
unsafe { CString::from_vec_unchecked(formated) }
}
}
impl Default for Config {
fn default() -> Self {
Config {
threads: 0,
user_threads: 0,
set_affinity: false,
start_threads: false,
isa: None,
max_isa: Isa::Avx512,
hugepages: cfg!(target_os = "linux"),
enable_selockmemoryprivilege: false,
verbose: 0,
frequency_level: None,
report_errors: true,
}
}
}
pub fn enable_ftz_and_daz() {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
use std::arch::asm;
const FTZ_DAZ: u32 = 0x8000 | 0x0040;
let mut mxcsr: u32 = 0;
unsafe {
asm!("stmxcsr [{ptr}]", ptr = in(reg) &mut mxcsr, options(nostack, preserves_flags));
mxcsr |= FTZ_DAZ;
asm!("ldmxcsr [{ptr}]", ptr = in(reg) &mxcsr, options(nostack, readonly, preserves_flags));
}
}
}
fn default_error_reporter(error: RTCError, msg: &str) {
#[cfg(feature = "log")]
log::error!("embree: {:?}: {}", error, msg);
#[cfg(not(feature = "log"))]
eprintln!("[embree] {:?}: {}", error, msg);
}
fn create_device(config: Option<CString>, report_errors: bool) -> Result<Device, Error> {
enable_ftz_and_daz();
let config = config.unwrap_or_else(|| Config::default().to_c_string());
let handle = unsafe { rtcNewDevice(config.as_ptr()) };
if handle.is_null() {
Err(unsafe { rtcGetDeviceError(ptr::null_mut()) })
} else {
let device = Device {
handle,
callbacks: Arc::new(Mutex::new(DeviceCallbacks::default())),
};
if report_errors {
device.set_error_function(default_error_reporter);
}
Ok(device)
}
}
fn error_function<F>() -> RTCErrorFunction
where
F: Fn(RTCError, &'static str) + Send + Sync + 'static,
{
unsafe extern "C" fn inner<F>(
f: *mut std::os::raw::c_void,
error: RTCError,
msg: *const std::os::raw::c_char,
) where
F: Fn(RTCError, &'static str) + Send + Sync + 'static,
{
let cb = &*(f as *const F);
cb(error, std::ffi::CStr::from_ptr(msg).to_str().unwrap())
}
Some(inner::<F>)
}
fn memory_monitor_function<F>() -> RTCMemoryMonitorFunction
where
F: Fn(isize, bool) -> bool + Send + Sync + 'static,
{
unsafe extern "C" fn inner<F>(f: *mut std::os::raw::c_void, bytes: isize, post: bool) -> bool
where
F: Fn(isize, bool) -> bool + Send + Sync + 'static,
{
let cb = &*(f as *const F);
cb(bytes, post)
}
Some(inner::<F>)
}