preprocess: implement stage separator to complete preprocessor

This commit is contained in:
chyyran 2022-10-22 02:54:06 -04:00
parent 394e09678d
commit 3f83e0fcd0
6 changed files with 125 additions and 25 deletions

View file

@ -15,13 +15,12 @@ pub enum PreprocessError {
UnexpectedEol(usize), UnexpectedEol(usize),
#[error("error parsing pragma")] #[error("error parsing pragma")]
PragmaParseError(String), PragmaParseError(String),
#[error("duplicate parameter but arguments do not match")] #[error("duplicate pragma found")]
DuplicateParameterError(String), DuplicatePragmaError(String),
#[error("shader format is unknown or not found")] #[error("shader format is unknown or not found")]
UnknownShaderFormat, UnknownShaderFormat,
#[error("tried to declare shader format twice")] #[error("stage must be either vertex or fragment")]
DuplicateShaderFormat, InvalidStage,
} }
impl From<Infallible> for PreprocessError { impl From<Infallible> for PreprocessError {

View file

@ -1,4 +1,4 @@
use crate::PreprocessError; use crate::{PreprocessError, SourceOutput};
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
@ -7,20 +7,6 @@ use std::str::Lines;
const GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE: &'static str = const GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE: &'static str =
"#extension GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE : require"; "#extension GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE : require";
trait SourceOutput {
fn push_line(&mut self, str: &str);
fn mark_line(&mut self, line_no: usize, comment: &str) {
self.push_line(&format!("#line {} \"{}\"", line_no, comment))
}
}
impl SourceOutput for String {
fn push_line(&mut self, str: &str) {
self.push_str(str);
self.push('\n');
}
}
fn read_file(path: impl AsRef<Path>) -> Result<String, PreprocessError> { fn read_file(path: impl AsRef<Path>) -> Result<String, PreprocessError> {
let path = path.as_ref(); let path = path.as_ref();
let mut source = String::new(); let mut source = String::new();

View file

@ -1,13 +1,54 @@
mod error; mod error;
mod include; mod include;
mod pragma; mod pragma;
mod stage;
use std::path::Path;
pub use error::*; pub use error::*;
use librashader::ShaderSource;
use crate::include::read_source;
pub(crate) trait SourceOutput {
fn push_line(&mut self, str: &str);
fn mark_line(&mut self, line_no: usize, comment: &str) {
self.push_line(&format!("#line {} \"{}\"", line_no, comment))
}
}
impl SourceOutput for String {
fn push_line(&mut self, str: &str) {
self.push_str(str);
self.push('\n');
}
}
pub fn load_shader_source(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
let source = read_source(path)?;
let meta = pragma::parse_pragma_meta(&source)?;
let text = stage::process_stages(&source)?;
Ok(ShaderSource {
vertex: text.vertex,
fragment: text.fragment,
name: meta.name,
parameters: meta.parameters,
format: meta.format,
})
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::include::read_source; use crate::include::read_source;
use crate::pragma; use crate::{load_shader_source, pragma};
#[test]
pub fn load_file() {
let result =
load_shader_source("../test/slang-shaders/blurs/shaders/royale/blur3x3-last-pass.slang")
.unwrap();
eprintln!("{result:#?}")
}
#[test] #[test]
pub fn preprocess_file() { pub fn preprocess_file() {

View file

@ -7,6 +7,12 @@ use nom::sequence::delimited;
use librashader::{ShaderFormat, ShaderParameter}; use librashader::{ShaderFormat, ShaderParameter};
use crate::PreprocessError; use crate::PreprocessError;
#[derive(Debug)]
pub(crate) struct ShaderMeta {
pub(crate) format: ShaderFormat,
pub(crate) parameters: Vec<ShaderParameter>,
pub(crate) name: Option<String>
}
fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessError>{ fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessError>{
fn parse_parameter_string_inner(input: &str) -> IResult<&str, ShaderParameter> { fn parse_parameter_string_inner(input: &str) -> IResult<&str, ShaderParameter> {
@ -39,16 +45,17 @@ fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessErro
} }
} }
pub fn parse_pragma_meta(source: impl AsRef<str>) -> Result<(ShaderFormat, Vec<ShaderParameter>), PreprocessError> { pub(crate) fn parse_pragma_meta(source: impl AsRef<str>) -> Result<ShaderMeta, PreprocessError> {
let source = source.as_ref(); let source = source.as_ref();
let mut parameters: Vec<ShaderParameter> = Vec::new(); let mut parameters: Vec<ShaderParameter> = Vec::new();
let mut format = ShaderFormat::default(); let mut format = ShaderFormat::default();
let mut name = None;
for line in source.lines() { for line in source.lines() {
if line.starts_with("#pragma parameter ") { if line.starts_with("#pragma parameter ") {
let parameter = parse_parameter_string(line)?; let parameter = parse_parameter_string(line)?;
if let Some(existing) = parameters.iter().find(|&p| p.id == parameter.id) { if let Some(existing) = parameters.iter().find(|&p| p.id == parameter.id) {
if existing != &parameter { if existing != &parameter {
return Err(PreprocessError::DuplicateParameterError(parameter.id)) return Err(PreprocessError::DuplicatePragmaError(parameter.id))
} }
} else { } else {
parameters.push(parameter); parameters.push(parameter);
@ -57,7 +64,7 @@ pub fn parse_pragma_meta(source: impl AsRef<str>) -> Result<(ShaderFormat, Vec<S
if line.starts_with("#pragma format ") { if line.starts_with("#pragma format ") {
if format != ShaderFormat::Unknown { if format != ShaderFormat::Unknown {
return Err(PreprocessError::DuplicateShaderFormat) return Err(PreprocessError::DuplicatePragmaError(line.to_string()))
} }
let format_string = line["#pragma format ".len()..].trim(); let format_string = line["#pragma format ".len()..].trim();
@ -67,9 +74,17 @@ pub fn parse_pragma_meta(source: impl AsRef<str>) -> Result<(ShaderFormat, Vec<S
return Err(PreprocessError::UnknownShaderFormat) return Err(PreprocessError::UnknownShaderFormat)
} }
} }
if line.starts_with("#pragma name ") {
if name.is_some() {
return Err(PreprocessError::DuplicatePragmaError(line.to_string()));
}
name = Some(line.trim().to_string())
}
} }
Ok((format, parameters)) Ok(ShaderMeta { name, format, parameters })
} }
#[cfg(test)] #[cfg(test)]

View file

@ -0,0 +1,58 @@
use std::str::FromStr;
use crate::{PreprocessError, SourceOutput};
enum ActiveStage {
Both,
Fragment,
Vertex
}
impl FromStr for ActiveStage {
type Err = PreprocessError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"vertex" => Ok(ActiveStage::Vertex),
"fragment" => Ok(ActiveStage::Fragment),
_ => Err(PreprocessError::InvalidStage)
}
}
}
#[derive(Default)]
pub(crate) struct ShaderOutput {
pub(crate) fragment: String,
pub(crate) vertex: String
}
pub(crate) fn process_stages(source: &str) -> Result<ShaderOutput, PreprocessError> {
let mut active_stage = ActiveStage::Both;
let mut output = ShaderOutput::default();
for line in source.lines() {
if line.starts_with("#pragma stage ") {
let stage = line["#pragma stage ".len()..].trim();
active_stage = ActiveStage::from_str(stage)?;
continue;
}
if line.starts_with("#pragma name ") || line.starts_with("#pragma format ") {
continue;
}
match active_stage {
ActiveStage::Both => {
output.fragment.push_line(line);
output.vertex.push_line(line);
}
ActiveStage::Fragment => {
output.fragment.push_line(line);
}
ActiveStage::Vertex => {
output.vertex.push_line(line)
}
}
}
Ok(output)
}

View file

@ -1,6 +1,7 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone, PartialEq)]
pub struct ShaderSource { pub struct ShaderSource {
pub vertex: String, pub vertex: String,
pub fragment: String, pub fragment: String,