use crate::action::ActionDescriptor;
use crate::authoring::model::binding::BindingDecl;
use crate::authoring::model::field::Extent;
use crate::params::{ParamDescriptor, ParamValue};
use crate::spatial_hash::HashGrid;
use crate::view::{StatDescriptor, StatValue};
#[derive(Debug)]
pub struct BufferSpec {
pub label: &'static str,
pub double_buffered: bool,
pub drawable: bool,
}
#[macro_export]
macro_rules! buffers {
($($(#[$meta:meta])* $vis:vis const $name:ident = $label:literal $($flag:ident)*;)+) => {
$crate::__indices!(0usize, $([$(#[$meta])* $vis $name],)+);
const BUFFER_SPECS: &[$crate::__macro_support::BufferSpec] = &[
$($crate::__buffer_flags!(
$crate::__macro_support::BufferSpec {
label: $label,
double_buffered: false,
drawable: false,
}; $($flag)*
)),+
];
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __buffer_flags {
($spec:expr;) => { $spec };
($spec:expr; double_buffered $($rest:ident)*) => {
$crate::__buffer_flags!(
$crate::__macro_support::BufferSpec { double_buffered: true, ..$spec }; $($rest)*
)
};
($spec:expr; drawable $($rest:ident)*) => {
$crate::__buffer_flags!(
$crate::__macro_support::BufferSpec { drawable: true, ..$spec }; $($rest)*
)
};
}
#[derive(Debug, Clone, Copy)]
pub enum Domain {
Agents,
Cells(u32),
AgentsOrCells,
}
impl Domain {
pub fn invocations(self, geom: &Geometry) -> u32 {
let cells = geom.n_cells;
match self {
Self::Agents => geom.num_agents,
Self::Cells(n) => cells * n,
Self::AgentsOrCells => geom.num_agents.max(cells),
}
}
}
pub struct PassSpec {
pub label: &'static str,
pub shader: &'static str,
pub bindings: &'static [BindingDecl],
pub domain: Domain,
}
impl std::fmt::Debug for PassSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PassSpec")
.field("label", &self.label)
.field("shader_len", &self.shader.len())
.field("bindings", &self.bindings)
.field("domain", &self.domain)
.finish_non_exhaustive()
}
}
pub struct DisplaySpec {
pub shader: &'static str,
pub bindings: &'static [BindingDecl],
pub workgroup: u32,
}
impl std::fmt::Debug for DisplaySpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DisplaySpec")
.field("shader_len", &self.shader.len())
.field("bindings", &self.bindings)
.field("workgroup", &self.workgroup)
.finish_non_exhaustive()
}
}
pub struct ReduceSpec {
pub shader: &'static str,
pub bindings: &'static [BindingDecl],
pub lanes: usize,
pub domain: Domain,
}
impl std::fmt::Debug for ReduceSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReduceSpec")
.field("shader_len", &self.shader.len())
.field("bindings", &self.bindings)
.field("lanes", &self.lanes)
.field("domain", &self.domain)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct GpuAgentAction {
pub desc: ActionDescriptor,
pub pass: PassSpec,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PassId {
Step(usize),
Display,
Reduce,
Action(usize),
}
#[derive(Clone, Copy, Debug)]
pub struct Geometry {
pub num_agents: u32,
pub extent: Extent,
pub width: u32,
pub height: u32,
pub n_cells: u32,
pub display: (u32, u32),
pub index: Option<HashGrid>,
}
#[derive(Clone, Copy, Debug)]
pub struct PassCtx<'a> {
pub geom: &'a Geometry,
pub invocations: u32,
pub groups_x: u32,
pub seed: u32,
}
pub trait GpuAgentModel: Send + Sync + 'static {
const NAME: &'static str;
const ID: &'static str;
const DESCRIPTION: &'static str;
const STATS: &'static [StatDescriptor];
const BUFFERS: &'static [BufferSpec];
const POS_BUFFER: usize;
const COLOR_BUFFER: usize;
const INDEX: bool = false;
const COUNTERS: usize = 0;
const STEP_PASSES: &'static [PassSpec];
const DISPLAY: Option<DisplaySpec> = None;
const ACTIONS: &'static [GpuAgentAction] = &[];
const REDUCE: ReduceSpec;
const REPLAYS_EXACTLY: bool = true;
fn param_descriptors() -> Vec<ParamDescriptor>;
fn dims(params: &[ParamValue]) -> (u32, Extent);
fn buffer_lens(geom: &Geometry) -> Vec<usize>;
fn seed_buffers(geom: &Geometry, params: &[ParamValue], seed: Option<u64>) -> Vec<Vec<u8>>;
fn index_cell_size(_params: &[ParamValue]) -> f32 {
1.0
}
fn pass_params_bytes(pass: PassId, ctx: PassCtx<'_>, params: &[ParamValue]) -> Vec<u8>;
fn stats(sums: &[f32], counters: &[u32], geom: &Geometry) -> Vec<StatValue>;
}
#[cfg(test)]
mod tests {
#[test]
fn buffers_macro_expands_in_function_scope() {
crate::buffers! {
const POS = "pos" double_buffered drawable;
const SITES = "sites";
}
assert_eq!((POS, SITES), (0, 1), "indices follow declaration order");
assert_eq!(BUFFER_SPECS.len(), 2);
assert!(BUFFER_SPECS[POS].double_buffered && BUFFER_SPECS[POS].drawable);
assert!(!BUFFER_SPECS[SITES].double_buffered && !BUFFER_SPECS[SITES].drawable);
}
}