]> git.nega.tv - josh/narcissus/commitdiff
shark: Render a square with mesh shaders
authorJoshua Simmons <josh@nega.tv>
Tue, 21 Oct 2025 21:19:31 +0000 (23:19 +0200)
committerJoshua Simmons <josh@nega.tv>
Wed, 22 Oct 2025 21:33:50 +0000 (23:33 +0200)
title/shark-shaders/build.rs
title/shark-shaders/shaders/block.slang [new file with mode: 0644]
title/shark-shaders/src/pipelines.rs
title/shark/src/draw.rs

index fdf583252227a02310f00f8c7d8f188e780d9386..cea4a21706ffc2a055fcadf5a2b9e118b5f65e7a 100644 (file)
@@ -78,6 +78,7 @@ struct SlangShader {
 
 const SLANG_SHADERS: &[SlangShader] = &[
     SlangShader { name: "basic" },
+    SlangShader { name: "block" },
     SlangShader { name: "draw_2d" },
     SlangShader { name: "composite" },
     SlangShader { name: "radix_sort" },
diff --git a/title/shark-shaders/shaders/block.slang b/title/shark-shaders/shaders/block.slang
new file mode 100644 (file)
index 0000000..017b705
--- /dev/null
@@ -0,0 +1,51 @@
+struct BlockConstants {
+    float4x4 clip_from_camera;
+}
+
+struct VertexAttributes {
+    float4 position : SV_Position;
+}
+
+struct PrimitiveAttributes {
+    float3 normal;
+    float3 color;
+}
+
+[shader("mesh")]
+[outputtopology("triangle")]
+[numthreads(32, 1, 1)]
+void mesh(uniform BlockConstants constants, OutputVertices<VertexAttributes, 32> vertices, OutputPrimitives<PrimitiveAttributes, 16> primitives, OutputIndices<uint3, 16> indices, uint thread_id_in_group: SV_GroupThreadID) {
+    SetMeshOutputCounts(4, 2);
+
+    static const float4 positions[4] = {
+        float4(-0.5, 0.0, -0.5, 1.0),
+        float4(-0.5, 0.0, 0.5, 1.0),
+        float4(0.5, 0.0, 0.5, 1.0),
+        float4(0.5, 0.0, -0.5, 1.0),
+    };
+
+    if (thread_id_in_group < 4) {
+        vertices[thread_id_in_group].position = mul(constants.clip_from_camera, positions[thread_id_in_group]);
+    }
+
+    if (thread_id_in_group < 2) {
+        primitives[thread_id_in_group].color = float3(0x9b / 255.0, 0x61 / 255.0, 0x56 / 255.0);
+        primitives[thread_id_in_group].normal = float3(0.0, 1.0, 0.0);
+    }
+
+    indices[0] = uint3(0, 1, 2);
+    indices[1] = uint3(2, 3, 0);
+}
+
+struct Fragment {
+    float4 color : SV_Target0;
+}
+
+[shader("fragment")]
+Fragment fragment(PrimitiveAttributes primitive, VertexAttributes vertex) {
+    let n_dot_l = max(dot(primitive.normal, float3(0.0, 1.0, 0.0)), 0.1);
+
+    Fragment output;
+    output.color = float4(primitive.color * n_dot_l, 1.0);
+    return output;
+}
index 0ae7369699c176a554415f5cab2984716059405d..ff16084384a4809b394dd8f9f65947d427ca8301 100644 (file)
@@ -5,7 +5,7 @@ use narcissus_gpu::{
     ComputePipelineDesc, CullingMode, FrontFace, Gpu, GraphicsPipelineAttachments,
     GraphicsPipelineDesc, ImageFormat, Pipeline, PipelineLayout, PolygonMode, PushConstantRange,
     Sampler, SamplerAddressMode, SamplerDesc, SamplerFilter, ShaderDesc, ShaderStageFlags,
-    SpecConstant, Topology,
+    SpecConstant, Topology, VertexOrMeshShader,
 };
 use narcissus_maths::{Mat4, Vec2};
 
@@ -174,6 +174,11 @@ pub struct BasicConstants<'a> {
     pub transform_buffer_address: BufferAddress<'a>,
 }
 
+#[repr(C)]
+pub struct BlockConstants {
+    pub clip_from_model: Mat4,
+}
+
 #[repr(C)]
 pub struct Draw2dClearConstants<'a> {
     pub tile_resolution_x: u32,
@@ -280,6 +285,7 @@ pub struct Pipelines {
     pub compute_bind_group_layout: BindGroupLayout,
 
     pub basic_pipeline: Pipeline,
+    pub block_pipeline: Pipeline,
 
     pub draw_2d_bin_0_clear_pipeline: Pipeline,
     pub draw_2d_bin_1_scatter_pipeline_workgroup_size: u32,
@@ -326,11 +332,11 @@ impl Pipelines {
         gpu.debug_name_bind_group_layout(compute_bind_group_layout, "compute");
 
         let basic_pipeline = gpu.create_graphics_pipeline(&GraphicsPipelineDesc {
-            vertex_shader: ShaderDesc {
+            vertex_or_mesh_shader: VertexOrMeshShader::Vertex(ShaderDesc {
                 code: crate::BASIC_SPV,
                 entry: c"vertex",
                 ..default()
-            },
+            }),
             fragment_shader: ShaderDesc {
                 code: crate::BASIC_SPV,
                 entry: c"fragment",
@@ -366,6 +372,47 @@ impl Pipelines {
 
         gpu.debug_name_pipeline(basic_pipeline, "basic");
 
+        let block_pipeline = gpu.create_graphics_pipeline(&GraphicsPipelineDesc {
+            vertex_or_mesh_shader: VertexOrMeshShader::Mesh(ShaderDesc {
+                code: crate::BLOCK_SPV,
+                entry: c"mesh",
+                ..default()
+            }),
+            fragment_shader: ShaderDesc {
+                code: crate::BLOCK_SPV,
+                entry: c"fragment",
+                ..default()
+            },
+            layout: PipelineLayout {
+                bind_group_layouts: &[graphics_bind_group_layout],
+                push_constant_ranges: &[PushConstantRange {
+                    stage_flags: ShaderStageFlags::MESH,
+                    offset: 0,
+                    size: std::mem::size_of::<BlockConstants>() as u32,
+                }],
+            },
+            attachments: GraphicsPipelineAttachments {
+                color_attachment_formats: &[ImageFormat::RGBA16_FLOAT],
+                depth_attachment_format: Some(ImageFormat::DEPTH_F32),
+                stencil_attachment_format: None,
+            },
+            topology: Topology::Triangles,
+            primitive_restart: false,
+            polygon_mode: PolygonMode::Fill,
+            culling_mode: CullingMode::Back,
+            front_face: FrontFace::CounterClockwise,
+            blend_mode: BlendMode::Opaque,
+            depth_bias: None,
+            depth_compare_op: CompareOp::GreaterOrEqual,
+            depth_test_enable: true,
+            depth_write_enable: true,
+            stencil_test_enable: false,
+            stencil_back: default(),
+            stencil_front: default(),
+        });
+
+        gpu.debug_name_pipeline(block_pipeline, "block");
+
         let create_compute_pipeline_with_entry =
             |code, entry, name, workgroup_size, require_full_subgroups, push_constant_size| {
                 let push_constant_range = PushConstantRange {
@@ -503,6 +550,7 @@ impl Pipelines {
             compute_bind_group_layout,
 
             basic_pipeline,
+            block_pipeline,
 
             draw_2d_bin_0_clear_pipeline,
             draw_2d_bin_1_scatter_pipeline_workgroup_size,
index dad79a239aa603a54796f17301e7e2a4d4e93187..bca0e4101976026df8b67d6074ebff5808220c3a 100644 (file)
@@ -6,10 +6,10 @@ use crate::{UiState, microshades};
 use narcissus_core::dds;
 
 use shark_shaders::pipelines::{
-    BasicConstants, CompositeConstants, ComputeBinds, DRAW_2D_TILE_SIZE, Draw2dClearConstants,
-    Draw2dRasterizeConstants, Draw2dResolveConstants, Draw2dScatterConstants, Draw2dSortConstants,
-    GraphicsBinds, Pipelines, RadixSortDownsweepConstants, RadixSortUpsweepConstants,
-    calculate_spine_size,
+    BasicConstants, BlockConstants, CompositeConstants, ComputeBinds, DRAW_2D_TILE_SIZE,
+    Draw2dClearConstants, Draw2dRasterizeConstants, Draw2dResolveConstants, Draw2dScatterConstants,
+    Draw2dSortConstants, GraphicsBinds, Pipelines, RadixSortDownsweepConstants,
+    RadixSortUpsweepConstants, calculate_spine_size,
 };
 
 use crate::helpers::load_obj;
@@ -684,6 +684,17 @@ impl<'gpu> DrawState<'gpu> {
                     }],
                 );
 
+                // Render blocks.
+                gpu.cmd_set_pipeline(cmd_encoder, self.pipelines.block_pipeline);
+                gpu.cmd_set_bind_group(cmd_encoder, 0, &graphics_bind_group);
+                gpu.cmd_push_constants_with_data(
+                    cmd_encoder,
+                    ShaderStageFlags::MESH,
+                    0,
+                    &BlockConstants { clip_from_model },
+                );
+                gpu.cmd_draw_mesh_tasks(cmd_encoder, 1, 1, 1);
+
                 gpu.cmd_set_pipeline(cmd_encoder, self.pipelines.basic_pipeline);
                 gpu.cmd_set_bind_group(cmd_encoder, 0, &graphics_bind_group);
                 gpu.cmd_push_constants_with_data(