use super::fingerprint::program_fingerprint;
use super::types::{
ColorParameter, Instruction, Parameter, ProgramOutputs, ScalarParameter, ValueKind,
VectorParameter, VisualCompatibility, VisualError, VisualOutput, VisualStage,
next_program_owner,
};
use super::{MAX_VISUAL_INSTRUCTIONS, MAX_VISUAL_PARAMETERS, MAX_VISUAL_PROPERTIES};
use crate::{AtomPropertyHandle, AttributeHandle, AttributeKind};
use std::fmt;
use std::sync::Arc;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct VisualColumnKey(pub u64);
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct VisualInstructionGpu {
pub control: [u32; 4],
pub data: [f32; 4],
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum VisualAttributeRef {
Attribute {
handle: AttributeHandle,
kind: AttributeKind,
},
Column {
key: VisualColumnKey,
kind: AttributeKind,
},
#[doc(hidden)]
LegacyScalar(AtomPropertyHandle),
}
impl VisualAttributeRef {
#[must_use]
pub const fn kind(self) -> AttributeKind {
match self {
Self::Attribute { kind, .. } | Self::Column { kind, .. } => kind,
Self::LegacyScalar(_) => AttributeKind::Scalar,
}
}
#[must_use]
pub const fn attribute(self) -> Option<AttributeHandle> {
match self {
Self::Attribute { handle, .. } => Some(handle),
Self::Column { .. } | Self::LegacyScalar(_) => None,
}
}
#[must_use]
pub const fn column(self) -> Option<VisualColumnKey> {
match self {
Self::Column { key, .. } => Some(key),
Self::Attribute { .. } | Self::LegacyScalar(_) => None,
}
}
#[doc(hidden)]
#[must_use]
pub const fn legacy_scalar(self) -> Option<AtomPropertyHandle> {
match self {
Self::Attribute { .. } | Self::Column { .. } => None,
Self::LegacyScalar(handle) => Some(handle),
}
}
}
#[derive(Clone, Debug)]
pub struct VisualProgram {
pub(crate) instructions: Arc<[Instruction]>,
pub(crate) outputs: ProgramOutputs,
pub(crate) attributes: Arc<[VisualAttributeRef]>,
pub(crate) properties: Arc<[AtomPropertyHandle]>,
pub(crate) parameter_kinds: Arc<[ValueKind]>,
pub(crate) parameter_defaults: Arc<[[f32; 4]]>,
pub(crate) parameter_owner: u64,
pub(crate) maximum_displacement: f32,
pub(crate) fingerprint: u64,
}
impl PartialEq for VisualProgram {
fn eq(&self, other: &Self) -> bool {
self.instructions == other.instructions
&& self.outputs == other.outputs
&& self.attributes == other.attributes
&& self.properties == other.properties
&& self.parameter_kinds == other.parameter_kinds
&& self.parameter_defaults == other.parameter_defaults
&& self.maximum_displacement == other.maximum_displacement
&& self.fingerprint == other.fingerprint
}
}
impl VisualProgram {
#[must_use]
pub fn instructions(&self) -> &[Instruction] {
&self.instructions
}
#[must_use]
pub fn attributes(&self) -> &[VisualAttributeRef] {
&self.attributes
}
#[doc(hidden)]
#[must_use]
pub fn properties(&self) -> &[AtomPropertyHandle] {
&self.properties
}
#[must_use]
pub const fn maximum_displacement(&self) -> f32 {
self.maximum_displacement
}
#[must_use]
pub const fn fingerprint(&self) -> u64 {
self.fingerprint
}
pub fn write_gpu_instructions(&self, output: &mut Vec<VisualInstructionGpu>) {
output.clear();
output.reserve(self.instructions.len().saturating_sub(output.capacity()));
output.extend(self.instructions.iter().map(|instruction| {
let operands = instruction.operands;
VisualInstructionGpu {
control: [
instruction.opcode(),
u32::from(instruction.kind_code()),
u32::from(operands[0])
| (u32::from(operands[1]) << 8)
| (u32::from(operands[2]) << 16),
u32::from(instruction.stage().code()),
],
data: instruction.data(),
}
}));
}
#[must_use]
pub const fn output_register(&self, output: VisualOutput) -> Option<u8> {
self.outputs.get(output)
}
#[must_use]
pub fn output_stage(&self, output: VisualOutput) -> Option<VisualStage> {
self.output_register(output)
.and_then(|register| self.instructions.get(usize::from(register)))
.map(Instruction::stage)
}
pub(crate) fn parameter_kinds(&self) -> &[ValueKind] {
&self.parameter_kinds
}
pub(crate) fn parameter_defaults(&self) -> &[[f32; 4]] {
&self.parameter_defaults
}
#[cfg(test)]
pub(crate) fn from_serialized(
instructions: Vec<Instruction>,
outputs: &[(VisualOutput, u8)],
properties: Vec<AtomPropertyHandle>,
parameter_kinds: Vec<ValueKind>,
parameter_defaults: Vec<[f32; 4]>,
maximum_displacement: f32,
) -> Result<Self, VisualError> {
let attributes = properties
.iter()
.copied()
.map(VisualAttributeRef::LegacyScalar)
.collect::<Vec<_>>();
Self::from_serialized_attributes(
instructions,
outputs,
attributes,
properties,
parameter_kinds,
parameter_defaults,
maximum_displacement,
)
}
pub(crate) fn from_serialized_attributes(
instructions: Vec<Instruction>,
outputs: &[(VisualOutput, u8)],
attributes: Vec<VisualAttributeRef>,
properties: Vec<AtomPropertyHandle>,
parameter_kinds: Vec<ValueKind>,
parameter_defaults: Vec<[f32; 4]>,
maximum_displacement: f32,
) -> Result<Self, VisualError> {
if instructions.is_empty()
|| instructions.len() > MAX_VISUAL_INSTRUCTIONS
|| attributes.len() > MAX_VISUAL_PROPERTIES
|| parameter_kinds.len() > MAX_VISUAL_PARAMETERS
|| parameter_kinds.len() != parameter_defaults.len()
|| !maximum_displacement.is_finite()
|| maximum_displacement < 0.0
|| parameter_defaults
.iter()
.flatten()
.any(|value| !value.is_finite())
{
return Err(VisualError::MalformedProgram);
}
super::validation::validate_instructions(
&instructions,
attributes.len(),
¶meter_kinds,
)?;
let mut program_outputs = ProgramOutputs::default();
for (output, register) in outputs {
if program_outputs.get(*output).is_some() {
return Err(VisualError::MalformedProgram);
}
let instruction = instructions
.get(usize::from(*register))
.ok_or(VisualError::MalformedProgram)?;
if instruction.kind != super::validation::output_kind(*output) {
return Err(VisualError::MalformedProgram);
}
if matches!(
output,
VisualOutput::RadiusScale | VisualOutput::WidthScale | VisualOutput::PositionOffset
) && instruction.stage == VisualStage::Fragment
{
return Err(VisualError::StageViolation { output: *output });
}
program_outputs.set(*output, *register);
}
if program_outputs.present == 0 {
return Err(VisualError::MissingOutput);
}
let fingerprint = program_fingerprint(
&instructions,
&attributes,
¶meter_kinds,
¶meter_defaults,
program_outputs,
maximum_displacement,
);
Ok(Self {
instructions: Arc::from(instructions),
outputs: program_outputs,
attributes: attributes.into(),
properties: Arc::from(properties),
parameter_kinds: Arc::from(parameter_kinds),
parameter_defaults: Arc::from(parameter_defaults),
parameter_owner: next_program_owner(),
maximum_displacement,
fingerprint,
})
}
#[must_use]
pub fn fragment_instruction_count(&self) -> usize {
self.stage_count(VisualStage::Fragment)
}
#[must_use]
pub fn entity_instruction_count(&self) -> usize {
self.stage_count(VisualStage::Entity)
}
#[must_use]
pub fn uniform_instruction_count(&self) -> usize {
self.stage_count(VisualStage::Uniform)
}
fn stage_count(&self, stage: VisualStage) -> usize {
self.instructions
.iter()
.filter(|instruction| instruction.stage == stage)
.count()
}
#[must_use]
pub fn scalar_parameter(&self, index: usize) -> Option<ScalarParameter> {
(self.parameter_kinds.get(index) == Some(&ValueKind::Scalar))
.then(|| ScalarParameter(self.parameter(index)))
}
#[must_use]
pub fn color_parameter(&self, index: usize) -> Option<ColorParameter> {
(self.parameter_kinds.get(index) == Some(&ValueKind::Color))
.then(|| ColorParameter(self.parameter(index)))
}
#[must_use]
pub fn vector_parameter(&self, index: usize) -> Option<VectorParameter> {
(self.parameter_kinds.get(index) == Some(&ValueKind::Vector))
.then(|| VectorParameter(self.parameter(index)))
}
fn parameter(&self, index: usize) -> Parameter {
Parameter {
builder: self.parameter_owner,
index: u8::try_from(index)
.into_iter()
.fold(u8::MAX, |_, index| index),
}
}
pub fn validate_compatibility(
&self,
compatibility: VisualCompatibility,
) -> Result<(), VisualError> {
for output in ALL_OUTPUTS {
if self.outputs.get(output).is_some() && !compatibility.supports(output) {
return Err(VisualError::UnsupportedOutput { output });
}
}
Ok(())
}
}
const ALL_OUTPUTS: [VisualOutput; 11] = [
VisualOutput::BaseColor,
VisualOutput::Opacity,
VisualOutput::Emission,
VisualOutput::Roughness,
VisualOutput::Specular,
VisualOutput::MaterialStrength,
VisualOutput::Visibility,
VisualOutput::SilhouetteSoftness,
VisualOutput::RadiusScale,
VisualOutput::WidthScale,
VisualOutput::PositionOffset,
];
#[derive(Clone, PartialEq, Debug)]
pub struct VisualStyle {
program: VisualProgram,
parameters: Arc<[[f32; 4]]>,
}
impl VisualStyle {
#[must_use]
pub fn new(program: VisualProgram) -> Self {
Self {
parameters: Arc::clone(&program.parameter_defaults),
program,
}
}
#[must_use]
pub const fn program(&self) -> &VisualProgram {
&self.program
}
#[must_use]
pub fn parameters(&self) -> &[[f32; 4]] {
&self.parameters
}
#[must_use]
pub fn parameter_fingerprint(&self) -> u64 {
let mut hash = 0xcbf2_9ce4_8422_2325u64;
for value in self.parameters.iter().flatten() {
hash ^= u64::from(value.to_bits());
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
}
hash
}
pub(crate) fn from_parameters(
program: VisualProgram,
parameters: Vec<[f32; 4]>,
) -> Result<Self, VisualError> {
if parameters.len() != program.parameter_kinds.len()
|| parameters.iter().flatten().any(|value| !value.is_finite())
{
return Err(VisualError::InvalidParameter);
}
Ok(Self {
program,
parameters: Arc::from(parameters),
})
}
pub fn set_scalar(
&mut self,
parameter: ScalarParameter,
value: f32,
) -> Result<(), VisualError> {
self.set_parameter(parameter.0, ValueKind::Scalar, [value, 0.0, 0.0, 0.0])
}
pub fn set_color(
&mut self,
parameter: ColorParameter,
value: [f32; 4],
) -> Result<(), VisualError> {
self.set_parameter(parameter.0, ValueKind::Color, value)
}
pub fn set_vector(
&mut self,
parameter: VectorParameter,
value: [f32; 3],
) -> Result<(), VisualError> {
self.set_parameter(
parameter.0,
ValueKind::Vector,
[value[0], value[1], value[2], 0.0],
)
}
fn set_parameter(
&mut self,
parameter: Parameter,
kind: ValueKind,
value: [f32; 4],
) -> Result<(), VisualError> {
let index = usize::from(parameter.index);
if parameter.builder != self.program.parameter_owner
|| self.program.parameter_kinds.get(index) != Some(&kind)
{
return Err(VisualError::InvalidParameter);
}
if value.iter().any(|component| !component.is_finite()) {
return Err(VisualError::NonFinite);
}
Arc::make_mut(&mut self.parameters)[index] = value;
Ok(())
}
}
impl fmt::Display for VisualProgram {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "visual-program:{:016x}", self.fingerprint)
}
}