use std::ops::{Deref, Sub};
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
macro_rules! work {
($($(#[$doc:meta])* $field:ident: $count:ident,)*) => {
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug, serde::Serialize, schemars::JsonSchema)]
pub struct Work {
$($(#[$doc])* pub $field: u64,)*
}
#[derive(Clone, Copy)]
pub(crate) enum Count {
$($count,)*
}
#[derive(Default)]
pub(crate) struct Counters {
$($field: AtomicU64,)*
}
impl Counters {
pub(crate) fn add(&self, count: Count, n: u64) {
let counter = match count {
$(Count::$count => &self.$field,)*
};
counter.fetch_add(n, Relaxed);
}
pub(crate) fn work(&self) -> Work {
Work { $($field: self.$field.load(Relaxed),)* }
}
}
impl Sub for Work {
type Output = Work;
fn sub(self, before: Work) -> Work {
Work { $($field: self.$field.saturating_sub(before.$field),)* }
}
}
};
}
work! {
uploaded: Uploaded,
written: Written,
evaluated: Evaluated,
readbacks: Readbacks,
jobs: Jobs,
texts: Texts,
pipelines: Pipelines,
late: Late,
}
pub(crate) struct Queue {
raw: wgpu::Queue,
counters: Arc<Counters>,
}
impl Queue {
pub(crate) fn new(raw: wgpu::Queue, counters: Arc<Counters>) -> Queue {
Queue { raw, counters }
}
#[expect(clippy::disallowed_methods, reason = "the counted write")]
pub(crate) fn write_buffer(&self, buffer: &wgpu::Buffer, offset: u64, bytes: &[u8]) {
self.counters.add(Count::Written, bytes.len() as u64);
self.raw.write_buffer(buffer, offset, bytes);
}
pub(crate) fn write_buffer_in(
&self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
buffer: &wgpu::Buffer,
offset: u64,
bytes: &[u8],
) {
use wgpu::util::DeviceExt;
self.counters.add(Count::Written, bytes.len() as u64);
let upload = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("latkit ordered upload"),
contents: bytes,
usage: wgpu::BufferUsages::COPY_SRC,
});
encoder.copy_buffer_to_buffer(&upload, 0, buffer, offset, bytes.len() as u64);
}
#[expect(clippy::disallowed_methods, reason = "the counted upload")]
pub(crate) fn upload(&self, buffer: &wgpu::Buffer, offset: u64, bytes: &[u8]) {
self.counters.add(Count::Uploaded, bytes.len() as u64);
self.raw.write_buffer(buffer, offset, bytes);
}
#[expect(clippy::disallowed_methods, reason = "the counted write")]
pub(crate) fn write_texture(
&self,
texture: wgpu::TexelCopyTextureInfo<'_>,
bytes: &[u8],
layout: wgpu::TexelCopyBufferLayout,
size: wgpu::Extent3d,
) {
self.counters.add(Count::Written, bytes.len() as u64);
self.raw.write_texture(texture, bytes, layout, size);
}
}
impl Deref for Queue {
type Target = wgpu::Queue;
fn deref(&self) -> &wgpu::Queue {
&self.raw
}
}