[glsl-out] binding location mapping

This commit is contained in:
Dzmitry Malyshau
2021-06-27 00:26:12 -04:00
parent 19de60f6e4
commit 57b3256020
12 changed files with 182 additions and 95 deletions

View File

@@ -7,6 +7,7 @@ use std::{env, error::Error, path::Path};
struct Parameters {
validation_flags: naga::valid::ValidationFlags,
index_bounds_check_policy: naga::back::IndexBoundsCheckPolicy,
entry_point: Option<String>,
spv_adjust_coordinate_space: bool,
spv_flow_dump_prefix: Option<String>,
spv: naga::back::spv::Options,
@@ -89,7 +90,7 @@ fn main() {
};
}
"flow-dir" => params.spv_flow_dump_prefix = args.next(),
"entry-point" => params.glsl.entry_point = args.next().unwrap(),
"entry-point" => params.entry_point = Some(args.next().unwrap()),
"profile" => {
use naga::back::glsl::Version;
let string = args.next().unwrap();
@@ -282,17 +283,28 @@ fn main() {
stage @ "vert" | stage @ "frag" | stage @ "comp" => {
use naga::back::glsl;
params.glsl.shader_stage = match stage {
"vert" => naga::ShaderStage::Vertex,
"frag" => naga::ShaderStage::Fragment,
"comp" => naga::ShaderStage::Compute,
_ => unreachable!(),
let pipeline_options = glsl::PipelineOptions {
entry_point: match params.entry_point {
Some(ref name) => name.clone(),
None => "main".to_string(),
},
shader_stage: match stage {
"vert" => naga::ShaderStage::Vertex,
"frag" => naga::ShaderStage::Fragment,
"comp" => naga::ShaderStage::Compute,
_ => unreachable!(),
},
};
let mut buffer = String::new();
let mut writer =
glsl::Writer::new(&mut buffer, &module, info.as_ref().unwrap(), &params.glsl)
.unwrap_pretty();
let mut writer = glsl::Writer::new(
&mut buffer,
&module,
info.as_ref().unwrap(),
&params.glsl,
&pipeline_options,
)
.unwrap_pretty();
writer.write().unwrap();
fs::write(output_path, buffer).unwrap();
}

View File

@@ -217,7 +217,7 @@ impl<'a, W> Writer<'a, W> {
self.varying_required_features(result.binding.as_ref(), result.ty);
}
if let ShaderStage::Compute = self.options.shader_stage {
if let ShaderStage::Compute = self.entry_point.stage {
self.features.request(Features::COMPUTE_SHADER)
}

View File

@@ -66,8 +66,12 @@ pub const SUPPORTED_CORE_VERSIONS: &[u16] = &[330, 400, 410, 420, 430, 440, 450]
/// List of supported es glsl versions
pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>;
/// glsl version
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum Version {
/// `core` glsl
Desktop(u16),
@@ -124,9 +128,29 @@ impl fmt::Display for Version {
/// Structure that contains the configuration used in the [`Writer`](Writer)
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct Options {
/// The glsl version to be used
pub version: Version,
/// Map of resources association to binding locations.
pub binding_map: BindingMap,
}
impl Default for Options {
fn default() -> Self {
Options {
version: Version::Embedded(310),
binding_map: BindingMap::default(),
}
}
}
// A subset of options that are meant to be changed per pipeline.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct PipelineOptions {
/// The stage of the entry point
pub shader_stage: ShaderStage,
/// The name of the entry point
@@ -136,16 +160,6 @@ pub struct Options {
pub entry_point: String,
}
impl Default for Options {
fn default() -> Self {
Options {
version: Version::Embedded(320),
shader_stage: ShaderStage::Compute,
entry_point: "main".to_string(),
}
}
}
/// Structure that contains a reflection info
pub struct ReflectionInfo {
pub texture_mapping: crate::FastHashMap<String, TextureMapping>,
@@ -297,6 +311,7 @@ impl<'a, W: Write> Writer<'a, W> {
module: &'a crate::Module,
info: &'a valid::ModuleInfo,
options: &'a Options,
pipeline_options: &'a PipelineOptions,
) -> Result<Self, Error> {
// Check if the requested version is supported
if !options.version.is_supported() {
@@ -308,7 +323,9 @@ impl<'a, W: Write> Writer<'a, W> {
let ep_idx = module
.entry_points
.iter()
.position(|ep| options.shader_stage == ep.stage && options.entry_point == ep.name)
.position(|ep| {
pipeline_options.shader_stage == ep.stage && pipeline_options.entry_point == ep.name
})
.ok_or(Error::EntryPointNotFound)?;
// Generate a map with names required to write the module
@@ -370,7 +387,7 @@ impl<'a, W: Write> Writer<'a, W> {
writeln!(self.out)?;
}
if self.options.shader_stage == ShaderStage::Compute {
if self.entry_point.stage == ShaderStage::Compute {
let workgroup_size = self.entry_point.workgroup_size;
writeln!(
self.out,
@@ -439,13 +456,36 @@ impl<'a, W: Write> Writer<'a, W> {
arrayed,
class,
} => {
// Write the storage format if needed
if let TypeInner::Image {
class: crate::ImageClass::Storage(format),
..
} = self.module.types[global.ty].inner
{
write!(self.out, "layout({}) ", glsl_storage_format(format))?;
// Gather the storage format if needed
let layout_storage_format = match self.module.types[global.ty].inner {
TypeInner::Image {
class: crate::ImageClass::Storage(format),
..
} => Some(glsl_storage_format(format)),
_ => None,
};
// Gether the location if needed
let layout_binding = if self.options.version.supports_explicit_locations() {
let br = global.binding.as_ref().unwrap();
self.options.binding_map.get(br).cloned()
} else {
None
};
// Write all the layout qualifiers
if layout_binding.is_some() || layout_storage_format.is_some() {
write!(self.out, "layout(")?;
if let Some(binding) = layout_binding {
write!(self.out, "binding = {}", binding)?;
}
if let Some(format) = layout_storage_format {
let separator = match layout_binding {
Some(_) => ",",
None => "",
};
write!(self.out, "{}{}", separator, format)?;
}
write!(self.out, ") ")?;
}
if let Some(storage_access) = glsl_storage_access(global.storage_access) {
@@ -702,6 +742,15 @@ impl<'a, W: Write> Writer<'a, W> {
handle: Handle<crate::GlobalVariable>,
global: &crate::GlobalVariable,
) -> BackendResult {
if self.options.version.supports_explicit_locations() {
if let Some(ref br) = global.binding {
match self.options.binding_map.get(br) {
Some(binding) => write!(self.out, "layout(binding = {}) ", binding)?,
None => log::debug!("unassigned binding for {:?}", global.name),
}
}
}
if let Some(storage_access) = glsl_storage_access(global.storage_access) {
write!(self.out, "{} ", storage_access)?;
}
@@ -782,7 +831,7 @@ impl<'a, W: Write> Writer<'a, W> {
//
// We ignore all interpolation and auxiliary modifiers that aren't used in fragment
// shaders' input globals or vertex shaders' output globals.
let emit_interpolation_and_auxiliary = match self.options.shader_stage {
let emit_interpolation_and_auxiliary = match self.entry_point.stage {
ShaderStage::Vertex => output,
ShaderStage::Fragment => !output,
_ => false,

View File

@@ -24,7 +24,10 @@ holding the result.
!*/
use crate::{arena::Handle, valid::ModuleInfo};
use std::fmt::{Error as FmtError, Write};
use std::{
fmt::{Error as FmtError, Write},
ops,
};
mod keywords;
pub mod sampler;
@@ -57,21 +60,16 @@ pub struct BindTarget {
pub mutable: bool,
}
#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct BindSource {
pub stage: crate::ShaderStage,
pub group: u32,
pub binding: u32,
}
// Using `BTreeMap` instead of `HashMap` so that we can hash itself.
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, BindTarget>;
pub type BindingMap = std::collections::BTreeMap<BindSource, BindTarget>;
#[derive(Clone, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct PerStageResources {
#[cfg_attr(feature = "deserialize", serde(default))]
pub resources: BindingMap,
#[cfg_attr(feature = "deserialize", serde(default))]
pub push_constant_buffer: Option<Slot>,
@@ -82,7 +80,7 @@ pub struct PerStageResources {
pub sizes_buffer: Option<Slot>,
}
#[derive(Clone, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct PerStageMap {
@@ -94,6 +92,17 @@ pub struct PerStageMap {
pub cs: PerStageResources,
}
impl ops::Index<crate::ShaderStage> for PerStageMap {
type Output = PerStageResources;
fn index(&self, stage: crate::ShaderStage) -> &PerStageResources {
match stage {
crate::ShaderStage::Vertex => &self.vs,
crate::ShaderStage::Fragment => &self.fs,
crate::ShaderStage::Compute => &self.cs,
}
}
}
enum ResolvedBinding {
BuiltIn(crate::BuiltIn),
Attribute(u32),
@@ -146,11 +155,11 @@ pub enum Error {
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum EntryPointError {
#[error("mapping of {0:?} is missing")]
MissingBinding(BindSource),
#[error("mapping for push constants at stage {0:?} is missing")]
MissingPushConstants(crate::ShaderStage),
#[error("mapping for sizes buffer for stage {0:?} is missing")]
MissingSizesBuffer(crate::ShaderStage),
MissingBinding(crate::ResourceBinding),
#[error("mapping for push constants is missing")]
MissingPushConstants,
#[error("mapping for sizes buffer is missing")]
MissingSizesBuffer,
}
#[derive(Clone, Copy, Debug)]
@@ -167,9 +176,7 @@ enum LocationMode {
pub struct Options {
/// (Major, Minor) target version of the Metal Shading Language.
pub lang_version: (u8, u8),
/// Binding model mapping to Metal.
pub binding_map: BindingMap,
/// Map of per-stage resources (e.g. push constants) to slots
/// Map of per-stage resources to slots.
pub per_stage_map: PerStageMap,
/// Samplers to be inlined into the code.
pub inline_samplers: Vec<sampler::InlineSampler>,
@@ -183,7 +190,6 @@ impl Default for Options {
fn default() -> Self {
Options {
lang_version: (1, 0),
binding_map: BindingMap::default(),
per_stage_map: PerStageMap::default(),
inline_samplers: Vec::new(),
spirv_cross_compatibility: false,
@@ -257,19 +263,14 @@ impl Options {
stage: crate::ShaderStage,
res_binding: &crate::ResourceBinding,
) -> Result<ResolvedBinding, EntryPointError> {
let source = BindSource {
stage,
group: res_binding.group,
binding: res_binding.binding,
};
match self.binding_map.get(&source) {
match self.per_stage_map[stage].resources.get(&res_binding) {
Some(target) => Ok(ResolvedBinding::Resource(target.clone())),
None if self.fake_missing_bindings => Ok(ResolvedBinding::User {
prefix: "fake",
index: 0,
interpolation: None,
}),
None => Err(EntryPointError::MissingBinding(source)),
None => Err(EntryPointError::MissingBinding(res_binding.clone())),
}
}
@@ -294,7 +295,7 @@ impl Options {
index: 0,
interpolation: None,
}),
None => Err(EntryPointError::MissingPushConstants(stage)),
None => Err(EntryPointError::MissingPushConstants),
}
}
@@ -302,12 +303,7 @@ impl Options {
&self,
stage: crate::ShaderStage,
) -> Result<ResolvedBinding, EntryPointError> {
let slot = match stage {
crate::ShaderStage::Vertex => self.per_stage_map.vs.sizes_buffer,
crate::ShaderStage::Fragment => self.per_stage_map.fs.sizes_buffer,
crate::ShaderStage::Compute => self.per_stage_map.cs.sizes_buffer,
};
let slot = self.per_stage_map[stage].sizes_buffer;
match slot {
Some(slot) => Ok(ResolvedBinding::Resource(BindTarget {
buffer: Some(slot),
@@ -320,7 +316,7 @@ impl Options {
index: 0,
interpolation: None,
}),
None => Err(EntryPointError::MissingSizesBuffer(stage)),
None => Err(EntryPointError::MissingSizesBuffer),
}
}
}

View File

@@ -625,7 +625,7 @@ pub enum Binding {
}
/// Pipeline binding information for global resources.
#[derive(Clone, Debug, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
pub struct ResourceBinding {

View File

@@ -5,11 +5,11 @@
msl_custom: true,
msl: (
lang_version: (2, 0),
binding_map: {
(stage: Vertex, group: 0, binding: 0): (buffer: Some(0), mutable: true),
},
per_stage_map: (
vs: (
resources: {
(group: 0, binding: 0): (buffer: Some(0), mutable: true),
},
sizes_buffer: Some(24),
),
),

View File

@@ -5,13 +5,13 @@
msl_custom: true,
msl: (
lang_version: (2, 0),
binding_map: {
(stage: Compute, group: 0, binding: 0): (buffer: Some(0), mutable: false),
(stage: Compute, group: 0, binding: 1): (buffer: Some(1), mutable: true),
(stage: Compute, group: 0, binding: 2): (buffer: Some(2), mutable: true),
},
per_stage_map: (
cs: (
resources: {
(group: 0, binding: 0): (buffer: Some(0), mutable: false),
(group: 0, binding: 1): (buffer: Some(1), mutable: true),
(group: 0, binding: 2): (buffer: Some(2), mutable: true),
},
sizes_buffer: Some(3),
)
),

View File

@@ -3,5 +3,9 @@
spv_capabilities: [ Shader, SampleRateShading ],
spv_debug: true,
spv_adjust_coordinate_space: true,
glsl_desktop_version: Some(400)
glsl: (
version: Desktop(400),
binding_map: {},
),
glsl_custom: true,
)

View File

@@ -6,12 +6,18 @@
msl_custom: true,
msl: (
lang_version: (2, 1),
binding_map: {
(stage: Vertex, group: 0, binding: 0): (buffer: Some(0)),
(stage: Fragment, group: 0, binding: 1): (texture: Some(0)),
(stage: Fragment, group: 0, binding: 2): (sampler: Some(Inline(0))),
},
per_stage_map: (
vs: (
resources: {
(group: 0, binding: 0): (buffer: Some(0)),
},
),
fs: (
resources: {
(group: 0, binding: 1): (texture: Some(0)),
(group: 0, binding: 2): (sampler: Some(Inline(0))),
},
),
),
inline_samplers: [
(
@@ -28,5 +34,13 @@
],
spirv_cross_compatibility: false,
fake_missing_bindings: false,
)
),
glsl_custom: true,
glsl: (
version: Embedded(320),
binding_map: {
(group: 0, binding: 0): 0,
(group: 0, binding: 1): 0,
},
),
)

View File

@@ -1,4 +1,4 @@
#version 310 es
#version 320 es
precision highp float;
@@ -7,7 +7,7 @@ struct VertexOutput {
vec3 uv;
};
uniform highp samplerCube _group_0_binding_1;
layout(binding = 0) uniform highp samplerCube _group_0_binding_1;
smooth in vec3 _vs2fs_location0;
layout(location = 0) out vec4 _fs2p_location0;

View File

@@ -1,4 +1,4 @@
#version 310 es
#version 320 es
precision highp float;
@@ -7,7 +7,7 @@ struct VertexOutput {
vec3 uv;
};
uniform Data_block_0 {
layout(binding = 0) uniform Data_block_0 {
mat4x4 proj_inv;
mat4x4 view;
} _group_0_binding_0;

View File

@@ -49,9 +49,12 @@ struct Parameters {
#[cfg(all(not(feature = "deserialize"), feature = "msl-out"))]
#[serde(default)]
msl_custom: bool,
#[cfg_attr(not(feature = "glsl-out"), allow(dead_code))]
#[cfg(all(feature = "deserialize", feature = "glsl-out"))]
#[serde(default)]
glsl_desktop_version: Option<u16>,
glsl: naga::back::glsl::Options,
#[cfg(all(not(feature = "deserialize"), feature = "glsl-out"))]
#[serde(default)]
glsl_custom: bool,
#[cfg_attr(not(feature = "glsl-out"), allow(dead_code))]
#[serde(default)]
glsl_vert_ep_name: Option<String>,
@@ -233,17 +236,26 @@ fn write_output_glsl(
) {
use naga::back::glsl;
let options = glsl::Options {
version: match params.glsl_desktop_version {
Some(v) => glsl::Version::Desktop(v),
None => glsl::Version::Embedded(310),
},
#[cfg_attr(feature = "deserialize", allow(unused_variables))]
let default_options = glsl::Options::default();
#[cfg(feature = "deserialize")]
let options = &params.glsl;
#[cfg(not(feature = "deserialize"))]
let options = if params.glsl_custom {
println!("Skipping {}", destination.display());
return;
} else {
&default_options
};
let pipeline_options = glsl::PipelineOptions {
shader_stage: stage,
entry_point: ep_name.to_string(),
};
let mut buffer = String::new();
let mut writer = glsl::Writer::new(&mut buffer, module, info, &options).unwrap();
let mut writer =
glsl::Writer::new(&mut buffer, module, info, &options, &pipeline_options).unwrap();
writer.write().unwrap();
fs::write(