use spirv_cross_sys as sys;
use spirv_cross_sys::spvc_context_s;
use std::ptr::NonNull;
use std::sync::Arc;
use crate::error::{ContextRooted, ToContextError};
use crate::targets::Target;
use crate::{error, Compiler, Module, PhantomCompiler, SpirvCrossError};
#[repr(transparent)]
pub(crate) struct CrossAllocationCell(Arc<CrossAllocationCellInner>);
pub(crate) struct CrossAllocationCellInner(NonNull<spvc_context_s>);
#[repr(transparent)]
pub(crate) struct AllocationDropGuard<T = CrossAllocationCellInner>(Arc<T>);
impl<T> Clone for AllocationDropGuard<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl CrossAllocationCell {
pub fn new() -> error::Result<Self> {
unsafe {
let mut context = std::ptr::null_mut();
let result = sys::spvc_context_create(&mut context);
if result != sys::spvc_result::SPVC_SUCCESS {
return Err(SpirvCrossError::OutOfMemory(String::from("Out of memory")));
}
let Some(context) = NonNull::new(context) else {
return Err(SpirvCrossError::OutOfMemory(String::from("Out of memory")));
};
#[allow(clippy::arc_with_non_send_sync)]
Ok(Self(Arc::new(CrossAllocationCellInner(context))))
}
}
pub(crate) fn into_compiler<T: Target>(self, spirv: Module) -> error::Result<Compiler<T>> {
unsafe {
let mut ir = std::ptr::null_mut();
sys::spvc_context_parse_spirv(
self.0 .0.as_ptr(),
spirv.0.as_ptr(),
spirv.0.len(),
&mut ir,
)
.ok(&self)?;
let mut compiler = std::ptr::null_mut();
sys::spvc_context_create_compiler(
self.0 .0.as_ptr(),
T::BACKEND,
ir,
spirv_cross_sys::spvc_capture_mode::TakeOwnership,
&mut compiler,
)
.ok(&self)?;
let Some(compiler) = NonNull::new(compiler) else {
return Err(SpirvCrossError::OutOfMemory(String::from("Out of memory")));
};
Ok(Compiler::new_from_raw(compiler, self))
}
}
pub unsafe fn as_ptr(&self) -> NonNull<spvc_context_s> {
self.0 .0
}
pub fn drop_guard(&self) -> AllocationDropGuard {
AllocationDropGuard(Arc::clone(&self.0))
}
}
impl Drop for CrossAllocationCellInner {
fn drop(&mut self) {
unsafe { sys::spvc_context_destroy(self.0.as_ptr()) }
}
}
impl ContextRooted for &CrossAllocationCell {
fn context(&self) -> NonNull<spvc_context_s> {
unsafe { self.as_ptr() }
}
}
impl<T> ContextRooted for &Compiler<T> {
fn context(&self) -> NonNull<spvc_context_s> {
unsafe { self.ctx.as_ptr() }
}
}
impl ContextRooted for &PhantomCompiler {
#[inline(always)]
fn context(&self) -> NonNull<spvc_context_s> {
self.ctx.0 .0
}
}