Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jimblandy committed Jan 30, 2025
1 parent 2b4fd03 commit e6f92e2
Show file tree
Hide file tree
Showing 7 changed files with 1,185 additions and 583 deletions.
206 changes: 206 additions & 0 deletions naga/src/common/wgsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,209 @@ impl StandardFilterableTriggeringRule {
}
}
}

pub struct Wgslish<T>(pub T);

impl Display for Wgslish<&crate::TypeInner> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self.0 {
crate::TypeInner::Scalar(scalar) => Wgslish(scalar).fmt(f),
crate::TypeInner::Vector { size, scalar } => {
write!(f, "vec{}<{}>", size as u8, Wgslish(scalar))
}
crate::TypeInner::Matrix {
columns,
rows,
scalar,
} => {
write!(
f,
"mat{}x{}<{}>",
columns as u8,
rows as u8,
Wgslish(scalar)
)
}
crate::TypeInner::Atomic(scalar) => {
write!(f, "atomic<{}>", Wgslish(scalar))
}
crate::TypeInner::Pointer { base, space } => {
write!(f, "ptr<{}, {base:?}>", Wgslish(space))
}
crate::TypeInner::ValuePointer {
size,
scalar,
space,
} => {
write!(f, "ptr<{}, ", Wgslish(space))?;
match size {
Some(size) => write!(f, "vec{}<{}>", size as u8, Wgslish(scalar))?,
None => Wgslish(scalar).fmt(f)?,
}
f.write_str(">")
}
crate::TypeInner::Array { base, size, stride } => {
write!(f, "@stride({stride}) array<{base:?}")?;
match size {
crate::ArraySize::Constant(non_zero) => write!(f, ", {non_zero}>"),
crate::ArraySize::Pending(pending_array_size) => match pending_array_size {
crate::PendingArraySize::Expression(handle) => {
write!(f, "expression {handle:?}")
}
crate::PendingArraySize::Override(handle) => {
write!(f, "override {handle:?}")
}
},
crate::ArraySize::Dynamic => f.write_str(">"),
}
}
crate::TypeInner::Struct { ref members, span } => {
write!(f, "@span({span}) struct {{ ")?;
for (i, member) in members.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
write!(f, "@offset({}) ", member.offset)?;
if let Some(ref binding) = member.binding {
Wgslish(binding).fmt(f)?;
}
write!(f, "{}: {:?}",
member.name.as_deref().unwrap_or("<anonymous>"),
member.ty)?;
}
f.write_str("}")
}
crate::TypeInner::Image {
dim: _,
arrayed: _,
class: _,
} => todo!(),
crate::TypeInner::Sampler { comparison: _ } => todo!(),
crate::TypeInner::AccelerationStructure => todo!(),
crate::TypeInner::RayQuery => todo!(),
crate::TypeInner::BindingArray { base, size } => {
write!(f, "array<{base:?}")?;
match size {
crate::ArraySize::Constant(non_zero) => write!(f, ", {non_zero}>"),
crate::ArraySize::Pending(pending_array_size) => match pending_array_size {
crate::PendingArraySize::Expression(handle) => {
write!(f, "expression {handle:?}")
}
crate::PendingArraySize::Override(handle) => {
write!(f, "override {handle:?}")
}
},
crate::ArraySize::Dynamic => f.write_str(">"),
}
}
}
}
}

impl Display for Wgslish<crate::Scalar> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let bits = self.0.width * 8;
match self.0.kind {
crate::ScalarKind::Sint => write!(f, "i{bits}"),
crate::ScalarKind::Uint => write!(f, "u{bits}"),
crate::ScalarKind::Float => write!(f, "f{bits}"),
crate::ScalarKind::Bool => f.write_str("bool"),
crate::ScalarKind::AbstractInt => f.write_str("{AbstractInt}"),
crate::ScalarKind::AbstractFloat => f.write_str("{AbstractFloat}"),
}
}
}

impl Display for Wgslish<crate::AddressSpace> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let wgsl = match self.0 {
crate::AddressSpace::Function => "function",
crate::AddressSpace::Private => "private",
crate::AddressSpace::WorkGroup => "workgroup",
crate::AddressSpace::Uniform => "uniform",
crate::AddressSpace::Storage { access } => {
return write!(f, "{access:?}");
}
crate::AddressSpace::Handle => "handle",
crate::AddressSpace::PushConstant => "push_constant",
};
f.write_str(wgsl)
}
}

impl Display for Wgslish<&crate::Binding> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self.0 {
crate::Binding::BuiltIn(built_in) => Wgslish(built_in).fmt(f),
crate::Binding::Location { location, second_blend_source, interpolation, sampling } => {
write!(f, "@location({location})")?;
if second_blend_source {
f.write_str(" @second_blend_source")?;
}
if let Some(interpolation) = interpolation {
write!(f, " {}", Wgslish(interpolation))?;
}
if let Some(sampling) = sampling {
write!(f, " {}", Wgslish(sampling))?;
}
Ok(())
}
}
}
}

impl Display for Wgslish<crate::BuiltIn> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self.0 {
crate::BuiltIn::Position { invariant: true } => "@position @invariant",
crate::BuiltIn::Position { invariant: false } => "@position",
crate::BuiltIn::ViewIndex => "view_index",
crate::BuiltIn::BaseInstance => "{BaseInstance}",
crate::BuiltIn::BaseVertex => "{BaseVertex}",
crate::BuiltIn::ClipDistance => "{ClipDistance}",
crate::BuiltIn::CullDistance => "{CullDistance}",
crate::BuiltIn::InstanceIndex => "instance_index",
crate::BuiltIn::PointSize => "{PointSize}",
crate::BuiltIn::VertexIndex => "vertex_index",
crate::BuiltIn::DrawID => "{DrawId}",
crate::BuiltIn::FragDepth => "frag_depth",
crate::BuiltIn::PointCoord => "{PointCoord}",
crate::BuiltIn::FrontFacing => "front_facing",
crate::BuiltIn::PrimitiveIndex => "primitive_index",
crate::BuiltIn::SampleIndex => "sample_index",
crate::BuiltIn::SampleMask => "sample_mask",
crate::BuiltIn::GlobalInvocationId => "global_invocation_id",
crate::BuiltIn::LocalInvocationId => "local_invocation_id",
crate::BuiltIn::LocalInvocationIndex => "local_invocation_index",
crate::BuiltIn::WorkGroupId => "workgroup_id",
crate::BuiltIn::WorkGroupSize => "{WorkGroupSize}",
crate::BuiltIn::NumWorkGroups => "num_workgroups",
crate::BuiltIn::NumSubgroups => "num_subgroups",
crate::BuiltIn::SubgroupId => "{SubgroupId}",
crate::BuiltIn::SubgroupSize => "subgroup_size",
crate::BuiltIn::SubgroupInvocationId => "subgroup_invocation_id",
})
}
}

impl Display for Wgslish<crate::Interpolation> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self.0 {
crate::Interpolation::Perspective => "perspective",
crate::Interpolation::Linear => "linear",
crate::Interpolation::Flat => "flat",
})
}
}

impl Display for Wgslish<crate::Sampling> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self.0 {
crate::Sampling::Center => "center",
crate::Sampling::Centroid => "centroid",
crate::Sampling::Sample => "sample",
crate::Sampling::First => "first",
crate::Sampling::Either => "either",
})
}
}
168 changes: 168 additions & 0 deletions naga/src/proc/builtins/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//! An overload set represented as a static list.
#![allow(dead_code, unused_variables)]

/// A simple list of overloads.
///
/// Note that this type is not quite as general as it looks, in that
/// the implementation of `most_preferred` doesn't work for arbitrary
/// lists of overloads. See the documentation for [`List::rules`] for
/// details.
pub struct List {
/// A bitmask of which elements of `rules` are included in the set.
members: u64,

/// A list of type rules that are members of the set.
///
/// These must be listed in order such that the first rule in the
/// list is always more preferred than any other rule in the list.
/// If there is no such arrangement, then you cannot use `List` to
/// represent the overload set.
rules: &'static [Rule],
}

#[derive(Clone)]
pub struct Rule {
pub subexpressions: &'static [crate::TypeInner],
pub conclusion: crate::TypeInner,
}

impl List {
pub const fn from_rules(rules: &'static [Rule]) -> List {
List {
members: len_to_full_mask(rules.len()),
rules,
}
}

fn members(&self) -> impl Iterator<Item = (u64, &Rule)> {
OneBitsIter(self.members).map(|mask| {
let index = mask.trailing_zeros() as usize;
(mask, &self.rules[index])
})
}

fn filter<F>(&self, mut pred: F) -> List
where
F: FnMut(&Rule) -> bool,
{
let mut filtered_members = 0;
for (mask, rule) in self.members() {
if pred(rule) {
filtered_members |= mask;
}
}

List {
members: filtered_members,
rules: self.rules,
}
}
}

impl super::OverloadSet for List {
type Rule = Rule;

fn is_empty(&self) -> bool {
self.members == 0
}

fn max_subexpressions(&self) -> Option<usize> {
self.members().fold(None, |n, (_, rule)| {
std::cmp::max(n, Some(rule.subexpressions.len()))
})
}

fn arg(self, i: usize, ty: &crate::TypeInner, types: &crate::UniqueArena<crate::Type>) -> Self {
use crate::common::wgsl::Wgslish;
self.filter(|rule| {
log::debug!("JIMB: arg {i} of type {} in rule {rule:?}", Wgslish(ty));
if let Some(rule_ty) = rule.subexpressions.get(i) {
log::debug!("JIMB: converts automatically: {:?}",
ty.automatically_converts_to(rule_ty, types));
}
rule.subexpressions
.get(i)
.and_then(|rule_ty| ty.automatically_converts_to(rule_ty, types))
.is_some()
})
}

fn concrete_only(self, types: &crate::UniqueArena<crate::Type>) -> Self {
self.filter(|rule| {
rule.subexpressions
.iter()
.all(|arg_ty| !arg_ty.is_abstract(types))
})
}

fn most_preferred(self) -> Option<Rule> {
self.members().next().map(|(_, rule)| rule.clone())
}
}

impl super::Rule for Rule {
fn leaf_scalar(&self, i: usize) -> Option<crate::Scalar> {
// TODO: This is wrong for arrays
self.subexpressions[i].scalar()
}

fn conclusion_type(&self) -> crate::TypeInner {
self.conclusion.clone()
}
}

const fn len_to_full_mask(n: usize) -> u64 {
if n >= 64 {
panic!("List::rules can only hold up to 63 rules");
}

(1_u64 << n) - 1
}

impl std::fmt::Debug for List {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(
self.members()
.map(|(mask, rule)| (rule, self.members & mask != 0)),
)
.finish()
}
}

impl std::fmt::Debug for Rule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::common::wgsl::Wgslish;

f.write_str("(")?;
for (i, subexpression) in self.subexpressions.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{}", Wgslish(subexpression))?;
}
write!(f, ") -> {}", Wgslish(&self.conclusion))
}
}

/// An iterator that produces the set bits in the given `u64`.
///
/// The values produced are masks, not bit numbers. Use
/// `u64::trailing_zeros` if you need bit numbers.
struct OneBitsIter(u64);

impl Iterator for OneBitsIter {
type Item = u64;

fn next(&mut self) -> Option<Self::Item> {
if self.0 == 0 {
return None;
}

// Return the lowest `1` bit, and remove it.
let mask = self.0 - 1;
let item = self.0 & !mask;
self.0 &= mask;
Some(item)
}
}
Loading

0 comments on commit e6f92e2

Please sign in to comment.