Initial commit

This commit is contained in:
Mark Poliakov 2022-07-05 19:12:54 +03:00
commit f8a1c65b8c
9 changed files with 2077 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1448
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "yray"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.58"
bytemuck = "1.10.0"
bytemuck_derive = "1.1.0"
log = "0.4.17"
nalgebra-glm = "0.17.0"
simplelog = "^0.12.0"
vulkano = "0.29.0"
vulkano-shaders = "0.29.0"
vulkano-win = "0.29.0"
winit = "^0.26.1"

56
src/error.rs Normal file
View File

@ -0,0 +1,56 @@
use std::fmt::{Display, Formatter};
use vulkano::{
device::{physical::SurfacePropertiesError, DeviceCreationError},
image::view::ImageViewCreationError,
instance::InstanceCreationError,
render_pass::FramebufferCreationError,
swapchain::SwapchainCreationError,
};
#[derive(Debug)]
pub enum EngineError {
NoPhysicalDeviceFound,
SwapchainRecreateFailed,
VulkanInstanceError(InstanceCreationError),
VulkanDeviceError(DeviceCreationError),
WindowError(vulkano_win::CreationError),
VulkanSwapchainError(SwapchainCreationError),
VulkanSurfacePropertiesError(SurfacePropertiesError),
VulkanImageViewError(ImageViewCreationError),
VulkanFramebufferError(FramebufferCreationError),
}
impl Display for EngineError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
macro_rules! wrap {
($dst:ty, $src:ty, $variant:ident) => {
impl From<$src> for $dst {
fn from(e: $src) -> Self {
Self::$variant(e)
}
}
};
}
wrap!(EngineError, DeviceCreationError, VulkanDeviceError);
wrap!(EngineError, InstanceCreationError, VulkanInstanceError);
wrap!(EngineError, vulkano_win::CreationError, WindowError);
wrap!(
EngineError,
SurfacePropertiesError,
VulkanSurfacePropertiesError
);
wrap!(EngineError, SwapchainCreationError, VulkanSwapchainError);
wrap!(EngineError, ImageViewCreationError, VulkanImageViewError);
wrap!(
EngineError,
FramebufferCreationError,
VulkanFramebufferError
);
impl std::error::Error for EngineError {}

173
src/main.rs Normal file
View File

@ -0,0 +1,173 @@
extern crate nalgebra_glm as glm;
use std::time::Instant;
use bytemuck_derive::{Pod, Zeroable};
use glm::TMat4;
use render::event::RenderEvent;
use vulkano::{
buffer::{BufferUsage, CpuAccessibleBuffer, CpuBufferPool},
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, SubpassContents},
descriptor_set::{PersistentDescriptorSet, WriteDescriptorSet},
pipeline::{
graphics::{
input_assembly::InputAssemblyState, vertex_input::BuffersDefinition,
viewport::ViewportState,
},
GraphicsPipeline, Pipeline, PipelineBindPoint,
},
render_pass::Subpass,
sync::GpuFuture,
};
use winit::{
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
struct Vertex {
position: [f32; 3],
color: [f32; 3],
}
vulkano::impl_vertex!(Vertex, position, color);
pub mod error;
pub mod render;
use crate::render::{context::VulkanContext, shaders};
#[repr(C)]
#[derive(Default, Clone, Copy)]
struct ModelViewProjection {
model: TMat4<f32>,
view: TMat4<f32>,
projection: TMat4<f32>,
}
fn main() {
let mut mvp = ModelViewProjection {
model: glm::identity(),
..Default::default()
};
let event_loop = EventLoop::new();
let vk = VulkanContext::new_windowed(&event_loop, WindowBuilder::new()).unwrap();
let vs = shaders::vs::load(vk.device().clone()).unwrap();
let fs = shaders::fs::load(vk.device().clone()).unwrap();
let uniform_buffer: CpuBufferPool<shaders::vs::ty::MVP_Data> =
CpuBufferPool::uniform_buffer(vk.device().clone());
let pipeline = GraphicsPipeline::start()
.vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
.vertex_shader(vs.entry_point("main").unwrap(), ())
.input_assembly_state(InputAssemblyState::new())
.viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant())
.fragment_shader(fs.entry_point("main").unwrap(), ())
.render_pass(Subpass::from(vk.render_pass().clone(), 0).unwrap())
.build(vk.device().clone())
.unwrap();
let vertex_buffer = CpuAccessibleBuffer::from_iter(
vk.device().clone(),
BufferUsage::vertex_buffer(),
false,
[
Vertex {
position: [-1.0, -1.0, 0.0],
color: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.0, 1.0, 0.0],
color: [0.0, 1.0, 0.0],
},
Vertex {
position: [1.0, -1.0, 0.0],
color: [0.0, 0.0, 1.0],
},
]
.iter()
.cloned(),
)
.unwrap();
let rotation_start = Instant::now();
let viewport = vk.viewport();
mvp.projection = glm::perspective(
viewport.dimensions[0] / viewport.dimensions[1],
45.0,
0.01,
100.0,
);
vk.run(event_loop, move |event, ctl| match event {
RenderEvent::CloseRequested => {
*ctl = ControlFlow::Exit;
}
RenderEvent::ViewportResized(size) => {
mvp.projection = glm::perspective(
size.0 / size.1,
45.0,
0.01,
100.0,
);
}
}, move |vk, future, framebuffer| {
let clear_values = vec![[0.0, 0.68, 1.0, 1.0].into()];
let uniform_buffer_subbuffer = {
let elapsed = rotation_start.elapsed().as_secs() as f64
+ rotation_start.elapsed().subsec_nanos() as f64 / 1_000_000_000.0;
mvp.view = glm::look_at(
&glm::vec3(elapsed.cos() as f32 * 5.0, 5.0, elapsed.sin() as f32 * 5.0),
&glm::vec3(0.0, 0.0, 0.0),
&glm::vec3(0.0, 1.0, 0.0),
);
let uniform_data = shaders::vs::ty::MVP_Data {
model: mvp.model.into(),
view: mvp.view.into(),
projection: mvp.projection.into(),
};
uniform_buffer.next(uniform_data).unwrap()
};
let layout = pipeline.layout().set_layouts().get(0).unwrap();
let set = PersistentDescriptorSet::new(
layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
)
.unwrap();
let mut cmd_buffer_builder = AutoCommandBufferBuilder::primary(vk.device().clone(), vk.queue_family(), CommandBufferUsage::OneTimeSubmit)
.unwrap();
cmd_buffer_builder
.begin_render_pass(
framebuffer,
SubpassContents::Inline,
clear_values,
)
.unwrap()
.set_viewport(0, [vk.viewport().clone()])
.bind_pipeline_graphics(pipeline.clone())
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
pipeline.layout().clone(),
0,
set,
)
.bind_vertex_buffers(0, vertex_buffer.clone())
.draw(3, 1, 0, 0)
.unwrap()
.end_render_pass()
.unwrap();
let command_buffer = cmd_buffer_builder.build().unwrap();
future.then_execute(vk.queue().clone(), command_buffer).unwrap()
});
}

328
src/render/context.rs Normal file
View File

@ -0,0 +1,328 @@
use std::sync::Arc;
use vulkano::{
device::{
physical::{PhysicalDevice, PhysicalDeviceType, QueueFamily},
Device, DeviceCreateInfo, DeviceExtensions, Queue, QueueCreateInfo,
},
image::{view::ImageView, SwapchainImage},
instance::{Instance, InstanceCreateInfo},
pipeline::graphics::viewport::Viewport,
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass},
swapchain::{
self, AcquireError, Surface, Swapchain, SwapchainAcquireFuture, SwapchainCreateInfo,
SwapchainCreationError,
},
sync::{self, FlushError},
sync::{GpuFuture, JoinFuture},
Version,
};
use vulkano_win::VkSurfaceBuild;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
use crate::error::EngineError;
use super::event::RenderEvent;
pub struct VulkanContext {
#[allow(dead_code)]
instance: Arc<Instance>,
device: Arc<Device>,
queue: Arc<Queue>,
swapchain: Arc<Swapchain<Window>>,
swapchain_images: Vec<Arc<SwapchainImage<Window>>>,
render_pass: Arc<RenderPass>,
framebuffers: Vec<Arc<Framebuffer>>,
surface: Arc<Surface<Window>>,
viewport: Viewport,
}
impl VulkanContext {
pub fn new_windowed(
event_loop: &EventLoop<()>,
window_builder: WindowBuilder,
) -> Result<Self, EngineError> {
let required_extensions = vulkano_win::required_extensions();
let device_extensions = DeviceExtensions {
khr_swapchain: true,
..DeviceExtensions::none()
};
let instance = Instance::new(InstanceCreateInfo {
enabled_extensions: required_extensions,
max_api_version: Some(Version::V1_1),
..Default::default()
})?;
let surface = window_builder.build_vk_surface(event_loop, instance.clone())?;
let (physical, queue_family) =
Self::select_physical_device(&instance, surface.clone(), device_extensions)
.ok_or(EngineError::NoPhysicalDeviceFound)?;
let (device, mut queues) = Device::new(
physical,
DeviceCreateInfo {
enabled_extensions: physical.required_extensions().union(&device_extensions),
queue_create_infos: vec![QueueCreateInfo::family(queue_family)],
..Default::default()
},
)?;
let queue = queues.next().unwrap();
let dim = surface.window().inner_size();
let (swapchain, swapchain_images) = {
let caps = physical
.surface_capabilities(&surface, Default::default())
.unwrap();
let usage = caps.supported_usage_flags;
let alpha = caps.supported_composite_alpha.iter().next().unwrap();
let image_format = Some(physical.surface_formats(&surface, Default::default())?[0].0);
Swapchain::new(
device.clone(),
surface.clone(),
SwapchainCreateInfo {
min_image_count: caps.min_image_count,
image_format,
image_extent: dim.into(),
image_usage: usage,
composite_alpha: alpha,
..Default::default()
},
)?
};
let render_pass = vulkano::single_pass_renderpass!(
device.clone(),
attachments: {
color: {
load: Clear,
store: Store,
format: swapchain.image_format(),
samples: 1,
}
},
pass: {
color: [color],
depth_stencil: {}
}
)
.unwrap();
let framebuffers = Self::create_framebuffers(&swapchain_images, render_pass.clone())?;
let viewport = Viewport {
origin: [0.0, 0.0],
dimensions: dim.into(),
depth_range: 0.0..1.0,
};
Ok(Self {
instance,
surface,
device,
queue,
swapchain,
swapchain_images,
render_pass,
framebuffers,
viewport,
})
}
pub fn run<
T: GpuFuture + 'static,
EventHandler: 'static + FnMut(RenderEvent, &mut ControlFlow),
RenderHandler: 'static
+ FnMut(
&mut VulkanContext,
JoinFuture<Box<dyn GpuFuture>, SwapchainAcquireFuture<Window>>,
Arc<Framebuffer>,
) -> T,
>(
mut self,
event_loop: EventLoop<()>,
mut event_handler: EventHandler,
mut render_handler: RenderHandler,
) -> ! {
let mut recreate_swapchain = false;
let mut previous_frame_end =
Some(Box::new(sync::now(self.device().clone())) as Box<dyn GpuFuture>);
event_loop.run(move |event, _, ctl| {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
event_handler(RenderEvent::CloseRequested, ctl);
}
Event::WindowEvent {
event: WindowEvent::Resized(_),
..
} => {
recreate_swapchain = true;
}
Event::RedrawEventsCleared => {
previous_frame_end
.as_mut()
.take()
.unwrap()
.cleanup_finished();
if recreate_swapchain {
let size = self.recreate_swapchain().unwrap();
event_handler(RenderEvent::ViewportResized(size), ctl);
recreate_swapchain = false;
}
let (image_num, suboptimal, acquire_future) =
match swapchain::acquire_next_image(self.swapchain.clone(), None) {
Ok(r) => r,
Err(AcquireError::OutOfDate) => {
recreate_swapchain = true;
return;
}
Err(e) => panic!("Failed to acquire next image: {:?}", e),
};
if suboptimal {
recreate_swapchain = true;
}
let future = previous_frame_end.take().unwrap().join(acquire_future);
let framebuffer = self.framebuffers[image_num].clone();
let future = render_handler(&mut self, future, framebuffer)
.then_swapchain_present(
self.queue.clone(),
self.swapchain.clone(),
image_num,
)
.then_signal_semaphore_and_flush();
match future {
Ok(future) => {
previous_frame_end = Some(Box::new(future) as Box<_>);
}
Err(FlushError::OutOfDate) => {
recreate_swapchain = true;
previous_frame_end =
Some(Box::new(sync::now(self.device.clone())) as Box<_>);
}
Err(e) => {
println!("Failed to flush future: {:?}", e);
previous_frame_end =
Some(Box::new(sync::now(self.device.clone())) as Box<_>);
}
}
}
_ => (),
};
});
}
fn recreate_swapchain(&mut self) -> Result<(f32, f32), EngineError> {
let dim = self.surface.window().inner_size();
self.viewport.dimensions = dim.into();
let (new_swapchain, new_images) = match self.swapchain.recreate(SwapchainCreateInfo {
image_extent: dim.into(),
..self.swapchain.create_info()
}) {
Ok(r) => r,
Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => {
return Err(EngineError::SwapchainRecreateFailed);
}
Err(e) => panic!("Failed to recreate swapchain: {:?}", e),
};
self.swapchain = new_swapchain;
self.framebuffers = Self::create_framebuffers(&new_images, self.render_pass.clone())?;
self.swapchain_images = new_images;
Ok(dim.into())
}
pub fn device(&self) -> &Arc<Device> {
&self.device
}
pub fn render_pass(&self) -> &Arc<RenderPass> {
&self.render_pass
}
pub fn viewport(&self) -> &Viewport {
&self.viewport
}
pub fn swapchain(&self) -> &Arc<Swapchain<Window>> {
&self.swapchain
}
pub fn queue_family(&self) -> QueueFamily {
self.queue.family()
}
pub fn framebuffers(&self) -> &[Arc<Framebuffer>] {
&self.framebuffers
}
pub fn queue(&self) -> &Arc<Queue> {
&self.queue
}
fn select_physical_device(
instance: &Arc<Instance>,
surface: Arc<Surface<Window>>,
device_extensions: DeviceExtensions,
) -> Option<(PhysicalDevice, QueueFamily)> {
PhysicalDevice::enumerate(instance)
.filter(|&p| p.supported_extensions().is_superset_of(&device_extensions))
.filter_map(|p| {
p.queue_families()
.find(|&q| {
q.supports_graphics() && q.supports_surface(&surface).unwrap_or(false)
})
.map(|q| (p, q))
})
.min_by_key(|(p, _)| match p.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1,
PhysicalDeviceType::VirtualGpu => 2,
PhysicalDeviceType::Cpu => 3,
PhysicalDeviceType::Other => 4,
})
}
fn create_single_framebuffer(
image: Arc<SwapchainImage<Window>>,
render_pass: Arc<RenderPass>,
) -> Result<Arc<Framebuffer>, EngineError> {
let view = ImageView::new_default(image)?;
let res = Framebuffer::new(
render_pass,
FramebufferCreateInfo {
attachments: vec![view],
..Default::default()
},
)?;
Ok(res)
}
fn create_framebuffers(
images: &[Arc<SwapchainImage<Window>>],
render_pass: Arc<RenderPass>,
) -> Result<Vec<Arc<Framebuffer>>, EngineError> {
images
.iter()
.map(|image| Self::create_single_framebuffer(image.clone(), render_pass.clone()))
.collect()
}
}

5
src/render/event.rs Normal file
View File

@ -0,0 +1,5 @@
pub enum RenderEvent {
ViewportResized((f32, f32)),
CloseRequested,
}

3
src/render/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod context;
pub mod shaders;
pub mod event;

45
src/render/shaders.rs Normal file
View File

@ -0,0 +1,45 @@
#![allow(clippy::needless_question_mark)]
pub mod vs {
vulkano_shaders::shader! {
ty: "vertex",
src: "
#version 450
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 out_color;
layout(set = 0, binding = 0) uniform MVP_Data {
mat4 model;
mat4 view;
mat4 projection;
} uniforms;
void main() {
mat4 worldview = uniforms.view * uniforms.model;
gl_Position = uniforms.projection * worldview * vec4(position, 1.0);
out_color = color;
}",
types_meta: {
use bytemuck::{Pod, Zeroable};
#[derive(Clone, Copy, Zeroable, Pod)]
},
}
}
pub mod fs {
vulkano_shaders::shader! {
ty: "fragment",
src: "
#version 450
layout(location = 0) in vec3 in_color;
layout(location = 0) out vec4 f_color;
void main() {
f_color = vec4(in_color, 1.0);
}"
}
}