use super::{address_space_str, ToWgsl, TryToWgsl};
use crate::common;
use crate::proc::TypeResolution;
use crate::{Handle, Scalar, TypeInner};
use alloc::string::String;
use core::fmt::Write;
pub trait TypeContext {
fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type;
fn type_name(&self, handle: Handle<crate::Type>) -> &str;
fn write_override<W: Write>(
&self,
r#override: Handle<crate::Override>,
out: &mut W,
) -> core::fmt::Result;
fn write_unnamed_struct<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result;
fn write_non_wgsl_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {
write!(out, "{{non-WGSL Naga type {inner:?}}}")
}
fn write_non_wgsl_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {
match scalar.kind {
crate::ScalarKind::Sint
| crate::ScalarKind::Uint
| crate::ScalarKind::Float
| crate::ScalarKind::Bool => write!(out, "{{non-WGSL Naga scalar {scalar:?}}}"),
crate::ScalarKind::AbstractInt => out.write_str("{AbstractInt}"),
crate::ScalarKind::AbstractFloat => out.write_str("{AbstractFloat}"),
}
}
fn write_type<W: Write>(&self, handle: Handle<crate::Type>, out: &mut W) -> core::fmt::Result {
let ty = self.lookup_type(handle);
match ty.inner {
TypeInner::Struct { .. } => out.write_str(self.type_name(handle))?,
ref other => self.write_type_inner(other, out)?,
}
Ok(())
}
fn write_type_inner<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {
match try_write_type_inner(self, inner, out) {
Ok(()) => Ok(()),
Err(WriteTypeError::Format(err)) => Err(err),
Err(WriteTypeError::NonWgsl) => self.write_non_wgsl_inner(inner, out),
}
}
fn write_scalar<W: Write>(&self, scalar: Scalar, out: &mut W) -> core::fmt::Result {
match scalar.try_to_wgsl() {
Some(string) => out.write_str(string),
None => self.write_non_wgsl_scalar(scalar, out),
}
}
fn write_type_resolution<W: Write>(
&self,
resolution: &TypeResolution,
out: &mut W,
) -> core::fmt::Result {
match *resolution {
TypeResolution::Handle(handle) => self.write_type(handle, out),
TypeResolution::Value(ref inner) => self.write_type_inner(inner, out),
}
}
fn write_type_conclusion<W: Write>(
&self,
conclusion: &crate::proc::Conclusion,
out: &mut W,
) -> core::fmt::Result {
use crate::proc::Conclusion as Co;
match *conclusion {
Co::Value(ref inner) => self.write_type_inner(inner, out),
Co::Predeclared(ref predeclared) => out.write_str(&predeclared.struct_name()),
}
}
fn write_type_rule<W: Write>(
&self,
name: &str,
rule: &crate::proc::Rule,
out: &mut W,
) -> core::fmt::Result {
write!(out, "fn {name}(")?;
for (i, arg) in rule.arguments.iter().enumerate() {
if i > 0 {
out.write_str(", ")?;
}
self.write_type_resolution(arg, out)?
}
out.write_str(") -> ")?;
self.write_type_conclusion(&rule.conclusion, out)?;
Ok(())
}
fn type_to_string(&self, handle: Handle<crate::Type>) -> String {
let mut buf = String::new();
self.write_type(handle, &mut buf).unwrap();
buf
}
fn type_resolution_to_string(&self, resolution: &TypeResolution) -> String {
let mut buf = String::new();
self.write_type_resolution(resolution, &mut buf).unwrap();
buf
}
fn type_rule_to_string(&self, name: &str, rule: &crate::proc::Rule) -> String {
let mut buf = String::new();
self.write_type_rule(name, rule, &mut buf).unwrap();
buf
}
}
fn try_write_type_inner<C, W>(ctx: &C, inner: &TypeInner, out: &mut W) -> Result<(), WriteTypeError>
where
C: TypeContext + ?Sized,
W: Write,
{
match *inner {
TypeInner::Vector { size, scalar } => {
write!(out, "vec{}<", common::vector_size_str(size))?;
ctx.write_scalar(scalar, out)?;
out.write_str(">")?;
}
TypeInner::Sampler { comparison: false } => {
write!(out, "sampler")?;
}
TypeInner::Sampler { comparison: true } => {
write!(out, "sampler_comparison")?;
}
TypeInner::Image {
dim,
arrayed,
class,
} => {
use crate::ImageClass as Ic;
let dim_str = dim.to_wgsl();
let arrayed_str = if arrayed { "_array" } else { "" };
match class {
Ic::Sampled { kind, multi } => {
let multisampled_str = if multi { "multisampled_" } else { "" };
write!(out, "texture_{multisampled_str}{dim_str}{arrayed_str}<")?;
ctx.write_scalar(Scalar { kind, width: 4 }, out)?;
out.write_str(">")?;
}
Ic::Depth { multi } => {
let multisampled_str = if multi { "multisampled_" } else { "" };
write!(
out,
"texture_depth_{multisampled_str}{dim_str}{arrayed_str}"
)?;
}
Ic::Storage { format, access } => {
let format_str = format.to_wgsl();
let access_str = if access.contains(crate::StorageAccess::ATOMIC) {
",atomic"
} else if access
.contains(crate::StorageAccess::LOAD | crate::StorageAccess::STORE)
{
",read_write"
} else if access.contains(crate::StorageAccess::LOAD) {
",read"
} else {
",write"
};
write!(
out,
"texture_storage_{dim_str}{arrayed_str}<{format_str}{access_str}>"
)?;
}
Ic::External => {
write!(out, "texture_external")?;
}
}
}
TypeInner::Scalar(scalar) => {
ctx.write_scalar(scalar, out)?;
}
TypeInner::Atomic(scalar) => {
out.write_str("atomic<")?;
ctx.write_scalar(scalar, out)?;
out.write_str(">")?;
}
TypeInner::Array {
base,
size,
stride: _,
} => {
write!(out, "array<")?;
match size {
crate::ArraySize::Constant(len) => {
ctx.write_type(base, out)?;
write!(out, ", {len}")?;
}
crate::ArraySize::Pending(r#override) => {
ctx.write_override(r#override, out)?;
}
crate::ArraySize::Dynamic => {
ctx.write_type(base, out)?;
}
}
write!(out, ">")?;
}
TypeInner::BindingArray { base, size } => {
write!(out, "binding_array<")?;
match size {
crate::ArraySize::Constant(len) => {
ctx.write_type(base, out)?;
write!(out, ", {len}")?;
}
crate::ArraySize::Pending(r#override) => {
ctx.write_override(r#override, out)?;
}
crate::ArraySize::Dynamic => {
ctx.write_type(base, out)?;
}
}
write!(out, ">")?;
}
TypeInner::Matrix {
columns,
rows,
scalar,
} => {
write!(
out,
"mat{}x{}<",
common::vector_size_str(columns),
common::vector_size_str(rows),
)?;
ctx.write_scalar(scalar, out)?;
out.write_str(">")?;
}
TypeInner::CooperativeMatrix {
columns,
rows,
scalar,
role,
} => {
write!(
out,
"coop_mat{}x{}<{},{}>",
columns as u32,
rows as u32,
scalar.try_to_wgsl().unwrap_or_default(),
role.to_wgsl(),
)?;
}
TypeInner::Pointer { base, space } => {
let (address, maybe_access) = address_space_str(space);
if let Some(space) = address {
write!(out, "ptr<{space}, ")?;
}
ctx.write_type(base, out)?;
if address.is_some() {
if let Some(access) = maybe_access {
write!(out, ", {access}")?;
}
write!(out, ">")?;
}
}
TypeInner::ValuePointer {
size: None,
scalar,
space,
} => {
let (address, maybe_access) = address_space_str(space);
if let Some(space) = address {
write!(out, "ptr<{space}, ")?;
ctx.write_scalar(scalar, out)?;
if let Some(access) = maybe_access {
write!(out, ", {access}")?;
}
write!(out, ">")?;
} else {
return Err(WriteTypeError::NonWgsl);
}
}
TypeInner::ValuePointer {
size: Some(size),
scalar,
space,
} => {
let (address, maybe_access) = address_space_str(space);
if let Some(space) = address {
write!(out, "ptr<{}, vec{}<", space, common::vector_size_str(size),)?;
ctx.write_scalar(scalar, out)?;
out.write_str(">")?;
if let Some(access) = maybe_access {
write!(out, ", {access}")?;
}
write!(out, ">")?;
} else {
return Err(WriteTypeError::NonWgsl);
}
write!(out, ">")?;
}
TypeInner::AccelerationStructure { vertex_return } => {
let caps = if vertex_return { "<vertex_return>" } else { "" };
write!(out, "acceleration_structure{caps}")?
}
TypeInner::Struct { .. } => {
ctx.write_unnamed_struct(inner, out)?;
}
TypeInner::RayQuery { vertex_return } => {
let caps = if vertex_return { "<vertex_return>" } else { "" };
write!(out, "ray_query{caps}")?
}
}
Ok(())
}
enum WriteTypeError {
Format(core::fmt::Error),
NonWgsl,
}
impl From<core::fmt::Error> for WriteTypeError {
fn from(err: core::fmt::Error) -> Self {
Self::Format(err)
}
}
impl TypeContext for crate::proc::GlobalCtx<'_> {
fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
&self.types[handle]
}
fn type_name(&self, handle: Handle<crate::Type>) -> &str {
self.types[handle]
.name
.as_deref()
.unwrap_or("{anonymous type}")
}
fn write_unnamed_struct<W: Write>(&self, _: &TypeInner, out: &mut W) -> core::fmt::Result {
write!(out, "{{unnamed struct}}")
}
fn write_override<W: Write>(
&self,
handle: Handle<crate::Override>,
out: &mut W,
) -> core::fmt::Result {
match self.overrides[handle].name {
Some(ref name) => out.write_str(name),
None => write!(out, "{{anonymous override {handle:?}}}"),
}
}
}
impl TypeContext for crate::UniqueArena<crate::Type> {
fn lookup_type(&self, handle: Handle<crate::Type>) -> &crate::Type {
&self[handle]
}
fn type_name(&self, handle: Handle<crate::Type>) -> &str {
self[handle].name.as_deref().unwrap_or("{anonymous type}")
}
fn write_unnamed_struct<W: Write>(&self, inner: &TypeInner, out: &mut W) -> core::fmt::Result {
write!(out, "{{unnamed struct {inner:?}}}")
}
fn write_override<W: Write>(
&self,
handle: Handle<crate::Override>,
out: &mut W,
) -> core::fmt::Result {
write!(out, "{{override {handle:?}}}")
}
}