use std::{
convert::TryInto,
sync::{Arc, Mutex},
};
use crate::{Client, SpanLocation};
#[repr(u8)]
pub enum GpuContextType {
Invalid = 0,
OpenGL = 1,
Vulkan = 2,
OpenCL = 3,
Direct3D12 = 4,
Direct3D11 = 5,
}
#[derive(Clone)]
pub struct GpuContext {
#[cfg(feature = "enable")]
_client: Client,
#[cfg(feature = "enable")]
value: u8,
#[cfg(feature = "enable")]
span_freelist: Arc<Mutex<Vec<u16>>>,
_private: (),
}
#[cfg(feature = "enable")]
static GPU_CONTEXT_INDEX: Mutex<u8> = Mutex::new(0);
#[derive(Debug)]
pub enum GpuContextCreationError {
TooManyContextsCreated,
}
impl std::fmt::Display for GpuContextCreationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"More than 255 contexts have been created at any point in the execution of this program."
)
}
}
impl std::error::Error for GpuContextCreationError {}
#[derive(Debug, PartialEq)]
enum GpuSpanState {
Started,
Ended,
}
#[must_use]
pub struct GpuSpan {
#[cfg(feature = "enable")]
context: GpuContext,
#[cfg(feature = "enable")]
start_query_id: u16,
#[cfg(feature = "enable")]
end_query_id: u16,
#[cfg(feature = "enable")]
state: GpuSpanState,
_private: (),
}
#[derive(Debug)]
pub enum GpuSpanCreationError {
TooManyPendingSpans,
}
impl std::fmt::Display for GpuSpanCreationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Too many spans still waiting for gpu data. There may not be more than 32767 spans that are pending gpu data at once."
)
}
}
impl std::error::Error for GpuSpanCreationError {}
impl Client {
pub fn new_gpu_context(
self,
name: Option<&str>,
ty: GpuContextType,
gpu_timestamp: i64,
period: f32,
) -> Result<GpuContext, GpuContextCreationError> {
#[cfg(feature = "enable")]
{
let mut context_index_guard = GPU_CONTEXT_INDEX.lock().unwrap();
if *context_index_guard == 255 {
return Err(GpuContextCreationError::TooManyContextsCreated);
}
let context = *context_index_guard;
*context_index_guard += 1;
drop(context_index_guard);
unsafe {
sys::___tracy_emit_gpu_new_context_serial(sys::___tracy_gpu_new_context_data {
gpuTime: gpu_timestamp,
period,
context,
flags: 0,
type_: ty as u8,
});
};
if let Some(name) = name {
unsafe {
sys::___tracy_emit_gpu_context_name_serial(
sys::___tracy_gpu_context_name_data {
context,
name: name.as_ptr().cast(),
len: name.len().try_into().unwrap_or(u16::MAX),
},
);
}
}
Ok(GpuContext {
_client: self,
value: context,
span_freelist: Arc::new(Mutex::new((0..=u16::MAX).collect())),
_private: (),
})
}
#[cfg(not(feature = "enable"))]
Ok(GpuContext { _private: () })
}
}
impl GpuContext {
#[cfg(feature = "enable")]
fn alloc_span_ids(&self) -> Result<(u16, u16), GpuSpanCreationError> {
let mut freelist = self.span_freelist.lock().unwrap();
if freelist.len() < 2 {
return Err(GpuSpanCreationError::TooManyPendingSpans);
}
let start = freelist.pop().unwrap();
let end = freelist.pop().unwrap();
Ok((start, end))
}
pub fn span(
&self,
span_location: &'static SpanLocation,
) -> Result<GpuSpan, GpuSpanCreationError> {
#[cfg(feature = "enable")]
{
let (start_query_id, end_query_id) = self.alloc_span_ids()?;
unsafe {
sys::___tracy_emit_gpu_zone_begin_serial(sys::___tracy_gpu_zone_begin_data {
srcloc: std::ptr::addr_of!(span_location.data) as usize as u64,
queryId: start_query_id,
context: self.value,
});
};
Ok(GpuSpan {
context: self.clone(),
start_query_id,
end_query_id,
state: GpuSpanState::Started,
_private: (),
})
}
#[cfg(not(feature = "enable"))]
Ok(GpuSpan { _private: () })
}
pub fn span_alloc(
&self,
name: &str,
function: &str,
file: &str,
line: u32,
) -> Result<GpuSpan, GpuSpanCreationError> {
#[cfg(feature = "enable")]
{
let srcloc = unsafe {
sys::___tracy_alloc_srcloc_name(
line,
file.as_ptr().cast(),
file.len(),
function.as_ptr().cast(),
function.len(),
name.as_ptr().cast(),
name.len(),
0,
)
};
let (start_query_id, end_query_id) = self.alloc_span_ids()?;
unsafe {
sys::___tracy_emit_gpu_zone_begin_alloc_serial(sys::___tracy_gpu_zone_begin_data {
srcloc,
queryId: start_query_id,
context: self.value,
});
};
Ok(GpuSpan {
context: self.clone(),
start_query_id,
end_query_id,
state: GpuSpanState::Started,
_private: (),
})
}
#[cfg(not(feature = "enable"))]
Ok(GpuSpan { _private: () })
}
}
impl GpuSpan {
pub fn end_zone(&mut self) {
#[cfg(feature = "enable")]
{
if self.state != GpuSpanState::Started {
return;
}
unsafe {
sys::___tracy_emit_gpu_zone_end_serial(sys::___tracy_gpu_zone_end_data {
queryId: self.end_query_id,
context: self.context.value,
});
};
self.state = GpuSpanState::Ended;
}
}
pub fn upload_timestamp_start(&self, start_timestamp: i64) {
#[cfg(feature = "enable")]
unsafe {
sys::___tracy_emit_gpu_time_serial(sys::___tracy_gpu_time_data {
gpuTime: start_timestamp,
queryId: self.start_query_id,
context: self.context.value,
});
};
}
pub fn upload_timestamp_end(&self, end_timestamp: i64) {
#[cfg(feature = "enable")]
unsafe {
sys::___tracy_emit_gpu_time_serial(sys::___tracy_gpu_time_data {
gpuTime: end_timestamp,
queryId: self.end_query_id,
context: self.context.value,
});
};
}
}
impl Drop for GpuSpan {
fn drop(&mut self) {
#[cfg(feature = "enable")]
{
match self.state {
GpuSpanState::Started => {
self.end_zone();
}
GpuSpanState::Ended => {}
}
let mut freelist = self.context.span_freelist.lock().unwrap();
freelist.push(self.start_query_id);
freelist.push(self.end_query_id);
drop(freelist);
}
}
}