Remove the call graph processor

Uses the GlobalUse in glsl-out to check for function availability
This commit is contained in:
João Capucho 2021-02-18 12:14:04 +00:00 committed by Dzmitry Malyshau
parent 9f113bdd95
commit 19e7f456c4
7 changed files with 41 additions and 139 deletions

View File

@ -249,15 +249,13 @@ fn main() {
let name = args.get(4).map_or("main", |p| p.as_str()).to_string();
let options = glsl::Options {
version,
entry_point: (
match stage {
"vert" => naga::ShaderStage::Vertex,
"frag" => naga::ShaderStage::Fragment,
"comp" => naga::ShaderStage::Compute,
_ => unreachable!(),
},
name,
),
shader_stage: match stage {
"vert" => naga::ShaderStage::Vertex,
"frag" => naga::ShaderStage::Fragment,
"comp" => naga::ShaderStage::Compute,
_ => unreachable!(),
},
entry_point: name,
};
let file = fs::OpenOptions::new()

View File

@ -175,8 +175,6 @@ impl<'a, W> Writer<'a, W> {
/// If the version doesn't support any of the needed [`Features`](Features) a
/// [`Error::MissingFeatures`](super::Error::MissingFeatures) will be returned
pub(super) fn collect_required_features(&mut self) -> BackendResult {
let stage = self.options.entry_point.0;
if let Some(depth_test) = self.entry_point.early_depth_test {
self.features.request(Features::IMAGE_LOAD_STORE);
@ -185,7 +183,7 @@ impl<'a, W> Writer<'a, W> {
}
}
if let ShaderStage::Compute = stage {
if let ShaderStage::Compute = self.options.shader_stage {
self.features.request(Features::COMPUTE_SHADER)
}

View File

@ -44,10 +44,7 @@
pub use features::Features;
use crate::{
proc::{
analyzer::Analysis, CallGraph, CallGraphBuilder, NameKey, Namer, ResolveContext,
ResolveError, Typifier,
},
proc::{analyzer::Analysis, NameKey, Namer, ResolveContext, ResolveError, Typifier},
Arena, ArraySize, BinaryOperator, Binding, BuiltIn, Bytes, ConservativeDepth, Constant,
ConstantInner, DerivativeAxis, Expression, FastHashMap, Function, GlobalVariable, Handle,
ImageClass, Interpolation, LocalVariable, Module, RelationalFunction, ScalarKind, ScalarValue,
@ -129,11 +126,13 @@ impl fmt::Display for Version {
pub struct Options {
/// The glsl version to be used
pub version: Version,
/// The name and stage of the entry point
/// The stage of the entry point
pub shader_stage: ShaderStage,
/// The name of the entry point
///
/// If no entry point that matches is found a error will be thrown while creating a new instance
/// of [`Writer`](struct.Writer.html)
pub entry_point: (ShaderStage, String),
pub entry_point: String,
}
/// Structure that connects a texture to a sampler or not
@ -275,8 +274,6 @@ pub struct Writer<'a, W> {
entry_point: &'a crate::EntryPoint,
/// The index of the selected entry point
entry_point_idx: crate::proc::EntryPointIndex,
/// The current entry point call_graph (doesn't contain the entry point)
call_graph: CallGraph,
/// Used to generate a unique number for blocks
block_id: IdGenerator,
}
@ -301,12 +298,12 @@ impl<'a, W: Write> Writer<'a, W> {
}
// Try to find the entry point and corresponding index
let (ep_idx, ep) = module
let (ep_idx, (_, ep)) = module
.entry_points
.iter()
.enumerate()
.find_map(|(i, (key, entry_point))| {
Some((i as u16, entry_point)).filter(|_| &options.entry_point == key)
.find(|(_, ((stage, name), _))| {
options.shader_stage == *stage && &options.entry_point == name
})
.ok_or(Error::EntryPointNotFound)?;
@ -314,12 +311,6 @@ impl<'a, W: Write> Writer<'a, W> {
let mut names = FastHashMap::default();
Namer::default().reset(module, keywords::RESERVED_KEYWORDS, &mut names);
// Generate a call graph for the entry point
let call_graph = CallGraphBuilder {
functions: &module.functions,
}
.process(&ep.function);
// Build the instance
let mut this = Self {
module,
@ -330,8 +321,7 @@ impl<'a, W: Write> Writer<'a, W> {
features: FeaturesManager::new(),
names,
entry_point: ep,
entry_point_idx: ep_idx,
call_graph,
entry_point_idx: ep_idx as u16,
block_id: IdGenerator::default(),
};
@ -407,7 +397,7 @@ impl<'a, W: Write> Writer<'a, W> {
let ep_info = self
.analysis
.get_entry_point(self.options.entry_point.0, &self.options.entry_point.1);
.get_entry_point(self.options.shader_stage, &self.options.entry_point);
// Write the globals
//
@ -475,25 +465,19 @@ impl<'a, W: Write> Writer<'a, W> {
_ => self.write_global(handle, global)?,
}
}
// Write all regular functions
for (handle, function) in self.module.functions.iter() {
// Check that the function doesn't use globals that aren't supported
// by the current entry point
if !ep_info.dominates_global_use(&self.analysis[handle]) {
continue;
}
// Sort the graph topologically so that functions calls are valid
// It's impossible for this to panic because the IR forbids cycles
let functions = petgraph::algo::toposort(&self.call_graph, None).unwrap();
// Write all regular functions that are in the call graph this is important
// because other functions might require for example globals that weren't written
for node in functions {
// We do this inside the loop instead of using `map` to satisfy the borrow checker
let handle = self.call_graph[node];
// We also `clone` to satisfy the borrow checker
let name = self.names[&NameKey::Function(handle)].clone();
// Write the function
self.write_function(
FunctionType::Function(handle),
&self.module.functions[handle],
name,
)?;
self.write_function(FunctionType::Function(handle), function, name)?;
writeln!(self.out)?;
}
@ -692,7 +676,7 @@ impl<'a, W: Write> Writer<'a, W> {
//
// TODO: Should this throw an error?
if let Some(interpolation) = global.interpolation {
match (self.options.entry_point.0, global.class) {
match (self.options.shader_stage, global.class) {
(ShaderStage::Fragment, StorageClass::Input)
| (ShaderStage::Vertex, StorageClass::Output) => {
write!(self.out, "{} ", glsl_interpolation(interpolation)?)?;
@ -731,7 +715,7 @@ impl<'a, W: Write> Writer<'a, W> {
format!(
"_location_{}{}",
location,
match (self.options.entry_point.0, global.class) {
match (self.options.shader_stage, global.class) {
(ShaderStage::Fragment, StorageClass::Input) => "_vs",
(ShaderStage::Vertex, StorageClass::Output) => "_vs",
_ => "",
@ -1755,7 +1739,7 @@ impl<'a, W: Write> Writer<'a, W> {
use std::collections::hash_map::Entry;
let info = self
.analysis
.get_entry_point(self.options.entry_point.0, &self.options.entry_point.1);
.get_entry_point(self.options.shader_stage, &self.options.entry_point);
let mut mappings = FastHashMap::default();
for sampling in info.sampling_set.iter() {

View File

@ -99,6 +99,16 @@ impl FunctionInfo {
pub fn expression_count(&self) -> usize {
self.expressions.len()
}
pub fn dominates_global_use(&self, other: &Self) -> bool {
for (self_global_uses, other_global_uses) in
self.global_uses.iter().zip(other.global_uses.iter())
{
if !self_global_uses.contains(*other_global_uses) {
return false;
}
}
true
}
}
impl ops::Index<Handle<crate::GlobalVariable>> for FunctionInfo {

View File

@ -1,85 +0,0 @@
use crate::{
arena::{Arena, Handle},
proc::{Interface, Visitor},
Function,
};
use bit_set::BitSet;
use petgraph::{
graph::{DefaultIx, NodeIndex},
Graph,
};
pub type CallGraph = Graph<Handle<Function>, ()>;
pub struct CallGraphBuilder<'a> {
pub functions: &'a Arena<Function>,
}
impl<'a> CallGraphBuilder<'a> {
pub fn process(&self, func: &Function) -> CallGraph {
let mut graph = Graph::new();
let mut children = Vec::new();
let mut mask = BitSet::with_capacity(func.expressions.len());
let visitor = CallGraphVisitor {
children: &mut children,
};
let mut interface = Interface {
expressions: &func.expressions,
local_variables: &func.local_variables,
visitor,
mask: &mut mask,
};
interface.traverse(&func.body);
for handle in children {
let id = graph.add_node(handle);
self.collect(handle, id, &mut graph, &mut mask);
}
graph
}
fn collect(
&self,
handle: Handle<Function>,
id: NodeIndex<DefaultIx>,
graph: &mut CallGraph,
mask: &mut BitSet,
) {
let mut children = Vec::new();
let visitor = CallGraphVisitor {
children: &mut children,
};
let func = &self.functions[handle];
mask.clear();
let mut interface = Interface {
expressions: &func.expressions,
local_variables: &func.local_variables,
visitor,
mask,
};
interface.traverse(&func.body);
for handle in children {
let child_id = graph.add_node(handle);
graph.add_edge(id, child_id, ());
self.collect(handle, child_id, graph, mask);
}
}
}
struct CallGraphVisitor<'a> {
children: &'a mut Vec<Handle<Function>>,
}
impl<'a> Visitor for CallGraphVisitor<'a> {
fn visit_fun(&mut self, func: Handle<Function>) {
self.children.push(func)
}
}

View File

@ -1,8 +1,6 @@
//! Module processing functionality.
pub mod analyzer;
#[cfg(feature = "petgraph")]
mod call_graph;
mod interface;
mod layouter;
mod namer;
@ -10,8 +8,6 @@ mod terminator;
mod typifier;
mod validator;
#[cfg(feature = "petgraph")]
pub use call_graph::{CallGraph, CallGraphBuilder};
pub use interface::{Interface, Visitor};
pub use layouter::{Alignment, Layouter};
pub use namer::{EntryPointIndex, NameKey, Namer};

View File

@ -187,7 +187,8 @@ fn check_output_glsl(
let options = glsl::Options {
version: glsl::Version::Embedded(310),
entry_point: (stage, ep_name.to_string()),
shader_stage: stage,
entry_point: ep_name.to_string(),
};
let mut buffer = Vec::new();