name: &'static str,
}
-const SHADERS: [Shader; 5] = [
+const SHADERS: [Shader; 3] = [
Shader {
stage: ShaderStage::Vertex,
name: "basic",
stage: ShaderStage::Compute,
name: "display_transform",
},
- Shader {
- stage: ShaderStage::Vertex,
- name: "ui",
- },
- Shader {
- stage: ShaderStage::Fragment,
- name: "ui",
- },
];
fn main() {
#version 460
-layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_control_flow_attributes : require
+
+struct CachedGlyph {
+ uint x0;
+ uint x1;
+ uint y0;
+ uint y1;
+
+ float offset_x0;
+ float offset_x1;
+ float offset_y0;
+ float offset_y1;
+};
+
+struct GlyphInstance {
+ float x;
+ float y;
+ uint index;
+ uint color;
+};
+
+layout(std430, set = 0, binding = 0) uniform uniformBuffer {
+ uint screen_width;
+ uint screen_height;
+ uint atlas_width;
+ uint atlas_height;
+ uint num_primitives;
+};
+
+layout (set = 0, binding = 1, rgba16f) uniform readonly image2D render_target;
+layout (set = 0, binding = 2, rgba16f) uniform writeonly image2D swapchain_image;
-layout (set = 0, binding = 0) uniform sampler linear_sampler;
+layout (set = 0, binding = 3) uniform sampler bilinear_sampler;
-layout (set = 0, binding = 1) uniform texture3D tony_mc_mapface_lut;
-layout (set = 0, binding = 2, rgba16f) uniform readonly image2D render_target;
-layout (set = 0, binding = 3, rgba16f) uniform writeonly image2D swapchain_image;
+layout (set = 0, binding = 4) uniform texture2D glyph_atlas;
+layout (set = 0, binding = 5) uniform texture3D tony_mc_mapface_lut;
+
+layout(std430, set = 0, binding = 6) readonly buffer glyphBuffer {
+ CachedGlyph cached_glyphs[];
+};
+
+layout(std430, set = 0, binding = 7) readonly buffer glyphInstanceBuffer {
+ GlyphInstance glyph_instances[];
+};
float srgb_oetf(float a) {
return (.0031308f >= a) ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f;
const vec3 encoded = stimulus / (stimulus + 1.0);
const float LUT_DIMS = 48.0;
const vec3 uv = (encoded * ((LUT_DIMS - 1.0) / LUT_DIMS) + 0.5 / LUT_DIMS);
- return textureLod(sampler3D(tony_mc_mapface_lut, linear_sampler), uv, 0.0).rgb;
+ return textureLod(sampler3D(tony_mc_mapface_lut, bilinear_sampler), uv, 0.0).rgb;
}
+layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
void main() {
+ vec4 accum = vec4(0.0);
+
+ for (int i = 0; i < num_primitives; i++) {
+ const GlyphInstance gi = glyph_instances[i];
+ const CachedGlyph cg = cached_glyphs[gi.index];
+ const vec4 color = unpackUnorm4x8(gi.color).bgra;
+ const vec2 glyph_top_left = vec2(gi.x + cg.offset_x0, gi.y + cg.offset_y0);
+ const vec2 glyph_bottom_right = vec2(gi.x + cg.offset_x1, gi.y + cg.offset_y1);
+ const vec2 glyph_size = vec2(cg.offset_x1 - cg.offset_x0, cg.offset_y1 - cg.offset_y0);
+ const vec2 sample_center = gl_GlobalInvocationID.xy; // half pixel offset goes here?
+ if (sample_center.x >= glyph_top_left.x &&
+ sample_center.x <= glyph_bottom_right.x &&
+ sample_center.y >= glyph_top_left.y &&
+ sample_center.y <= glyph_bottom_right.y) {
+ const vec2 uv = mix(vec2(cg.x0, cg.y0), vec2(cg.x1, cg.y1), (sample_center - glyph_top_left) / glyph_size) / vec2(atlas_width, atlas_height);
+ const float coverage = textureLod(sampler2D(glyph_atlas, bilinear_sampler), uv, 0.0).r;
+ accum = coverage * color;
+ accum.a = coverage;
+ break;
+ }
+ }
+
vec3 stimulus = imageLoad(render_target, ivec2(gl_GlobalInvocationID.xy)).rgb;
- vec3 srgb = srgb_oetf(tony_mc_mapface(stimulus));
- imageStore(swapchain_image, ivec2(gl_GlobalInvocationID.xy), vec4(srgb, 1.0));
+ vec3 transformed = tony_mc_mapface(stimulus);
+ vec3 srgb = srgb_oetf(transformed);
+ vec3 composited = accum.rgb + (srgb * (1.0 - accum.a));
+ imageStore(swapchain_image, ivec2(gl_GlobalInvocationID.xy), vec4(composited, 1.0));
}
+++ /dev/null
-#version 460
-
-layout(set = 0, binding = 4) uniform sampler linear_sampler;
-layout(set = 0, binding = 5) uniform texture2D tex;
-
-layout(location = 0) in vec2 texcoord;
-layout(location = 1) in vec4 color;
-layout(location = 0) out vec4 outColor;
-
-void main() {
- float coverage = texture(sampler2D(tex, linear_sampler), texcoord).r;
- outColor = color * coverage;
-}
+++ /dev/null
-#version 460
-
-#extension GL_EXT_scalar_block_layout : require
-
-struct CachedGlyph {
- uint x0;
- uint x1;
- uint y0;
- uint y1;
-
- float offset_x0;
- float offset_x1;
- float offset_y0;
- float offset_y1;
-};
-
-struct GlyphInstance {
- float x;
- float y;
- uint index;
- uint color;
-};
-
-layout(std430, set = 0, binding = 0) uniform uniformBuffer {
- uint screen_width;
- uint screen_height;
- uint atlas_width;
- uint atlas_height;
-};
-
-layout(std430, set = 0, binding = 1) readonly buffer primitiveBuffer {
- uint primitive_vertices[];
-};
-
-layout(std430, set = 0, binding = 2) readonly buffer glyphBuffer {
- CachedGlyph cached_glyphs[];
-};
-
-layout(std430, set = 0, binding = 3) readonly buffer glyphInstanceBuffer {
- GlyphInstance glyph_instances[];
-};
-
-layout(location = 0) out vec2 out_texcoord;
-layout(location = 1) out flat vec4 out_color;
-
-void main() {
- uint primitive_packed = primitive_vertices[gl_VertexIndex];
- uint primitive_kind = bitfieldExtract(primitive_packed, 26, 6);
- uint primitive_data = bitfieldExtract(primitive_packed, 24, 2);
- uint instance_index = bitfieldExtract(primitive_packed, 0, 24);
-
- GlyphInstance gi = glyph_instances[instance_index];
- CachedGlyph cg = cached_glyphs[gi.index];
-
- vec2 positions[4] = {
- vec2(cg.offset_x0, cg.offset_y0),
- vec2(cg.offset_x0, cg.offset_y1),
- vec2(cg.offset_x1, cg.offset_y0),
- vec2(cg.offset_x1, cg.offset_y1)
- };
-
- vec2 position = positions[primitive_data];
- vec2 half_screen_size = vec2(screen_width, screen_height) / 2.0;
- vec2 glyph_position = vec2(gi.x, gi.y);
- vec2 vertex_position = (position + glyph_position) / half_screen_size - 1.0;
- gl_Position = vec4(vertex_position, 0.0, 1.0);
-
- vec2 texcoords[4] = {
- vec2(cg.x0, cg.y0),
- vec2(cg.x0, cg.y1),
- vec2(cg.x1, cg.y0),
- vec2(cg.x1, cg.y1)
- };
-
- vec2 texcoord = texcoords[primitive_data];
- vec4 color = unpackUnorm4x8(gi.color).bgra;
-
- out_texcoord = texcoord / vec2(atlas_width, atlas_height);
- out_color = color;
-}
use narcissus_maths::{
clamp, perlin_noise3, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec3,
};
-use pipelines::{
- BasicPipeline, BasicUniforms, DisplayTransformPipeline, PrimitiveInstance, PrimitiveVertex,
- UiPipeline,
-};
+use pipelines::{BasicPipeline, BasicUniforms, DisplayTransformPipeline, PrimitiveInstance};
use spring::simple_spring_damper_exact;
+use crate::pipelines::DisplayTransformUniforms;
+
mod fonts;
mod helpers;
mod pipelines;
tmp_string: String,
primitive_instances: Vec<PrimitiveInstance>,
- primitive_vertices: Vec<PrimitiveVertex>,
}
impl<'a> UiState<'a> {
glyph_cache,
tmp_string: default(),
primitive_instances: vec![],
- primitive_vertices: vec![],
}
}
x += advance * scale;
- let instance_index = self.primitive_instances.len() as u32;
self.primitive_instances.push(PrimitiveInstance {
x,
y,
touched_glyph_index,
color: 0xff000000,
});
- 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),
- ];
- self.primitive_vertices.extend_from_slice(glyph_vertices);
x += advance_width * scale;
}
gpu: &'gpu Gpu,
basic_pipeline: BasicPipeline,
- ui_pipeline: UiPipeline,
display_transform_pipeline: DisplayTransformPipeline,
width: u32,
impl<'gpu> DrawState<'gpu> {
fn new(gpu: &'gpu Gpu, thread_token: &ThreadToken) -> Self {
let basic_pipeline = BasicPipeline::new(gpu);
- let ui_pipeline = UiPipeline::new(gpu);
- let primitive_pipeline = DisplayTransformPipeline::new(gpu);
+ let display_transform_pipeline = DisplayTransformPipeline::new(gpu);
let samplers = Samplers::load(gpu);
let models = Models::load(gpu);
Self {
gpu,
basic_pipeline,
- ui_pipeline,
- display_transform_pipeline: primitive_pipeline,
+ display_transform_pipeline,
width: 0,
height: 0,
depth_image: default(),
self.transforms.clear();
}
- // Render UI stuff.
- {
- let ui_uniforms = &pipelines::UiUniforms {
- screen_width: width,
- screen_height: height,
- atlas_width,
- atlas_height,
- };
- let primitive_vertices = ui_state.primitive_vertices.as_slice();
- let primitive_instances = ui_state.primitive_instances.as_slice();
- let uniforms_buffer = gpu.request_transient_buffer_with_data(
- frame,
- thread_token,
- BufferUsageFlags::UNIFORM,
- ui_uniforms,
- );
- let primitive_vertex_buffer = gpu.request_transient_buffer_with_data(
- frame,
- thread_token,
- BufferUsageFlags::STORAGE,
- primitive_vertices,
- );
- let cached_glyphs_buffer = gpu.request_transient_buffer_with_data(
- frame,
- thread_token,
- BufferUsageFlags::STORAGE,
- touched_glyphs,
- );
- let glyph_instance_buffer = gpu.request_transient_buffer_with_data(
- frame,
- thread_token,
- BufferUsageFlags::STORAGE,
- primitive_instances,
- );
-
- {
- gpu.cmd_set_pipeline(cmd_encoder, self.ui_pipeline.pipeline);
- gpu.cmd_set_bind_group(
- frame,
- cmd_encoder,
- self.ui_pipeline.bind_group_layout,
- 0,
- &[
- Bind {
- binding: 0,
- array_element: 0,
- typed: TypedBind::UniformBuffer(&[uniforms_buffer.to_arg()]),
- },
- Bind {
- binding: 1,
- array_element: 0,
- typed: TypedBind::StorageBuffer(
- &[primitive_vertex_buffer.to_arg()],
- ),
- },
- Bind {
- binding: 2,
- array_element: 0,
- typed: TypedBind::StorageBuffer(&[cached_glyphs_buffer.to_arg()]),
- },
- Bind {
- binding: 3,
- array_element: 0,
- typed: TypedBind::StorageBuffer(&[glyph_instance_buffer.to_arg()]),
- },
- Bind {
- binding: 4,
- array_element: 0,
- typed: TypedBind::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
- },
- Bind {
- binding: 5,
- array_element: 0,
- typed: TypedBind::SampledImage(&[(
- ImageLayout::Optimal,
- self.glyph_atlas_image,
- )]),
- },
- ],
- );
- };
-
- gpu.cmd_draw(
- cmd_encoder,
- ui_state.primitive_vertices.len() as u32,
- 1,
- 0,
- 0,
- );
-
- ui_state.primitive_instances.clear();
- ui_state.primitive_vertices.clear();
- }
-
gpu.cmd_end_rendering(cmd_encoder);
gpu.cmd_barrier(
gpu.cmd_set_pipeline(cmd_encoder, self.display_transform_pipeline.pipeline);
+ let uniforms_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::UNIFORM,
+ &DisplayTransformUniforms {
+ screen_width: width,
+ screen_height: height,
+ atlas_width,
+ atlas_height,
+ num_primitives: ui_state.primitive_instances.len() as u32,
+ },
+ );
+ let cached_glyphs_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ touched_glyphs,
+ );
+ let glyph_instance_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ ui_state.primitive_instances.as_slice(),
+ );
+
+ ui_state.primitive_instances.clear();
+
gpu.cmd_set_bind_group(
frame,
cmd_encoder,
Bind {
binding: 0,
array_element: 0,
- typed: TypedBind::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
+ typed: TypedBind::UniformBuffer(&[uniforms_buffer.to_arg()]),
},
Bind {
binding: 1,
array_element: 0,
+ typed: TypedBind::StorageImage(&[(
+ ImageLayout::General,
+ self.render_target_image,
+ )]),
+ },
+ Bind {
+ binding: 2,
+ array_element: 0,
+ typed: TypedBind::StorageImage(&[(ImageLayout::General, swapchain_image)]),
+ },
+ Bind {
+ binding: 3,
+ array_element: 0,
+ typed: TypedBind::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
+ },
+ Bind {
+ binding: 4,
+ array_element: 0,
typed: TypedBind::SampledImage(&[(
ImageLayout::Optimal,
- self.images[ImageRes::TonyMcMapfaceLut],
+ self.glyph_atlas_image,
)]),
},
Bind {
- binding: 2,
+ binding: 5,
array_element: 0,
- typed: TypedBind::StorageImage(&[(
- ImageLayout::General,
- self.render_target_image,
+ typed: TypedBind::SampledImage(&[(
+ ImageLayout::Optimal,
+ self.images[ImageRes::TonyMcMapfaceLut],
)]),
},
Bind {
- binding: 3,
+ binding: 6,
array_element: 0,
- typed: TypedBind::StorageImage(&[(ImageLayout::General, swapchain_image)]),
+ typed: TypedBind::StorageBuffer(&[cached_glyphs_buffer.to_arg()]),
+ },
+ Bind {
+ binding: 7,
+ array_element: 0,
+ typed: TypedBind::StorageBuffer(&[glyph_instance_buffer.to_arg()]),
},
],
);
ui_state.text_fmt(
5.0,
- 30.0,
+ 40.0,
FontFamily::RobotoRegular,
- 22.0,
+ 30.0,
format_args!("tick: {:?}", tick_duration),
);
ui_state.text_fmt(
5.0,
- 60.0,
+ 90.0,
FontFamily::NotoSansJapanese,
- 18.0,
+ 30.0,
format_args!("ใๆกใ The Quick Brown Fox Jumped Over The Lazy Dog"),
);
+use narcissus_font::TouchedGlyphIndex;
use narcissus_gpu::{
BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType,
ComputePipelineDesc, Pipeline, ShaderDesc, ShaderStageFlags,
#[allow(unused)]
#[repr(C)]
pub struct DisplayTransformUniforms {
- pub width: u32,
- pub height: u32,
+ pub screen_width: u32,
+ pub screen_height: u32,
+ pub atlas_width: u32,
+ pub atlas_height: u32,
+ pub num_primitives: u32,
+}
+
+#[allow(unused)]
+#[repr(C)]
+pub struct PrimitiveInstance {
+ pub x: f32,
+ pub y: f32,
+ pub touched_glyph_index: TouchedGlyphIndex,
+ pub color: u32,
}
pub struct DisplayTransformPipeline {
let bind_group_layout = gpu.create_bind_group_layout(&BindGroupLayoutDesc {
entries: &[
BindGroupLayoutEntryDesc {
+ // uniforms
slot: 0,
stages: ShaderStageFlags::COMPUTE,
- binding_type: BindingType::Sampler,
+ binding_type: BindingType::UniformBuffer,
count: 1,
},
BindGroupLayoutEntryDesc {
+ // rt
slot: 1,
stages: ShaderStageFlags::COMPUTE,
- binding_type: BindingType::SampledImage,
+ binding_type: BindingType::StorageImage,
count: 1,
},
BindGroupLayoutEntryDesc {
+ // swapchain
slot: 2,
stages: ShaderStageFlags::COMPUTE,
binding_type: BindingType::StorageImage,
count: 1,
},
BindGroupLayoutEntryDesc {
+ // sampler
slot: 3,
stages: ShaderStageFlags::COMPUTE,
- binding_type: BindingType::StorageImage,
+ binding_type: BindingType::Sampler,
+ count: 1,
+ },
+ BindGroupLayoutEntryDesc {
+ // glyph atlas
+ slot: 4,
+ stages: ShaderStageFlags::COMPUTE,
+ binding_type: BindingType::SampledImage,
+ count: 1,
+ },
+ BindGroupLayoutEntryDesc {
+ // lut
+ slot: 5,
+ stages: ShaderStageFlags::COMPUTE,
+ binding_type: BindingType::SampledImage,
+ count: 1,
+ },
+ BindGroupLayoutEntryDesc {
+ // glyphs
+ slot: 6,
+ stages: ShaderStageFlags::COMPUTE,
+ binding_type: BindingType::StorageBuffer,
+ count: 1,
+ },
+ BindGroupLayoutEntryDesc {
+ // glyph instances
+ slot: 7,
+ stages: ShaderStageFlags::COMPUTE,
+ binding_type: BindingType::StorageBuffer,
count: 1,
},
],
mod basic;
mod display_transform;
-mod ui;
pub use basic::{BasicPipeline, BasicUniforms, Vertex};
-pub use ui::{PrimitiveInstance, PrimitiveVertex, UiPipeline, UiUniforms};
-
-pub use display_transform::DisplayTransformPipeline;
+pub use display_transform::{
+ DisplayTransformPipeline, DisplayTransformUniforms, PrimitiveInstance,
+};
+++ /dev/null
-use narcissus_core::default;
-use narcissus_font::TouchedGlyphIndex;
-use narcissus_gpu::{
- BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
- CompareOp, CullingMode, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout, ImageFormat,
- Pipeline, PolygonMode, ShaderDesc, ShaderStageFlags, Topology,
-};
-
-use crate::Gpu;
-
-#[allow(unused)]
-#[repr(C)]
-pub struct UiUniforms {
- pub screen_width: u32,
- pub screen_height: u32,
- pub atlas_width: u32,
- 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 PrimitiveInstance {
- pub x: f32,
- pub y: f32,
- pub touched_glyph_index: TouchedGlyphIndex,
- pub color: u32,
-}
-
-pub struct UiPipeline {
- pub bind_group_layout: BindGroupLayout,
- pub pipeline: Pipeline,
-}
-
-impl UiPipeline {
- pub fn new(gpu: &Gpu) -> Self {
- let bind_group_layout = gpu.create_bind_group_layout(&BindGroupLayoutDesc {
- entries: &[
- BindGroupLayoutEntryDesc {
- slot: 0,
- stages: ShaderStageFlags::ALL,
- binding_type: BindingType::UniformBuffer,
- count: 1,
- },
- BindGroupLayoutEntryDesc {
- slot: 1,
- stages: ShaderStageFlags::ALL,
- binding_type: BindingType::StorageBuffer,
- count: 1,
- },
- BindGroupLayoutEntryDesc {
- slot: 2,
- stages: ShaderStageFlags::ALL,
- binding_type: BindingType::StorageBuffer,
- count: 1,
- },
- BindGroupLayoutEntryDesc {
- slot: 3,
- stages: ShaderStageFlags::ALL,
- 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::SampledImage,
- count: 1,
- },
- ],
- });
-
- let pipeline = gpu.create_graphics_pipeline(&GraphicsPipelineDesc {
- vertex_shader: ShaderDesc {
- entry: c"main",
- code: shark_shaders::UI_VERT_SPV,
- },
- fragment_shader: ShaderDesc {
- entry: c"main",
- code: shark_shaders::UI_FRAG_SPV,
- },
- bind_group_layouts: &[bind_group_layout],
- layout: GraphicsPipelineLayout {
- 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::None,
- front_face: FrontFace::CounterClockwise,
- blend_mode: BlendMode::Premultiplied,
- depth_bias: None,
- depth_compare_op: CompareOp::Always,
- depth_test_enable: false,
- depth_write_enable: false,
- stencil_test_enable: false,
- stencil_back: default(),
- stencil_front: default(),
- });
-
- Self {
- bind_group_layout,
- pipeline,
- }
- }
-}