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<P: AsRef<Path>>(path: P) -> (Vec<Vertex>, Vec<u16>) {
#[derive(Default)]
data: &[T],
) -> Buffer
where
- T: Blittable,
+ T: Blit,
{
// SAFETY: T: Blittable which implies it's freely convertable to a byte slice.
unsafe {
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;
/// # 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<T> 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::<Self>())
+ 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<T> 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();
}
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();
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 {
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;
}
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();
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);
use narcissus_gpu::{Buffer, BufferDesc, BufferUsageFlags, Device, MemoryLocation};
-use crate::Blittable;
+use crate::Blit;
pub struct MappedBuffer<'a> {
device: &'a dyn Device,
pub fn write_slice<T>(&mut self, values: &[T])
where
- T: Blittable,
+ T: Blit,
{
unsafe {
let len = std::mem::size_of_val(values);
};
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");
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,
stencil_attachment_format: None,
},
topology: Topology::Triangles,
+ primitive_restart: false,
polygon_mode: PolygonMode::Fill,
culling_mode: CullingMode::Back,
front_face: FrontFace::CounterClockwise,
pub use basic::{BasicPipeline, BasicUniforms, Vertex};
-pub use text::{GlyphInstance, TextPipeline, TextUniforms};
+pub use text::{GlyphInstance, PrimitiveVertex, TextPipeline, TextUniforms};
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");
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 {
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,
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,
},
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,
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::<TextUniforms>(),
+ 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(
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)]),
},
],
#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;
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[];
};
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] = {
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;
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;
];
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);
let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::default();
let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo {
topology,
+ primitive_restart_enable,
..default()
};
let viewport_state = vk::PipelineViewportStateCreateInfo::default();
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,