Initial compute/vertex/fragment render system
This commit is contained in:
17
crates/flare-shader/Cargo.toml
Normal file
17
crates/flare-shader/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "flare-shader"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["dylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
bytemuck.workspace = true
|
||||
glam.workspace = true
|
||||
spirv-std.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
141
crates/flare-shader/src/lib.rs
Normal file
141
crates/flare-shader/src/lib.rs
Normal file
@ -0,0 +1,141 @@
|
||||
#![cfg_attr(target_arch = "spirv", no_std)]
|
||||
|
||||
use glam::{UVec2, Vec4, Vec4Swizzles, uvec2, vec2, vec4};
|
||||
use spirv_std::spirv;
|
||||
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct IfsConstants {
|
||||
accum_width: u32,
|
||||
accum_height: u32,
|
||||
viewport_width: u32,
|
||||
viewport_height: u32,
|
||||
background_color: Vec4,
|
||||
}
|
||||
|
||||
impl IfsConstants {
|
||||
pub fn new(
|
||||
accum_width: u32,
|
||||
accum_height: u32,
|
||||
viewport_width: u32,
|
||||
viewport_height: u32,
|
||||
background_color: Vec4,
|
||||
) -> Self {
|
||||
Self {
|
||||
accum_width,
|
||||
accum_height,
|
||||
viewport_width,
|
||||
viewport_height,
|
||||
background_color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn viewport_dimensions(&self) -> UVec2 {
|
||||
uvec2(self.viewport_width, self.viewport_height)
|
||||
}
|
||||
|
||||
pub fn with_accumulate(&mut self, width: u32, height: u32) {
|
||||
self.accum_width = width;
|
||||
self.accum_height = height;
|
||||
}
|
||||
|
||||
pub fn with_viewport(&mut self, width: u32, height: u32) {
|
||||
self.viewport_width = width;
|
||||
self.viewport_height = height;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Color {
|
||||
type Element;
|
||||
const BLACK: Self;
|
||||
|
||||
const WHITE: Self;
|
||||
}
|
||||
|
||||
impl Color for Vec4 {
|
||||
type Element = f32;
|
||||
|
||||
const BLACK: Self = vec4(0., 0., 0., 1.);
|
||||
const WHITE: Self = vec4(1., 1., 1., 1.);
|
||||
}
|
||||
|
||||
pub(crate) fn image_index(pixel_x: usize, pixel_y: usize, image_width: u32) -> usize {
|
||||
pixel_x + pixel_y * image_width as usize
|
||||
}
|
||||
|
||||
#[spirv(compute(threads(1)))]
|
||||
pub fn main_cs(
|
||||
#[spirv(push_constant)] constants: &IfsConstants,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] accum_image: &mut [Vec4],
|
||||
) {
|
||||
let block_size = 64;
|
||||
for i_width in 0..constants.accum_width as usize {
|
||||
for i_height in 0..constants.accum_height as usize {
|
||||
let color = if (i_width / block_size % 2 == 1) != (i_height / block_size % 2 == 1) {
|
||||
Vec4::BLACK
|
||||
} else {
|
||||
Vec4::WHITE
|
||||
};
|
||||
|
||||
accum_image[image_index(i_width, i_height, constants.accum_width)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[spirv(vertex)]
|
||||
pub fn main_vs(
|
||||
#[spirv(vertex_index)] vert_id: u32,
|
||||
#[spirv(position, invariant)] position: &mut Vec4,
|
||||
) {
|
||||
// Create a "quad" that fills the viewport for the fragment shader.
|
||||
// The `draw` call issued by the main application will be for three vertex ID's (0, 1, 2).
|
||||
// This code maps them to the points (-1, -1), (3, -1), and (-1, 3) respectively.
|
||||
// Because the interior of that triangle covers the entire viewport,
|
||||
// the GPU clips to the viewport and invokes the fragment shader for each pixel.
|
||||
// https://stackoverflow.com/a/59739538
|
||||
// https://www.saschawillems.de/blog/2016/08/13/vulkan-tutorial-on-rendering-a-fullscreen-quad-without-buffers/
|
||||
let output_uv = vec2(((vert_id << 1) & 2) as f32, (vert_id & 2) as f32);
|
||||
*position = (output_uv * 2.0 - 1.0, 0.0, 1.0).into();
|
||||
}
|
||||
|
||||
#[spirv(fragment)]
|
||||
pub fn main_fs(
|
||||
#[spirv(frag_coord)] frag_coord: Vec4,
|
||||
#[spirv(push_constant)] ifs_constants: &IfsConstants,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] accum_image: &[Vec4],
|
||||
output: &mut Vec4,
|
||||
) {
|
||||
// Bootleg texture sampling; map from viewport image pixel coordinates to accumulator image
|
||||
// pixel coordinates
|
||||
let viewport_coordinate = frag_coord.xy().as_uvec2();
|
||||
|
||||
let a_width = ifs_constants.accum_width as f32;
|
||||
let a_height = ifs_constants.accum_height as f32;
|
||||
let v_width = ifs_constants.viewport_width as f32;
|
||||
let v_height = ifs_constants.viewport_height as f32;
|
||||
|
||||
// Scale both width and height by the same factor; preserves aspect ratio
|
||||
let scale_width = a_width / v_width;
|
||||
let scale_height = a_height / v_height;
|
||||
let scale = scale_width.max(scale_height);
|
||||
|
||||
// Re-center the image in the viewport after scale
|
||||
let offset_x = (v_width * scale - a_width) / 2.0;
|
||||
let offset_y = (v_height * scale - a_height) / 2.0;
|
||||
|
||||
let accum_coordinate = viewport_coordinate.as_vec2() * scale - vec2(offset_x, offset_y);
|
||||
|
||||
if accum_coordinate.x < 0.0
|
||||
|| accum_coordinate.x >= ifs_constants.accum_width as f32
|
||||
|| accum_coordinate.y < 0.0
|
||||
|| accum_coordinate.y >= ifs_constants.accum_height as f32
|
||||
{
|
||||
*output = ifs_constants.background_color;
|
||||
} else {
|
||||
*output = accum_image[image_index(
|
||||
accum_coordinate.x as usize,
|
||||
accum_coordinate.y as usize,
|
||||
ifs_constants.accum_width,
|
||||
)];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user