1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

use std::collections::HashSet;
use std::path::PathBuf;
use vulkano::pipeline::GraphicsPipelineAbstract;
use vulkano::framebuffer::RenderPassAbstract;
use std::sync::Arc;
use vulkano::pipeline::shader::{ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode};
use vulkano::device::Device;
use shade_runner::Entry;
use shaderc::ShaderKind;
use crate::canvas::managed::handles::CompiledShaderHandle;
use vulkano::pipeline::vertex::Vertex;

/*

Realistically, what should the API for this thing look like...

It's going to just generate a pipeline. But that consists of loading and compiling various shaders,
    and generating a pipeline for those shaders and other customer behaviour.

This best works I think if I allow users to
    A.) impl from a base trait which allows resource lookup
    B.) Generate 1 of each of the types of shaders
    C.) Modify specilization constants, whatever that might mean
    D.) impl from a base trait which defines it's interface

*/



/// Inheriting this gives private functions to grab resources
pub trait CompiledShaderResources {

    fn get_path(filename: String, shader_type: ShaderType) -> PathBuf {
        let project_root =
            std::env::current_dir()
                .expect("failed to get root directory");

        let mut shader_path = project_root.clone();
        shader_path.push(PathBuf::from("resources/shaders/"));


        let mut shader_path = shader_path.clone();

        match shader_type {
            ShaderType::VERTEX => {
                shader_path.push(PathBuf::from(filename.clone() + ".vert"));
            }
            ShaderType::FRAGMENT => {
                shader_path.push(PathBuf::from(filename.clone() + ".frag"));
            }
            ShaderType::GEOMETRY => {
                shader_path.push(PathBuf::from(filename.clone() + ".geom"));
            }
            ShaderType::TESSELLATION_CONTROL => {
                shader_path.push(PathBuf::from(filename.clone() + ".tesscont"));
            }
            ShaderType::TESSELLATION_EVALUATION => {
                shader_path.push(PathBuf::from(filename.clone() + ".tesseval"));
            }
        }

        shader_path
    }


    fn compile(filepath: PathBuf, device: Arc<Device>, shader_type: ShaderType) -> (Entry, Arc<ShaderModule>) {
        let compiled_shader = shade_runner::load(filepath, Self::convert_sr(shader_type))
            .expect("Shader didn't compile");

        let vulkano_entry =
            shade_runner::parse(&compiled_shader)
                .expect("failed to parse");

        (vulkano_entry, unsafe {
            ShaderModule::from_words(device.clone(), &compiled_shader.spriv.clone())
        }.unwrap())
    }

    fn convert_vk(shader_type: ShaderType) -> GraphicsShaderType {
        match shader_type {
            ShaderType::VERTEX => { GraphicsShaderType::Vertex }
            ShaderType::FRAGMENT => { GraphicsShaderType::Fragment }
            ShaderType::GEOMETRY => { GraphicsShaderType::Geometry(GeometryShaderExecutionMode::Triangles) }
            ShaderType::TESSELLATION_CONTROL => { GraphicsShaderType::TessellationControl }
            ShaderType::TESSELLATION_EVALUATION => { GraphicsShaderType::TessellationEvaluation }
        }
    }

    fn convert_sr(shader_type: ShaderType) -> ShaderKind {
        match shader_type {
            ShaderType::VERTEX => { ShaderKind::Vertex }
            ShaderType::FRAGMENT => { ShaderKind::Fragment }
            ShaderType::GEOMETRY => { ShaderKind::Geometry }
            ShaderType::TESSELLATION_CONTROL => { ShaderKind::TessControl }
            ShaderType::TESSELLATION_EVALUATION => { ShaderKind::TessEvaluation }
        }
    }
}



pub trait CompiledShader {
    fn new<V>(filename: String,
           device: Arc<Device>,
           handle: Arc<CompiledShaderHandle>,
           render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> Self where Self: Sized, V: Vertex,;
    fn get_name(&self) -> String;
    fn get_handle(&self) -> Arc<CompiledShaderHandle>;
    fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send>;
    fn get_renderpass(&self) -> Arc<dyn RenderPassAbstract + Send + Sync>;
    fn recompile<V: Vertex>(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>)
                 -> Self where Self: Sized;
}

/// Legacy ShaderType enum for single type shaders.
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum ShaderType {
    VERTEX = 0,
    FRAGMENT = 1,
    GEOMETRY = 2,
    TESSELLATION_CONTROL = 3,
    TESSELLATION_EVALUATION = 4,
}