use cranelift_codegen::settings::{self, Configurable};
use cranelift_jit::{JITBuilder, JITModule};
use glam::Vec3;
use std::mem;
use thiserror::Error;
use super::codegen::{extract_jit_params, JitCompiler};
use crate::types::SdfNode;
#[derive(Error, Debug)]
pub enum JitError {
#[error("Module error: {0}")]
ModuleError(String),
#[error("Compilation error: {0}")]
CompilationError(String),
#[error("Unsupported node type: {0}")]
UnsupportedNode(String),
}
type SdfEvalFn = unsafe extern "C" fn(f32, f32, f32) -> f32;
pub struct JitCompiledSdf {
_module: JITModule,
eval_fn: SdfEvalFn,
}
unsafe impl Send for JitCompiledSdf {}
unsafe impl Sync for JitCompiledSdf {}
impl JitCompiledSdf {
pub fn compile(node: &SdfNode) -> Result<Self, JitError> {
let mut flag_builder = settings::builder();
flag_builder
.set("use_colocated_libcalls", "true")
.map_err(|e| JitError::ModuleError(e.to_string()))?;
flag_builder
.set("opt_level", "speed")
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let isa_builder =
cranelift_native::builder().map_err(|e| JitError::ModuleError(e.to_string()))?;
let isa = isa_builder
.finish(settings::Flags::new(flag_builder))
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = JITModule::new(builder);
let mut compiler = JitCompiler::new(&mut module);
let func_id = compiler.compile_sdf(node)?;
module
.finalize_definitions()
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let code_ptr = module.get_finalized_function(func_id);
let eval_fn: SdfEvalFn = unsafe { mem::transmute(code_ptr) };
Ok(Self {
_module: module,
eval_fn,
})
}
#[inline]
pub fn eval(&self, point: Vec3) -> f32 {
unsafe { (self.eval_fn)(point.x, point.y, point.z) }
}
pub fn eval_batch(&self, points: &[Vec3]) -> Vec<f32> {
points.iter().map(|p| self.eval(*p)).collect()
}
pub fn eval_batch_parallel(&self, points: &[Vec3]) -> Vec<f32> {
use rayon::prelude::*;
points.par_iter().map(|p| self.eval(*p)).collect()
}
}
impl std::fmt::Debug for JitCompiledSdf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JitCompiledSdf")
.field("_module", &"<JITModule>")
.field("eval_fn", &format!("{:p}", self.eval_fn as *const ()))
.finish()
}
}
type SdfEvalDynamicFn = unsafe extern "C" fn(f32, f32, f32, *const f32) -> f32;
pub struct JitCompiledSdfDynamic {
_module: JITModule,
eval_fn: SdfEvalDynamicFn,
params: Vec<f32>,
}
unsafe impl Send for JitCompiledSdfDynamic {}
unsafe impl Sync for JitCompiledSdfDynamic {}
impl JitCompiledSdfDynamic {
pub fn compile(node: &SdfNode) -> Result<Self, JitError> {
let mut flag_builder = settings::builder();
flag_builder
.set("use_colocated_libcalls", "true")
.map_err(|e| JitError::ModuleError(e.to_string()))?;
flag_builder
.set("opt_level", "speed")
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let isa_builder =
cranelift_native::builder().map_err(|e| JitError::ModuleError(e.to_string()))?;
let isa = isa_builder
.finish(settings::Flags::new(flag_builder))
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = JITModule::new(builder);
let mut compiler = JitCompiler::new(&mut module);
let (func_id, params) = compiler.compile_sdf_dynamic(node)?;
module
.finalize_definitions()
.map_err(|e| JitError::ModuleError(e.to_string()))?;
let code_ptr = module.get_finalized_function(func_id);
let eval_fn: SdfEvalDynamicFn = unsafe { mem::transmute(code_ptr) };
Ok(Self {
_module: module,
eval_fn,
params,
})
}
pub fn update_params(&mut self, node: &SdfNode) {
self.params = extract_jit_params(node);
}
pub fn params(&self) -> &[f32] {
&self.params
}
#[inline]
pub fn eval(&self, point: Vec3) -> f32 {
unsafe { (self.eval_fn)(point.x, point.y, point.z, self.params.as_ptr()) }
}
pub fn eval_batch(&self, points: &[Vec3]) -> Vec<f32> {
points.iter().map(|p| self.eval(*p)).collect()
}
pub fn eval_batch_parallel(&self, points: &[Vec3]) -> Vec<f32> {
use rayon::prelude::*;
points.par_iter().map(|p| self.eval(*p)).collect()
}
}
impl std::fmt::Debug for JitCompiledSdfDynamic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JitCompiledSdfDynamic")
.field("_module", &"<JITModule>")
.field("eval_fn", &format!("{:p}", self.eval_fn as *const ()))
.field("params", &self.params)
.finish()
}
}