diff --git a/librashader-capi/src/reflect/mod.rs b/librashader-capi/src/reflect/mod.rs index 719d0f8..5901972 100644 --- a/librashader-capi/src/reflect/mod.rs +++ b/librashader-capi/src/reflect/mod.rs @@ -6,9 +6,8 @@ use librashader::reflect::targets::SPIRV; use librashader::reflect::{CompileShader, ReflectShader, ShaderCompilerOutput, ShaderReflection}; use librashader::{FilterMode, WrapMode}; -use librashader::reflect::SpirvCompilation; -use librashader::reflect::f use librashader::reflect::helper::image::{Image, UVDirection, RGBA8}; +use librashader::reflect::SpirvCompilation; pub(crate) struct LookupTexture { wrap_mode: WrapMode, diff --git a/librashader-reflect/Cargo.toml b/librashader-reflect/Cargo.toml index c6c4c09..9159976 100644 --- a/librashader-reflect/Cargo.toml +++ b/librashader-reflect/Cargo.toml @@ -25,7 +25,7 @@ librashader-presets = { path = "../librashader-presets", version = "0.2.0-beta.9 spirv_cross = { package = "librashader-spirv-cross", version = "0.24", optional = true } -naga = { version = "0.19.0", features = ["spv-in", "spv-out"], optional = true } +naga = { version = "0.19.0", optional = true } rspirv = { version = "0.12.0", optional = true } spirv = { version = "0.3.0", optional = true} @@ -39,10 +39,10 @@ version = "0.4" optional = true [features] -default = ["cross", "naga", "serialize"] +default = ["cross", "naga", "serialize", "wgsl", "msl"] dxil = ["spirv_cross/hlsl", "spirv-to-dxil"] wgsl = ["cross", "naga/wgsl-out", "spirv", "rspirv"] cross = [ "spirv_cross", "spirv_cross/glsl", "spirv_cross/hlsl", "spirv_cross/msl" ] -naga = [ "wgsl" ] +naga = [ "rspirv", "spirv", "naga/spv-in", "naga/spv-out", "naga/wgsl-out", "naga/msl-out" ] serialize = [ "serde" ] msl = [ "spirv_cross/msl", "naga/msl-out" ] \ No newline at end of file diff --git a/librashader-reflect/src/back/mod.rs b/librashader-reflect/src/back/mod.rs index 4aba7e0..fce028f 100644 --- a/librashader-reflect/src/back/mod.rs +++ b/librashader-reflect/src/back/mod.rs @@ -126,7 +126,7 @@ where #[cfg(test)] mod test { - use crate::front::{Glslang, ShaderInputCompiler, SpirvCompilation}; + use crate::front::{Glslang, ShaderInputCompiler}; use librashader_preprocess::ShaderSource; pub fn test() { diff --git a/librashader-reflect/src/back/msl.rs b/librashader-reflect/src/back/msl.rs index 35c416d..c8e71be 100644 --- a/librashader-reflect/src/back/msl.rs +++ b/librashader-reflect/src/back/msl.rs @@ -6,6 +6,8 @@ use crate::reflect::cross::msl::MslReflect; use crate::reflect::cross::{CompiledProgram, SpirvCross}; use crate::reflect::naga::{Naga, NagaReflect}; use crate::reflect::ReflectShader; +use naga::back::msl::TranslationInfo; +use naga::Module; /// The HLSL shader model version to target. pub use spirv_cross::msl::Version as MslVersion; @@ -25,7 +27,7 @@ pub struct CrossMslContext { impl FromCompilation for MSL { type Target = MSL; - type Options = Option; + type Options = Option; type Context = CrossMslContext; type Output = impl CompileShader + ReflectShader; @@ -39,10 +41,21 @@ impl FromCompilation for MSL { } } +/// The naga module for a shader after compilation +pub struct NagaMslModule { + pub translation_info: TranslationInfo, + pub module: Module, +} + +pub struct NagaMslContext { + pub vertex: NagaMslModule, + pub fragment: NagaMslModule, +} + impl FromCompilation for MSL { type Target = MSL; - type Options = (); - type Context = (); + type Options = Option; + type Context = NagaMslContext; type Output = impl CompileShader + ReflectShader; diff --git a/librashader-reflect/src/back/wgsl.rs b/librashader-reflect/src/back/wgsl.rs index c9ebce5..768bf5e 100644 --- a/librashader-reflect/src/back/wgsl.rs +++ b/librashader-reflect/src/back/wgsl.rs @@ -35,9 +35,9 @@ mod test { use crate::reflect::naga::NagaLoweringOptions; use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics}; use crate::reflect::ReflectShader; + use bitflags::Flags; use librashader_preprocess::ShaderSource; use rustc_hash::FxHashMap; - use bitflags::Flags; #[test] pub fn test_into() { diff --git a/librashader-reflect/src/error.rs b/librashader-reflect/src/error.rs index 1ecd30a..2ff7054 100644 --- a/librashader-reflect/src/error.rs +++ b/librashader-reflect/src/error.rs @@ -35,10 +35,15 @@ pub enum ShaderCompileError { /// Error when transpiling from naga #[cfg(feature = "naga")] #[error("naga-spv")] - NagaGlslError(#[from] naga::back::spv::Error), + NagaSpvError(#[from] naga::back::spv::Error), /// Error when transpiling from naga - #[cfg(feature = "wgsl")] + #[cfg(all(feature = "naga", feature = "msl"))] + #[error("naga-spv")] + NagaMslError(#[from] naga::back::msl::Error), + + /// Error when transpiling from naga + #[cfg(any(feature = "naga", feature = "wgsl"))] #[error("naga-wgsl")] NagaValidationError(#[from] naga::WithSpan), } diff --git a/librashader-reflect/src/reflect/cross/msl.rs b/librashader-reflect/src/reflect/cross/msl.rs index e4dfa9e..9201e5c 100644 --- a/librashader-reflect/src/reflect/cross/msl.rs +++ b/librashader-reflect/src/reflect/cross/msl.rs @@ -1,5 +1,3 @@ -use std::collections::BTreeMap; -use naga::{Module}; use crate::back::msl::CrossMslContext; use crate::back::targets::MSL; use crate::back::{CompileShader, ShaderCompilerOutput}; @@ -8,6 +6,7 @@ use crate::reflect::cross::{CompiledAst, CompiledProgram, CrossReflect}; use spirv_cross::msl; use spirv_cross::msl::{ResourceBinding, ResourceBindingLocation}; use spirv_cross::spirv::{Ast, Decoration, ExecutionModel}; +use std::collections::BTreeMap; pub(crate) type MslReflect = CrossReflect; @@ -26,7 +25,11 @@ impl CompileShader for CrossReflect { vert_options.version = version; frag_options.version = version; - fn get_binding(ast: &Ast, stage: ExecutionModel, binding_map: &mut BTreeMap) -> Result<(), ShaderCompileError>{ + fn set_bindings( + ast: &Ast, + stage: ExecutionModel, + binding_map: &mut BTreeMap, + ) -> Result<(), ShaderCompileError> { let resources = ast.get_shader_resources()?; for resource in &resources.push_constant_buffers { let location = ResourceBindingLocation { @@ -45,7 +48,11 @@ impl CompileShader for CrossReflect { binding_map.insert(location, overridden); } - for resource in resources.uniform_buffers.iter().chain(resources.sampled_images.iter()) { + for resource in resources + .uniform_buffers + .iter() + .chain(resources.sampled_images.iter()) + { let binding = ast.get_decoration(resource.id, Decoration::Binding)?; let location = ResourceBindingLocation { stage, @@ -66,19 +73,18 @@ impl CompileShader for CrossReflect { Ok(()) } - get_binding( + set_bindings( &self.vertex, ExecutionModel::Vertex, - &mut vert_options.resource_binding_overrides + &mut vert_options.resource_binding_overrides, )?; - get_binding( + set_bindings( &self.fragment, ExecutionModel::Fragment, - &mut frag_options.resource_binding_overrides + &mut frag_options.resource_binding_overrides, )?; - eprintln!("{:?}", frag_options.resource_binding_overrides); self.vertex.set_compiler_options(&vert_options)?; self.fragment.set_compiler_options(&frag_options)?; @@ -97,17 +103,17 @@ impl CompileShader for CrossReflect { #[cfg(test)] mod test { - use std::io::Write; use crate::back::targets::{MSL, WGSL}; use crate::back::{CompileShader, FromCompilation}; + use crate::reflect::cross::SpirvCross; use crate::reflect::naga::{Naga, NagaLoweringOptions}; use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics}; use crate::reflect::ReflectShader; + use bitflags::Flags; use librashader_preprocess::ShaderSource; use rustc_hash::FxHashMap; - use bitflags::Flags; use spirv_cross::msl; - use crate::reflect::cross::SpirvCross; + use std::io::Write; #[test] pub fn test_into() { @@ -129,7 +135,8 @@ mod test { let compilation = crate::front::SpirvCompilation::try_from(&result).unwrap(); - let mut msl = >::from_compilation(compilation).unwrap(); + let mut msl = + >::from_compilation(compilation).unwrap(); msl.reflect( 0, @@ -138,25 +145,10 @@ mod test { texture_semantics: Default::default(), }, ) - .expect(""); + .expect(""); - let compiled = msl - .compile(Some(msl::Version::V2_0)) - .unwrap(); + let compiled = msl.compile(Some(msl::Version::V2_0)).unwrap(); println!("{}", compiled.fragment); - - // println!("{}", compiled.fragment); - // let mut loader = rspirv::dr::Loader::new(); - // rspirv::binary::parse_words(compilation.vertex.as_binary(), &mut loader).unwrap(); - // let module = loader.module(); - // - // let outputs: Vec<&Instruction> = module - // .types_global_values - // .iter() - // .filter(|i| i.class.opcode == Op::Variable) - // .collect(); - // - // println!("{outputs:#?}"); } -} \ No newline at end of file +} diff --git a/librashader-reflect/src/reflect/naga/mod.rs b/librashader-reflect/src/reflect/naga/mod.rs index 78f897a..485a06f 100644 --- a/librashader-reflect/src/reflect/naga/mod.rs +++ b/librashader-reflect/src/reflect/naga/mod.rs @@ -37,7 +37,10 @@ pub(crate) struct NagaReflect { /// Options to lower samplers and pcbs #[derive(Debug, Default, Clone)] pub struct NagaLoweringOptions { + /// Whether to write the PCB as a UBO. pub write_pcb_as_ubo: bool, + /// The bind group to assign samplers to. This is to ensure that samplers will + /// maintain the same bindings as textures. pub sampler_bind_group: u32, } @@ -360,13 +363,13 @@ impl NagaReflect { let binding = self.get_next_binding(0); // Reassign to UBO later if we want during compilation. if let Some(vertex_pcb) = vertex_pcb { - let ubo = &mut self.vertex.global_variables[vertex_pcb]; - ubo.binding = Some(ResourceBinding { group: 0, binding }); + let pcb = &mut self.vertex.global_variables[vertex_pcb]; + pcb.binding = Some(ResourceBinding { group: 0, binding }); } if let Some(fragment_pcb) = fragment_pcb { - let ubo = &mut self.fragment.global_variables[fragment_pcb]; - ubo.binding = Some(ResourceBinding { group: 0, binding }); + let pcb = &mut self.fragment.global_variables[fragment_pcb]; + pcb.binding = Some(ResourceBinding { group: 0, binding }); }; match (vertex_pcb, fragment_pcb) { diff --git a/librashader-reflect/src/reflect/naga/msl.rs b/librashader-reflect/src/reflect/naga/msl.rs index 8e51604..3731f7d 100644 --- a/librashader-reflect/src/reflect/naga/msl.rs +++ b/librashader-reflect/src/reflect/naga/msl.rs @@ -1,18 +1,201 @@ -use naga::{Module, ResourceBinding}; +use crate::back::msl::{MslVersion, NagaMslContext, NagaMslModule}; use crate::back::targets::MSL; use crate::back::{CompileShader, ShaderCompilerOutput}; use crate::error::ShaderCompileError; -use crate::reflect::naga::NagaReflect; +use crate::reflect::naga::{NagaLoweringOptions, NagaReflect}; +use naga::back::msl::{ + BindSamplerTarget, BindTarget, EntryPointResources, Options, PipelineOptions, TranslationInfo, +}; +use naga::valid::{Capabilities, ValidationFlags}; +use naga::{Module, TypeInner}; +use spirv_cross::msl::Version; + +fn msl_version_to_naga_msl(version: MslVersion) -> (u8, u8) { + match version { + Version::V1_0 => (1, 0), + Version::V1_1 => (1, 1), + Version::V1_2 => (1, 2), + Version::V2_0 => (2, 0), + Version::V2_1 => (2, 1), + Version::V2_2 => (2, 2), + Version::V2_3 => (2, 3), + _ => (0, 0), + } +} impl CompileShader for NagaReflect { - type Options = (); - type Context = (); + type Options = Option; + type Context = NagaMslContext; fn compile( - self, + mut self, options: Self::Options, ) -> Result, ShaderCompileError> { // https://github.com/libretro/RetroArch/blob/434e94c782af2e4d4277a24b7ed8e5fc54870088/gfx/drivers_shader/slang_process.cpp#L524 - todo!() + + let lang_version = msl_version_to_naga_msl(options.unwrap_or(MslVersion::V2_0)); + + let mut vert_options = Options { + lang_version, + per_entry_point_map: Default::default(), + inline_samplers: vec![], + spirv_cross_compatibility: true, + fake_missing_bindings: false, + bounds_check_policies: Default::default(), + zero_initialize_workgroup_memory: false, + }; + + let mut frag_options = vert_options.clone(); + + fn write_msl( + module: &Module, + options: Options, + ) -> Result<(String, TranslationInfo), ShaderCompileError> { + let mut valid = + naga::valid::Validator::new(ValidationFlags::all(), Capabilities::empty()); + let info = valid.validate(&module)?; + + let pipeline_options = PipelineOptions { + allow_and_force_point_size: false, + }; + + let msl = naga::back::msl::write_string(&module, &info, &options, &pipeline_options)?; + Ok(msl) + } + + fn generate_bindings(module: &Module) -> EntryPointResources { + let mut resources = EntryPointResources::default(); + let binding_map = &mut resources.resources; + // Don't set PCB because they'll be gone after lowering.. + // resources.push_constant_buffer = Some(1u8); + + for (_, variable) in module.global_variables.iter() { + let Some(binding) = &variable.binding else { + continue; + }; + + let Ok(ty) = module.types.get_handle(variable.ty) else { + continue; + }; + + match ty.inner { + TypeInner::Sampler { .. } => { + binding_map.insert( + binding.clone(), + BindTarget { + buffer: None, + texture: None, + sampler: Some(BindSamplerTarget::Resource(binding.binding as u8)), + binding_array_size: None, + mutable: false, + }, + ); + } + TypeInner::Struct { .. } => { + binding_map.insert( + binding.clone(), + BindTarget { + buffer: Some(binding.binding as u8), + texture: None, + sampler: None, + binding_array_size: None, + mutable: false, + }, + ); + } + TypeInner::Image { .. } => { + binding_map.insert( + binding.clone(), + BindTarget { + buffer: None, + texture: Some(binding.binding as u8), + sampler: None, + binding_array_size: None, + mutable: false, + }, + ); + } + _ => continue, + } + } + + resources + } + + self.do_lowering(&NagaLoweringOptions { + write_pcb_as_ubo: true, + sampler_bind_group: 1, + }); + + frag_options + .per_entry_point_map + .insert(String::from("main"), generate_bindings(&self.fragment)); + vert_options + .per_entry_point_map + .insert(String::from("main"), generate_bindings(&self.vertex)); + + let fragment = write_msl(&self.fragment, frag_options)?; + let vertex = write_msl(&self.vertex, vert_options)?; + Ok(ShaderCompilerOutput { + vertex: vertex.0, + fragment: fragment.0, + context: NagaMslContext { + fragment: NagaMslModule { + translation_info: fragment.1, + module: self.fragment, + }, + vertex: NagaMslModule { + translation_info: vertex.1, + module: self.vertex, + }, + }, + }) + } +} + +#[cfg(test)] +mod test { + use crate::back::targets::MSL; + use crate::back::{CompileShader, FromCompilation}; + use crate::reflect::naga::{Naga, NagaLoweringOptions}; + use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics}; + use crate::reflect::ReflectShader; + use bitflags::Flags; + use librashader_preprocess::ShaderSource; + use rustc_hash::FxHashMap; + use spirv_cross::msl; + + #[test] + pub fn test_into() { + let result = ShaderSource::load("../test/basic.slang").unwrap(); + + let mut uniform_semantics: FxHashMap = Default::default(); + + for (_index, param) in result.parameters.iter().enumerate() { + uniform_semantics.insert( + param.1.id.clone(), + UniformSemantic::Unique(Semantic { + semantics: UniqueSemantics::FloatParameter, + index: (), + }), + ); + } + + let compilation = crate::front::SpirvCompilation::try_from(&result).unwrap(); + + let mut msl = >::from_compilation(compilation).unwrap(); + + msl.reflect( + 0, + &ShaderSemantics { + uniform_semantics, + texture_semantics: Default::default(), + }, + ) + .expect(""); + + let compiled = msl.compile(Some(msl::Version::V2_0)).unwrap(); + + println!("{}", compiled.fragment); } } diff --git a/librashader-runtime-d3d12/src/filter_chain.rs b/librashader-runtime-d3d12/src/filter_chain.rs index c31a7ec..2e1d237 100644 --- a/librashader-runtime-d3d12/src/filter_chain.rs +++ b/librashader-runtime-d3d12/src/filter_chain.rs @@ -501,9 +501,9 @@ impl FilterChainD3D12 { (dxil_reflection, graphics_pipeline) } else { let hlsl_reflection = hlsl.reflect(index, semantics)?; - let hlsl = hlsl.compile( - Some(librashader_reflect::back::hlsl::HlslShaderModel::V6_0) - )?; + let hlsl = hlsl.compile(Some( + librashader_reflect::back::hlsl::HlslShaderModel::V6_0, + ))?; let graphics_pipeline = D3D12GraphicsPipeline::new_from_hlsl( device, diff --git a/librashader-runtime-d3d12/src/graphics_pipeline.rs b/librashader-runtime-d3d12/src/graphics_pipeline.rs index 743ae93..ef64921 100644 --- a/librashader-runtime-d3d12/src/graphics_pipeline.rs +++ b/librashader-runtime-d3d12/src/graphics_pipeline.rs @@ -3,8 +3,8 @@ use crate::error::assume_d3d12_init; use crate::error::FilterChainError::Direct3DOperationError; use crate::{error, util}; use librashader_cache::{cache_pipeline, cache_shader_object}; -use librashader_reflect::back::hlsl::CrossHlslContext; use librashader_reflect::back::dxil::DxilObject; +use librashader_reflect::back::hlsl::CrossHlslContext; use librashader_reflect::back::ShaderCompilerOutput; use std::mem::ManuallyDrop; use std::ops::Deref; diff --git a/librashader/src/lib.rs b/librashader/src/lib.rs index 4845aaa..1cae8f1 100644 --- a/librashader/src/lib.rs +++ b/librashader/src/lib.rs @@ -150,7 +150,7 @@ pub mod reflect { FromCompilation, ShaderCompilerOutput, }; - pub use librashader_reflect::front::{SpirvCompilation, Glslang, ShaderReflectObject }; + pub use librashader_reflect::front::{Glslang, ShaderReflectObject, SpirvCompilation}; /// Reflection via SPIRV-Cross. #[cfg(feature = "reflect-cross")] @@ -196,8 +196,8 @@ pub mod reflect { #[cfg(feature = "reflect-naga")] #[doc(cfg(feature = "reflect-naga"))] pub mod naga { - pub use librashader_reflect::reflect::naga::Naga; pub use librashader_reflect::back::wgsl::NagaWgslContext; + pub use librashader_reflect::reflect::naga::Naga; pub use librashader_reflect::reflect::naga::NagaLoweringOptions; }