From: Joshua Simmons Date: Sat, 5 Aug 2023 09:00:20 +0000 (+0200) Subject: narcissus: Avoid using draw indexed for glyphs X-Git-Url: https://git.nega.tv//gitweb.cgi?a=commitdiff_plain;h=526d6cca59e034b6aa28e930d00f8d66524c08af;p=josh%2Fnarcissus narcissus: Avoid using draw indexed for glyphs In preparation for being able to draw different types of primitive from the same system, stop using indexed drawing for glyphs. Instead pack information into the index buffer. --- diff --git a/bins/narcissus/src/helpers.rs b/bins/narcissus/src/helpers.rs index dfb4560..8ab78df 100644 --- a/bins/narcissus/src/helpers.rs +++ b/bins/narcissus/src/helpers.rs @@ -5,7 +5,7 @@ use narcissus_gpu::{Buffer, BufferDesc, BufferUsageFlags, Device, MemoryLocation use narcissus_image as image; use narcissus_maths::{vec2, vec3, vec4, Vec2, Vec3}; -use crate::{pipelines::Vertex, Blittable}; +use crate::{pipelines::Vertex, Blit}; pub fn load_obj>(path: P) -> (Vec, Vec) { #[derive(Default)] @@ -95,7 +95,7 @@ pub fn create_host_buffer_with_data( data: &[T], ) -> Buffer where - T: Blittable, + T: Blit, { // SAFETY: T: Blittable which implies it's freely convertable to a byte slice. unsafe { diff --git a/bins/narcissus/src/main.rs b/bins/narcissus/src/main.rs index eb7e182..31a07d7 100644 --- a/bins/narcissus/src/main.rs +++ b/bins/narcissus/src/main.rs @@ -16,7 +16,7 @@ use narcissus_gpu::{ RenderingDesc, Scissor, StoreOp, ThreadToken, Viewport, }; use narcissus_maths::{sin_cos_pi_f32, vec3, Affine3, HalfTurn, Mat3, Mat4, Point3, Vec3}; -use pipelines::{BasicUniforms, GlyphInstance, TextUniforms}; +use pipelines::{BasicUniforms, GlyphInstance, PrimitiveVertex, TextUniforms}; mod fonts; mod helpers; @@ -36,19 +36,40 @@ const MAX_GLYPHS: usize = 8192; /// # Safety /// /// Must not be applied to any types with padding -pub unsafe trait Blittable: Sized { +pub unsafe trait Blit {} + +unsafe impl Blit for u8 {} +unsafe impl Blit for u16 {} +unsafe impl Blit for Affine3 {} +unsafe impl Blit for TouchedGlyph {} + +trait AsBytes { + fn as_bytes(&self) -> &[u8]; +} + +impl AsBytes for T +where + T: Blit, +{ fn as_bytes(&self) -> &[u8] { - // SAFETY: Safe whilst trait is correctly applied. + // SAFETY: Safe while `Blit` trait is correctly applied. unsafe { - std::slice::from_raw_parts(self as *const _ as *const u8, std::mem::size_of::()) + std::slice::from_raw_parts(self as *const _ as *const u8, std::mem::size_of_val(self)) } } } -unsafe impl Blittable for u8 {} -unsafe impl Blittable for u16 {} -unsafe impl Blittable for Affine3 {} -unsafe impl Blittable for TouchedGlyph {} +impl AsBytes for [T] +where + T: Blit, +{ + fn as_bytes(&self) -> &[u8] { + // SAFETY: Safe while `Blit` trait is correctly applied. + unsafe { + std::slice::from_raw_parts(self as *const _ as *const u8, std::mem::size_of_val(self)) + } + } +} pub fn main() { let app = create_app(); @@ -238,7 +259,8 @@ pub fn main() { } let mut font_size_str = String::new(); - let mut glyph_instances = Vec::new(); + let mut primitive_instances = Vec::new(); + let mut primitive_vertices = Vec::new(); let mut line_glyph_indices = Vec::new(); let mut line_kern_advances = Vec::new(); @@ -368,7 +390,8 @@ pub fn main() { let mut rng = Pcg64::new(); - glyph_instances.clear(); + primitive_instances.clear(); + primitive_vertices.clear(); for line in 0.. { let (font_family, font_size_px, text) = if line & 1 == 0 { @@ -428,16 +451,25 @@ pub fn main() { x += advance * scale; } - let color = *rng - .select(&[0xfffac228, 0xfff57d15, 0xffd44842, 0xff9f2a63]) - .unwrap(); + let color = + *rng.array_select(&[0xfffac228, 0xfff57d15, 0xffd44842, 0xff9f2a63]); - glyph_instances.push(GlyphInstance { + let instance_index = primitive_instances.len() as u32; + primitive_instances.push(GlyphInstance { x, y, touched_glyph_index, color, }); + let glyph_vertices = &[ + PrimitiveVertex::glyph(0, instance_index), + PrimitiveVertex::glyph(1, instance_index), + PrimitiveVertex::glyph(2, instance_index), + PrimitiveVertex::glyph(2, instance_index), + PrimitiveVertex::glyph(1, instance_index), + PrimitiveVertex::glyph(3, instance_index), + ]; + primitive_vertices.extend_from_slice(glyph_vertices); x += advance_width * scale; } @@ -447,7 +479,7 @@ pub fn main() { let atlas_width = glyph_cache.width() as u32; let atlas_height = glyph_cache.height() as u32; - glyph_instance_buffer.write_slice(&glyph_instances); + glyph_instance_buffer.write_slice(&primitive_instances); let (touched_glyphs, texture) = glyph_cache.update_atlas(); @@ -587,12 +619,13 @@ pub fn main() { atlas_width, atlas_height, }, + primitive_vertices.as_slice(), 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, primitive_vertices.len() as u32, 1, 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 index f25b1b5..fedfcf6 100644 --- a/bins/narcissus/src/mapped_buffer.rs +++ b/bins/narcissus/src/mapped_buffer.rs @@ -1,6 +1,6 @@ use narcissus_gpu::{Buffer, BufferDesc, BufferUsageFlags, Device, MemoryLocation}; -use crate::Blittable; +use crate::Blit; pub struct MappedBuffer<'a> { device: &'a dyn Device, @@ -33,7 +33,7 @@ impl<'a> MappedBuffer<'a> { pub fn write_slice(&mut self, values: &[T]) where - T: Blittable, + T: Blit, { unsafe { let len = std::mem::size_of_val(values); diff --git a/bins/narcissus/src/pipelines/basic.rs b/bins/narcissus/src/pipelines/basic.rs index 50e56ba..41fa7d9 100644 --- a/bins/narcissus/src/pipelines/basic.rs +++ b/bins/narcissus/src/pipelines/basic.rs @@ -8,7 +8,7 @@ use narcissus_gpu::{ }; use narcissus_maths::Mat4; -use crate::Blittable; +use crate::{AsBytes, Blit}; const VERT_SPV: &[u8] = include_bytes_align!(4, "../shaders/basic.vert.spv"); const FRAG_SPV: &[u8] = include_bytes_align!(4, "../shaders/basic.frag.spv"); @@ -27,8 +27,8 @@ pub struct Vertex { pub texcoord: [f32; 4], } -unsafe impl Blittable for BasicUniforms {} -unsafe impl Blittable for Vertex {} +unsafe impl Blit for BasicUniforms {} +unsafe impl Blit for Vertex {} pub struct BasicPipeline { pub uniforms_bind_group_layout: BindGroupLayout, @@ -102,6 +102,7 @@ impl BasicPipeline { stencil_attachment_format: None, }, topology: Topology::Triangles, + primitive_restart: false, polygon_mode: PolygonMode::Fill, culling_mode: CullingMode::Back, front_face: FrontFace::CounterClockwise, diff --git a/bins/narcissus/src/pipelines/mod.rs b/bins/narcissus/src/pipelines/mod.rs index 6f5c2e6..f5241c0 100644 --- a/bins/narcissus/src/pipelines/mod.rs +++ b/bins/narcissus/src/pipelines/mod.rs @@ -3,4 +3,4 @@ mod text; pub use basic::{BasicPipeline, BasicUniforms, Vertex}; -pub use text::{GlyphInstance, TextPipeline, TextUniforms}; +pub use text::{GlyphInstance, PrimitiveVertex, TextPipeline, TextUniforms}; diff --git a/bins/narcissus/src/pipelines/text.rs b/bins/narcissus/src/pipelines/text.rs index 3651fc7..f4ed102 100644 --- a/bins/narcissus/src/pipelines/text.rs +++ b/bins/narcissus/src/pipelines/text.rs @@ -8,7 +8,7 @@ use narcissus_gpu::{ ShaderStageFlags, ThreadToken, Topology, TypedBind, }; -use crate::Blittable; +use crate::{AsBytes, Blit}; const VERT_SPV: &[u8] = include_bytes_align!(4, "../shaders/text.vert.spv"); const FRAG_SPV: &[u8] = include_bytes_align!(4, "../shaders/text.frag.spv"); @@ -22,6 +22,23 @@ pub struct TextUniforms { pub atlas_height: u32, } +#[repr(u32)] +pub enum PrimitiveKind { + Glyph, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PrimitiveVertex(u32); + +impl PrimitiveVertex { + #[inline(always)] + pub fn glyph(corner: u32, index: u32) -> Self { + let kind = PrimitiveKind::Glyph as u32; + Self(kind << 26 | corner << 24 | index) + } +} + #[allow(unused)] #[repr(C)] pub struct GlyphInstance { @@ -31,8 +48,9 @@ pub struct GlyphInstance { pub color: u32, } -unsafe impl Blittable for TextUniforms {} -unsafe impl Blittable for GlyphInstance {} +unsafe impl Blit for TextUniforms {} +unsafe impl Blit for PrimitiveVertex {} +unsafe impl Blit for GlyphInstance {} pub struct TextPipeline { bind_group_layout: BindGroupLayout, @@ -65,12 +83,18 @@ impl TextPipeline { BindGroupLayoutEntryDesc { slot: 3, stages: ShaderStageFlags::ALL, - binding_type: BindingType::Sampler, + binding_type: BindingType::StorageBuffer, count: 1, }, BindGroupLayoutEntryDesc { slot: 4, stages: ShaderStageFlags::ALL, + binding_type: BindingType::Sampler, + count: 1, + }, + BindGroupLayoutEntryDesc { + slot: 5, + stages: ShaderStageFlags::ALL, binding_type: BindingType::Image, count: 1, }, @@ -101,7 +125,8 @@ impl TextPipeline { depth_attachment_format: Some(ImageFormat::DEPTH_F32), stencil_attachment_format: None, }, - topology: Topology::TriangleStrip, + topology: Topology::Triangles, + primitive_restart: false, polygon_mode: PolygonMode::Fill, culling_mode: CullingMode::None, front_face: FrontFace::CounterClockwise, @@ -129,18 +154,24 @@ impl TextPipeline { thread_token: &ThreadToken, cmd_buffer: &mut CmdBuffer, text_uniforms: &TextUniforms, + primitive_vertices: &[PrimitiveVertex], cached_glyphs: Buffer, glyph_instances: Buffer, atlas: Image, ) { - let mut uniforms = device.request_transient_buffer( + let uniforms_buffer = device.request_transient_buffer_with_data( frame, thread_token, BufferUsageFlags::UNIFORM, - std::mem::size_of::(), + text_uniforms.as_bytes(), ); - uniforms.copy_from_slice(text_uniforms.as_bytes()); + let primitive_vertex_buffer = device.request_transient_buffer_with_data( + frame, + thread_token, + BufferUsageFlags::STORAGE, + primitive_vertices.as_bytes(), + ); device.cmd_set_pipeline(cmd_buffer, self.pipeline); device.cmd_set_bind_group( @@ -152,26 +183,31 @@ impl TextPipeline { Bind { binding: 0, array_element: 0, - typed: TypedBind::UniformBuffer(&[uniforms.into()]), + typed: TypedBind::UniformBuffer(&[uniforms_buffer.into()]), }, Bind { binding: 1, array_element: 0, - typed: TypedBind::StorageBuffer(&[cached_glyphs.into()]), + typed: TypedBind::StorageBuffer(&[primitive_vertex_buffer.into()]), }, Bind { binding: 2, array_element: 0, - typed: TypedBind::StorageBuffer(&[glyph_instances.into()]), + typed: TypedBind::StorageBuffer(&[cached_glyphs.into()]), }, Bind { binding: 3, array_element: 0, - typed: TypedBind::Sampler(&[self.sampler]), + typed: TypedBind::StorageBuffer(&[glyph_instances.into()]), }, Bind { binding: 4, array_element: 0, + typed: TypedBind::Sampler(&[self.sampler]), + }, + Bind { + binding: 5, + array_element: 0, typed: TypedBind::Image(&[(ImageLayout::Optimal, atlas)]), }, ], diff --git a/bins/narcissus/src/shaders/text.frag.glsl b/bins/narcissus/src/shaders/text.frag.glsl index f241618..e8955e9 100644 --- a/bins/narcissus/src/shaders/text.frag.glsl +++ b/bins/narcissus/src/shaders/text.frag.glsl @@ -1,7 +1,7 @@ #version 460 -layout(set = 0, binding = 3) uniform sampler texSampler; -layout(set = 0, binding = 4) uniform texture2D tex; +layout(set = 0, binding = 4) uniform sampler texSampler; +layout(set = 0, binding = 5) uniform texture2D tex; layout(location = 0) in vec2 texcoord; layout(location = 1) in vec4 color; diff --git a/bins/narcissus/src/shaders/text.frag.spv b/bins/narcissus/src/shaders/text.frag.spv index 3bb4aa0..d858dfd 100644 Binary files a/bins/narcissus/src/shaders/text.frag.spv and b/bins/narcissus/src/shaders/text.frag.spv differ diff --git a/bins/narcissus/src/shaders/text.vert.glsl b/bins/narcissus/src/shaders/text.vert.glsl index 0efddaf..314f917 100644 --- a/bins/narcissus/src/shaders/text.vert.glsl +++ b/bins/narcissus/src/shaders/text.vert.glsl @@ -28,11 +28,15 @@ layout(std430, set = 0, binding = 0) uniform uniformBuffer { uint atlasHeight; }; -layout(std430, set = 0, binding = 1) readonly buffer glyphBuffer { +layout(std430, set = 0, binding = 1) readonly buffer primitiveBuffer { + uint primitiveVertices[]; +}; + +layout(std430, set = 0, binding = 2) readonly buffer glyphBuffer { CachedGlyph cachedGlyphs[]; }; -layout(std430, set = 0, binding = 2) readonly buffer glyphInstanceBuffer { +layout(std430, set = 0, binding = 3) readonly buffer glyphInstanceBuffer { GlyphInstance glyphInstances[]; }; @@ -40,7 +44,12 @@ layout(location = 0) out vec2 outTexcoord; layout(location = 1) out flat vec4 outColor; void main() { - GlyphInstance gi = glyphInstances[gl_InstanceIndex]; + uint primitivePacked = primitiveVertices[gl_VertexIndex]; + uint primitiveKind = bitfieldExtract(primitivePacked, 26, 6); + uint primitiveData = bitfieldExtract(primitivePacked, 24, 2); + uint instanceIndex = bitfieldExtract(primitivePacked, 0, 24); + + GlyphInstance gi = glyphInstances[instanceIndex]; CachedGlyph cg = cachedGlyphs[gi.index]; vec2 positions[4] = { @@ -50,7 +59,7 @@ void main() { vec2(cg.offset_x1, cg.offset_y1) }; - vec2 position = positions[gl_VertexIndex]; + vec2 position = positions[primitiveData]; vec2 halfScreenSize = vec2(screenWidth, screenHeight) / 2.0; vec2 glyphPosition = vec2(gi.x, gi.y); vec2 vertexPosition = (position + glyphPosition) / halfScreenSize - 1.0; @@ -63,7 +72,7 @@ void main() { vec2(cg.x1, cg.y1) }; - vec2 texcoord = texcoords[gl_VertexIndex]; + vec2 texcoord = texcoords[primitiveData]; outTexcoord = texcoord / vec2(atlasWidth, atlasHeight); vec4 color = unpackUnorm4x8(gi.color).bgra; diff --git a/bins/narcissus/src/shaders/text.vert.spv b/bins/narcissus/src/shaders/text.vert.spv index 4f9060d..3dd51db 100644 Binary files a/bins/narcissus/src/shaders/text.vert.spv and b/bins/narcissus/src/shaders/text.vert.spv differ diff --git a/libs/narcissus-gpu/src/backend/vulkan/mod.rs b/libs/narcissus-gpu/src/backend/vulkan/mod.rs index f430ffb..0c83b3b 100644 --- a/libs/narcissus-gpu/src/backend/vulkan/mod.rs +++ b/libs/narcissus-gpu/src/backend/vulkan/mod.rs @@ -1214,6 +1214,7 @@ impl Device for VulkanDevice { ]; let topology = vulkan_primitive_topology(desc.topology); + let primitive_restart_enable = vulkan_bool32(desc.primitive_restart); let polygon_mode = vulkan_polygon_mode(desc.polygon_mode); let cull_mode = vulkan_cull_mode(desc.culling_mode); let front_face = vulkan_front_face(desc.front_face); @@ -1242,6 +1243,7 @@ impl Device for VulkanDevice { let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default(); let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo { topology, + primitive_restart_enable, ..default() }; let viewport_state = vk::PipelineViewportStateCreateInfo::default(); diff --git a/libs/narcissus-gpu/src/lib.rs b/libs/narcissus-gpu/src/lib.rs index 41a9b30..071fb8a 100644 --- a/libs/narcissus-gpu/src/lib.rs +++ b/libs/narcissus-gpu/src/lib.rs @@ -391,6 +391,7 @@ pub struct GraphicsPipelineDesc<'a> { pub bind_group_layouts: &'a [BindGroupLayout], pub layout: GraphicsPipelineLayout<'a>, pub topology: Topology, + pub primitive_restart: bool, pub polygon_mode: PolygonMode, pub culling_mode: CullingMode, pub front_face: FrontFace,