refactor: renderable

ecs
q3k 2020-01-26 01:57:15 +01:00
parent 8b82aeac1c
commit 86e5d03a91
9 changed files with 262 additions and 206 deletions

View File

@ -6,9 +6,11 @@ rust_binary(
srcs = [
"src/main.rs",
"src/render/mod.rs",
"src/render/renderable.rs",
"src/render/vulkan/mod.rs",
"src/render/vulkan/binding.rs",
"src/render/vulkan/data.rs",
"src/render/vulkan/pipeline.rs",
"src/render/vulkan/qfi.rs",
"src/render/vulkan/shaders.rs",
"src/render/vulkan/swapchains.rs",

View File

@ -1,12 +1,35 @@
use log;
use env_logger;
use std::sync::Arc;
use cgmath as cgm;
mod render;
mod util;
use render::vulkan::data;
use render::renderable::Renderable;
fn main() {
env_logger::init();
log::info!("Starting...");
let elapsed = 0.0;
let transform = cgm::Matrix4::from_angle_z(cgm::Rad::from(cgm::Deg(elapsed as f32 * 0.180)));
let vertices = Arc::new(vec![
data::Vertex::new([-0.5, -0.5, 0.0], [1.0, 0.0, 0.0]),
data::Vertex::new([0.5, -0.5, 0.0], [0.0, 1.0, 0.0]),
data::Vertex::new([0.5, 0.5, 0.0], [0.0, 0.0, 1.0]),
data::Vertex::new([-0.5, 0.5, 0.0], [1.0, 1.0, 1.0])
]);
let indices = Arc::new(vec![
0, 1, 2, 2, 3, 0,
]);
let demo = render::renderable::Mesh {
transform, vertices, indices
};
let mut renderer = render::Renderer::initialize();
renderer.set_render_data(vec![demo.data().unwrap()]);
renderer.main_loop();
}

View File

@ -12,7 +12,8 @@ use vulkano_win::VkSurfaceBuild;
use vulkano::instance as vi;
use vulkano::swapchain as vs;
mod vulkan;
pub mod vulkan;
pub mod renderable;
const WIDTH: u32 = 800;
const HEIGHT: u32 = 600;
@ -20,6 +21,8 @@ const HEIGHT: u32 = 600;
pub struct Renderer {
instance: vulkan::Instance<winit::Window>,
events_loop: EventsLoop,
render_data: Vec<renderable::Data>,
}
impl Renderer {
@ -31,9 +34,14 @@ impl Renderer {
Self {
instance,
events_loop,
render_data: vec![],
}
}
pub fn set_render_data(&mut self, render_data: Vec<renderable::Data>) {
self.render_data = render_data;
}
fn init_window(instance: Arc<vi::Instance>) -> (EventsLoop, Arc<vs::Surface<Window>>) {
let events_loop = EventsLoop::new();
let surface = WindowBuilder::new()
@ -45,7 +53,7 @@ impl Renderer {
}
fn draw_frame(&mut self) {
self.instance.flip();
self.instance.flip(self.render_data.clone());
}
pub fn main_loop(&mut self) {

View File

@ -0,0 +1,34 @@
use std::sync::Arc;
use cgmath as cgm;
use crate::render::vulkan::data;
pub trait Renderable {
fn data(&self) -> Option<Data> {
None
}
}
#[derive(Clone)]
pub struct Data {
pub vertices: Arc<Vec<data::Vertex>>,
pub indices: Arc<Vec<u16>>,
pub transform: cgm::Matrix4<f32>,
}
pub struct Mesh {
pub vertices: Arc<Vec<data::Vertex>>,
pub indices: Arc<Vec<u16>>,
pub transform: cgm::Matrix4<f32>,
}
impl Renderable for Mesh {
fn data(&self) -> Option<Data> {
Some(Data {
vertices: self.vertices.clone(),
indices: self.indices.clone(),
transform: self.transform.clone(),
})
}
}

View File

@ -13,19 +13,6 @@ impl Vertex {
}
vulkano::impl_vertex!(Vertex, pos, color);
pub fn vertices() -> [Vertex; 4] {
[
Vertex::new([-0.5, -0.5, 0.0], [1.0, 0.0, 0.0]),
Vertex::new([0.5, -0.5, 0.0], [0.0, 1.0, 0.0]),
Vertex::new([0.5, 0.5, 0.0], [0.0, 0.0, 1.0]),
Vertex::new([-0.5, 0.5, 0.0], [1.0, 1.0, 1.0])
]
}
pub fn indices() -> [u16; 6] {
[0, 1, 2, 2, 3, 0]
}
#[derive(Copy, Clone)]
pub struct UniformBufferObject {
pub model: cgm::Matrix4<f32>,

View File

@ -1,23 +1,23 @@
use std::sync::Arc;
use std::sync::Mutex;
use std::time;
use log;
use cgmath as cgm;
use cgmath::prelude::SquareMatrix;
use vulkano::buffer as vb;
use vulkano::command_buffer as vc;
use vulkano::descriptor::descriptor_set as vdd;
use vulkano::framebuffer as vf;
use vulkano::instance as vi;
use vulkano::pipeline as vp;
use vulkano::swapchain as vs;
use vulkano::sync::{FenceSignalFuture, GpuFuture};
mod binding;
mod data;
pub mod data;
mod pipeline;
mod qfi;
mod shaders;
mod swapchains;
mod qfi;
use crate::render::renderable;
const VERSION: vi::Version = vi::Version { major: 1, minor: 0, patch: 0};
@ -33,10 +33,8 @@ pub struct Instance<WT> {
surface_binding: Option<binding::SurfaceBinding<WT>>,
swapchain_binding: Option<swapchains::SwapchainBinding<WT>>,
command_buffers: Vec<Arc<vc::AutoCommandBuffer>>,
uniform_buffers: Vec<Arc<vb::CpuAccessibleBuffer<data::UniformBufferObject>>>,
descriptor_sets: Vec<Arc<vdd::FixedSizeDescriptorSet<Arc<dyn vp::GraphicsPipelineAbstract + Send + Sync>, ((), vdd::PersistentDescriptorSetBuf<Arc<vb::CpuAccessibleBuffer<data::UniformBufferObject>>>)>>>,
pipeline: Option<Box<dyn pipeline::Pipeline>>,
armed: bool,
previous_frame_end: Option<Box<FlipFuture<WT>>>,
fps_counter: crate::util::counter::Counter,
@ -66,10 +64,8 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
surface_binding: None,
swapchain_binding: None,
command_buffers: vec![],
uniform_buffers: vec![],
descriptor_sets: vec![],
pipeline: None,
previous_frame_end: None,
armed: false,
fps_counter: crate::util::counter::Counter::new(time::Duration::from_millis(1000)),
@ -102,33 +98,18 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
let chain = self.swapchain_binding().chain.clone();
let render_pass = self.swapchain_binding().render_pass.clone();
let pipeline = shaders::pipeline_forward(device.clone(), chain.dimensions(), render_pass);
let (vbuffer, future) = vb::immutable::ImmutableBuffer::from_iter(
data::vertices().iter().cloned(),
vb::BufferUsage::vertex_buffer(),
self.surface_binding().graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
let (ibuffer, future) = vb::immutable::ImmutableBuffer::from_iter(
data::indices().iter().cloned(),
vb::BufferUsage::index_buffer(),
self.surface_binding().graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
self.create_uniform_buffers();
self.create_descriptor_sets(pipeline.clone());
self.create_command_buffers(pipeline.clone(), vbuffer, ibuffer);
self.pipeline = Some(Box::new(pipeline::Forward::new(device.clone(), chain.dimensions(), render_pass)));
self.previous_frame_end = None;
self.armed = true;
}
// (╯°□°)╯︵ ┻━┻
pub fn flip(&mut self) {
pub fn flip(
&mut self,
render_data: Vec<renderable::Data>,
) {
match &self.previous_frame_end {
None => (),
Some(future) => future.wait(None).unwrap(),
@ -148,7 +129,9 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
},
Err(err) => panic!("{:?}", err),
};
let command_buffer = self.command_buffers[image_index].clone();
let fb = self.swapchain_binding().framebuffers[image_index].clone();
let command_buffer = self.make_command_buffer(fb, render_data);
let gq = self.surface_binding().graphics_queue.clone();
let pq = self.surface_binding().present_queue.clone();
@ -176,103 +159,69 @@ impl<WT: 'static + Send + Sync> Instance<WT> {
}
}
fn create_descriptor_sets(
&mut self,
pipeline: Arc<dyn vp::GraphicsPipelineAbstract + Send + Sync>
) {
let pool = Arc::new(
Mutex::new(
vdd::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0)
)
);
self.descriptor_sets = self.uniform_buffers
.iter()
.map(|uniform_buffer|
Arc::new(
pool
.lock()
.unwrap()
.next()
.add_buffer(uniform_buffer.clone())
.unwrap()
.build()
.unwrap()
)
)
.collect();
}
fn dimensions(&self) -> [f32; 2] {
let dimensions_u32 = self.swapchain_binding().chain.dimensions();
[dimensions_u32[0] as f32, dimensions_u32[1] as f32]
}
fn build_uniform_buffer(&self) -> data::UniformBufferObject {
let elapsed = 40.0 as f32;
fn make_command_buffer(
&mut self,
framebuffer: Arc<dyn vf::FramebufferAbstract + Send + Sync>,
render_data: Vec<renderable::Data>,
) -> Arc<vc::AutoCommandBuffer> {
let device = self.surface_binding().device.clone();
let qf = self.surface_binding().graphics_queue.family();
let dimensions = self.dimensions();
let mut c = vc::AutoCommandBufferBuilder::primary_simultaneous_use(device.clone(), qf)
.unwrap()
.begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()])
.unwrap();
let model = cgm::Matrix4::from_angle_z(cgm::Rad::from(cgm::Deg(elapsed as f32 * 0.180)));
let view = cgm::Matrix4::look_at(
cgm::Point3::new(2.0, 2.0, 2.0),
cgm::Point3::new(0.0, 0.0, 0.0),
cgm::Vector3::new(0.0, 0.0, 1.0)
);
let mut proj = cgm::perspective(
let proj = cgm::perspective(
cgm::Rad::from(cgm::Deg(45.0)),
dimensions[0] as f32 / dimensions[1] as f32,
dimensions[0] / dimensions[1],
0.1,
10.0
);
log::info!("model: {:?}", model);
log::info!(" view: {:?}", view);
log::info!(" proj: {:?}", proj);
data::UniformBufferObject { model, view, proj }
}
fn create_uniform_buffers(&mut self) {
let mut buffers = Vec::new();
let uniform_buffer = self.build_uniform_buffer();
for _ in 0..self.swapchain_binding().images.len() {
let buffer = vb::CpuAccessibleBuffer::from_data(
self.surface_binding().device.clone(),
vb::BufferUsage::uniform_buffer_transfer_destination(),
uniform_buffer,
for d in render_data {
let (vbuffer, future) = vb::immutable::ImmutableBuffer::from_iter(
d.vertices.iter().cloned(),
vb::BufferUsage::vertex_buffer(),
self.surface_binding().graphics_queue.clone(),
).unwrap();
buffers.push(buffer);
future.flush().unwrap();
let (ibuffer, future) = vb::immutable::ImmutableBuffer::from_iter(
d.indices.iter().cloned(),
vb::BufferUsage::index_buffer(),
self.surface_binding().graphics_queue.clone(),
).unwrap();
future.flush().unwrap();
let ubo = data::UniformBufferObject {
model: d.transform.clone(),
view: view.clone(),
proj: proj.clone(),
};
let ds = self.pipeline.as_mut().unwrap().make_descriptor_set(ubo);
let pipeline = self.pipeline.as_ref().unwrap().get_pipeline();
c = c.draw_indexed(pipeline, &vc::DynamicState::none(),
vec![vbuffer.clone()],
ibuffer.clone(),
ds,
()).unwrap();
}
self.uniform_buffers = buffers;
}
fn create_command_buffers(
&mut self,
pipeline: Arc<dyn vp::GraphicsPipelineAbstract + Send + Sync>,
vertex_buffer: Arc<dyn vb::BufferAccess + Send + Sync>,
index_buffer: Arc<dyn vb::TypedBufferAccess<Content=[u16]> + Send + Sync>,
) {
let device = self.surface_binding().device.clone();
let qf = self.surface_binding().graphics_queue.family();
self.command_buffers = self.swapchain_binding().framebuffers.iter()
.enumerate()
.map(|(i, framebuffer)| {
Arc::new(vc::AutoCommandBufferBuilder::primary_simultaneous_use(device.clone(), qf)
.unwrap()
.begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()])
.unwrap()
.draw_indexed(pipeline.clone(), &vc::DynamicState::none(),
vec![vertex_buffer.clone()],
index_buffer.clone(),
self.descriptor_sets[i].clone(),
())
.unwrap()
.end_render_pass()
.unwrap()
.build()
.unwrap())
})
.collect();
Arc::new(c.end_render_pass().unwrap()
.build().unwrap())
}

View File

@ -0,0 +1,118 @@
use std::borrow::Cow;
use std::sync::Arc;
use vulkano::buffer as vb;
use vulkano::descriptor::descriptor_set as vdd;
use vulkano::device as vd;
use vulkano::format::Format;
use vulkano::framebuffer as vf;
use vulkano::pipeline as vp;
use vulkano::pipeline::shader as vps;
use crate::render::vulkan::data;
use crate::render::vulkan::shaders;
type VulkanoPipeline = dyn vp::GraphicsPipelineAbstract + Send + Sync;
type VulkanoDescriptorSet = dyn vdd::DescriptorSet + Send + Sync;
pub trait Pipeline {
fn get_pipeline(&self) -> Arc<VulkanoPipeline>;
fn make_descriptor_set(&mut self, ubo: data::UniformBufferObject) -> Arc<VulkanoDescriptorSet>;
}
pub struct Forward {
device: Arc<vd::Device>,
pipeline: Arc<VulkanoPipeline>,
descriptor_set_pool: vdd::FixedSizeDescriptorSetsPool<Arc<VulkanoPipeline>>,
}
impl Forward {
pub fn new(
device: Arc<vd::Device>,
viewport_dimensions: [u32; 2],
render_pass: Arc<dyn vf::RenderPassAbstract + Send + Sync>,
) -> Forward {
let vertex_shader = shaders::ShaderDefinition {
name: "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("color")) },
],
outputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) }
],
}.load_into(device.clone()).expect("could not load vertex shader");
let fragment_shader = shaders::ShaderDefinition {
name: "forward_frag.spv".to_string(),
ty: vps::GraphicsShaderType::Fragment,
inputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) }
],
outputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("outColor")) }
],
}.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_single_buffer::<data::Vertex>()
.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()
.render_pass(vf::Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap())
as Arc<VulkanoPipeline>;
let descriptor_set_pool = vdd::FixedSizeDescriptorSetsPool::new(pipeline.clone(), 0);
Forward {
device,
pipeline,
descriptor_set_pool
}
}
}
impl Pipeline for Forward {
fn get_pipeline(&self) -> Arc<VulkanoPipeline> {
self.pipeline.clone()
}
fn make_descriptor_set(&mut self, ubo: data::UniformBufferObject) -> Arc<VulkanoDescriptorSet> {
let buffer = vb::CpuAccessibleBuffer::from_data(
self.device.clone(),
vb::BufferUsage::uniform_buffer_transfer_destination(),
ubo,
).unwrap();
Arc::new(self.descriptor_set_pool.next()
.add_buffer(buffer).unwrap()
.build().unwrap())
}
}

View File

@ -1,5 +1,4 @@
use log;
use std::borrow::Cow;
use std::ffi::CStr;
use std::fs::File;
use std::io::prelude::*;
@ -9,81 +8,17 @@ use runfiles::Runfiles;
use vulkano::descriptor::descriptor 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::pipeline as vp;
use vulkano::pipeline::shader as vps;
pub fn pipeline_forward(
device: Arc<vd::Device>,
swap_chain_extent: [u32; 2],
render_pass: Arc<dyn vf::RenderPassAbstract + Send + Sync>,
) -> Arc<dyn vp::GraphicsPipelineAbstract + Send + Sync> {
let vertex = ShaderDefinition {
name: "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("color")) },
],
outputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) }
],
}.load_into(device.clone()).expect("could not load vertex shader");
let fragment = ShaderDefinition {
name: "forward_frag.spv".to_string(),
ty: vps::GraphicsShaderType::Fragment,
inputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32Sfloat, name: Some(Cow::Borrowed("fragColor")) }
],
outputs: vec![
vps::ShaderInterfaceDefEntry { location: 0..1, format: Format::R32G32B32A32Sfloat, name: Some(Cow::Borrowed("outColor")) }
],
}.load_into(device.clone()).expect("could not load fragment shader");
let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[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.
Arc::new(vp::GraphicsPipeline::start()
.vertex_input_single_buffer::<super::data::Vertex>()
.vertex_shader(vertex.entry_point(), ())
.triangle_list()
.primitive_restart(false)
.viewports(vec![viewport])
.fragment_shader(fragment.entry_point(), ())
.depth_clamp(false)
.polygon_mode_fill()
.line_width(1.0)
.cull_mode_back()
.front_face_counter_clockwise()
.blend_pass_through()
.render_pass(vf::Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap()
)
}
struct ShaderDefinition {
name: String,
ty: vps::GraphicsShaderType,
inputs: Vec<vps::ShaderInterfaceDefEntry>,
outputs: Vec<vps::ShaderInterfaceDefEntry>,
pub struct ShaderDefinition {
pub name: String,
pub ty: vps::GraphicsShaderType,
pub inputs: Vec<vps::ShaderInterfaceDefEntry>,
pub outputs: Vec<vps::ShaderInterfaceDefEntry>,
}
impl ShaderDefinition {
fn load_into(self, device: Arc<vd::Device>) -> Result<LoadedShader, String> {
pub fn load_into(self, device: Arc<vd::Device>) -> Result<LoadedShader, String> {
fn stringify(x: std::io::Error) -> String { format!("IO error: {}", x) }
let r = Runfiles::create().map_err(stringify)?;
@ -106,7 +41,7 @@ impl ShaderDefinition {
}
}
struct LoadedShader {
pub struct LoadedShader {
def: ShaderDefinition,
module: Arc<vps::ShaderModule>,
}
@ -143,7 +78,7 @@ impl LoadedShader {
}
#[derive (Debug, Clone)]
struct ShaderLayout(vdd::ShaderStages);
pub struct ShaderLayout(vdd::ShaderStages);
unsafe impl vdp::PipelineLayoutDesc for ShaderLayout {
fn num_sets(&self) -> usize { 1 }
@ -177,7 +112,7 @@ unsafe impl vdp::PipelineLayoutDesc for ShaderLayout {
fn push_constants_range(&self, _num: usize) -> Option<vdp::PipelineLayoutDescPcRange> { None }
}
struct ShaderInterface {
pub struct ShaderInterface {
entries: Vec<vps::ShaderInterfaceDefEntry>,
}

View File

@ -123,14 +123,14 @@ impl<WT: 'static + Send + Sync> SwapchainBinding<WT> {
}
fn choose_swap_present_mode(available_present_modes: vs::SupportedPresentModes) -> vs::PresentMode {
//if available_present_modes.mailbox {
// vs::PresentMode::Mailbox
//} else if available_present_modes.immediate {
// vs::PresentMode::Immediate
//} else {
// vs::PresentMode::Fifo
//}
vs::PresentMode::Fifo
if available_present_modes.mailbox {
vs::PresentMode::Mailbox
} else if available_present_modes.immediate {
vs::PresentMode::Immediate
} else {
vs::PresentMode::Fifo
}
//vs::PresentMode::Fifo
}
fn choose_swap_extent(capabilities: &vs::Capabilities) -> [u32; 2] {