Skip to content
This repository has been archived by the owner on Jan 29, 2025. It is now read-only.

Commit

Permalink
[glsl-out] Emit globals of any type, as required by Naga IR.
Browse files Browse the repository at this point in the history
Fixes #1811.
  • Loading branch information
jimblandy committed Apr 13, 2022
1 parent 78975f2 commit 0ef61e4
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 78 deletions.
170 changes: 101 additions & 69 deletions src/back/glsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,12 @@ impl crate::AddressSpace {
/// Whether a variable with this address space can be initialized
fn initializable(&self) -> bool {
match *self {
crate::AddressSpace::Function | crate::AddressSpace::Private => true,
crate::AddressSpace::WorkGroup
| crate::AddressSpace::Uniform
| crate::AddressSpace::PushConstant
| crate::AddressSpace::Storage { .. } => false,
_ => true,
| crate::AddressSpace::Storage { .. }
| crate::AddressSpace::Handle
| crate::AddressSpace::PushConstant => false,
}
}
}
Expand Down Expand Up @@ -570,27 +571,19 @@ impl<'a, W: Write> Writer<'a, W> {

let ep_info = self.info.get_entry_point(self.entry_point_idx as usize);

// Write all structs
// Write struct types.
//
// This are always ordered because of the IR is structured in a way that you can't make a
// struct without adding all of it's members first
// This are always ordered because the IR is structured in a way that
// you can't make a struct without adding all of its members first.
for (handle, ty) in self.module.types.iter() {
if let TypeInner::Struct { ref members, .. } = ty.inner {
let generate_struct = match GlobalTypeKind::new(self.module, handle) {
GlobalTypeKind::WrappedStruct => true,
// If it's a global non-wrapped struct, it will be printed
// with the corresponding global variable.
GlobalTypeKind::Unsized(_) => false,
GlobalTypeKind::Other => {
let used_by_global =
self.module.global_variables.iter().any(|(vh, var)| {
!ep_info[vh].is_empty() && var.space.is_buffer() && var.ty == handle
});
// If not used by a global, it's safe to just spew it here
!used_by_global
}
};
if generate_struct {
// Structures ending with runtime-sized arrays can only be
// rendered as shader storage blocks in GLSL, not stand-alone
// struct types.
if !self.module.types[members.last().unwrap().ty]
.inner
.is_dynamically_sized(&self.module.types)
{
let name = &self.names[&NameKey::Type(handle)];
write!(self.out, "struct {} ", name)?;
self.write_struct_body(handle, members)?;
Expand Down Expand Up @@ -955,56 +948,38 @@ impl<'a, W: Write> Writer<'a, W> {
self.write_storage_access(access)?;
}

// Write the storage class
// Trailing space is important
if let Some(storage_class) = glsl_storage_class(global.space) {
write!(self.out, "{} ", storage_class)?;
if let Some(storage_qualifier) = glsl_storage_qualifier(global.space) {
write!(self.out, "{} ", storage_qualifier)?;
}

// If struct is a block we need to write `block_name { members }` where `block_name` must be
// unique between blocks and structs so we add `_block_ID` where `ID` is a `IdGenerator`
// generated number so it's unique and `members` are the same as in a struct

// Write the block name, it's just the struct name appended with `_block_ID`
let needs_wrapper = if global.space.is_buffer() {
let ty_name = &self.names[&NameKey::Type(global.ty)];
let block_name = format!(
"{}_block_{}{:?}",
ty_name,
self.block_id.generate(),
self.entry_point.stage,
);
write!(self.out, "{} ", block_name)?;
self.reflection_names_globals.insert(handle, block_name);

match GlobalTypeKind::new(self.module, global.ty) {
GlobalTypeKind::WrappedStruct => {
write!(self.out, "{{ ")?;
// Write the type
// `write_type` adds no leading or trailing spaces
self.write_type(global.ty)?;
true
}
GlobalTypeKind::Unsized(members) => {
self.write_struct_body(global.ty, members)?;
false
}
GlobalTypeKind::Other => {
return Err(Error::Custom("Non-struct type of a buffer".to_string()));
}
match global.space {
crate::AddressSpace::Private => {
self.write_simple_global(handle, global)?;
}
} else {
self.write_type(global.ty)?;
false
};

if let crate::AddressSpace::PushConstant = global.space {
let global_name = self.get_global_name(handle, global);
self.reflection_names_globals.insert(handle, global_name);
crate::AddressSpace::WorkGroup => {
self.write_simple_global(handle, global)?;
}
crate::AddressSpace::PushConstant => {
self.write_simple_global(handle, global)?;
}
crate::AddressSpace::Uniform | crate::AddressSpace::Handle => {
self.write_interface_block(handle, global)?;
}
crate::AddressSpace::Storage { .. } => {
self.write_interface_block(handle, global)?;
}
crate::AddressSpace::Function => unreachable!(),
}

// Finally write the global name and end the global with a `;` and a newline
// Leading space is important
Ok(())
}

fn write_simple_global(
&mut self,
handle: Handle<crate::GlobalVariable>,
global: &crate::GlobalVariable,
) -> BackendResult {
self.write_type(global.ty)?;
write!(self.out, " ")?;
self.write_global_name(handle, global)?;

Expand All @@ -1021,11 +996,68 @@ impl<'a, W: Write> Writer<'a, W> {
}
}

if needs_wrapper {
write!(self.out, "; }}")?;
writeln!(self.out, ";")?;

if let crate::AddressSpace::PushConstant = global.space {
let global_name = self.get_global_name(handle, global);
self.reflection_names_globals.insert(handle, global_name);
}

Ok(())
}

/// Write an interface block for a single Naga global.
///
/// Write `block_name { members }`. Since `block_name` must be unique
/// between blocks and structs, we add `_block_ID` where `ID` is a
/// `IdGenerator` generated number. Write `members` in the same way we write
/// a struct's members.
fn write_interface_block(
&mut self,
handle: Handle<crate::GlobalVariable>,
global: &crate::GlobalVariable,
) -> BackendResult {
// Write the block name, it's just the struct name appended with `_block_ID`
let ty_name = &self.names[&NameKey::Type(global.ty)];
let block_name = format!(
"{}_block_{}{:?}",
ty_name,
self.block_id.generate(),
self.entry_point.stage,
);
write!(self.out, "{} ", block_name)?;
self.reflection_names_globals.insert(handle, block_name);

match self.module.types[global.ty].inner {
crate::TypeInner::Struct { ref members, .. }
if self.module.types[members.last().unwrap().ty]
.inner
.is_dynamically_sized(&self.module.types) =>
{
// Structs with dynamically sized arrays must have their
// members lifted up as members of the interface block. GLSL
// can't write such struct types anyway.
self.write_struct_body(global.ty, members)?;
write!(self.out, " ")?;
self.write_global_name(handle, global)?;
}
_ => {
// A global of any other type is written as the sole member
// of the interface block. Since the interface block is
// anonymous, this becomes visible in the global scope.
write!(self.out, "{{ ")?;
self.write_type(global.ty)?;
write!(self.out, " ")?;
self.write_global_name(handle, global)?;
if let TypeInner::Array { base, size, .. } = self.module.types[global.ty].inner {
self.write_array_size(base, size)?;
}
write!(self.out, "; }}")?;
}
}

writeln!(self.out, ";")?;

Ok(())
}

Expand Down Expand Up @@ -3209,7 +3241,7 @@ fn glsl_built_in(built_in: crate::BuiltIn, output: bool) -> &'static str {
}

/// Helper function that returns the string corresponding to the address space
fn glsl_storage_class(space: crate::AddressSpace) -> Option<&'static str> {
fn glsl_storage_qualifier(space: crate::AddressSpace) -> Option<&'static str> {
use crate::AddressSpace as As;

match space {
Expand Down
16 changes: 7 additions & 9 deletions src/back/hlsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,13 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
// Write all structs
for (handle, ty) in module.types.iter() {
if let TypeInner::Struct { ref members, span } = ty.inner {
if let Some(member) = members.last() {
if let TypeInner::Array {
size: crate::ArraySize::Dynamic,
..
} = module.types[member.ty].inner
{
// unsized arrays can only be in storage buffers, for which we use `ByteAddressBuffer` anyway.
continue;
}
if module.types[members.last().unwrap().ty]
.inner
.is_dynamically_sized(&module.types)
{
// unsized arrays can only be in storage buffers,
// for which we use `ByteAddressBuffer` anyway.
continue;
}

let ep_result = ep_results.iter().find(|e| {
Expand Down
12 changes: 12 additions & 0 deletions src/proc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@ impl super::TypeInner {
let right = rhs.canonical_form(types);
left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
}

pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
use crate::TypeInner as Ti;
match *self {
Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
Ti::Struct { ref members, .. } => members
.last()
.map(|last| types[last.ty].inner.is_dynamically_sized(types))
.unwrap_or(false),
_ => false,
}
}
}

impl super::AddressSpace {
Expand Down

0 comments on commit 0ef61e4

Please sign in to comment.