use crate::{
error::CudaError,
memory::{
CudaBufferPoolReuseGuard, CudaDeviceBuffer, CudaDeviceBufferRange, CudaPooledDeviceBuffer,
},
};
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaKernelOutput {
#[doc(hidden)]
pub buffer: CudaDeviceBuffer,
#[doc(hidden)]
pub execution: CudaExecutionStats,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaKernelBatchOutput {
#[doc(hidden)]
pub outputs: Vec<CudaDeviceBuffer>,
#[doc(hidden)]
pub execution: CudaExecutionStats,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaKernelContiguousBatchOutput {
#[doc(hidden)]
pub output: CudaDeviceBuffer,
#[doc(hidden)]
pub ranges: Vec<CudaDeviceBufferRange>,
#[doc(hidden)]
pub execution: CudaExecutionStats,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaPooledKernelOutput {
#[doc(hidden)]
pub buffer: CudaPooledDeviceBuffer,
#[doc(hidden)]
pub execution: CudaExecutionStats,
}
#[doc(hidden)]
#[derive(Debug)]
#[must_use = "queued CUDA work must be finished or retained until Drop synchronizes it"]
pub struct CudaQueuedExecution {
#[doc(hidden)]
pub resources: Vec<CudaPooledDeviceBuffer>,
#[doc(hidden)]
pub execution: CudaExecutionStats,
#[doc(hidden)]
pub pool_reuse_guard: Option<CudaBufferPoolReuseGuard>,
}
impl CudaQueuedExecution {
#[doc(hidden)]
pub fn new(
resources: Vec<CudaPooledDeviceBuffer>,
execution: CudaExecutionStats,
pool_reuse_guard: Option<CudaBufferPoolReuseGuard>,
) -> Self {
Self {
resources,
execution,
pool_reuse_guard,
}
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn resource_count(&self) -> usize {
self.resources.len()
}
pub fn finish(mut self) -> Result<CudaExecutionStats, CudaError> {
let completion_result = self
.pool_reuse_guard
.take()
.map_or(Ok(()), CudaBufferPoolReuseGuard::synchronize_and_release);
self.resources.clear();
completion_result?;
Ok(self.execution)
}
#[doc(hidden)]
pub fn finish_with_resources(
mut self,
) -> Result<(Vec<CudaPooledDeviceBuffer>, CudaExecutionStats), CudaError> {
let completion_result = self
.pool_reuse_guard
.take()
.map_or(Ok(()), CudaBufferPoolReuseGuard::synchronize_and_release);
if let Err(error) = completion_result {
self.resources.clear();
return Err(error);
}
Ok((std::mem::take(&mut self.resources), self.execution))
}
#[doc(hidden)]
pub unsafe fn finish_with_resources_after_completion(
mut self,
) -> Result<(Vec<CudaPooledDeviceBuffer>, CudaExecutionStats), CudaError> {
if let Some(guard) = self.pool_reuse_guard.take() {
guard.release()?;
}
Ok((std::mem::take(&mut self.resources), self.execution))
}
#[doc(hidden)]
pub unsafe fn release_pool_reuse_after_completion(&mut self) -> Result<(), CudaError> {
self.resources.clear();
if let Some(guard) = self.pool_reuse_guard.take() {
guard.release()?;
}
Ok(())
}
}
impl Drop for CudaQueuedExecution {
fn drop(&mut self) {
let Some(guard) = self.pool_reuse_guard.take() else {
return;
};
let outcome = guard.synchronize_pool_context();
self.resources.clear();
if outcome.completion_established() {
let _ = guard.release();
} else {
guard.abandon();
}
}
}
impl CudaKernelOutput {
#[doc(hidden)]
pub fn new(buffer: CudaDeviceBuffer, execution: CudaExecutionStats) -> Self {
Self { buffer, execution }
}
pub fn buffer(&self) -> &CudaDeviceBuffer {
&self.buffer
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn into_parts(self) -> (CudaDeviceBuffer, CudaExecutionStats) {
(self.buffer, self.execution)
}
}
impl CudaKernelBatchOutput {
#[doc(hidden)]
pub fn new(outputs: Vec<CudaDeviceBuffer>, execution: CudaExecutionStats) -> Self {
Self { outputs, execution }
}
pub fn outputs(&self) -> &[CudaDeviceBuffer] {
&self.outputs
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn into_parts(self) -> (Vec<CudaDeviceBuffer>, CudaExecutionStats) {
(self.outputs, self.execution)
}
}
impl CudaKernelContiguousBatchOutput {
#[doc(hidden)]
pub fn new(
output: CudaDeviceBuffer,
ranges: Vec<CudaDeviceBufferRange>,
execution: CudaExecutionStats,
) -> Self {
Self {
output,
ranges,
execution,
}
}
pub fn output(&self) -> &CudaDeviceBuffer {
&self.output
}
pub fn ranges(&self) -> &[CudaDeviceBufferRange] {
&self.ranges
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn into_parts(
self,
) -> (
CudaDeviceBuffer,
Vec<CudaDeviceBufferRange>,
CudaExecutionStats,
) {
(self.output, self.ranges, self.execution)
}
}
impl CudaPooledKernelOutput {
#[doc(hidden)]
pub fn new(buffer: CudaPooledDeviceBuffer, execution: CudaExecutionStats) -> Self {
Self { buffer, execution }
}
pub fn buffer(&self) -> Option<&CudaDeviceBuffer> {
self.buffer.as_device_buffer()
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn into_parts(self) -> (CudaPooledDeviceBuffer, CudaExecutionStats) {
(self.buffer, self.execution)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CudaExecutionStats {
#[doc(hidden)]
pub kernel_dispatches: usize,
#[doc(hidden)]
pub copy_kernel_dispatches: usize,
#[doc(hidden)]
pub decode_kernel_dispatches: usize,
#[doc(hidden)]
pub hardware_decode: bool,
}
impl CudaExecutionStats {
#[doc(hidden)]
pub const fn new(
kernel_dispatches: usize,
copy_kernel_dispatches: usize,
decode_kernel_dispatches: usize,
hardware_decode: bool,
) -> Self {
Self {
kernel_dispatches,
copy_kernel_dispatches,
decode_kernel_dispatches,
hardware_decode,
}
}
pub fn kernel_dispatches(self) -> usize {
self.kernel_dispatches
}
pub fn copy_kernel_dispatches(self) -> usize {
self.copy_kernel_dispatches
}
pub fn decode_kernel_dispatches(self) -> usize {
self.decode_kernel_dispatches
}
pub fn used_hardware_decode(self) -> bool {
self.hardware_decode
}
}