kitgit

tirbofish/dropbear · diff

d925ed0 · Thribhu K

refactor: changed from wgsl to slang (except for mipmap, and one is still coming along) Unverified

diff --git a/crates/dropbear-engine/Cargo.toml b/crates/dropbear-engine/Cargo.toml
index a596f34..5a68677 100644
--- a/crates/dropbear-engine/Cargo.toml
+++ b/crates/dropbear-engine/Cargo.toml
@@ -12,7 +12,7 @@ readme.workspace = true
 dropbear_future-queue = { path = "../dropbear_future-queue" }
 dropbear-traits = { path = "../dropbear-traits" }
 dropbear-macro = { path = "../dropbear-macro" }
-slank = { path = "../slank", features = ["download-slang"] }
+slank = { path = "../slank", features = ["download-slang", "use-wgpu"] }
 
 anyhow.workspace = true
 app_dirs2.workspace = true
@@ -54,3 +54,6 @@ rfd.workspace = true
 version = "0.25"
 default-features = false
 features = ["png", "jpeg"]
+
+[build-dependencies]
+slank = { path = "../slank", features = ["download-slang"] }
\ No newline at end of file
diff --git a/crates/dropbear-engine/build.rs b/crates/dropbear-engine/build.rs
new file mode 100644
index 0000000..7f01aa4
--- /dev/null
+++ b/crates/dropbear-engine/build.rs
@@ -0,0 +1,19 @@
+use slank::{SlangShaderBuilder, SlangTarget};
+
+fn main() {
+    // to copy paste:         
+    // let shader = Shader::from_slang(graphics.clone(), &slank::compiled::CompiledSlangShader::from_bytes("light cube", include_slang!("light_cube")));
+
+    SlangShaderBuilder::new("light_cube")
+        .add_source_path("src/pipelines/shaders/light.slang").unwrap()
+        .compile_to_out_dir(SlangTarget::SpirV).unwrap();
+
+    SlangShaderBuilder::new("blit_shader")
+        .add_source_path("src/pipelines/shaders/blit.slang").unwrap()
+        .compile_to_out_dir(SlangTarget::SpirV).unwrap();
+
+    // unable to do mipmap.slang because it required texture_storage_2d, one write and one read. 
+    // just wasn't possible with slang :(
+
+    
+}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/mipmap.rs b/crates/dropbear-engine/src/mipmap.rs
index d397eca..fe20ae4 100644
--- a/crates/dropbear-engine/src/mipmap.rs
+++ b/crates/dropbear-engine/src/mipmap.rs
@@ -1,4 +1,6 @@
-use crate::texture::Texture;
+use slank::{include_slang, utils::WgpuUtils};
+
+use crate::{texture::Texture};
 
 pub struct MipMapper {
     blit_mipmap: wgpu::RenderPipeline,
@@ -10,10 +12,10 @@ pub struct MipMapper {
 
 impl MipMapper {
     pub fn new(device: &wgpu::Device) -> Self {
-        let blit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
-            label: Some("mipmap blit shader"),
-            source: wgpu::ShaderSource::Wgsl(include_str!("pipelines/shaders/blit.wgsl").into()),
-        });
+        let blit_shader = device.create_shader_module(slank::compiled::CompiledSlangShader::from_bytes(
+            "mipmap blit_shader", 
+            include_slang!("blit_shader")
+        ).create_wgpu_shader());
 
         // Keep this SRGB so we can render directly into the SRGB textures we create for materials.
         let blit_format = Texture::TEXTURE_FORMAT;
@@ -88,10 +90,12 @@ impl MipMapper {
             bind_group_layouts: &[&storage_texture_layout],
             push_constant_ranges: &[],
         });
+
         let compute_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
             label: Some("mipmap compute shader"),
             source: wgpu::ShaderSource::Wgsl(include_str!("pipelines/shaders/mipmap.wgsl").into()),
         });
+
         let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
             label: Some("Mipmapper"),
             layout: Some(&pipeline_layout),
diff --git a/crates/dropbear-engine/src/pipelines/light_cube.rs b/crates/dropbear-engine/src/pipelines/light_cube.rs
index 6a599a9..3975d61 100644
--- a/crates/dropbear-engine/src/pipelines/light_cube.rs
+++ b/crates/dropbear-engine/src/pipelines/light_cube.rs
@@ -1,6 +1,7 @@
 use std::sync::Arc;
 use std::mem::size_of;
 use glam::DMat4;
+use slank::include_slang;
 use wgpu::{BufferAddress, VertexAttribute, VertexFormat};
 use crate::buffer::{StorageBuffer, UniformBuffer};
 use crate::entity::{EntityTransform, Transform};
@@ -26,11 +27,7 @@ pub struct LightCubePipeline {
 
 impl DropbearShaderPipeline for LightCubePipeline {
     fn new(graphics: Arc<SharedGraphicsContext>) -> Self {
-        let shader = Shader::new(
-            graphics.clone(),
-            include_str!("shaders/light.wgsl"),
-            Some("light cube shader"),
-        );
+        let shader = Shader::from_slang(graphics.clone(), &slank::compiled::CompiledSlangShader::from_bytes("light cube", include_slang!("light_cube")));
 
         let pipeline_layout = graphics.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
             label: Some("light cube pipeline layout"),
diff --git a/crates/dropbear-engine/src/pipelines/shaders/blit.slang b/crates/dropbear-engine/src/pipelines/shaders/blit.slang
new file mode 100644
index 0000000..4b14a95
--- /dev/null
+++ b/crates/dropbear-engine/src/pipelines/shaders/blit.slang
@@ -0,0 +1,35 @@
+#include "dropbear.slang"
+
+struct VertexOutput {
+    float4 clip_position : SV_Position;
+
+    [[vk_location(0)]]
+    float2 uv;
+};
+
+[[vk_binding(0, 0)]]
+Texture2D tex;
+
+[[vk_binding(1, 0)]]
+SamplerState tex_sampler;
+
+[shader("vertex")]
+VertexOutput vs_main(
+    uint in_vertex_index: SV_VulkanVertexID
+) {
+    VertexOutput output;
+
+    float x = float((in_vertex_index << 1u) & 2u);
+    float y = float(in_vertex_index & 2u);
+
+    output.clip_position = float4(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
+    output.uv = float2(x, 1.0 - y);
+
+    return output;
+}
+
+[shader("fragment")]
+float4 fs_main(VertexOutput input) : SV_Target0 {
+    float4 s = tex.Sample(tex_sampler, input.uv);
+    return s;
+}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/pipelines/shaders/blit.wgsl b/crates/dropbear-engine/src/pipelines/shaders/blit.wgsl
index aa10b01..bd6b79b 100644
--- a/crates/dropbear-engine/src/pipelines/shaders/blit.wgsl
+++ b/crates/dropbear-engine/src/pipelines/shaders/blit.wgsl
@@ -13,7 +13,7 @@ fn vs_main(
     @builtin(vertex_index) in_vertex_index: u32,
 ) -> VertexOutput {
     var out: VertexOutput;
-    // Create fullscreen triangle
+
     let x = f32((in_vertex_index << 1u) & 2u);
     let y = f32(in_vertex_index & 2u);
     out.clip_position = vec4<f32>(x * 2.0 - 1.0, y * 2.0 - 1.0, 0.0, 1.0);
diff --git a/crates/dropbear-engine/src/pipelines/shaders/dropbear.slang b/crates/dropbear-engine/src/pipelines/shaders/dropbear.slang
new file mode 100644
index 0000000..11a2cfe
--- /dev/null
+++ b/crates/dropbear-engine/src/pipelines/shaders/dropbear.slang
@@ -0,0 +1,32 @@
+/// A standard light descriptor struct. 
+struct Light {
+    float4 position;
+    float4 direction; // x, y, z, outer_cutoff_angle
+    float4 color; // r, g, b, light_type (0, 1, 2)
+
+    float constant;
+    float lin;
+    float quadratic;
+
+    float cutoff;
+};
+
+struct CameraUniform {
+    float4 view_pos;
+    float4x4 view_proj;
+};
+
+/// Converts a wgpu matrix (in the form of 4 float4) into a matrix usable for Slang. 
+float4x4 vs_input_matrix(
+    float4 mat1, 
+    float4 mat2, 
+    float4 mat3, 
+    float4 mat4
+) {
+    return transpose(float4x4(
+        mat1,
+        mat2,
+        mat3,
+        mat4
+    ));
+}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/pipelines/shaders/light.slang b/crates/dropbear-engine/src/pipelines/shaders/light.slang
index 428e996..9a2a330 100644
--- a/crates/dropbear-engine/src/pipelines/shaders/light.slang
+++ b/crates/dropbear-engine/src/pipelines/shaders/light.slang
@@ -1,48 +1,42 @@
-struct Light {
-    float4 position;
-    float4 direction; // x, y, z, outer_cutoff_angle
-    float4 color; // r, g, b, light_type (0, 1, 2)
-
-    float constant;
-    float lin;
-    float quadratic;
-
-    float cutoff;
-};
-
-
-struct CameraUniform {
-    float4 view_pos;
-    float4x4 view_proj;
-};
+#include "dropbear.slang"
 
 [[vk::binding(0, 0)]]
 ConstantBuffer<CameraUniform> camera;
 
-[[vk::binding(1, 0)]]
+[[vk::binding(0, 1)]]
 ConstantBuffer<Light> light;
 
 struct VertexInput {
+    [[vk::location(0)]]
     float3 position : POSITION;
+    
+    [[vk::location(5)]]
     float4 model_matrix_0 : TEXCOORD5;
+    
+    [[vk::location(6)]]
     float4 model_matrix_1 : TEXCOORD6;
+    
+    [[vk::location(7)]]
     float4 model_matrix_2 : TEXCOORD7;
+    
+    [[vk::location(8)]]
     float4 model_matrix_3 : TEXCOORD8;
 };
 
 struct VertexOutput {
     float4 clip_position : SV_Position;
+    
+    [[vk::location(0)]]
     float3 color : COLOR0;
 };
 
-
 [shader("vertex")]
 VertexOutput vs_main(VertexInput input) {
-    float4x4 model_matrix = float4x4(
+    float4x4 model_matrix = vs_input_matrix(
         input.model_matrix_0,
         input.model_matrix_1,
         input.model_matrix_2,
-        input.model_matrix_3
+        input.model_matrix_3,
     );
 
     VertexOutput output;
diff --git a/crates/dropbear-engine/src/pipelines/shaders/light.wgsl b/crates/dropbear-engine/src/pipelines/shaders/light.wgsl
deleted file mode 100644
index d31ebc8..0000000
--- a/crates/dropbear-engine/src/pipelines/shaders/light.wgsl
+++ /dev/null
@@ -1,62 +0,0 @@
-// Shader for rendering the white cube for lighting (or diff depending on colour)
-
-struct Light {
-    position: vec4<f32>,
-    direction: vec4<f32>, // x, y, z, outer_cutoff_angle
-    color: vec4<f32>, // r, g, b, light_type (0, 1, 2)
-    constant: f32,
-    lin: f32,
-    quadratic: f32,
-    cutoff: f32,
-}
-
-struct CameraUniform {
-    view_pos: vec4<f32>,
-    view_proj: mat4x4<f32>,
-};
-
-@group(0) @binding(0)
-var<uniform> camera: CameraUniform;
-
-@group(1) @binding(0)
-var<uniform> light: Light;
-
-struct InstanceInput {
-    @location(5) model_matrix_0: vec4<f32>,
-    @location(6) model_matrix_1: vec4<f32>,
-    @location(7) model_matrix_2: vec4<f32>,
-    @location(8) model_matrix_3: vec4<f32>,
-}
-
-struct VertexInput {
-    @location(0) position: vec3<f32>,
-};
-
-struct VertexOutput {
-    @builtin(position) clip_position: vec4<f32>,
-    @location(0) color: vec3<f32>,
-};
-
-@vertex
-fn vs_main(
-    model: VertexInput,
-    instance: InstanceInput,
-) -> VertexOutput {
-    let model_matrix = mat4x4<f32>(
-        instance.model_matrix_0,
-        instance.model_matrix_1,
-        instance.model_matrix_2,
-        instance.model_matrix_3,
-    );
-    var out: VertexOutput;
-    out.clip_position = camera.view_proj * model_matrix * vec4<f32>(model.position, 1.0);
-    out.color = light.color.xyz;
-    return out;
-}
-
-// Fragment shaders
-
-@fragment
-fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
-    return vec4<f32>(in.color, 1.0);
-}
\ No newline at end of file
diff --git a/crates/dropbear-engine/src/shader.rs b/crates/dropbear-engine/src/shader.rs
index 772572f..4681432 100644
--- a/crates/dropbear-engine/src/shader.rs
+++ b/crates/dropbear-engine/src/shader.rs
@@ -3,6 +3,7 @@
 use std::ops::Deref;
 use crate::graphics::SharedGraphicsContext;
 use std::sync::Arc;
+use slank::{compiled::CompiledSlangShader, utils::WgpuUtils};
 use wgpu::ShaderModule;
 
 /// A nice little struct that stored basic information about a WGPU shaders.
@@ -47,6 +48,8 @@ impl Shader {
 
         log::debug!("Created new shaders under the label: {:?}", label);
 
+        slank::compiled::CompiledSlangShader::from_bytes("light cube", slank::include_slang!("light_cube"));
+
         Self {
             label: match label {
                 Some(label) => label.into(),
@@ -56,4 +59,18 @@ impl Shader {
             content: shader_file_contents.to_string(),
         }
     }
+
+    pub fn from_slang(graphics: Arc<SharedGraphicsContext>, shader: &CompiledSlangShader) -> Self {
+        let module = graphics
+            .device
+            .create_shader_module(shader.create_wgpu_shader());
+
+        log::debug!("Created new shaders [slang] under the label: {}", shader.label());
+
+        Self {
+            label: shader.label().clone(),
+            module,
+            content: String::from_utf8(shader.source.clone()).unwrap_or_default(),
+        }
+    }
 }
diff --git a/crates/slank/build.rs b/crates/slank/build.rs
index 8fb5429..0def8ad 100644
--- a/crates/slank/build.rs
+++ b/crates/slank/build.rs
@@ -7,7 +7,7 @@ fn main() -> anyhow::Result<()> {
     let path = find_or_download_slangc()?;
 
     println!("cargo:rustc-env=SLANG_DIR={}", path.display());
-    println!("cargo:warning=Found slangc at: {}", path.display());
+    // println!("cargo:warning=Found slangc at: {}", path.display());
     Ok(())
 }
 
@@ -127,7 +127,7 @@ fn check_cached_download() -> Option<PathBuf> {
 fn download_slang() -> anyhow::Result<PathBuf> {
     use std::fs;
 
-    println!("cargo:warning=Downloading slangc compiler...");
+    // println!("cargo:warning=Downloading slangc compiler...");
 
     let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
     let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
@@ -147,10 +147,10 @@ fn download_slang() -> anyhow::Result<PathBuf> {
     let archive_path = cache_dir.join(&archive_name);
 
     if !archive_path.exists() {
-        println!("cargo:warning=Downloading from: {}", download_url);
+        // println!("cargo:warning=Downloading from: {}", download_url);
         download_file(&download_url, &archive_path)?;
     } else {
-        println!("cargo:warning=Using cached download: {}", archive_path.display());
+        // println!("cargo:warning=Using cached download: {}", archive_path.display());
     }
 
     extract_archive(&archive_path, &cache_dir)?;
diff --git a/crates/slank/src/compiled.rs b/crates/slank/src/compiled.rs
index ed9b879..7237852 100644
--- a/crates/slank/src/compiled.rs
+++ b/crates/slank/src/compiled.rs
@@ -1,6 +1,7 @@
 use std::path::Path;
 
 pub struct CompiledSlangShader {
+    pub(crate) label: String,
     args: String,
     pub source: Vec<u8>,
 }
@@ -8,10 +9,11 @@ pub struct CompiledSlangShader {
 impl CompiledSlangShader {
     /// Internal: Creates a new instance of [CompiledSlangShader] from the compiled args
     /// of [crate::SlangShaderBuilder]
-    pub(crate) fn new(args: String, source: Vec<u8>) -> Self {
+    pub(crate) fn new(label: String, args: String, source: Vec<u8>) -> Self {
         Self {
             args,
-            source
+            source,
+            label,
         }
     }
 
@@ -19,6 +21,22 @@ impl CompiledSlangShader {
     pub fn args(&self) -> String {
         self.args.clone()
     }
+    
+    /// Creates a [CompiledSlangShader] from raw bytes.
+    ///
+    /// This is useful when loading shaders compiled at build time using the 
+    /// `include_slang!` macro.
+    pub fn from_bytes(label: &str, source: &[u8]) -> Self {
+        Self {
+            label: label.to_string(),
+            args: String::new(),
+            source: source.to_vec(),
+        }
+    }
+
+    pub fn label(&self) -> String {
+        self.label.clone()
+    }
 
     /// Writes the output to a file.
     pub fn output(&self, path: impl AsRef<Path>) -> std::io::Result<()> {
diff --git a/crates/slank/src/lib.rs b/crates/slank/src/lib.rs
index 00ce744..40dc1b4 100644
--- a/crates/slank/src/lib.rs
+++ b/crates/slank/src/lib.rs
@@ -1,9 +1,27 @@
-//! slank - slangc for rust
+//! slank - slangc for rust. 
+//! 
+//! Compiles slang code during build and stores the shaders locally (or in the crate with [`include_slang`])
 //!
 //! Check out [`SlangShaderBuilder`] to get started.
 
-pub mod compiled;
+/// Fetches the slang file (located in {OUT_DIR}/{label}.spv) (assuming it is compiled as .spv) 
+/// and includes the bytes of the file. 
+#[macro_export]
+macro_rules! include_slang {
+    ($label:expr) => {
+        include_bytes!(concat!(env!("OUT_DIR"), "/", $label, ".spv"))
+    };
+}
+
+/// Fetches the path of the shader (with the same label) and returns it to you. 
+#[macro_export]
+macro_rules! include_slang_path {
+    ($label:expr) => {
+        concat!(env!("OUT_DIR"), "/", $label, ".spv")
+    };
+}
 
+pub mod compiled;
 pub mod utils;
 
 use std::path::{Path, PathBuf};
@@ -28,27 +46,46 @@ pub struct EntryPoint {
 ///
 /// This is the entry point of the library.
 /// # Usage
-/// ```rust
+/// 
+/// Add `slank` to your `[build-dependencies]`.
+///
+/// In your `build.rs`:
+/// ```rust,no_run
 /// use slank::{ShaderStage, SlangShaderBuilder, SlangTarget};
+/// use std::path::Path;
 ///
-/// SlangShaderBuilder::new()
-///     .add_source_str(include_str!("../test/light.slang"))
-///     .entry_with_stage("vs_main", ShaderStage::Vertex)
-///     .entry_with_stage("fs_main", ShaderStage::Fragment)
-///     .build(SlangTarget::SpirV).unwrap();
+/// fn main() {
+///     let out_dir = std::env::var("OUT_DIR").unwrap();
+///     let dest_path = Path::new(&out_dir).join("shader.spv");
 ///
+///     SlangShaderBuilder::new("shader_label")
+///         .add_source_path("src/shader.slang").unwrap()
+///         .entry_with_stage("vs_main", ShaderStage::Vertex)
+///         .entry_with_stage("fs_main", ShaderStage::Fragment)
+///         .build(SlangTarget::SpirV).unwrap()
+///         .output(&dest_path).unwrap();
+/// 
+///     println!("cargo:rerun-if-changed=src/shader.slang");
+/// }
+/// ```
+///
+/// Then in your main code:
+/// ```rust,ignore
+/// let shader_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/shader.spv"));
 /// ```
 pub struct SlangShaderBuilder {
+    label: String,
     sources: Vec<SourceFile>,
     entries: Vec<EntryPoint>,
 }
 
 impl SlangShaderBuilder {
     /// Creates a new instance of a [SlangShaderBuilder].
-    pub fn new() -> Self {
+    pub fn new(label: &str) -> Self {
         Self {
             sources: Vec::new(),
             entries: Vec::new(),
+            label: label.to_string(),
         }
     }
 
@@ -175,6 +212,15 @@ impl SlangShaderBuilder {
         self
     }
 
+    pub fn compile_to_out_dir(self, target: SlangTarget) -> anyhow::Result<()> {
+        let label = self.label.clone();
+        let compiled = self.build(target)?;
+        let out_dir = std::env::var("OUT_DIR")
+            .map_err(|_| anyhow::anyhow!("OUT_DIR not set. Are you running this from build.rs?"))?;
+        let dest = Path::new(&out_dir).join(format!("{}.spv", label));
+        compiled.output(&dest).map_err(Into::into)
+    }
+
     pub fn build(self, target: SlangTarget) -> anyhow::Result<CompiledSlangShader> {
         let slang_dir = PathBuf::from(env!("SLANG_DIR"));
         let slangc_path = slang_dir.join("bin").join(if cfg!(windows) { "slangc.exe" } else { "slangc" });
@@ -234,20 +280,20 @@ impl SlangShaderBuilder {
         let output = cmd.output()?;
 
         if !output.status.success() {
-             let stderr = String::from_utf8_lossy(&output.stderr).to_string();
+            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
             return Err(anyhow::anyhow!("Compilation error: {}", stderr));
         }
 
 
         let binary_output = std::fs::read(&output_path)?;
 
-        Ok(CompiledSlangShader::new(args_record, binary_output))
+        Ok(CompiledSlangShader::new(self.label, args_record, binary_output))
     }
 }
 
 impl Default for SlangShaderBuilder {
     fn default() -> Self {
-        Self::new()
+        Self::new("no named shader")
     }
 }
 
@@ -302,6 +348,7 @@ pub enum SlangTarget {
 
     // GLSL/Vulkan/SPIR-V
     Glsl,
+    /// Most optimal for WGPU and Vulkan/ 
     SpirV,
     SpirVAssembly,
 
@@ -467,13 +514,20 @@ mod tests {
     use super::*;
 
     #[test]
-    fn test_from_str() {
-        let shader = SlangShaderBuilder::new()
-            .add_source_str(include_str!("../../dropbear-engine/src/pipelines/shaders/light.slang"))
+    fn test_compile() {
+        let result = SlangShaderBuilder::new("light")
+            .add_source_str(include_str!("../test/light.slang"))
             .entry_with_stage("vs_main", ShaderStage::Vertex)
             .entry_with_stage("fs_main", ShaderStage::Fragment)
-            .build(SlangTarget::SpirV).unwrap();
+            .build(SlangTarget::SpirV);
+        assert!(result.is_ok());
+    }
 
-        shader.output("/home/tirbofish/shader.spv").unwrap();
+    #[test]
+    fn test_from_bytes() {
+        let bytes = vec![1, 2, 3, 4];
+        let shader = CompiledSlangShader::from_bytes("idk", &bytes);
+        assert_eq!(shader.source, bytes);
+        assert_eq!(shader.label(), "unknown");
     }
 }
\ No newline at end of file
diff --git a/crates/slank/src/utils.rs b/crates/slank/src/utils.rs
index e3bd12e..0461cee 100644
--- a/crates/slank/src/utils.rs
+++ b/crates/slank/src/utils.rs
@@ -2,14 +2,14 @@
 #[cfg(feature = "use-wgpu")]
 pub trait WgpuUtils {
     /// Creates a new [`wgpu::ShaderModuleDescriptor`] for you to create your own shaders with.
-    fn create_wgpu_shader(&self, label: Option<&str>) -> wgpu::ShaderModuleDescriptor;
+    fn create_wgpu_shader(&self) -> wgpu::ShaderModuleDescriptor<'_>;
 }
 
 #[cfg(feature = "use-wgpu")]
 impl WgpuUtils for crate::CompiledSlangShader {
-    fn create_wgpu_shader(&self, label: Option<&str>) -> wgpu::ShaderModuleDescriptor {
+    fn create_wgpu_shader(&self) -> wgpu::ShaderModuleDescriptor<'_> {
         wgpu::ShaderModuleDescriptor {
-            label,
+            label: Some(self.label.as_str()),
             source: wgpu::util::make_spirv(self.source.as_ref()),
         }
     }
diff --git a/crates/slank/test/light.slang b/crates/slank/test/light.slang
index 428e996..af1dd42 100644
--- a/crates/slank/test/light.slang
+++ b/crates/slank/test/light.slang
@@ -10,7 +10,6 @@ struct Light {
     float cutoff;
 };
 
-
 struct CameraUniform {
     float4 view_pos;
     float4x4 view_proj;