From cbac01196965e870405d328d57248e7363e7c7bb Mon Sep 17 00:00:00 2001 From: chyyran Date: Thu, 15 Feb 2024 18:57:51 -0500 Subject: [PATCH] reflect(wgsl): only analyze active ubo members --- Cargo.lock | 50 ++++++- librashader-reflect/Cargo.toml | 5 +- librashader-reflect/src/error.rs | 4 + librashader-reflect/src/reflect/naga/mod.rs | 71 +++++++++- .../src/reflect/naga/trim_unused_inputs.rs | 129 ++++++++++++++++++ librashader-reflect/src/reflect/naga/wgsl.rs | 21 +-- librashader-runtime-wgpu/Cargo.toml | 1 - librashader-runtime-wgpu/src/draw_quad.rs | 22 +-- librashader-runtime-wgpu/src/filter_chain.rs | 111 ++++++++------- .../src/graphics_pipeline.rs | 13 +- .../tests/hello_triangle.rs | 16 ++- test/basic.slang | 2 +- 12 files changed, 352 insertions(+), 93 deletions(-) create mode 100644 librashader-reflect/src/reflect/naga/trim_unused_inputs.rs diff --git a/Cargo.lock b/Cargo.lock index c4af4cc..a3fc38a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1009,6 +1009,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1643,7 +1652,8 @@ dependencies = [ "rspirv", "rustc-hash", "serde", - "spirv", + "spirv 0.2.0+1.5.4", + "spirv-linker", "spirv-to-dxil", "thiserror", ] @@ -1924,7 +1934,7 @@ dependencies = [ "num-traits", "petgraph", "rustc-hash", - "spirv", + "spirv 0.3.0+sdk-1.3.268.0", "termcolor", "thiserror", "unicode-xid", @@ -2612,12 +2622,13 @@ dependencies = [ [[package]] name = "rspirv" -version = "0.12.0+sdk-1.3.268.0" +version = "0.11.0+1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cf3a93856b6e5946537278df0d3075596371b1950ccff012f02b0f7eafec8d" +checksum = "1503993b59ca9ae4127365c3293517576d7ce56be9f3d8abb1625c85ddc583ba" dependencies = [ - "rustc-hash", - "spirv", + "fxhash", + "num-traits", + "spirv 0.2.0+1.5.4", ] [[package]] @@ -2816,6 +2827,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags 1.3.2", + "num-traits", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -2825,6 +2846,17 @@ dependencies = [ "bitflags 2.4.2", ] +[[package]] +name = "spirv-linker" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d236255ec7387809e18d57221d0fa428d6f909abc88524944cdfb7ebab01acb" +dependencies = [ + "rspirv", + "thiserror", + "topological-sort", +] + [[package]] name = "spirv-to-dxil" version = "0.4.6" @@ -3023,6 +3055,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "topological-sort" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c" + [[package]] name = "tracing" version = "0.1.40" diff --git a/librashader-reflect/Cargo.toml b/librashader-reflect/Cargo.toml index 0f30e4e..a1a7a21 100644 --- a/librashader-reflect/Cargo.toml +++ b/librashader-reflect/Cargo.toml @@ -23,10 +23,11 @@ librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.0- librashader-presets = { path = "../librashader-presets", version = "0.2.0-rc.2" } spirv_cross = { package = "librashader-spirv-cross", version = "0.25.1", optional = true } +spirv-linker = "0.1.0" naga = { version = "0.19.0", optional = true } -rspirv = { version = "0.12.0", optional = true } -spirv = { version = "0.3.0", optional = true} +rspirv = { version = "0.11.0", optional = true } +spirv = { version = "0.2.0", optional = true} serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/librashader-reflect/src/error.rs b/librashader-reflect/src/error.rs index 2ff7054..ba7cb7a 100644 --- a/librashader-reflect/src/error.rs +++ b/librashader-reflect/src/error.rs @@ -125,6 +125,10 @@ pub enum ShaderReflectError { #[cfg(feature = "naga")] #[error("naga-spv")] NagaInputError(#[from] naga::front::spv::Error), + /// Error when transpiling from naga + #[cfg(feature = "naga")] + #[error("naga-spv")] + NagaReflectError(#[from] naga::WithSpan), } #[cfg(feature = "unstable-naga")] diff --git a/librashader-reflect/src/reflect/naga/mod.rs b/librashader-reflect/src/reflect/naga/mod.rs index 18d2dec..77691c7 100644 --- a/librashader-reflect/src/reflect/naga/mod.rs +++ b/librashader-reflect/src/reflect/naga/mod.rs @@ -1,17 +1,21 @@ mod lower_samplers; pub mod msl; pub mod spirv; +mod trim_unused_inputs; pub mod wgsl; use crate::error::{SemanticsErrorKind, ShaderReflectError}; +use bitflags::Flags; use crate::front::SpirvCompilation; +use naga::valid::{Capabilities, ModuleInfo, ValidationFlags, Validator}; use naga::{ - AddressSpace, Binding, GlobalVariable, Handle, ImageClass, Module, ResourceBinding, Scalar, - ScalarKind, TypeInner, VectorSize, + AddressSpace, Binding, Expression, GlobalVariable, Handle, ImageClass, Module, ResourceBinding, + Scalar, ScalarKind, StructMember, TypeInner, VectorSize, }; use rspirv::binary::Assemble; use rspirv::dr::Builder; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::reflect::helper::{SemanticErrorBlame, TextureData, UboData}; use crate::reflect::semantics::{ @@ -602,6 +606,41 @@ impl NagaReflect { Ok(()) } + fn collect_uniform_names( + module: &Module, + buffer_handle: Handle, + blame: SemanticErrorBlame, + ) -> Result, ShaderReflectError> { + let mut names = FxHashSet::default(); + let ubo = &module.global_variables[buffer_handle]; + + let TypeInner::Struct { members, .. } = &module.types[ubo.ty].inner else { + return Err(blame.error(SemanticsErrorKind::InvalidResourceType)); + }; + + // struct access is AccessIndex + for (_, fun) in module.functions.iter() { + for (_, expr) in fun.expressions.iter() { + let &Expression::AccessIndex { base, index } = expr else { + continue; + }; + + let &Expression::GlobalVariable(base) = &fun.expressions[base] else { + continue; + }; + + if base == buffer_handle { + let member = members + .get(index as usize) + .ok_or(blame.error(SemanticsErrorKind::InvalidRange(index)))?; + names.insert(member); + } + } + } + + Ok(names) + } + fn reflect_buffer_struct_members( module: &Module, resource: Handle, @@ -611,7 +650,10 @@ impl NagaReflect { offset_type: UniformMemberBlock, blame: SemanticErrorBlame, ) -> Result<(), ShaderReflectError> { + let reachable = Self::collect_uniform_names(&module, resource, blame)?; + let resource = &module.global_variables[resource]; + let TypeInner::Struct { members, .. } = &module.types[resource.ty].inner else { return Err(blame.error(SemanticsErrorKind::InvalidResourceType)); }; @@ -620,6 +662,11 @@ impl NagaReflect { let Some(name) = member.name.clone() else { return Err(blame.error(SemanticsErrorKind::InvalidRange(member.offset))); }; + + if !reachable.contains(member) { + continue; + } + let member_type = &module.types[member.ty].inner; if let Some(parameter) = semantics.uniform_semantics.get_unique_semantic(&name) { @@ -884,7 +931,6 @@ impl ReflectShader for NagaReflect { }); let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?; - let mut meta = BindingMeta::default(); if let Some(ubo) = vertex_ubo { @@ -965,6 +1011,10 @@ impl ReflectShader for NagaReflect { #[cfg(test)] mod test { + use crate::reflect::semantics::{Semantic, TextureSemantics, UniformSemantic}; + use librashader_common::map::FastHashMap; + use librashader_preprocess::ShaderSource; + use librashader_presets::ShaderPreset; // #[test] // pub fn test_into() { @@ -983,4 +1033,19 @@ mod test { // // println!("{outputs:#?}"); // } + + // #[test] + // pub fn mega_bezel_reflect() { + // let preset = ShaderPreset::try_parse( + // "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", + // ) + // .unwrap(); + // + // let mut uniform_semantics: FastHashMap = Default::default(); + // let mut texture_semantics: FastHashMap> = Default::default(); + // + // + // + // + // } } diff --git a/librashader-reflect/src/reflect/naga/trim_unused_inputs.rs b/librashader-reflect/src/reflect/naga/trim_unused_inputs.rs new file mode 100644 index 0000000..6d431dc --- /dev/null +++ b/librashader-reflect/src/reflect/naga/trim_unused_inputs.rs @@ -0,0 +1,129 @@ +use rspirv::dr::{Builder, Module, Operand}; +use rustc_hash::{FxHashMap, FxHashSet}; +use spirv::{Op, StorageClass}; + +pub struct LinkInputs<'a> { + pub frag_builder: &'a mut Builder, + pub vert: &'a Module, + + pub inputs: FxHashMap, +} + +impl<'a> LinkInputs<'a> { + fn find_location(module: &Module, id: spirv::Word) -> Option { + module.annotations.iter().find_map(|op| { + if op.class.opcode != Op::Decorate { + return None; + } + + eprintln!("{:?}", op); + return None; + }) + } + + pub fn new(vert: &'a Module, frag: &'a mut Builder) -> Self { + let mut inputs = FxHashMap::default(); + + for global in frag.module_ref().types_global_values.iter() { + if global.class.opcode == spirv::Op::Variable + && global.operands[0] == Operand::StorageClass(StorageClass::Input) + { + if let Some(id) = global.result_id { + inputs.insert(id, 0); + } + } + } + + // for global in vert.types_global_values.iter() { + // if global.class.opcode == spirv::Op::Variable + // && global.operands[0] == Operand::StorageClass(StorageClass::Output) + // { + // if let Some(id) = global.result_id { + // inputs.insert(id, 0); + // } + // } + // } + + let mut val = Self { + frag_builder: frag, + vert, + inputs, + }; + val + } + + pub fn do_pass(&mut self) { + let functions = &self.frag_builder.module_ref().functions; + + // literally if it has any reference at all we can keep it + for function in functions { + for param in &function.parameters { + for op in ¶m.operands { + if let Some(word) = op.id_ref_any() { + if self.inputs.contains_key(&word) { + self.inputs.remove(&word); + } + } + } + } + + for block in &function.blocks { + for inst in &block.instructions { + for op in &inst.operands { + if let Some(word) = op.id_ref_any() { + if self.inputs.contains_key(&word) { + self.inputs.remove(&word); + } + } + } + } + } + } + + // ok well guess we dont + + self.frag_builder.module_mut().debug_names.retain(|instr| { + for op in &instr.operands { + if let Some(word) = op.id_ref_any() { + if self.inputs.contains_key(&word) { + return false; + } + } + } + return true; + }); + + self.frag_builder.module_mut().annotations.retain(|instr| { + for op in &instr.operands { + if let Some(word) = op.id_ref_any() { + if self.inputs.contains_key(&word) { + return false; + } + } + } + return true; + }); + + for entry_point in self.frag_builder.module_mut().entry_points.iter_mut() { + entry_point.operands.retain(|op| { + if let Some(word) = op.id_ref_any() { + if self.inputs.contains_key(&word) { + return false; + } + } + return true; + }) + } + + self.frag_builder + .module_mut() + .types_global_values + .retain(|instr| { + let Some(id) = instr.result_id else { + return true; + }; + + !self.inputs.contains_key(&id) + }); + } +} diff --git a/librashader-reflect/src/reflect/naga/wgsl.rs b/librashader-reflect/src/reflect/naga/wgsl.rs index 98ee8c6..90389da 100644 --- a/librashader-reflect/src/reflect/naga/wgsl.rs +++ b/librashader-reflect/src/reflect/naga/wgsl.rs @@ -4,8 +4,8 @@ use crate::back::{CompileShader, ShaderCompilerOutput}; use crate::error::ShaderCompileError; use crate::reflect::naga::{NagaLoweringOptions, NagaReflect}; use naga::back::wgsl::WriterFlags; -use naga::valid::{Capabilities, ValidationFlags}; -use naga::Module; +use naga::valid::{Capabilities, ModuleInfo, ValidationFlags, Validator}; +use naga::{Expression, Module, Statement}; impl CompileShader for NagaReflect { type Options = NagaLoweringOptions; @@ -15,19 +15,20 @@ impl CompileShader for NagaReflect { mut self, options: Self::Options, ) -> Result, ShaderCompileError> { - fn write_wgsl(module: &Module) -> Result { - let mut valid = - naga::valid::Validator::new(ValidationFlags::all(), Capabilities::empty()); - let info = valid.validate(&module)?; - - let wgsl = naga::back::wgsl::write_string(&module, &info, WriterFlags::EXPLICIT_TYPES)?; + fn write_wgsl(module: &Module, info: &ModuleInfo) -> Result { + let wgsl = naga::back::wgsl::write_string(&module, &info, WriterFlags::empty())?; Ok(wgsl) } self.do_lowering(&options); - let fragment = write_wgsl(&self.fragment)?; - let vertex = write_wgsl(&self.vertex)?; + let mut valid = Validator::new(ValidationFlags::all(), Capabilities::empty()); + + let vertex_info = valid.validate(&self.vertex)?; + let fragment_info = valid.validate(&self.fragment)?; + + let fragment = write_wgsl(&self.fragment, &fragment_info)?; + let vertex = write_wgsl(&self.vertex, &vertex_info)?; Ok(ShaderCompilerOutput { vertex, fragment, diff --git a/librashader-runtime-wgpu/Cargo.toml b/librashader-runtime-wgpu/Cargo.toml index f2b743c..1ce2d27 100644 --- a/librashader-runtime-wgpu/Cargo.toml +++ b/librashader-runtime-wgpu/Cargo.toml @@ -26,7 +26,6 @@ thiserror = "1.0.50" bytemuck = { version = "1.14.0", features = ["derive"] } array-concat = "0.5.2" - [features] # workaround for docsrs to not build metal-rs. wgpu_dx12 = ["wgpu/dx12"] diff --git a/librashader-runtime-wgpu/src/draw_quad.rs b/librashader-runtime-wgpu/src/draw_quad.rs index db2d20e..f6206c6 100644 --- a/librashader-runtime-wgpu/src/draw_quad.rs +++ b/librashader-runtime-wgpu/src/draw_quad.rs @@ -8,45 +8,45 @@ use wgpu::{Buffer, Device, RenderPass}; // WGPU does vertex expansion #[repr(C)] #[derive(Debug, Copy, Clone, Default, Zeroable, Pod)] -struct WgpuVertex { - position: [f32; 2], - texcoord: [f32; 2], +pub struct WgpuVertex { + pub position: [f32; 4], + pub texcoord: [f32; 2], } const OFFSCREEN_VBO_DATA: [WgpuVertex; 4] = [ WgpuVertex { - position: [-1.0, -1.0], + position: [-1.0, -1.0, 0.0, 1.0], texcoord: [0.0, 0.0], }, WgpuVertex { - position: [-1.0, 1.0], + position: [-1.0, 1.0, 0.0, 1.0], texcoord: [0.0, 1.0], }, WgpuVertex { - position: [1.0, -1.0], + position: [1.0, -1.0, 0.0, 1.0], texcoord: [1.0, 0.0], }, WgpuVertex { - position: [1.0, 1.0], + position: [1.0, 1.0, 0.0, 1.0], texcoord: [1.0, 1.0], }, ]; const FINAL_VBO_DATA: [WgpuVertex; 4] = [ WgpuVertex { - position: [0.0, 0.0], + position: [0.0, 0.0, 0.0, 1.0], texcoord: [0.0, 0.0], }, WgpuVertex { - position: [0.0, 1.0], + position: [0.0, 1.0, 0.0, 1.0], texcoord: [0.0, 1.0], }, WgpuVertex { - position: [1.0, 0.0], + position: [1.0, 0.0, 0.0, 1.0], texcoord: [1.0, 0.0], }, WgpuVertex { - position: [1.0, 1.0], + position: [1.0, 1.0, 0.0, 1.0], texcoord: [1.0, 1.0], }, ]; diff --git a/librashader-runtime-wgpu/src/filter_chain.rs b/librashader-runtime-wgpu/src/filter_chain.rs index 1385aec..97a70da 100644 --- a/librashader-runtime-wgpu/src/filter_chain.rs +++ b/librashader-runtime-wgpu/src/filter_chain.rs @@ -15,6 +15,7 @@ use rayon::prelude::*; use std::collections::VecDeque; use std::path::Path; +use rayon::ThreadPoolBuilder; use std::sync::Arc; use crate::buffer::WgpuStagedBuffer; @@ -267,63 +268,73 @@ impl FilterChainWgpu { #[cfg(target_arch = "wasm32")] let passes_iter = passes.into_iter(); - let filters: Vec> = passes_iter - .enumerate() - .map(|(index, (config, source, mut reflect))| { - let reflection = reflect.reflect(index, semantics)?; - let wgsl = reflect.compile(NagaLoweringOptions { - write_pcb_as_ubo: true, - sampler_bind_group: 1, - })?; + let thread_pool = ThreadPoolBuilder::new() + .stack_size(10 * 1048576) + .build() + .unwrap(); - let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); - let push_size = reflection - .push_constant - .as_ref() - .map_or(0, |push| push.size as wgpu::BufferAddress); + let filters = thread_pool.install(|| { + let filters: Vec> = passes_iter + .enumerate() + .map(|(index, (config, source, mut reflect))| { + let reflection = reflect.reflect(index, semantics)?; + let wgsl = reflect.compile(NagaLoweringOptions { + write_pcb_as_ubo: true, + sampler_bind_group: 1, + })?; - let uniform_storage = UniformStorage::new_with_storage( - WgpuStagedBuffer::new( - &device, - wgpu::BufferUsages::UNIFORM, - ubo_size as wgpu::BufferAddress, - Some("ubo"), - ), - WgpuStagedBuffer::new( - &device, - wgpu::BufferUsages::UNIFORM, - push_size as wgpu::BufferAddress, - Some("push"), - ), - ); + let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); + let push_size = reflection + .push_constant + .as_ref() + .map_or(0, |push| push.size as wgpu::BufferAddress); - let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); + let uniform_storage = UniformStorage::new_with_storage( + WgpuStagedBuffer::new( + &device, + wgpu::BufferUsages::UNIFORM, + ubo_size as wgpu::BufferAddress, + Some("ubo"), + ), + WgpuStagedBuffer::new( + &device, + wgpu::BufferUsages::UNIFORM, + push_size as wgpu::BufferAddress, + Some("push"), + ), + ); - let render_pass_format: Option = - if let Some(format) = config.get_format_override() { - format.into() - } else { - source.format.into() - }; + let uniform_bindings = + reflection.meta.create_binding_map(|param| param.offset()); - let graphics_pipeline = WgpuGraphicsPipeline::new( - Arc::clone(&device), - &wgsl, - &reflection, - render_pass_format.unwrap_or(TextureFormat::Rgba8Unorm), - ); + let render_pass_format: Option = + if let Some(format) = config.get_format_override() { + format.into() + } else { + source.format.into() + }; - Ok(FilterPass { - device: Arc::clone(&device), - reflection, - uniform_storage, - uniform_bindings, - source, - config, - graphics_pipeline, + let graphics_pipeline = WgpuGraphicsPipeline::new( + Arc::clone(&device), + &wgsl, + &reflection, + render_pass_format.unwrap_or(TextureFormat::Rgba8Unorm), + ); + + Ok(FilterPass { + device: Arc::clone(&device), + reflection, + uniform_storage, + uniform_bindings, + source, + config, + graphics_pipeline, + }) }) - }) - .collect(); + .collect(); + filters + }); + // let filters: error::Result> = filters.into_iter().collect(); let filters = filters?; diff --git a/librashader-runtime-wgpu/src/graphics_pipeline.rs b/librashader-runtime-wgpu/src/graphics_pipeline.rs index b1e2c40..25b25cd 100644 --- a/librashader-runtime-wgpu/src/graphics_pipeline.rs +++ b/librashader-runtime-wgpu/src/graphics_pipeline.rs @@ -1,3 +1,4 @@ +use crate::draw_quad::WgpuVertex; use crate::framebuffer::WgpuOutputView; use crate::util; use librashader_reflect::back::wgsl::NagaWgslContext; @@ -152,17 +153,19 @@ impl PipelineLayoutObjects { module: &self.vertex, entry_point: &self.vertex_entry_name, buffers: &[VertexBufferLayout { - array_stride: 4 * std::mem::size_of::() as wgpu::BufferAddress, + array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[ wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, + format: wgpu::VertexFormat::Float32x4, + offset: bytemuck::offset_of!(WgpuVertex, position) + as wgpu::BufferAddress, shader_location: 0, }, wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x2, - offset: (2 * std::mem::size_of::()) as wgpu::BufferAddress, + offset: bytemuck::offset_of!(WgpuVertex, texcoord) + as wgpu::BufferAddress, shader_location: 1, }, ], @@ -173,7 +176,7 @@ impl PipelineLayoutObjects { entry_point: &self.fragment_entry_name, targets: &[Some(wgpu::ColorTargetState { format: framebuffer_format, - blend: Some(wgpu::BlendState::REPLACE), + blend: None, write_mask: wgpu::ColorWrites::ALL, })], }), diff --git a/librashader-runtime-wgpu/tests/hello_triangle.rs b/librashader-runtime-wgpu/tests/hello_triangle.rs index ed90661..cf6c9c4 100644 --- a/librashader-runtime-wgpu/tests/hello_triangle.rs +++ b/librashader-runtime-wgpu/tests/hello_triangle.rs @@ -117,11 +117,19 @@ impl<'a> State<'a> { let device = Arc::new(device); let queue = Arc::new(queue); + // + // let preset = ShaderPreset::try_parse( + // "../test/basic.slangp", + // ) + // .unwrap(); - let preset = ShaderPreset::try_parse( - "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", - ) - .unwrap(); + let preset = + ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap(); + + // let preset = ShaderPreset::try_parse( + // "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", + // ) + // .unwrap(); let chain = FilterChainWgpu::load_from_preset( preset, diff --git a/test/basic.slang b/test/basic.slang index 6638990..7c5aa62 100644 --- a/test/basic.slang +++ b/test/basic.slang @@ -32,5 +32,5 @@ layout(location = 0) out vec4 FragColor; layout(binding = 1) uniform sampler2D Source; void main() { - FragColor = texture(Source, vTexCoord) * params.ColorMod * ColorMod2; + FragColor = texture(Source, vTexCoord) * params.ColorMod; } \ No newline at end of file