mod profile_with_puffin;
use {
bytemuck::{Pod, Zeroable, cast_slice},
clap::Parser,
half::f16,
inline_spirv::inline_spirv,
screen_13::prelude::*,
screen_13_window::{FrameContext, WindowBuilder},
std::{mem::size_of, sync::Arc},
};
fn main() -> anyhow::Result<()> {
pretty_env_logger::init();
profile_with_puffin::init();
let args = Args::parse();
let window = WindowBuilder::default().debug(args.debug).build()?;
let f16_pipeline = create_f16_pipeline(&window.device).ok();
let f16_vertex_buf = {
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vertex([f16; 2], [f32; 3]);
let vec2 = |x, y| [f16::from_f32(x), f16::from_f32(y)];
Arc::new(Buffer::create_from_slice(
&window.device,
vk::BufferUsageFlags::VERTEX_BUFFER,
cast_slice(&[
Vertex(vec2(-1.0, -1.0), [1.0, 0.0, 0.0]),
Vertex(vec2(1.0, 1.0), [0.0, 0.0, 1.0]),
Vertex(vec2(1.0, -1.0), [0.0, 1.0, 0.0]),
]),
)?)
};
let f32_pipeline = create_f32_pipeline(&window.device)?;
let f32_vertex_buf = {
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vertex([f32; 2], [f32; 3]);
Arc::new(Buffer::create_from_slice(
&window.device,
vk::BufferUsageFlags::VERTEX_BUFFER,
cast_slice(&[
Vertex([-1f32, -1.0], [1.0, 0.0, 0.0]),
Vertex([-1.0, 1.0], [0.0, 1.0, 0.0]),
Vertex([1.0, 1.0], [0.0, 0.0, 1.0]),
]),
)?)
};
let f64_pipeline = create_f64_pipeline(&window.device).ok();
let f64_vertex_buf = {
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vertex([f64; 2], [f32; 3], u32);
Arc::new(Buffer::create_from_slice(
&window.device,
vk::BufferUsageFlags::VERTEX_BUFFER,
cast_slice(&[
Vertex([-1.0, -1.0], [1.0, 0.0, 0.0], 0),
Vertex([1.0, 1.0], [0.0, 0.0, 1.0], 0),
Vertex([1.0, -1.0], [0.0, 1.0, 0.0], 0),
]),
)?)
};
window.run(|mut frame| {
draw_triangle(&mut frame, &f32_pipeline, &f32_vertex_buf);
if let Some(f64_pipeline) = &f64_pipeline {
draw_triangle(&mut frame, f64_pipeline, &f64_vertex_buf);
} else if let Some(f16_pipeline) = &f16_pipeline {
draw_triangle(&mut frame, f16_pipeline, &f16_vertex_buf);
}
})?;
Ok(())
}
fn draw_triangle(
frame: &mut FrameContext,
pipeline: &Arc<GraphicPipeline>,
vertex_buf: &Arc<Buffer>,
) {
let vertex_buf = frame.render_graph.bind_node(vertex_buf);
frame
.render_graph
.begin_pass("Triangle")
.bind_pipeline(pipeline)
.load_color(0, frame.swapchain_image)
.store_color(0, frame.swapchain_image)
.access_node(vertex_buf, AccessType::VertexBuffer)
.record_subpass(move |subpass, _| {
subpass.bind_vertex_buffer(vertex_buf).draw(3, 1, 0, 0);
});
}
fn create_f16_pipeline(device: &Arc<Device>) -> Result<Arc<GraphicPipeline>, DriverError> {
if !supports_vertex_buffer(device, vk::Format::R16G16_SFLOAT) {
return Err(DriverError::Unsupported);
}
const POSITION_SIZE: u32 = 2 * size_of::<f16>() as u32;
const COLOR_SIZE: u32 = 3 * size_of::<f32>() as u32;
let vertex = create_vertex_shader(false).vertex_input(
[vk::VertexInputBindingDescription {
binding: 0,
stride: POSITION_SIZE + COLOR_SIZE,
input_rate: vk::VertexInputRate::VERTEX,
}],
[
vk::VertexInputAttributeDescription {
binding: 0,
location: 0,
format: vk::Format::R16G16_SFLOAT,
offset: 0,
},
vk::VertexInputAttributeDescription {
binding: 0,
location: 1,
format: vk::Format::R32G32B32_SFLOAT,
offset: POSITION_SIZE,
},
],
);
create_pipeline(device, vertex)
}
fn create_f32_pipeline(device: &Arc<Device>) -> Result<Arc<GraphicPipeline>, DriverError> {
let vertex = create_vertex_shader(false);
create_pipeline(device, vertex)
}
fn create_f64_pipeline(device: &Arc<Device>) -> Result<Arc<GraphicPipeline>, DriverError> {
if !supports_vertex_buffer(device, vk::Format::R64G64_SFLOAT) {
return Err(DriverError::Unsupported);
}
const POSITION_SIZE: u32 = 2 * size_of::<f64>() as u32;
const COLOR_SIZE: u32 = 3 * size_of::<f32>() as u32;
const PAD_SIZE: u32 = size_of::<u32>() as u32;
let vertex = create_vertex_shader(true).vertex_input(
[vk::VertexInputBindingDescription {
binding: 0,
stride: POSITION_SIZE + COLOR_SIZE + PAD_SIZE,
input_rate: vk::VertexInputRate::VERTEX,
}],
[
vk::VertexInputAttributeDescription {
binding: 0,
location: 0,
format: vk::Format::R64G64_SFLOAT,
offset: 0,
},
vk::VertexInputAttributeDescription {
binding: 0,
location: 1,
format: vk::Format::R32G32B32_SFLOAT,
offset: POSITION_SIZE,
},
],
);
create_pipeline(device, vertex)
}
fn create_vertex_shader(is_double: bool) -> ShaderBuilder {
macro_rules! compile_vert {
($vec2_ty:literal) => {
inline_spirv!(
r#"
#version 460 core
layout(location = 0) in VEC2_TY position_in;
layout(location = 1) in vec3 color_in;
layout(location = 0) out vec3 color_out;
void main() {
gl_Position = vec4(position_in, 0, 1);
color_out = color_in;
}
"#,
vert,
D VEC2_TY = $vec2_ty,
)};
}
let spirv = if is_double {
compile_vert!("dvec2").as_slice()
} else {
compile_vert!("vec2").as_slice()
};
Shader::new_vertex(spirv)
}
fn create_pipeline(
device: &Arc<Device>,
vertex: ShaderBuilder,
) -> Result<Arc<GraphicPipeline>, DriverError> {
let fragment_spirv = inline_spirv!(
r#"
#version 460 core
layout(location = 0) in vec3 color_in;
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(color_in, 1.0);
}
"#,
frag
);
Ok(Arc::new(GraphicPipeline::create(
device,
GraphicPipelineInfo::default(),
[vertex, Shader::new_fragment(fragment_spirv.as_slice())],
)?))
}
fn supports_vertex_buffer(device: &Device, format: vk::Format) -> bool {
Device::format_properties(device, format)
.buffer_features
.contains(vk::FormatFeatureFlags::VERTEX_BUFFER)
}
#[derive(Parser)]
struct Args {
#[arg(long)]
debug: bool,
}