// Copyright 2020 Sergiusz 'q3k' Bazanski // // This file is part of Abrasion. // // Abrasion is free software: you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation, version 3. // // Abrasion is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // Abrasion. If not, see . use std::ffi::CStr; use std::fs::File; use std::io::prelude::*; use std::sync::Arc; use vulkano::descriptor::descriptor as vdd; use vulkano::descriptor::pipeline_layout as vdp; use vulkano::device as vd; use vulkano::pipeline::shader as vps; use crate::util::file; pub struct ShaderDefinition { pub name: String, pub ty: vps::GraphicsShaderType, pub inputs: Vec, pub outputs: Vec, pub uniforms: Vec, pub push_constants: Vec, } #[derive(Debug)] pub enum ShaderError { ResourceError(file::ResourceError), IOError(std::io::Error), } impl From for ShaderError { fn from(v: file::ResourceError) -> Self { ShaderError::ResourceError(v) } } impl From for ShaderError { fn from(v: std::io::Error) -> Self { ShaderError::IOError(v) } } type Result = std::result::Result; impl ShaderDefinition { pub fn load_into(self, device: Arc) -> Result { let mut r = file::resource(self.name.clone())?; let mut v = vec![]; r.read_to_end(&mut v)?; let module = unsafe { vps::ShaderModule::new(device.clone(), &v).unwrap() }; Ok(LoadedShader { def: self, module: module, }) } } pub struct LoadedShader { def: ShaderDefinition, module: Arc, } impl LoadedShader { fn ios(&self) -> (ShaderInterface, ShaderInterface) { ( ShaderInterface { entries: self.def.inputs.clone() }, ShaderInterface { entries: self.def.outputs.clone() }, ) } fn layout(&self) -> RuntimeShaderLayout { RuntimeShaderLayout{ descs: vec![self.def.uniforms.clone()], push_constants: self.def.push_constants.clone(), } } pub fn entry_point<'a, S>(&'a self) -> vps::GraphicsEntryPoint<'a, S, ShaderInterface, ShaderInterface, RuntimeShaderLayout> { let (input, output) = self.ios(); let layout = self.layout(); unsafe { self.module.graphics_entry_point( CStr::from_bytes_with_nul_unchecked(b"main\0"), input, output, layout, self.def.ty) } } } #[derive (Debug, Clone)] pub struct RuntimeShaderLayout { descs: Vec>, push_constants: Vec, } unsafe impl vdp::PipelineLayoutDesc for RuntimeShaderLayout { fn num_sets(&self) -> usize { self.descs.len() } fn num_bindings_in_set(&self, set: usize) -> Option { if set >= self.descs.len() { return None } Some(self.descs[set].len()) } fn descriptor(&self, set: usize, binding: usize) -> Option { if set >= self.descs.len() { return None } if binding >= self.descs[set].len() { return None } Some(self.descs[set][binding].clone()) } fn num_push_constants_ranges(&self) -> usize { self.push_constants.len() } fn push_constants_range(&self, num: usize) -> Option { if num >= self.push_constants.len() { return None } Some(self.push_constants[0].clone()) } } pub struct ShaderInterface { entries: Vec, } unsafe impl vps::ShaderInterfaceDef for ShaderInterface { type Iter = InterfaceIterator; fn elements(&self) -> Self::Iter { InterfaceIterator { entries: self.entries.clone(), } } } pub struct InterfaceIterator { entries: Vec, } impl std::iter::Iterator for InterfaceIterator { type Item = vps::ShaderInterfaceDefEntry; fn next(&mut self) -> Option { let cur = self.entries.first()?.clone(); self.entries = self.entries[1..].to_vec(); Some(cur) } fn size_hint(&self) -> (usize, Option) { let len = self.entries.len(); (len, Some(len)) } } impl std::iter::ExactSizeIterator for InterfaceIterator {}