]> git.nega.tv - josh/narcissus/commitdiff
Move a bunch of helper code out of main.rs
authorJoshua Simmons <josh@nega.tv>
Mon, 27 Feb 2023 20:15:43 +0000 (21:15 +0100)
committerJoshua Simmons <josh@nega.tv>
Mon, 27 Feb 2023 20:15:43 +0000 (21:15 +0100)
Re-organize the pipeline structures as well.

bins/narcissus/src/helpers.rs [new file with mode: 0644]
bins/narcissus/src/main.rs
bins/narcissus/src/mapped_buffer.rs [new file with mode: 0644]
bins/narcissus/src/pipelines/basic.rs
bins/narcissus/src/pipelines/mod.rs
bins/narcissus/src/pipelines/text.rs
bins/narcissus/src/shaders/text.frag.glsl
bins/narcissus/src/shaders/text.frag.spv
bins/narcissus/src/shaders/text.vert.glsl
bins/narcissus/src/shaders/text.vert.spv

diff --git a/bins/narcissus/src/helpers.rs b/bins/narcissus/src/helpers.rs
new file mode 100644 (file)
index 0000000..08c03a3
--- /dev/null
@@ -0,0 +1,194 @@
+use std::path::Path;
+
+use narcissus_core::{default, obj};
+use narcissus_gpu::{
+    Access, Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags, Device, Extent3d, Image,
+    ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout,
+    ImageUsageFlags, MemoryLocation, Offset3d, ThreadToken,
+};
+use narcissus_image as image;
+use narcissus_maths::{vec2, vec3, vec4, Vec2, Vec3};
+
+use crate::{pipelines::Vertex, Blittable};
+
+pub fn load_obj<P: AsRef<Path>>(path: P) -> (Vec<Vertex>, Vec<u16>) {
+    #[derive(Default)]
+    struct ObjVisitor {
+        positions: Vec<Vec3>,
+        normals: Vec<Vec3>,
+        texcoords: Vec<Vec2>,
+        indices: Vec<[(i32, i32, i32); 3]>,
+    }
+
+    impl obj::Visitor for ObjVisitor {
+        fn visit_position(&mut self, x: f32, y: f32, z: f32, _w: f32) {
+            self.positions.push(vec3(x, y, z))
+        }
+
+        fn visit_texcoord(&mut self, u: f32, v: f32, _w: f32) {
+            self.texcoords.push(vec2(u, v));
+        }
+
+        fn visit_normal(&mut self, x: f32, y: f32, z: f32) {
+            self.normals.push(vec3(x, y, z))
+        }
+
+        fn visit_face(&mut self, indices: &[(i32, i32, i32)]) {
+            self.indices
+                .push(indices.try_into().expect("not a triangle"));
+        }
+
+        fn visit_object(&mut self, _name: &str) {}
+        fn visit_group(&mut self, _name: &str) {}
+        fn visit_smooth_group(&mut self, _group: i32) {}
+    }
+
+    let start = std::time::Instant::now();
+    let path = path.as_ref();
+    let file = std::fs::File::open(path).expect("couldn't open file");
+    let mut visitor = ObjVisitor::default();
+
+    obj::Parser::new(file)
+        .visit(&mut visitor)
+        .expect("failed to parse obj file");
+
+    let (vertices, indices): (Vec<_>, Vec<_>) = visitor
+        .indices
+        .iter()
+        .flatten()
+        .enumerate()
+        .map(|(index, &(position_index, texcoord_index, normal_index))| {
+            let position = visitor.positions[position_index as usize - 1];
+            let normal = visitor.normals[normal_index as usize - 1];
+            let texcoord = visitor.texcoords[texcoord_index as usize - 1];
+            (
+                Vertex {
+                    position: vec4(position.x, position.y, position.z, 0.0).into(),
+                    normal: vec4(normal.x, normal.y, normal.z, 0.0).into(),
+                    texcoord: vec4(texcoord.x, texcoord.y, 0.0, 0.0).into(),
+                },
+                index as u16,
+            )
+        })
+        .unzip();
+
+    println!(
+        "parsing obj {path:?} took {:?}",
+        std::time::Instant::now() - start
+    );
+
+    (vertices, indices)
+}
+
+pub fn load_image<P: AsRef<Path>>(path: P) -> image::Image {
+    let start = std::time::Instant::now();
+    let path = path.as_ref();
+    let texture =
+        image::Image::from_buffer(std::fs::read(path).expect("failed to read file").as_slice())
+            .expect("failed to load image");
+    println!(
+        "loading image {path:?} took {:?}",
+        std::time::Instant::now() - start
+    );
+    texture
+}
+
+pub fn create_buffer_with_data<T>(
+    device: &dyn Device,
+    usage: BufferUsageFlags,
+    data: &[T],
+) -> Buffer
+where
+    T: Blittable,
+{
+    let len = data.len() * std::mem::size_of::<T>();
+    let buffer = device.create_buffer(&BufferDesc {
+        location: MemoryLocation::HostMapped,
+        usage,
+        size: len,
+    });
+    // Safety: T: Blittable which implies it's freely convertable to a byte slice.
+    unsafe {
+        let dst = std::slice::from_raw_parts_mut(device.map_buffer(buffer), len);
+        let src = std::slice::from_raw_parts(data.as_ptr() as *const u8, len);
+        dst.copy_from_slice(src);
+        device.unmap_buffer(buffer);
+    }
+    buffer
+}
+
+pub fn create_image_with_data(
+    device: &dyn Device,
+    thread_token: &ThreadToken,
+    width: u32,
+    height: u32,
+    data: &[u8],
+) -> Image {
+    let frame = device.begin_frame();
+
+    let buffer = create_buffer_with_data(device, BufferUsageFlags::TRANSFER_SRC, data);
+
+    let image = device.create_image(&ImageDesc {
+        location: MemoryLocation::Device,
+        usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
+        dimension: ImageDimension::Type2d,
+        format: ImageFormat::RGBA8_SRGB,
+        initial_layout: ImageLayout::Optimal,
+        width,
+        height,
+        depth: 1,
+        layer_count: 1,
+        mip_levels: 1,
+    });
+
+    let mut cmd_buffer = device.create_cmd_buffer(&frame, thread_token);
+
+    device.cmd_barrier(
+        &mut cmd_buffer,
+        None,
+        &[ImageBarrier::layout_optimal(
+            &[Access::None],
+            &[Access::TransferWrite],
+            image,
+            ImageAspectFlags::COLOR,
+        )],
+    );
+
+    device.cmd_copy_buffer_to_image(
+        &mut cmd_buffer,
+        buffer,
+        image,
+        ImageLayout::Optimal,
+        &[BufferImageCopy {
+            buffer_offset: 0,
+            buffer_row_length: 0,
+            buffer_image_height: 0,
+            image_subresource: default(),
+            image_offset: Offset3d { x: 0, y: 0, z: 0 },
+            image_extent: Extent3d {
+                width,
+                height,
+                depth: 1,
+            },
+        }],
+    );
+
+    device.cmd_barrier(
+        &mut cmd_buffer,
+        None,
+        &[ImageBarrier::layout_optimal(
+            &[Access::TransferWrite],
+            &[Access::FragmentShaderSampledImageRead],
+            image,
+            ImageAspectFlags::COLOR,
+        )],
+    );
+
+    device.submit(&frame, cmd_buffer);
+
+    device.destroy_buffer(&frame, buffer);
+
+    device.end_frame(frame);
+
+    image
+}
index 7848f73908ee43cdc32748da58e654b37fe9a4b4..42e3835d242f4ed145deb50c936603e51144bd64 100644 (file)
@@ -1,26 +1,28 @@
-use std::{path::Path, time::Instant};
+use std::time::Instant;
 
+use helpers::{create_buffer_with_data, create_image_with_data, load_image, load_obj};
+use mapped_buffer::MappedBuffer;
 use narcissus_app::{create_app, Event, Key, PressedState, WindowDesc};
-use narcissus_core::{default, obj, rand::Pcg64};
-use narcissus_font::{CachedGlyph, CachedGlyphIndex, FontCollection, GlyphCache};
+use narcissus_core::{default, rand::Pcg64};
+use narcissus_font::{CachedGlyph, FontCollection, GlyphCache};
 use narcissus_gpu::{
-    create_device, Access, Bind, Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags, ClearValue,
-    Device, Extent2d, Extent3d, Image, ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension,
-    ImageFormat, ImageLayout, ImageUsageFlags, IndexType, LoadOp, MemoryLocation, Offset2d,
-    Offset3d, RenderingAttachment, RenderingDesc, SamplerAddressMode, SamplerDesc, SamplerFilter,
-    Scissor, StoreOp, ThreadToken, TypedBind, Viewport,
-};
-use narcissus_image as image;
-use narcissus_maths::{
-    sin_cos_pi_f32, vec2, vec3, vec4, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec2, Vec3,
+    create_device, Access, BufferImageCopy, BufferUsageFlags, ClearValue, Extent2d, Extent3d,
+    ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout,
+    ImageUsageFlags, LoadOp, MemoryLocation, Offset2d, Offset3d, RenderingAttachment,
+    RenderingDesc, Scissor, StoreOp, ThreadToken, Viewport,
 };
 
+use narcissus_maths::{sin_cos_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec3};
+use pipelines::{BasicUniforms, GlyphInstance, TextUniforms};
+
 use crate::{
     fonts::{FontFamily, Fonts},
     pipelines::{BasicPipeline, TextPipeline},
 };
 
 mod fonts;
+mod helpers;
+mod mapped_buffer;
 mod pipelines;
 
 const MAX_SHARKS: usize = 262_144;
@@ -36,291 +38,12 @@ const MAX_GLYPHS: usize = 8192;
 /// # Safety
 ///
 /// Must not be applied to any types with padding
-unsafe trait Blittable: Sized {}
-
-#[allow(unused)]
-#[repr(C)]
-struct BasicUniforms {
-    clip_from_model: Mat4,
-}
+pub unsafe trait Blittable: Sized {}
 
-unsafe impl Blittable for BasicUniforms {}
-
-#[allow(unused)]
-#[repr(C)]
-struct Vertex {
-    position: [f32; 4],
-    normal: [f32; 4],
-    texcoord: [f32; 4],
-}
-
-#[allow(unused)]
-#[repr(C)]
-struct TextUniforms {
-    screen_width: u32,
-    screen_height: u32,
-    atlas_width: u32,
-    atlas_height: u32,
-}
-
-unsafe impl Blittable for TextUniforms {}
-
-#[allow(unused)]
-#[repr(C)]
-struct GlyphInstance {
-    cached_glyph_index: CachedGlyphIndex,
-    x: f32,
-    y: f32,
-    color: u32,
-}
-
-unsafe impl Blittable for CachedGlyph {}
-unsafe impl Blittable for GlyphInstance {}
-
-unsafe impl Blittable for Vertex {}
 unsafe impl Blittable for u8 {}
 unsafe impl Blittable for u16 {}
 unsafe impl Blittable for Affine3 {}
-
-fn load_obj<P: AsRef<Path>>(path: P) -> (Vec<Vertex>, Vec<u16>) {
-    #[derive(Default)]
-    struct ObjVisitor {
-        positions: Vec<Vec3>,
-        normals: Vec<Vec3>,
-        texcoords: Vec<Vec2>,
-        indices: Vec<[(i32, i32, i32); 3]>,
-    }
-
-    impl obj::Visitor for ObjVisitor {
-        fn visit_position(&mut self, x: f32, y: f32, z: f32, _w: f32) {
-            self.positions.push(vec3(x, y, z))
-        }
-
-        fn visit_texcoord(&mut self, u: f32, v: f32, _w: f32) {
-            self.texcoords.push(vec2(u, v));
-        }
-
-        fn visit_normal(&mut self, x: f32, y: f32, z: f32) {
-            self.normals.push(vec3(x, y, z))
-        }
-
-        fn visit_face(&mut self, indices: &[(i32, i32, i32)]) {
-            self.indices
-                .push(indices.try_into().expect("not a triangle"));
-        }
-
-        fn visit_object(&mut self, _name: &str) {}
-        fn visit_group(&mut self, _name: &str) {}
-        fn visit_smooth_group(&mut self, _group: i32) {}
-    }
-
-    let start = std::time::Instant::now();
-    let path = path.as_ref();
-    let file = std::fs::File::open(path).expect("couldn't open file");
-    let mut visitor = ObjVisitor::default();
-
-    obj::Parser::new(file)
-        .visit(&mut visitor)
-        .expect("failed to parse obj file");
-
-    let (vertices, indices): (Vec<_>, Vec<_>) = visitor
-        .indices
-        .iter()
-        .flatten()
-        .enumerate()
-        .map(|(index, &(position_index, texcoord_index, normal_index))| {
-            let position = visitor.positions[position_index as usize - 1];
-            let normal = visitor.normals[normal_index as usize - 1];
-            let texcoord = visitor.texcoords[texcoord_index as usize - 1];
-            (
-                Vertex {
-                    position: vec4(position.x, position.y, position.z, 0.0).into(),
-                    normal: vec4(normal.x, normal.y, normal.z, 0.0).into(),
-                    texcoord: vec4(texcoord.x, texcoord.y, 0.0, 0.0).into(),
-                },
-                index as u16,
-            )
-        })
-        .unzip();
-
-    println!(
-        "parsing obj {path:?} took {:?}",
-        std::time::Instant::now() - start
-    );
-
-    (vertices, indices)
-}
-
-fn load_image<P: AsRef<Path>>(path: P) -> image::Image {
-    let start = std::time::Instant::now();
-    let path = path.as_ref();
-    let texture =
-        image::Image::from_buffer(std::fs::read(path).expect("failed to read file").as_slice())
-            .expect("failed to load image");
-    println!(
-        "loading image {path:?} took {:?}",
-        std::time::Instant::now() - start
-    );
-    texture
-}
-
-fn create_buffer_with_data<T>(device: &dyn Device, usage: BufferUsageFlags, data: &[T]) -> Buffer
-where
-    T: Blittable,
-{
-    let len = data.len() * std::mem::size_of::<T>();
-    let buffer = device.create_buffer(&BufferDesc {
-        location: MemoryLocation::HostMapped,
-        usage,
-        size: len,
-    });
-    // Safety: T: Blittable which implies it's freely convertable to a byte slice.
-    unsafe {
-        let dst = std::slice::from_raw_parts_mut(device.map_buffer(buffer), len);
-        let src = std::slice::from_raw_parts(data.as_ptr() as *const u8, len);
-        dst.copy_from_slice(src);
-        device.unmap_buffer(buffer);
-    }
-    buffer
-}
-
-fn create_image_with_data(
-    device: &dyn Device,
-    thread_token: &ThreadToken,
-    width: u32,
-    height: u32,
-    data: &[u8],
-) -> Image {
-    let frame = device.begin_frame();
-
-    let buffer = create_buffer_with_data(device, BufferUsageFlags::TRANSFER_SRC, data);
-
-    let image = device.create_image(&ImageDesc {
-        location: MemoryLocation::Device,
-        usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
-        dimension: ImageDimension::Type2d,
-        format: ImageFormat::RGBA8_SRGB,
-        initial_layout: ImageLayout::Optimal,
-        width,
-        height,
-        depth: 1,
-        layer_count: 1,
-        mip_levels: 1,
-    });
-
-    let mut cmd_buffer = device.create_cmd_buffer(&frame, thread_token);
-
-    device.cmd_barrier(
-        &mut cmd_buffer,
-        None,
-        &[ImageBarrier::layout_optimal(
-            &[Access::None],
-            &[Access::TransferWrite],
-            image,
-            ImageAspectFlags::COLOR,
-        )],
-    );
-
-    device.cmd_copy_buffer_to_image(
-        &mut cmd_buffer,
-        buffer,
-        image,
-        ImageLayout::Optimal,
-        &[BufferImageCopy {
-            buffer_offset: 0,
-            buffer_row_length: 0,
-            buffer_image_height: 0,
-            image_subresource: default(),
-            image_offset: Offset3d { x: 0, y: 0, z: 0 },
-            image_extent: Extent3d {
-                width,
-                height,
-                depth: 1,
-            },
-        }],
-    );
-
-    device.cmd_barrier(
-        &mut cmd_buffer,
-        None,
-        &[ImageBarrier::layout_optimal(
-            &[Access::TransferWrite],
-            &[Access::FragmentShaderSampledImageRead],
-            image,
-            ImageAspectFlags::COLOR,
-        )],
-    );
-
-    device.submit(&frame, cmd_buffer);
-
-    device.destroy_buffer(&frame, buffer);
-
-    device.end_frame(frame);
-
-    image
-}
-
-struct MappedBuffer<'a> {
-    device: &'a dyn Device,
-    buffer: Buffer,
-    slice: &'a mut [u8],
-}
-
-impl<'a> MappedBuffer<'a> {
-    pub fn new(device: &'a dyn Device, usage: BufferUsageFlags, len: usize) -> Self {
-        let buffer = device.create_buffer(&BufferDesc {
-            location: MemoryLocation::HostMapped,
-            usage,
-            size: len,
-        });
-        unsafe {
-            let ptr = device.map_buffer(buffer);
-            let slice = std::slice::from_raw_parts_mut(ptr, len);
-            Self {
-                device,
-                buffer,
-                slice,
-            }
-        }
-    }
-
-    pub fn buffer(&self) -> Buffer {
-        self.buffer
-    }
-
-    pub fn write<T>(&mut self, value: T)
-    where
-        T: Blittable,
-    {
-        unsafe {
-            let src = std::slice::from_raw_parts(
-                &value as *const T as *const u8,
-                std::mem::size_of::<T>(),
-            );
-            self.slice.copy_from_slice(src)
-        }
-    }
-
-    pub fn write_slice<T>(&mut self, values: &[T])
-    where
-        T: Blittable,
-    {
-        unsafe {
-            let len = values.len() * std::mem::size_of::<T>();
-            let src = std::slice::from_raw_parts(values.as_ptr() as *const u8, len);
-            self.slice[..len].copy_from_slice(src)
-        }
-    }
-}
-
-impl<'a> Drop for MappedBuffer<'a> {
-    fn drop(&mut self) {
-        // Safety: Make sure we don't have the slice outlive the mapping.
-        unsafe {
-            self.device.unmap_buffer(self.buffer);
-        }
-    }
-}
+unsafe impl Blittable for CachedGlyph {}
 
 pub fn main() {
     let app = create_app();
@@ -362,28 +85,19 @@ pub fn main() {
         blÃ¥haj_image.as_slice(),
     );
 
-    let mut basic_uniforms = MappedBuffer::new(
+    let mut basic_uniform_buffer = MappedBuffer::new(
         device.as_ref(),
         BufferUsageFlags::UNIFORM,
         std::mem::size_of::<BasicUniforms>(),
     );
 
-    let mut basic_transforms = MappedBuffer::new(
+    let mut basic_transform_buffer = MappedBuffer::new(
         device.as_ref(),
         BufferUsageFlags::STORAGE,
         std::mem::size_of::<Affine3>() * MAX_SHARKS,
     );
 
-    let basic_sampler = device.create_sampler(&SamplerDesc {
-        filter: SamplerFilter::Point,
-        address_mode: SamplerAddressMode::Clamp,
-        compare_op: None,
-        mip_lod_bias: 0.0,
-        min_lod: 0.0,
-        max_lod: 1000.0,
-    });
-
-    let mut text_uniforms = MappedBuffer::new(
+    let mut text_uniform_buffer = MappedBuffer::new(
         device.as_ref(),
         BufferUsageFlags::UNIFORM,
         std::mem::size_of::<TextUniforms>(),
@@ -395,7 +109,7 @@ pub fn main() {
         std::mem::size_of::<GlyphInstance>() * MAX_GLYPH_INSTANCES,
     );
 
-    let mut cached_glyph_buffer = MappedBuffer::new(
+    let mut glyph_buffer = MappedBuffer::new(
         device.as_ref(),
         BufferUsageFlags::STORAGE,
         std::mem::size_of::<CachedGlyph>() * MAX_GLYPHS,
@@ -431,15 +145,6 @@ pub fn main() {
         device.end_frame(frame);
     }
 
-    let text_sampler = device.create_sampler(&SamplerDesc {
-        filter: SamplerFilter::Bilinear,
-        address_mode: SamplerAddressMode::Clamp,
-        compare_op: None,
-        mip_lod_bias: 0.0,
-        min_lod: 0.0,
-        max_lod: 0.0,
-    });
-
     let mut depth_width = 0;
     let mut depth_height = 0;
     let mut depth_image = default();
@@ -549,7 +254,7 @@ pub fn main() {
             transform.matrix *= Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.002 * direction))
         }
 
-        basic_transforms.write_slice(&shark_transforms);
+        basic_transform_buffer.write_slice(&shark_transforms);
 
         let (s, c) = sin_cos_pi_f32(frame_start * 0.2);
         let camera_height = c * 8.0;
@@ -561,7 +266,7 @@ pub fn main() {
             Mat4::perspective_rev_inf_zo(Deg::new(45.0).into(), width as f32 / height as f32, 0.01);
         let clip_from_model = clip_from_camera * camera_from_model;
 
-        basic_uniforms.write(BasicUniforms { clip_from_model });
+        basic_uniform_buffer.write(BasicUniforms { clip_from_model });
 
         // Do some Font Shit.'
         let line0 = "Snarfe, BlÃ¥haj! And the Quick Brown Fox jumped Over the Lazy doge. Â½Â½Â½Â½ Snarfe, BlÃ¥haj! And the Quick Brown Fox jumped Over the Lazy doge. Â½Â½Â½Â½ Snarfe, BlÃ¥haj! And the Quick Brown Fox jumped Over the Lazy doge. Â½Â½Â½Â½";
@@ -624,7 +329,7 @@ pub fn main() {
         let atlas_width = glyph_cache.width() as u32;
         let atlas_height = glyph_cache.height() as u32;
 
-        text_uniforms.write(TextUniforms {
+        text_uniform_buffer.write(TextUniforms {
             screen_width: width,
             screen_height: height,
             atlas_width,
@@ -632,8 +337,9 @@ pub fn main() {
         });
         glyph_instance_buffer.write_slice(&glyph_instances);
 
+        // If the atlas has been updated, we need to upload it to the GPU
         if let Some((cached_glyphs, texture)) = glyph_cache.update_atlas() {
-            cached_glyph_buffer.write_slice(cached_glyphs);
+            glyph_buffer.write_slice(cached_glyphs);
 
             // upload atlas
             {
@@ -739,109 +445,38 @@ pub fn main() {
         );
 
         // Render basic stuff.
-        {
-            device.cmd_set_pipeline(&mut cmd_buffer, basic_pipeline.pipeline);
-
-            device.cmd_set_bind_group(
-                &frame,
-                &mut cmd_buffer,
-                basic_pipeline.uniforms_bind_group_layout,
-                0,
-                &[Bind {
-                    binding: 0,
-                    array_element: 0,
-                    typed: TypedBind::UniformBuffer(&[basic_uniforms.buffer()]),
-                }],
-            );
-
-            device.cmd_set_bind_group(
-                &frame,
-                &mut cmd_buffer,
-                basic_pipeline.storage_bind_group_layout,
-                1,
-                &[
-                    Bind {
-                        binding: 0,
-                        array_element: 0,
-                        typed: TypedBind::StorageBuffer(&[blÃ¥haj_vertex_buffer]),
-                    },
-                    Bind {
-                        binding: 1,
-                        array_element: 0,
-                        typed: TypedBind::StorageBuffer(&[basic_transforms.buffer()]),
-                    },
-                    Bind {
-                        binding: 2,
-                        array_element: 0,
-                        typed: TypedBind::Sampler(&[basic_sampler]),
-                    },
-                    Bind {
-                        binding: 3,
-                        array_element: 0,
-                        typed: TypedBind::Image(&[(ImageLayout::Optimal, blÃ¥haj_image)]),
-                    },
-                ],
-            );
-
-            device.cmd_set_index_buffer(&mut cmd_buffer, blÃ¥haj_index_buffer, 0, IndexType::U16);
+        basic_pipeline.bind(
+            device.as_ref(),
+            &frame,
+            &mut cmd_buffer,
+            basic_uniform_buffer.buffer(),
+            blÃ¥haj_vertex_buffer,
+            blÃ¥haj_index_buffer,
+            basic_transform_buffer.buffer(),
+            blÃ¥haj_image,
+        );
 
-            device.cmd_draw_indexed(
-                &mut cmd_buffer,
-                blÃ¥haj_indices.len() as u32,
-                shark_transforms.len() as u32,
-                0,
-                0,
-                0,
-            );
-        }
+        device.cmd_draw_indexed(
+            &mut cmd_buffer,
+            blÃ¥haj_indices.len() as u32,
+            shark_transforms.len() as u32,
+            0,
+            0,
+            0,
+        );
 
         // Render text stuff.
-        {
-            device.cmd_set_pipeline(&mut cmd_buffer, text_pipeline.pipeline);
-
-            device.cmd_set_bind_group(
-                &frame,
-                &mut cmd_buffer,
-                text_pipeline.uniforms_bind_group_layout,
-                0,
-                &[Bind {
-                    binding: 0,
-                    array_element: 0,
-                    typed: TypedBind::UniformBuffer(&[text_uniforms.buffer()]),
-                }],
-            );
-
-            device.cmd_set_bind_group(
-                &frame,
-                &mut cmd_buffer,
-                basic_pipeline.storage_bind_group_layout,
-                1,
-                &[
-                    Bind {
-                        binding: 0,
-                        array_element: 0,
-                        typed: TypedBind::StorageBuffer(&[cached_glyph_buffer.buffer()]),
-                    },
-                    Bind {
-                        binding: 1,
-                        array_element: 0,
-                        typed: TypedBind::StorageBuffer(&[glyph_instance_buffer.buffer()]),
-                    },
-                    Bind {
-                        binding: 2,
-                        array_element: 0,
-                        typed: TypedBind::Sampler(&[text_sampler]),
-                    },
-                    Bind {
-                        binding: 3,
-                        array_element: 0,
-                        typed: TypedBind::Image(&[(ImageLayout::Optimal, glyph_atlas)]),
-                    },
-                ],
-            );
+        text_pipeline.bind(
+            device.as_ref(),
+            &frame,
+            &mut cmd_buffer,
+            text_uniform_buffer.buffer(),
+            glyph_buffer.buffer(),
+            glyph_instance_buffer.buffer(),
+            glyph_atlas,
+        );
 
-            device.cmd_draw(&mut cmd_buffer, 4, glyph_instances.len() as u32, 0, 0);
-        }
+        device.cmd_draw(&mut cmd_buffer, 4, glyph_instances.len() as u32, 0, 0);
 
         device.cmd_end_rendering(&mut cmd_buffer);
 
diff --git a/bins/narcissus/src/mapped_buffer.rs b/bins/narcissus/src/mapped_buffer.rs
new file mode 100644 (file)
index 0000000..1b7575c
--- /dev/null
@@ -0,0 +1,65 @@
+use narcissus_gpu::{Buffer, BufferDesc, BufferUsageFlags, Device, MemoryLocation};
+
+use crate::Blittable;
+
+pub struct MappedBuffer<'a> {
+    device: &'a dyn Device,
+    buffer: Buffer,
+    slice: &'a mut [u8],
+}
+
+impl<'a> MappedBuffer<'a> {
+    pub fn new(device: &'a dyn Device, usage: BufferUsageFlags, len: usize) -> Self {
+        let buffer = device.create_buffer(&BufferDesc {
+            location: MemoryLocation::HostMapped,
+            usage,
+            size: len,
+        });
+        unsafe {
+            let ptr = device.map_buffer(buffer);
+            let slice = std::slice::from_raw_parts_mut(ptr, len);
+            Self {
+                device,
+                buffer,
+                slice,
+            }
+        }
+    }
+
+    pub fn buffer(&self) -> Buffer {
+        self.buffer
+    }
+
+    pub fn write<T>(&mut self, value: T)
+    where
+        T: Blittable,
+    {
+        unsafe {
+            let src = std::slice::from_raw_parts(
+                &value as *const T as *const u8,
+                std::mem::size_of::<T>(),
+            );
+            self.slice.copy_from_slice(src)
+        }
+    }
+
+    pub fn write_slice<T>(&mut self, values: &[T])
+    where
+        T: Blittable,
+    {
+        unsafe {
+            let len = values.len() * std::mem::size_of::<T>();
+            let src = std::slice::from_raw_parts(values.as_ptr() as *const u8, len);
+            self.slice[..len].copy_from_slice(src)
+        }
+    }
+}
+
+impl<'a> Drop for MappedBuffer<'a> {
+    fn drop(&mut self) {
+        // Safety: Make sure we don't have the slice outlive the mapping.
+        unsafe {
+            self.device.unmap_buffer(self.buffer);
+        }
+    }
+}
index 47fa48699ee03e2dea969ae9805d28069878d2b9..bd94ef016ab7152dd9806160a03737aec6add88e 100644 (file)
@@ -1,16 +1,39 @@
 use narcissus_core::{cstr, default, include_bytes_align};
 use narcissus_gpu::{
-    BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
-    CompareOp, CullingMode, Device, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout,
-    ImageFormat, Pipeline, PolygonMode, ShaderDesc, ShaderStageFlags, Topology,
+    Bind, BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
+    Buffer, CmdBuffer, CompareOp, CullingMode, Device, Frame, FrontFace, GraphicsPipelineDesc,
+    GraphicsPipelineLayout, Image, ImageFormat, ImageLayout, IndexType, Pipeline, PolygonMode,
+    Sampler, SamplerAddressMode, SamplerDesc, SamplerFilter, ShaderDesc, ShaderStageFlags,
+    Topology, TypedBind,
 };
+use narcissus_maths::Mat4;
+
+use crate::Blittable;
 
 const VERT_SPV: &'static [u8] = include_bytes_align!(4, "../shaders/basic.vert.spv");
 const FRAG_SPV: &'static [u8] = include_bytes_align!(4, "../shaders/basic.frag.spv");
 
+#[allow(unused)]
+#[repr(C)]
+pub struct BasicUniforms {
+    pub clip_from_model: Mat4,
+}
+
+#[allow(unused)]
+#[repr(C)]
+pub struct Vertex {
+    pub position: [f32; 4],
+    pub normal: [f32; 4],
+    pub texcoord: [f32; 4],
+}
+
+unsafe impl Blittable for BasicUniforms {}
+unsafe impl Blittable for Vertex {}
+
 pub struct BasicPipeline {
     pub uniforms_bind_group_layout: BindGroupLayout,
     pub storage_bind_group_layout: BindGroupLayout,
+    pub sampler: Sampler,
     pub pipeline: Pipeline,
 }
 
@@ -54,6 +77,15 @@ impl BasicPipeline {
             ],
         });
 
+        let sampler = device.create_sampler(&SamplerDesc {
+            filter: SamplerFilter::Point,
+            address_mode: SamplerAddressMode::Clamp,
+            compare_op: None,
+            mip_lod_bias: 0.0,
+            min_lod: 0.0,
+            max_lod: 1000.0,
+        });
+
         let pipeline = device.create_graphics_pipeline(&GraphicsPipelineDesc {
             vertex_shader: ShaderDesc {
                 entry: cstr!("main"),
@@ -86,7 +118,65 @@ impl BasicPipeline {
         Self {
             uniforms_bind_group_layout,
             storage_bind_group_layout,
+            sampler,
             pipeline,
         }
     }
+
+    pub fn bind(
+        &self,
+        device: &dyn Device,
+        frame: &Frame,
+        cmd_buffer: &mut CmdBuffer,
+        uniform_buffer: Buffer,
+        vertex_buffer: Buffer,
+        index_buffer: Buffer,
+        transform_buffer: Buffer,
+        texture: Image,
+    ) {
+        device.cmd_set_pipeline(cmd_buffer, self.pipeline);
+
+        device.cmd_set_bind_group(
+            frame,
+            cmd_buffer,
+            self.uniforms_bind_group_layout,
+            0,
+            &[Bind {
+                binding: 0,
+                array_element: 0,
+                typed: TypedBind::UniformBuffer(&[uniform_buffer]),
+            }],
+        );
+
+        device.cmd_set_bind_group(
+            frame,
+            cmd_buffer,
+            self.storage_bind_group_layout,
+            1,
+            &[
+                Bind {
+                    binding: 0,
+                    array_element: 0,
+                    typed: TypedBind::StorageBuffer(&[vertex_buffer]),
+                },
+                Bind {
+                    binding: 1,
+                    array_element: 0,
+                    typed: TypedBind::StorageBuffer(&[transform_buffer]),
+                },
+                Bind {
+                    binding: 2,
+                    array_element: 0,
+                    typed: TypedBind::Sampler(&[self.sampler]),
+                },
+                Bind {
+                    binding: 3,
+                    array_element: 0,
+                    typed: TypedBind::Image(&[(ImageLayout::Optimal, texture)]),
+                },
+            ],
+        );
+
+        device.cmd_set_index_buffer(cmd_buffer, index_buffer, 0, IndexType::U16);
+    }
 }
index ecc2c49c08200e2db66df94f97ca77b6d5539aa1..3a2f9d9dd0bec69bbeed90e24ec7c44f1dde2c57 100644 (file)
@@ -2,4 +2,9 @@ mod basic;
 mod text;
 
 pub use basic::BasicPipeline;
+pub use basic::BasicUniforms;
+pub use basic::Vertex;
+
+pub use text::GlyphInstance;
 pub use text::TextPipeline;
+pub use text::TextUniforms;
index 203011bf1318549466fc657ad873789e6720ccb2..48e5f71b559d70971cc8c2bdbfc1564fb5ef8abd 100644 (file)
@@ -1,36 +1,53 @@
 use narcissus_core::{cstr, default, include_bytes_align};
+use narcissus_font::CachedGlyphIndex;
 use narcissus_gpu::{
-    BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
-    CompareOp, CullingMode, Device, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout,
-    ImageFormat, Pipeline, PolygonMode, ShaderDesc, ShaderStageFlags, Topology,
+    Bind, BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
+    Buffer, CmdBuffer, CompareOp, CullingMode, Device, Frame, FrontFace, GraphicsPipelineDesc,
+    GraphicsPipelineLayout, Image, ImageFormat, ImageLayout, Pipeline, PolygonMode, Sampler,
+    SamplerAddressMode, SamplerDesc, SamplerFilter, ShaderDesc, ShaderStageFlags, Topology,
+    TypedBind,
 };
 
+use crate::Blittable;
+
 const VERT_SPV: &'static [u8] = include_bytes_align!(4, "../shaders/text.vert.spv");
 const FRAG_SPV: &'static [u8] = include_bytes_align!(4, "../shaders/text.frag.spv");
 
+#[allow(unused)]
+#[repr(C)]
+pub struct TextUniforms {
+    pub screen_width: u32,
+    pub screen_height: u32,
+    pub atlas_width: u32,
+    pub atlas_height: u32,
+}
+
+#[allow(unused)]
+#[repr(C)]
+pub struct GlyphInstance {
+    pub cached_glyph_index: CachedGlyphIndex,
+    pub x: f32,
+    pub y: f32,
+    pub color: u32,
+}
+
+unsafe impl Blittable for TextUniforms {}
+unsafe impl Blittable for GlyphInstance {}
+
 pub struct TextPipeline {
-    pub uniforms_bind_group_layout: BindGroupLayout,
-    pub storage_bind_group_layout: BindGroupLayout,
-    pub pipeline: Pipeline,
+    bind_group_layout: BindGroupLayout,
+    sampler: Sampler,
+    pipeline: Pipeline,
 }
 
 impl TextPipeline {
     pub fn new(device: &dyn Device) -> Self {
-        let uniforms_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
-            entries: &[BindGroupLayoutEntryDesc {
-                slot: 0,
-                stages: ShaderStageFlags::ALL,
-                binding_type: BindingType::UniformBuffer,
-                count: 1,
-            }],
-        });
-
-        let storage_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
+        let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
             entries: &[
                 BindGroupLayoutEntryDesc {
                     slot: 0,
                     stages: ShaderStageFlags::ALL,
-                    binding_type: BindingType::StorageBuffer,
+                    binding_type: BindingType::UniformBuffer,
                     count: 1,
                 },
                 BindGroupLayoutEntryDesc {
@@ -42,18 +59,33 @@ impl TextPipeline {
                 BindGroupLayoutEntryDesc {
                     slot: 2,
                     stages: ShaderStageFlags::ALL,
-                    binding_type: BindingType::Sampler,
+                    binding_type: BindingType::StorageBuffer,
                     count: 1,
                 },
                 BindGroupLayoutEntryDesc {
                     slot: 3,
                     stages: ShaderStageFlags::ALL,
+                    binding_type: BindingType::Sampler,
+                    count: 1,
+                },
+                BindGroupLayoutEntryDesc {
+                    slot: 4,
+                    stages: ShaderStageFlags::ALL,
                     binding_type: BindingType::Image,
                     count: 1,
                 },
             ],
         });
 
+        let sampler = device.create_sampler(&SamplerDesc {
+            filter: SamplerFilter::Bilinear,
+            address_mode: SamplerAddressMode::Clamp,
+            compare_op: None,
+            mip_lod_bias: 0.0,
+            min_lod: 0.0,
+            max_lod: 0.0,
+        });
+
         let pipeline = device.create_graphics_pipeline(&GraphicsPipelineDesc {
             vertex_shader: ShaderDesc {
                 entry: cstr!("main"),
@@ -63,7 +95,7 @@ impl TextPipeline {
                 entry: cstr!("main"),
                 code: FRAG_SPV,
             },
-            bind_group_layouts: &[uniforms_bind_group_layout, storage_bind_group_layout],
+            bind_group_layouts: &[bind_group_layout],
             layout: GraphicsPipelineLayout {
                 color_attachment_formats: &[ImageFormat::BGRA8_SRGB],
                 depth_attachment_format: Some(ImageFormat::DEPTH_F32),
@@ -84,9 +116,55 @@ impl TextPipeline {
         });
 
         Self {
-            uniforms_bind_group_layout,
-            storage_bind_group_layout,
+            bind_group_layout,
+            sampler,
             pipeline,
         }
     }
+
+    pub fn bind(
+        &self,
+        device: &dyn Device,
+        frame: &Frame,
+        cmd_buffer: &mut CmdBuffer,
+        uniforms: Buffer,
+        cached_glyphs: Buffer,
+        glyph_instances: Buffer,
+        atlas: Image,
+    ) {
+        device.cmd_set_pipeline(cmd_buffer, self.pipeline);
+        device.cmd_set_bind_group(
+            frame,
+            cmd_buffer,
+            self.bind_group_layout,
+            0,
+            &[
+                Bind {
+                    binding: 0,
+                    array_element: 0,
+                    typed: TypedBind::UniformBuffer(&[uniforms]),
+                },
+                Bind {
+                    binding: 1,
+                    array_element: 0,
+                    typed: TypedBind::StorageBuffer(&[cached_glyphs]),
+                },
+                Bind {
+                    binding: 2,
+                    array_element: 0,
+                    typed: TypedBind::StorageBuffer(&[glyph_instances]),
+                },
+                Bind {
+                    binding: 3,
+                    array_element: 0,
+                    typed: TypedBind::Sampler(&[self.sampler]),
+                },
+                Bind {
+                    binding: 4,
+                    array_element: 0,
+                    typed: TypedBind::Image(&[(ImageLayout::Optimal, atlas)]),
+                },
+            ],
+        );
+    }
 }
index a94619298774b289601625dbbd572421b4cb986d..0163eda64e5d7436231f565e77ddb93c67cbd625 100644 (file)
@@ -1,7 +1,7 @@
 #version 460
 
-layout(set = 1, binding = 2) uniform sampler texSampler;
-layout(set = 1, binding = 3) uniform texture2D tex;
+layout(set = 0, binding = 3) uniform sampler texSampler;
+layout(set = 0, binding = 4) uniform texture2D tex;
 
 layout(location = 0) in vec2 texcoord;
 layout(location = 1) in vec4 color;
index d15569a0af9b1a3b4b06ecd5d158a0196802a9ec..c54b72753fa2755e08fda86ebadd2ef387a0872d 100644 (file)
Binary files a/bins/narcissus/src/shaders/text.frag.spv and b/bins/narcissus/src/shaders/text.frag.spv differ
index 44519e577077819a2225324016492926b573dc9e..146096e629520e754dfe62c3291be0a566e2c2fd 100644 (file)
@@ -26,11 +26,11 @@ layout(set = 0, binding = 0) uniform uniformBuffer {
     uint atlasHeight;
 };
 
-layout(std430, set = 1, binding = 0) readonly buffer glyphBuffer {
+layout(std430, set = 0, binding = 1) readonly buffer glyphBuffer {
     CachedGlyph cachedGlyphs[];
 };
 
-layout(std430, set = 1, binding = 1) readonly buffer glyphInstanceBuffer {
+layout(std430, set = 0, binding = 2) readonly buffer glyphInstanceBuffer {
     GlyphInstance glyphInstances[];
 };
 
index 5a948e74a70fd7372b0ec74488d6263c5914f252..ed9eb1ce047c2bebecc5218d1c9b6250a55cebbd 100644 (file)
Binary files a/bins/narcissus/src/shaders/text.vert.spv and b/bins/narcissus/src/shaders/text.vert.spv differ