371 lines
13 KiB
Rust
371 lines
13 KiB
Rust
use std::{convert::TryInto, io::{self, ErrorKind, Read, Write}, path::PathBuf, sync::Arc};
|
|
|
|
use vulkano::{command_buffer::AutoCommandBufferBuilder, descriptor::{DescriptorSet, descriptor::ShaderStages, descriptor_set::PersistentDescriptorSet}, pipeline::{shader::{ShaderModule}}};
|
|
use vulkano::command_buffer::DynamicState;
|
|
use vulkano::device::Device;
|
|
use vulkano::framebuffer::RenderPassAbstract;
|
|
use vulkano::framebuffer::Subpass;
|
|
use vulkano::pipeline::GraphicsPipeline;
|
|
use vulkano::pipeline::GraphicsPipelineAbstract;
|
|
use vulkano::pipeline::shader::GraphicsShaderType;
|
|
|
|
use crate::{GameObject, vulkan::{TextVertex}};
|
|
use crate::vulkan::{LinePoint, Vertex};
|
|
use crate::vulkan::GameData;
|
|
use crate::VulkanRenderer;
|
|
|
|
use super::gameobject::TextureData;
|
|
|
|
type RP = Arc<dyn RenderPassAbstract + Send + Sync>;
|
|
type GP = Arc<dyn GraphicsPipelineAbstract + Send + Sync>;
|
|
type DS = Arc<dyn DescriptorSet + Send + Sync>;
|
|
|
|
pub trait Drawcall {
|
|
fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData, dynamic_state: &DynamicState);
|
|
fn create_descriptor_set(self: &Self, textures: &TextureData, renderer: &VulkanRenderer) -> Vec<DS>;
|
|
fn recreate_pipeline(self: &mut Self, device: Arc<Device>, render_pass: RP);
|
|
fn get_pipeline(self: &Self) -> &GP;
|
|
}
|
|
|
|
pub mod vs {
|
|
vulkano_shaders::shader!{
|
|
ty: "vertex",
|
|
path: "shaders/triangle.vert"
|
|
}
|
|
}
|
|
|
|
pub mod fs {
|
|
vulkano_shaders::shader! {
|
|
ty: "fragment",
|
|
path: "shaders/triangle.frag"
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DefaultShader {
|
|
pipeline: GP,
|
|
}
|
|
|
|
fn shader_module_from_file(device: Arc<Device>, path: &str) -> Arc<ShaderModule> {
|
|
let mut file = std::fs::File::open(path).unwrap();
|
|
let mut buffer = vec![];
|
|
file.read_to_end(&mut buffer).unwrap();
|
|
let words: Vec<u32> = buffer.chunks_exact(4).map(|c| u32::from_ne_bytes(c.try_into().unwrap())).collect();
|
|
unsafe {
|
|
ShaderModule::from_words(device.clone(), &words).unwrap()
|
|
}
|
|
}
|
|
|
|
fn matches_extension(path: &PathBuf, extensions: &Vec<&str>) -> bool {
|
|
if let Some(Some(path_extension)) = path.extension().map(|e| e.to_str()) {
|
|
for extension in extensions {
|
|
if *extension == path_extension { return true; }
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn compile_shaders() -> io::Result<()> {
|
|
for file_maybe in std::fs::read_dir("./shaders")? {
|
|
let path = file_maybe?.path();
|
|
if !path.is_dir() && matches_extension(&path, &vec!["frag", "vert"]) {
|
|
let mut target_path = path.to_str().ok_or(ErrorKind::Other)?.to_string();
|
|
target_path.push_str(".spv");
|
|
|
|
let output = std::process::Command::new("glslangValidator")
|
|
.arg("-V")
|
|
.arg(path.to_str().ok_or(ErrorKind::Other)?)
|
|
.arg("-o")
|
|
.arg(target_path)
|
|
.output().unwrap();
|
|
std::io::stdout().write_all(&output.stdout)?;
|
|
|
|
if !output.status.success() {
|
|
eprintln!("Shader compiler {:?}", output.status);
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
impl DefaultShader {
|
|
pub fn new(device: Arc<Device>, render_pass: RP) -> Self {
|
|
DefaultShader {
|
|
pipeline: Self::create_pipeline(device, render_pass)
|
|
}
|
|
}
|
|
|
|
fn create_pipeline(device: Arc<Device>, render_pass: RP) -> GP {
|
|
let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
println!("Compiling shaders...");
|
|
compile_shaders().unwrap();
|
|
}
|
|
|
|
unsafe {
|
|
static ENTRY_NAME: [u8; 5usize] = [109u8, 97u8, 105u8, 110u8, 0];
|
|
let entry_name_c = std::ffi::CStr::from_ptr(ENTRY_NAME.as_ptr() as *const _);
|
|
|
|
let fs_module = shader_module_from_file(device.clone(), "shaders/triangle.frag.spv");
|
|
let fs_layout = fs::Layout(ShaderStages {
|
|
fragment: true,
|
|
..ShaderStages::none()
|
|
});
|
|
let fs_entry = fs_module.graphics_entry_point(entry_name_c, fs::MainInput, fs::MainOutput, fs_layout, GraphicsShaderType::Fragment);
|
|
|
|
let vs_module = shader_module_from_file(device.clone(), "shaders/triangle.vert.spv");
|
|
let vs_layout = vs::Layout(ShaderStages {
|
|
vertex: true,
|
|
..ShaderStages::none()
|
|
});
|
|
let vs_entry = vs_module.graphics_entry_point(entry_name_c, vs::MainInput, vs::MainOutput, vs_layout, GraphicsShaderType::Vertex);
|
|
|
|
Arc::new(GraphicsPipeline::start()
|
|
.vertex_input_single_buffer::<Vertex>()
|
|
.vertex_shader(vs_entry, ())
|
|
.triangle_list()
|
|
.viewports_dynamic_scissors_irrelevant(1)
|
|
.depth_stencil_simple_depth()
|
|
.fragment_shader(fs_entry, ())
|
|
.blend_alpha_blending()
|
|
.cull_mode_back()
|
|
.render_pass(sub_pass.clone())
|
|
.build(device.clone())
|
|
.unwrap())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drawcall for DefaultShader {
|
|
fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData, dynamic_state: &DynamicState) {
|
|
for i in 0..game_objects.len() {
|
|
let game_object = &game_objects[i];
|
|
let mesh = &game_data.meshes[game_object.mesh_index];
|
|
let push_constants = game_object.get_push_constants();
|
|
|
|
builder.draw_indexed(
|
|
self.pipeline.clone(),
|
|
dynamic_state,
|
|
vec![mesh.vertex_buffer.clone()],
|
|
mesh.index_buffer.clone(),
|
|
game_object.descriptor_sets[fb_index].clone(),
|
|
push_constants).unwrap();
|
|
}
|
|
}
|
|
|
|
fn create_descriptor_set(self: &Self, textures: &TextureData, renderer: &VulkanRenderer) -> Vec<DS> {
|
|
let descriptor_set_layout = self.get_pipeline().descriptor_set_layout(0).unwrap().clone();
|
|
|
|
println!("Diff: {:?}, Norm: {:?}", textures.texture_index, textures.normal_map_index);
|
|
|
|
renderer.uniform_buffers.iter().map(|uniform_buffer| {
|
|
let descriptor_set: Arc<(dyn vulkano::descriptor::DescriptorSet + std::marker::Send + std::marker::Sync + 'static)>;
|
|
let builder = PersistentDescriptorSet::start(descriptor_set_layout.clone());
|
|
|
|
let diffuse = &renderer.game_data.textures[textures.texture_index];
|
|
let normal_map = &renderer.game_data.textures[textures.normal_map_index];
|
|
|
|
descriptor_set = Arc::new(builder
|
|
.add_buffer(uniform_buffer.clone()).unwrap()
|
|
.add_sampled_image(diffuse.image.clone(), diffuse.sampler.clone()).unwrap()
|
|
.add_sampled_image(normal_map.image.clone(), normal_map.sampler.clone()).unwrap()
|
|
.build().unwrap());
|
|
|
|
descriptor_set
|
|
}).collect()
|
|
}
|
|
|
|
fn recreate_pipeline(self: &mut Self, device: Arc<Device>, render_pass: RP) {
|
|
self.pipeline = Self::create_pipeline(device, render_pass);
|
|
}
|
|
|
|
fn get_pipeline(self: &Self) -> &GP {
|
|
&self.pipeline
|
|
}
|
|
}
|
|
|
|
pub mod line_vs {
|
|
vulkano_shaders::shader!{
|
|
ty: "vertex",
|
|
path: "shaders/line.vert"
|
|
}
|
|
}
|
|
|
|
pub mod line_fs {
|
|
vulkano_shaders::shader! {
|
|
ty: "fragment",
|
|
path: "shaders/line.frag"
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct LineShader {
|
|
pipeline: GP,
|
|
vertex_buffer: Arc<vulkano::buffer::CpuAccessibleBuffer<[LinePoint]>>
|
|
}
|
|
|
|
impl LineShader {
|
|
pub fn _new(device: Arc<Device>, render_pass: RP, vertex_buffer: Arc<vulkano::buffer::CpuAccessibleBuffer<[LinePoint]>>) -> Self {
|
|
LineShader {
|
|
pipeline: Self::create_pipeline(device, render_pass),
|
|
vertex_buffer
|
|
}
|
|
}
|
|
|
|
fn create_pipeline(device: Arc<Device>, render_pass: RP) -> GP {
|
|
let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
let vertex_shader = line_vs::Shader::load(device.clone()).unwrap();
|
|
let fragment_shader = line_fs::Shader::load(device.clone()).unwrap();
|
|
|
|
Arc::new(GraphicsPipeline::start()
|
|
.vertex_input_single_buffer::<LinePoint>()
|
|
.vertex_shader(vertex_shader.main_entry_point(), ())
|
|
.line_list()
|
|
.viewports_dynamic_scissors_irrelevant(1)
|
|
.depth_stencil_simple_depth()
|
|
.fragment_shader(fragment_shader.main_entry_point(), ())
|
|
.render_pass(sub_pass.clone())
|
|
.build(device.clone())
|
|
.unwrap())
|
|
}
|
|
}
|
|
|
|
impl Drawcall for LineShader {
|
|
fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, _fb_index: usize, _game_objects: Vec<&GameObject>, game_data: &GameData, dynamic_state: &DynamicState) {
|
|
builder.draw(self.pipeline.clone(),
|
|
&dynamic_state,
|
|
vec![self.vertex_buffer.clone()],
|
|
(),
|
|
game_data.line_push_constants.clone()).unwrap();
|
|
}
|
|
|
|
fn create_descriptor_set(self: &Self, _textures: &TextureData, _renderer: &VulkanRenderer) -> Vec<DS> {
|
|
vec![]
|
|
}
|
|
|
|
fn recreate_pipeline(self: &mut Self, device: Arc<Device>, render_pass: RP) {
|
|
self.pipeline = Self::create_pipeline(device, render_pass);
|
|
}
|
|
|
|
fn get_pipeline(self: &Self) -> &GP {
|
|
&self.pipeline
|
|
}
|
|
}
|
|
|
|
pub mod vs_text {
|
|
vulkano_shaders::shader!{
|
|
ty: "vertex",
|
|
path: "shaders/text.vert"
|
|
}
|
|
}
|
|
|
|
pub mod fs_text {
|
|
vulkano_shaders::shader!{
|
|
ty: "fragment",
|
|
path: "shaders/text.frag"
|
|
}
|
|
}
|
|
|
|
pub struct TextShader {
|
|
pipeline: GP,
|
|
}
|
|
|
|
impl TextShader {
|
|
pub fn new(device: Arc<Device>, render_pass: RP) -> Self {
|
|
TextShader {
|
|
pipeline: Self::create_pipeline(device, render_pass)
|
|
}
|
|
}
|
|
|
|
fn create_pipeline(device: Arc<Device>, render_pass: RP) -> GP {
|
|
let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap();
|
|
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
println!("Compiling shaders...");
|
|
compile_shaders().unwrap();
|
|
}
|
|
|
|
unsafe {
|
|
static ENTRY_NAME: [u8; 5usize] = [109u8, 97u8, 105u8, 110u8, 0];
|
|
let entry_name_c = std::ffi::CStr::from_ptr(ENTRY_NAME.as_ptr() as *const _);
|
|
|
|
let fs_module = shader_module_from_file(device.clone(), "shaders/text.frag.spv");
|
|
let fs_layout = fs::Layout(ShaderStages {
|
|
fragment: true,
|
|
..ShaderStages::none()
|
|
});
|
|
let fs_entry = fs_module.graphics_entry_point(entry_name_c, fs_text::MainInput, fs_text::MainOutput, fs_layout, GraphicsShaderType::Fragment);
|
|
|
|
let vs_module = shader_module_from_file(device.clone(), "shaders/text.vert.spv");
|
|
let vs_layout = vs::Layout(ShaderStages {
|
|
vertex: true,
|
|
..ShaderStages::none()
|
|
});
|
|
let vs_entry = vs_module.graphics_entry_point(entry_name_c, vs_text::MainInput, vs_text::MainOutput, vs_layout, GraphicsShaderType::Vertex);
|
|
|
|
let gp = Arc::new(GraphicsPipeline::start()
|
|
.vertex_input_single_buffer::<TextVertex>()
|
|
.vertex_shader(vs_entry, ())
|
|
.triangle_list()
|
|
.viewports_dynamic_scissors_irrelevant(1)
|
|
.depth_stencil_simple_depth()
|
|
.fragment_shader(fs_entry, ())
|
|
.blend_alpha_blending()
|
|
.cull_mode_disabled()
|
|
.render_pass(sub_pass.clone())
|
|
.build(device.clone())
|
|
.unwrap());
|
|
|
|
gp
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drawcall for TextShader {
|
|
fn draw(self: &Self, builder: &mut AutoCommandBufferBuilder, fb_index: usize, game_objects: Vec<&GameObject>, game_data: &GameData, dynamic_state: &DynamicState) {
|
|
for i in 0..game_objects.len() {
|
|
let game_object = &game_objects[i];
|
|
let mesh = &game_data.meshes_text[game_object.mesh_index];
|
|
let push_constants = game_object.get_push_constants();
|
|
|
|
builder.draw_indexed(
|
|
self.pipeline.clone(),
|
|
dynamic_state,
|
|
vec![mesh.vertex_buffer.clone()],
|
|
mesh.index_buffer.clone(),
|
|
game_object.descriptor_sets[fb_index].clone(),
|
|
push_constants).unwrap();
|
|
}
|
|
}
|
|
|
|
fn create_descriptor_set(self: &Self, textures: &TextureData, renderer: &VulkanRenderer) -> Vec<DS> {
|
|
let descriptor_set_layout = self.get_pipeline().descriptor_set_layout(0).unwrap().clone();
|
|
|
|
renderer.uniform_buffers.iter().map(|uniform_buffer| {
|
|
let descriptor_set: Arc<(dyn vulkano::descriptor::DescriptorSet + std::marker::Send + std::marker::Sync + 'static)>;
|
|
let builder = PersistentDescriptorSet::start(descriptor_set_layout.clone());
|
|
|
|
let diffuse = &renderer.game_data.textures[textures.texture_index];
|
|
println!("Using diffuse image with size {:?}", diffuse.image.dimensions());
|
|
|
|
descriptor_set = Arc::new(builder
|
|
.add_buffer(uniform_buffer.clone()).unwrap()
|
|
.add_sampled_image(diffuse.image.clone(), diffuse.sampler.clone()).unwrap()
|
|
.add_sampled_image(diffuse.image.clone(), diffuse.sampler.clone()).unwrap()
|
|
.build().unwrap());
|
|
|
|
descriptor_set
|
|
}).collect()
|
|
}
|
|
|
|
fn recreate_pipeline(self: &mut Self, device: Arc<Device>, render_pass: RP) {
|
|
self.pipeline = Self::create_pipeline(device, render_pass);
|
|
}
|
|
|
|
fn get_pipeline(self: &Self) -> &GP {
|
|
&self.pipeline
|
|
}
|
|
} |