// 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::borrow::Cow; use std::sync::Arc; use vulkano::buffer as vb; use vulkano::descriptor::descriptor as vdD; use vulkano::descriptor::descriptor_set as vdd; use vulkano::descriptor::pipeline_layout as vdp; use vulkano::device as vd; use vulkano::format::Format; use vulkano::framebuffer as vf; use vulkano::memory as vm; use vulkano::pipeline as vp; use vulkano::pipeline::shader as vps; use vulkano::pipeline::vertex as vpv; use vulkano::sampler as vs; use crate::render::vulkan::data; use crate::render::vulkan::shaders; use crate::render::vulkan::pipeline; pub struct Forward { pipeline: Arc, descriptor_set_pool: vdd::FixedSizeDescriptorSetsPool, device: Arc, } impl Forward { pub fn new( device: Arc, viewport_dimensions: [u32; 2], render_pass: Arc, ) -> Forward { let vertex_shader = shaders::ShaderDefinition { name: "//assets/shaders/forward_vert.spv".to_string(), ty: vps::GraphicsShaderType::Vertex, inputs: vec![ vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("pos")), }, vps::ShaderInterfaceDefEntry { location: 1..2, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("normal")), }, vps::ShaderInterfaceDefEntry { location: 2..6, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("model")), }, vps::ShaderInterfaceDefEntry { location: 6..7, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("tex")), }, ], outputs: vec![ vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("fragTexCoord")), }, vps::ShaderInterfaceDefEntry { location: 1..2, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragWorldPos")), }, vps::ShaderInterfaceDefEntry { location: 2..3, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragNormal")), }, ], uniforms: vec![], push_constants: vec![ vdp::PipelineLayoutDescPcRange { offset: 0, size: 64usize, stages: vdD::ShaderStages { vertex: true, ..vdD::ShaderStages::none() }, }, ], }.load_into(device.clone()).expect("could not load vertex shader"); let fragment_shader = shaders::ShaderDefinition { name: "//assets/shaders/forward_frag.spv".to_string(), ty: vps::GraphicsShaderType::Fragment, inputs: vec![ vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32Sfloat, name: Some(Cow::Borrowed("fragTexCoord")), }, vps::ShaderInterfaceDefEntry { location: 1..2, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragWorldPos")), }, vps::ShaderInterfaceDefEntry { location: 2..3, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragNormal")), }, ], outputs: vec![ vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("outColor")), } ], uniforms: vec![ vdD::DescriptorDesc { ty: vdD::DescriptorDescTy::Buffer(vdD::DescriptorBufferDesc { dynamic: Some(false), storage: false, }), array_count: 1, readonly: true, stages: vdD::ShaderStages { fragment: true, ..vdD::ShaderStages::none() }, }, vdD::DescriptorDesc { ty: vdD::DescriptorDescTy::CombinedImageSampler(vdD::DescriptorImageDesc { sampled: true, dimensions: vdD::DescriptorImageDescDimensions::TwoDimensional, format: None, multisampled: false, array_layers: vdD::DescriptorImageDescArray::NonArrayed, }), array_count: 1, readonly: true, stages: vdD::ShaderStages { fragment: true, ..vdD::ShaderStages::none() }, }, vdD::DescriptorDesc { ty: vdD::DescriptorDescTy::CombinedImageSampler(vdD::DescriptorImageDesc { sampled: true, dimensions: vdD::DescriptorImageDescDimensions::TwoDimensional, format: None, multisampled: false, array_layers: vdD::DescriptorImageDescArray::NonArrayed, }), array_count: 1, readonly: true, stages: vdD::ShaderStages { fragment: true, ..vdD::ShaderStages::none() }, }, ], push_constants: vec![ ], }.load_into(device.clone()).expect("could not load fragment shader"); let dimensions = [viewport_dimensions[0] as f32, viewport_dimensions[1] as f32]; let viewport = vp::viewport::Viewport { origin: [0.0, 0.0], dimensions, depth_range: 0.0 .. 1.0, }; // Counter-clockwise facing triangles - this is because geometry data is left-handed, and // the vertex shader performs a handedness flip by doing .y *= -1 on emitted vertices. To // keep geomtry-space triangles clockwise after this transformation, the pipeline must be // set to treat counter-clockwise triangles as front-facing. An alternative would be to // fully embrace the vulkan coordinate system, including geometry - however this goes // against most existing software and practices. This might bite us in the ass at some // point in the future. let pipeline = Arc::new(vp::GraphicsPipeline::start() .vertex_input(vpv::OneVertexOneInstanceDefinition::::new()) .vertex_shader(vertex_shader.entry_point(), ()) .triangle_list() .primitive_restart(false) .viewports(vec![viewport]) .fragment_shader(fragment_shader.entry_point(), ()) .depth_clamp(false) .polygon_mode_fill() .line_width(1.0) .cull_mode_back() .front_face_counter_clockwise() .blend_pass_through() .depth_stencil(vulkano::pipeline::depth_stencil::DepthStencil::simple_depth_test()) .render_pass(vf::Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap()) as Arc; let layout = pipeline.descriptor_set_layout(0).unwrap(); let descriptor_set_pool = vdd::FixedSizeDescriptorSetsPool::new(layout.clone()); Forward { pipeline, descriptor_set_pool, device } } } impl pipeline::Pipeline for Forward { fn get_pipeline(&self) -> Arc { self.pipeline.clone() } fn make_descriptor_set( &mut self, textures: data::Textures, fragment_ubo_buffer: Arc>>, ) -> Arc { let image_sampler = vs::Sampler::simple_repeat_linear(self.device.clone()); Arc::new(self.descriptor_set_pool.next() .add_buffer(fragment_ubo_buffer).unwrap() .add_sampled_image(textures.diffuse.clone(), image_sampler.clone()).unwrap() .add_sampled_image(textures.roughness.clone(), image_sampler.clone()).unwrap() .build().unwrap()) } }