name: &'static str,
}
-const SHADERS: [Shader; 3] = [
+const SHADERS: &[Shader] = &[
Shader {
stage: "vert",
name: "basic",
stage: "frag",
name: "basic",
},
+ Shader {
+ stage: "comp",
+ name: "primitive_2d_tiled",
+ },
Shader {
stage: "comp",
name: "display_transform",
_ => "0",
};
- let commands = SHADERS.map(|Shader { stage, name }| {
- Command::new("glslangValidator")
- .args(["--target-env", "vulkan1.3"])
- .arg("--quiet")
- .arg(&format!("-g{debug}"))
- .args(["--depfile", &format!("{out_dir}/{name}.{stage}.d")])
- .args(["-o", &format!("{out_dir}/{name}.{stage}.spv")])
- .arg(&format!("{SHADER_ROOT}/{name}.{stage}.glsl"))
- .spawn()
- .unwrap()
- });
+ let commands = SHADERS
+ .iter()
+ .map(|Shader { stage, name }| {
+ Command::new("glslangValidator")
+ .args(["--target-env", "vulkan1.3"])
+ .arg("--quiet")
+ .arg(&format!("-g{debug}"))
+ .args(["--depfile", &format!("{out_dir}/{name}.{stage}.d")])
+ .args(["-o", &format!("{out_dir}/{name}.{stage}.spv")])
+ .arg(&format!("{SHADER_ROOT}/{name}.{stage}.glsl"))
+ .spawn()
+ .unwrap()
+ })
+ .collect::<Vec<_>>();
let dst_path = std::path::Path::new(&out_dir).join("shaders.rs");
let mut file = std::fs::File::create(dst_path).unwrap();
#version 460
-#extension GL_GOOGLE_include_directive : require
+layout (set = 0, binding = 0) uniform sampler bilinear_sampler;
-#extension GL_EXT_scalar_block_layout : require
-#extension GL_EXT_control_flow_attributes : require
+layout (set = 0, binding = 1) uniform texture3D tony_mc_mapface_lut;
-#include "primitive_types.h"
+layout (set = 0, binding = 2, rgba16f) uniform readonly image2D layer_rt;
+layout (set = 0, binding = 3, rgba16f) uniform readonly image2D layer_ui;
-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) uniform sampler bilinear_sampler;
-
-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[];
-};
+layout (set = 0, binding = 4, rgba16f) uniform writeonly image2D composited_output;
float srgb_oetf(float a) {
return (.0031308f >= a) ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f;
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;
- }
- }
-
- const vec3 stimulus = imageLoad(render_target, ivec2(gl_GlobalInvocationID.xy)).rgb;
+ const vec3 stimulus = imageLoad(layer_rt, ivec2(gl_GlobalInvocationID.xy)).rgb;
const vec3 transformed = tony_mc_mapface(stimulus);
const vec3 srgb = srgb_oetf(transformed);
- const vec3 composited = accum.rgb + (srgb * (1.0 - accum.a));
- imageStore(swapchain_image, ivec2(gl_GlobalInvocationID.xy), vec4(composited, 1.0));
+ const vec4 ui = imageLoad(layer_ui, ivec2(gl_GlobalInvocationID.xy)).rgba;
+ const vec3 composited = ui.rgb + (srgb * (1.0 - ui.a));
+ imageStore(composited_output, ivec2(gl_GlobalInvocationID.xy), vec4(composited, 1.0));
}
--- /dev/null
+#version 460
+
+#extension GL_GOOGLE_include_directive : require
+
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_control_flow_attributes : require
+
+#include "primitive_2d_types.h"
+
+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) uniform sampler bilinear_sampler;
+layout (set = 0, binding = 2) uniform texture2D glyph_atlas;
+
+layout(std430, set = 0, binding = 3) readonly buffer glyphBuffer {
+ Glyph glyphs[];
+};
+
+layout(std430, set = 0, binding = 4) readonly buffer glyphInstanceBuffer {
+ GlyphInstance glyph_instances[];
+};
+
+layout(std430, set = 0, binding = 5) readonly buffer primitiveInstanceBuffer {
+ PrimitiveInstance primitive_instances[];
+};
+
+layout(std430, set = 0, binding = 6) readonly buffer tileBuffer {
+ Tile tiles[];
+};
+
+layout (set = 0, binding = 7, rgba16f) uniform writeonly image2D ui_image;
+
+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 Glyph gl = glyphs[gi.index];
+ const vec4 color = unpackUnorm4x8(gi.color).bgra;
+ const vec2 glyph_top_left = vec2(gi.x + gl.offset_x0, gi.y + gl.offset_y0);
+ const vec2 glyph_bottom_right = vec2(gi.x + gl.offset_x1, gi.y + gl.offset_y1);
+ const vec2 glyph_size = vec2(gl.offset_x1 - gl.offset_x0, gl.offset_y1 - gl.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(gl.x0, gl.y0), vec2(gl.x1, gl.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;
+ }
+ }
+
+ imageStore(ui_image, ivec2(gl_GlobalInvocationID.xy), accum);
+}
-struct CachedGlyph {
+struct Glyph {
uint x0;
uint x1;
uint y0;
uint index;
uint color;
};
+
+struct PrimitiveInstance {
+ uint type;
+ uint index;
+};
+
+struct Tile {
+ uint index;
+ uint count;
+};
use narcissus_maths::{
clamp, perlin_noise3, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec3,
};
-use pipelines::{BasicPipeline, BasicUniforms, DisplayTransformPipeline, PrimitiveInstance};
+use pipelines::{
+ BasicPipeline, BasicUniforms, DisplayTransformPipeline, GlyphInstance, Primitive2dPipeline,
+};
use spring::simple_spring_damper_exact;
-use crate::pipelines::DisplayTransformUniforms;
+use crate::pipelines::PrimitiveUniforms;
mod fonts;
mod helpers;
tmp_string: String,
- primitive_instances: Vec<PrimitiveInstance>,
+ primitive_instances: Vec<GlyphInstance>,
}
impl<'a> UiState<'a> {
x += advance * scale;
- self.primitive_instances.push(PrimitiveInstance {
+ self.primitive_instances.push(GlyphInstance {
x,
y,
touched_glyph_index,
gpu: &'gpu Gpu,
basic_pipeline: BasicPipeline,
+ primitive_2d_pipeline: Primitive2dPipeline,
display_transform_pipeline: DisplayTransformPipeline,
width: u32,
height: u32,
depth_image: Image,
- render_target_image: Image,
+ rt_image: Image,
+ ui_image: Image,
glyph_atlas_image: Image,
impl<'gpu> DrawState<'gpu> {
fn new(gpu: &'gpu Gpu, thread_token: &ThreadToken) -> Self {
let basic_pipeline = BasicPipeline::new(gpu);
+ let primitive_2d_pipeline = Primitive2dPipeline::new(gpu);
let display_transform_pipeline = DisplayTransformPipeline::new(gpu);
let samplers = Samplers::load(gpu);
Self {
gpu,
basic_pipeline,
+ primitive_2d_pipeline,
display_transform_pipeline,
width: 0,
height: 0,
depth_image: default(),
- render_target_image: default(),
+ rt_image: default(),
+ ui_image: default(),
glyph_atlas_image: default(),
samplers,
models,
if width != self.width || height != self.height {
gpu.destroy_image(frame, self.depth_image);
- gpu.destroy_image(frame, self.render_target_image);
+ gpu.destroy_image(frame, self.rt_image);
+ gpu.destroy_image(frame, self.ui_image);
self.depth_image = gpu.create_image(&ImageDesc {
memory_location: MemoryLocation::Device,
mip_levels: 1,
});
- self.render_target_image = gpu.create_image(&ImageDesc {
+ self.rt_image = gpu.create_image(&ImageDesc {
memory_location: MemoryLocation::Device,
host_mapped: false,
usage: ImageUsageFlags::COLOR_ATTACHMENT | ImageUsageFlags::STORAGE,
mip_levels: 1,
});
+ self.ui_image = gpu.create_image(&ImageDesc {
+ memory_location: MemoryLocation::Device,
+ host_mapped: false,
+ usage: ImageUsageFlags::STORAGE,
+ dimension: ImageDimension::Type2d,
+ format: ImageFormat::RGBA16_FLOAT,
+ tiling: ImageTiling::Optimal,
+ width,
+ height,
+ depth: 1,
+ layer_count: 1,
+ mip_levels: 1,
+ });
+
gpu.cmd_barrier(
cmd_encoder,
None,
gpu.cmd_barrier(
cmd_encoder,
None,
- &[ImageBarrier::layout_optimal(
- &[Access::None],
- &[Access::ColorAttachmentWrite],
- self.render_target_image,
- ImageAspectFlags::COLOR,
- )],
+ &[
+ ImageBarrier::layout_optimal(
+ &[Access::None],
+ &[Access::ColorAttachmentWrite],
+ self.rt_image,
+ ImageAspectFlags::COLOR,
+ ),
+ ImageBarrier {
+ prev_access: &[Access::None],
+ next_access: &[Access::ShaderWrite],
+ prev_layout: ImageLayout::Optimal,
+ next_layout: ImageLayout::General,
+ image: self.ui_image,
+ subresource_range: default(),
+ },
+ ],
);
gpu.cmd_begin_rendering(
width,
height,
color_attachments: &[RenderingAttachment {
- image: self.render_target_image,
+ image: self.rt_image,
load_op: LoadOp::Clear(ClearValue::ColorF32([1.0, 1.0, 1.0, 1.0])),
store_op: StoreOp::Store,
}],
gpu.cmd_end_rendering(cmd_encoder);
- gpu.cmd_barrier(
- cmd_encoder,
- None,
- &[ImageBarrier {
- prev_access: &[Access::ColorAttachmentWrite],
- prev_layout: ImageLayout::Optimal,
- next_access: &[Access::ShaderOtherRead],
- next_layout: ImageLayout::General,
- image: self.render_target_image,
- subresource_range: ImageSubresourceRange::default(),
- }],
- );
+ // Render UI
+ {
+ gpu.cmd_set_pipeline(cmd_encoder, self.primitive_2d_pipeline.pipeline);
- gpu.cmd_compute_touch_swapchain(cmd_encoder, swapchain_image);
+ let uniforms_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::UNIFORM,
+ &PrimitiveUniforms {
+ screen_width: width,
+ screen_height: height,
+ atlas_width,
+ atlas_height,
+ num_primitives: ui_state.primitive_instances.len() as u32,
+ },
+ );
+ let glyph_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(),
+ );
+ let primitive_instance_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ &[0u32],
+ );
+ let tile_buffer = gpu.request_transient_buffer_with_data(
+ frame,
+ thread_token,
+ BufferUsageFlags::STORAGE,
+ &[0u32],
+ );
- gpu.cmd_set_pipeline(cmd_encoder, self.display_transform_pipeline.pipeline);
+ ui_state.primitive_instances.clear();
- 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(),
- );
+ gpu.cmd_set_bind_group(
+ frame,
+ cmd_encoder,
+ self.primitive_2d_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::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
+ },
+ Bind {
+ binding: 2,
+ array_element: 0,
+ typed: TypedBind::SampledImage(&[(
+ ImageLayout::Optimal,
+ self.glyph_atlas_image,
+ )]),
+ },
+ Bind {
+ binding: 3,
+ array_element: 0,
+ typed: TypedBind::StorageBuffer(&[glyph_buffer.to_arg()]),
+ },
+ Bind {
+ binding: 4,
+ array_element: 0,
+ typed: TypedBind::StorageBuffer(&[glyph_instance_buffer.to_arg()]),
+ },
+ Bind {
+ binding: 5,
+ array_element: 0,
+ typed: TypedBind::StorageBuffer(&[primitive_instance_buffer.to_arg()]),
+ },
+ Bind {
+ binding: 6,
+ array_element: 0,
+ typed: TypedBind::StorageBuffer(&[tile_buffer.to_arg()]),
+ },
+ Bind {
+ binding: 7,
+ array_element: 0,
+ typed: TypedBind::StorageImage(&[(
+ ImageLayout::General,
+ self.ui_image,
+ )]),
+ },
+ ],
+ );
- ui_state.primitive_instances.clear();
+ gpu.cmd_dispatch(cmd_encoder, (self.width + 7) / 8, (self.height + 7) / 8, 1);
+ }
- gpu.cmd_set_bind_group(
- frame,
- cmd_encoder,
- self.display_transform_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::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
- },
- Bind {
- binding: 2,
- array_element: 0,
- typed: TypedBind::StorageImage(&[(
- ImageLayout::General,
- self.render_target_image,
- )]),
- },
- Bind {
- binding: 3,
- array_element: 0,
- typed: TypedBind::StorageImage(&[(ImageLayout::General, swapchain_image)]),
- },
- Bind {
- binding: 4,
- array_element: 0,
- typed: TypedBind::SampledImage(&[(
- ImageLayout::Optimal,
- self.glyph_atlas_image,
- )]),
- },
- Bind {
- binding: 5,
- array_element: 0,
- typed: TypedBind::SampledImage(&[(
- ImageLayout::Optimal,
- self.images[ImageRes::TonyMcMapfaceLut],
- )]),
- },
- Bind {
- binding: 6,
- array_element: 0,
- typed: TypedBind::StorageBuffer(&[cached_glyphs_buffer.to_arg()]),
- },
- Bind {
- binding: 7,
- array_element: 0,
- typed: TypedBind::StorageBuffer(&[glyph_instance_buffer.to_arg()]),
- },
- ],
- );
+ // Display transform and composite
+ {
+ gpu.cmd_barrier(
+ cmd_encoder,
+ None,
+ &[
+ ImageBarrier {
+ prev_access: &[Access::ColorAttachmentWrite],
+ prev_layout: ImageLayout::Optimal,
+ next_access: &[Access::ShaderOtherRead],
+ next_layout: ImageLayout::General,
+ image: self.rt_image,
+ subresource_range: ImageSubresourceRange::default(),
+ },
+ ImageBarrier {
+ prev_access: &[Access::ShaderWrite],
+ prev_layout: ImageLayout::General,
+ next_access: &[Access::ShaderOtherRead],
+ next_layout: ImageLayout::General,
+ image: self.ui_image,
+ subresource_range: ImageSubresourceRange::default(),
+ },
+ ],
+ );
+
+ gpu.cmd_compute_touch_swapchain(cmd_encoder, swapchain_image);
+
+ gpu.cmd_set_pipeline(cmd_encoder, self.display_transform_pipeline.pipeline);
+
+ gpu.cmd_set_bind_group(
+ frame,
+ cmd_encoder,
+ self.display_transform_pipeline.bind_group_layout,
+ 0,
+ &[
+ Bind {
+ binding: 0,
+ array_element: 0,
+ typed: TypedBind::Sampler(&[self.samplers[SamplerRes::Bilinear]]),
+ },
+ Bind {
+ binding: 1,
+ array_element: 0,
+ typed: TypedBind::SampledImage(&[(
+ ImageLayout::Optimal,
+ self.images[ImageRes::TonyMcMapfaceLut],
+ )]),
+ },
+ Bind {
+ binding: 2,
+ array_element: 0,
+ typed: TypedBind::StorageImage(&[(
+ ImageLayout::General,
+ self.rt_image,
+ )]),
+ },
+ Bind {
+ binding: 3,
+ array_element: 0,
+ typed: TypedBind::StorageImage(&[(
+ ImageLayout::General,
+ self.ui_image,
+ )]),
+ },
+ Bind {
+ binding: 4,
+ array_element: 0,
+ typed: TypedBind::StorageImage(&[(
+ ImageLayout::General,
+ swapchain_image,
+ )]),
+ },
+ ],
+ );
- gpu.cmd_dispatch(cmd_encoder, (self.width + 7) / 8, (self.height + 7) / 8, 1);
+ gpu.cmd_dispatch(cmd_encoder, (self.width + 7) / 8, (self.height + 7) / 8, 1);
+ }
}
gpu.submit(frame, cmd_encoder);
}
-use narcissus_font::TouchedGlyphIndex;
use narcissus_gpu::{
BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, ShaderDesc,
ShaderStageFlags,
use crate::Gpu;
-#[allow(unused)]
-#[repr(C)]
-pub struct DisplayTransformUniforms {
- 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 {
pub bind_group_layout: BindGroupLayout,
pub pipeline: Pipeline,
impl DisplayTransformPipeline {
pub fn new(gpu: &Gpu) -> Self {
let bind_group_layout = gpu.create_bind_group_layout(&[
- // Uniforms
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::UniformBuffer),
// Sampler
BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::Sampler),
- // RT
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
- // Swapchain
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
- // Glyph Atlas
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::SampledImage),
// Tony Mc'mapface LUT
BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::SampledImage),
- // Glyphs
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
- // Glyph Instances
- BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
+ // Layer RT
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
+ // Layer UI
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
+ // Composited Output
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
]);
let pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
mod basic;
mod display_transform;
+mod primitive_2d;
pub use basic::{BasicPipeline, BasicUniforms, Vertex};
-
-pub use display_transform::{
- DisplayTransformPipeline, DisplayTransformUniforms, PrimitiveInstance,
-};
+pub use display_transform::DisplayTransformPipeline;
+pub use primitive_2d::{GlyphInstance, Primitive2dPipeline, PrimitiveUniforms};
--- /dev/null
+use narcissus_font::TouchedGlyphIndex;
+use narcissus_gpu::{
+ BindDesc, BindGroupLayout, BindingType, ComputePipelineDesc, Pipeline, ShaderDesc,
+ ShaderStageFlags,
+};
+
+use crate::Gpu;
+
+#[allow(unused)]
+#[repr(C)]
+pub struct PrimitiveUniforms {
+ 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 GlyphInstance {
+ pub x: f32,
+ pub y: f32,
+ pub touched_glyph_index: TouchedGlyphIndex,
+ pub color: u32,
+}
+
+pub struct Primitive2dPipeline {
+ pub bind_group_layout: BindGroupLayout,
+ pub pipeline: Pipeline,
+}
+
+impl Primitive2dPipeline {
+ pub fn new(gpu: &Gpu) -> Self {
+ let bind_group_layout = gpu.create_bind_group_layout(&[
+ // Uniforms
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::UniformBuffer),
+ // Sampler
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::Sampler),
+ // Glyph Atlas
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::SampledImage),
+ // Glyphs
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
+ // Glyph Instances
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
+ // Primitive Instances
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
+ // Tiles
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageBuffer),
+ // UI
+ BindDesc::new(ShaderStageFlags::COMPUTE, BindingType::StorageImage),
+ ]);
+
+ let pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc {
+ shader: ShaderDesc {
+ entry: c"main",
+ code: shark_shaders::PRIMITIVE_2D_TILED_COMP_SPV,
+ },
+ bind_group_layouts: &[bind_group_layout],
+ });
+
+ Self {
+ bind_group_layout,
+ pipeline,
+ }
+ }
+}