Basic mesh rendering

This commit is contained in:
2025-04-25 12:04:11 +03:00
commit d57cb33b98
12 changed files with 3962 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
Generated
+2930
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "gaym"
version = "0.1.0"
edition = "2024"
[dependencies]
bytemuck = { version = "1.22.0", features = ["derive"] }
env_logger = "0.11.8"
glam = { version = "0.30.2", features = ["bytemuck"] }
log = "0.4.27"
thiserror = "2.0.12"
vulkano = "0.35.1"
vulkano-shaders = "0.35.0"
vulkano-win = "0.34.0"
winit = "0.30.9"
+55
View File
@@ -0,0 +1,55 @@
use vulkano::{
buffer::AllocateBufferError, command_buffer::CommandBufferExecError, image::AllocateImageError,
memory::allocator::MemoryAllocatorError, pipeline::layout::IntoPipelineLayoutCreateInfoError,
swapchain::FromWindowError, sync::HostAccessError, LoadingError, Validated, ValidationError,
VulkanError,
};
use winit::raw_window_handle::HandleError;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Event loop error: {0}")]
WinitEventLoop(#[from] winit::error::EventLoopError),
#[error("Window creation error: {0}")]
WinitOs(#[from] winit::error::OsError),
#[error("Vulkan loading error: {0}")]
Loading(#[from] LoadingError),
#[error("Window handle error: {0}")]
HandleError(#[from] HandleError),
#[error("Vulkan error: {0}")]
Vulkan(#[from] VulkanError),
#[error("Buffer allocation error: {0}")]
BufferAllocation(#[from] AllocateBufferError),
#[error("Command buffer execution error: {0}")]
CommandBufferExec(#[from] CommandBufferExecError),
#[error("Cannot select a physical device")]
NoValidDevice,
#[error("Vulkan surface creation error: {0}")]
FromWindow(#[from] FromWindowError),
#[error("Pipeline layout create error: {0}")]
IntoPipelineLayoutCreateInfo(#[from] IntoPipelineLayoutCreateInfoError),
#[error("Host buffer access error: {0}")]
HostAccess(#[from] HostAccessError),
#[error("Image allocation error: {0}")]
AllocateImage(#[from] AllocateImageError),
#[error("GPU memory allocation error: {0}")]
MemoryAllocator(#[from] MemoryAllocatorError),
}
impl From<Box<ValidationError>> for Error {
fn from(value: Box<ValidationError>) -> Self {
log::error!("Validation error: {value}");
panic!();
}
}
impl<T> From<Validated<T>> for Error
where
Error: From<T>,
{
fn from(value: Validated<T>) -> Self {
Error::from(value.unwrap())
}
}
+142
View File
@@ -0,0 +1,142 @@
use std::{process::ExitCode, sync::Arc};
use error::Error;
use render::Renderer;
use winit::{
application::ApplicationHandler,
dpi::PhysicalSize,
event::{DeviceEvent, DeviceId, StartCause, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
};
pub mod error;
pub mod render;
#[derive(Default)]
pub struct Engine {
window: Option<Arc<Window>>,
renderer: Option<Renderer>,
}
impl Engine {
pub fn ensure_renderer(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Error> {
if self.window.is_none() {
let window_attributes = Window::default_attributes()
.with_resizable(false)
.with_inner_size(PhysicalSize::new(800, 600));
let window = Arc::new(event_loop.create_window(window_attributes)?);
let render = Renderer::from_window(window.clone())?;
self.window = Some(window);
self.renderer = Some(render);
}
Ok(())
}
}
impl ApplicationHandler for Engine {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if let Err(error) = self.ensure_renderer(event_loop) {
log::error!("Renderer setup error: {error}");
event_loop.exit();
}
}
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
log::info!("Exiting");
self.renderer = None;
}
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
let _ = event_loop;
let _ = cause;
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: ()) {
let _ = event_loop;
let _ = event;
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let _ = window_id;
match event {
WindowEvent::CloseRequested => {
log::info!("Close requested");
event_loop.exit();
}
WindowEvent::Resized(size) => {
log::info!("Window resized: {size:?}");
if let Some(render) = self.renderer.as_mut() {
render.set_swapchain_dirty();
}
}
WindowEvent::RedrawRequested => {
if let Some(render) = self.renderer.as_mut() {
if let Err(error) = render.render() {
log::error!("Render error: {error}");
event_loop.exit();
}
}
}
_ => (),
}
}
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
let _ = event_loop;
let _ = device_id;
let _ = event;
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
}
fn run() -> Result<(), Error> {
let event_loop = EventLoop::new()?;
let mut engine = Engine::default();
event_loop.run_app(&mut engine)?;
Ok(())
}
fn main() -> ExitCode {
env_logger::init();
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
log::error!("Exited with error: {error}");
ExitCode::FAILURE
}
}
}
+128
View File
@@ -0,0 +1,128 @@
use std::sync::Arc;
use glam::Vec3;
use vulkano::{
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
};
use crate::error::Error;
use super::{util::Allocators, vertex::InputVertex};
#[derive(Default)]
pub struct MeshBuilder {
vertices: Vec<InputVertex>,
}
impl MeshBuilder {
pub fn new() -> Self {
Self {
vertices: Vec::new(),
}
}
pub fn add_cube(&mut self, x: i32, y: i32, z: i32) {
let position = Vec3::new(x as f32, y as f32, z as f32);
// Front
self.add_face(
position,
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
);
// Left
self.add_face(
position,
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.0, 1.0, 0.0),
);
// Back
self.add_face(
position,
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(1.0, 0.0, 1.0),
Vec3::new(1.0, 1.0, 1.0),
Vec3::new(0.0, 1.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
);
// Right
self.add_face(
position,
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(1.0, 1.0, 1.0),
Vec3::new(1.0, 0.0, 1.0),
Vec3::new(1.0, 1.0, 0.0),
);
// Bottom
self.add_face(
position,
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(1.0, 0.0, 1.0),
);
// Top
self.add_face(
position,
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(1.0, 1.0, 0.0),
Vec3::new(1.0, 1.0, 1.0),
Vec3::new(0.0, 1.0, 1.0),
Vec3::new(0.0, 1.0, 1.0),
);
}
fn add_face(&mut self, d: Vec3, p0: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, c: Vec3) {
self.vertices.push(InputVertex {
i_position: p0 + d,
i_color: c,
});
self.vertices.push(InputVertex {
i_position: p1 + d,
i_color: c,
});
self.vertices.push(InputVertex {
i_position: p2 + d,
i_color: c,
});
self.vertices.push(InputVertex {
i_position: p2 + d,
i_color: c,
});
self.vertices.push(InputVertex {
i_position: p3 + d,
i_color: c,
});
self.vertices.push(InputVertex {
i_position: p0 + d,
i_color: c,
});
}
pub fn upload(self, allocators: &Arc<Allocators>) -> Result<Subbuffer<[InputVertex]>, Error> {
let buffer = Buffer::from_iter(
allocators.memory.clone(),
BufferCreateInfo {
usage: BufferUsage::VERTEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::HOST_SEQUENTIAL_WRITE
| MemoryTypeFilter::PREFER_DEVICE,
..Default::default()
},
self.vertices.into_iter(),
)?;
Ok(buffer)
}
}
+340
View File
@@ -0,0 +1,340 @@
use std::{sync::Arc, time::Instant};
use glam::{Mat4, Vec3};
use mesh::MeshBuilder;
use util::{Allocators, ViewportExt};
use vertex::InputVertex;
use vulkano::{
buffer::{
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
BufferUsage, Subbuffer,
},
command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, RenderPassBeginInfo, SubpassBeginInfo,
SubpassContents, SubpassEndInfo,
},
descriptor_set::{DescriptorSet, WriteDescriptorSet},
device::{Device, DeviceExtensions, Queue},
format::Format,
image::Image,
instance::{InstanceCreateFlags, InstanceCreateInfo},
memory::allocator::MemoryTypeFilter,
pipeline::{graphics::viewport::Viewport, GraphicsPipeline, Pipeline, PipelineBindPoint},
render_pass::{Framebuffer, RenderPass},
shader::EntryPoint,
swapchain::{self, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo},
sync::{self, GpuFuture},
Validated, VulkanError,
};
use winit::window::Window;
use crate::error::Error;
pub mod mesh;
pub mod shaders;
pub mod util;
pub mod vertex;
pub const DEPTH_FORMAT: Format = Format::D32_SFLOAT;
pub struct Renderer {
window: Arc<Window>,
device: Arc<Device>,
queue: Arc<Queue>,
allocators: Arc<Allocators>,
swapchain: Arc<Swapchain>,
swapchain_images: Vec<Arc<Image>>,
swapchain_dirty: bool,
vertex_buffer: Subbuffer<[InputVertex]>,
uniform_allocator: SubbufferAllocator,
render_pass: Arc<RenderPass>,
viewport: Viewport,
framebuffers: Vec<Arc<Framebuffer>>,
vertex_shader: EntryPoint,
fragment_shader: EntryPoint,
pipeline: Arc<GraphicsPipeline>,
start: Instant,
}
impl Renderer {
pub fn from_window(window: Arc<Window>) -> Result<Self, Error> {
let instance_extensions = Surface::required_extensions(&window)?;
let device_extensions = DeviceExtensions {
khr_swapchain: true,
khr_maintenance1: true,
..Default::default()
};
let instance = util::create_instance(InstanceCreateInfo {
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
enabled_extensions: instance_extensions,
..Default::default()
})?;
let surface = Surface::from_window(instance.clone(), window.clone())?;
let (device, queue) = util::create_device(&instance, device_extensions, &surface)?;
let dimensions = window.inner_size();
let (swapchain, swapchain_images) = util::create_swapchain(&device, &surface, dimensions)?;
let allocators = Allocators::new_default(device.clone())?;
let vertex_buffer = {
let mut builder = MeshBuilder::new();
builder.add_cube(0, 0, 0);
builder.add_cube(-1, 0, 0);
builder.add_cube(0, 0, -1);
builder.add_cube(-1, 0, -1);
builder.add_cube(0, 1, 0);
builder.upload(&allocators)?
};
let uniform_allocator = SubbufferAllocator::new(
allocators.memory.clone(),
SubbufferAllocatorCreateInfo {
buffer_usage: BufferUsage::UNIFORM_BUFFER,
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
);
let render_pass = vulkano::single_pass_renderpass!(
device.clone(),
attachments: {
color: {
format: swapchain.image_format(),
samples: 1,
load_op: DontCare,
store_op: Store,
},
msaa: {
format: swapchain.image_format(),
samples: 4,
load_op: Clear,
store_op: DontCare,
},
depth: {
format: DEPTH_FORMAT,
samples: 4,
load_op: Clear,
store_op: DontCare,
}
},
pass: {
color: [msaa],
color_resolve: [color],
depth_stencil: {depth},
},
)?;
let framebuffers = util::create_3d_framebuffers(
&allocators,
&render_pass,
&swapchain_images,
DEPTH_FORMAT,
)?;
let viewport = Viewport::from_window(&window);
let vertex_shader = shaders::plain::vs::load(device.clone())?
.entry_point("main")
.expect("shader entry point");
let fragment_shader = shaders::plain::fs::load(device.clone())?
.entry_point("main")
.expect("shader entry point");
let pipeline = shaders::create_two_stage_3d_pipeline::<InputVertex>(
&device,
&render_pass,
viewport.clone(),
&vertex_shader,
&fragment_shader,
)?;
let start = Instant::now();
Ok(Self {
window,
device,
queue,
allocators,
swapchain,
swapchain_images,
swapchain_dirty: false,
vertex_buffer,
uniform_allocator,
render_pass,
viewport,
framebuffers,
vertex_shader,
fragment_shader,
pipeline,
start,
})
}
pub fn render(&mut self) -> Result<(), Error> {
self.refresh_swapchain()?;
let (image_index, suboptimal, acquire_future) =
match swapchain::acquire_next_image(self.swapchain.clone(), None)
.map_err(Validated::unwrap)
{
Ok(r) => r,
Err(VulkanError::OutOfDate) => {
self.swapchain_dirty = true;
return Ok(());
}
Err(e) => return Err(e.into()),
};
if suboptimal {
self.swapchain_dirty = true;
}
let framebuffer = &self.framebuffers[image_index as usize];
let dimensions = self.window.inner_size();
let aspect = dimensions.width as f32 / dimensions.height as f32;
let delta = self.start.elapsed();
let dt = delta.as_secs_f64() * 1.0;
let model = Mat4::IDENTITY;
let projection = Mat4::perspective_rh_gl(45.0f32.to_radians(), aspect, 0.01, 100.0);
fn make_view(camera: Vec3, target: Vec3) -> Mat4 {
Mat4::look_at_rh(camera, target, Vec3::Y)
}
let view = make_view(
Vec3::new((dt.cos() * 3.0) as f32, 3.0, (dt.sin() * 3.0) as f32),
Vec3::new(0.0, 0.0, 0.0),
);
let uniform_buffer = self
.uniform_allocator
.allocate_sized::<shaders::plain::SceneData>()?;
{
let mut write = uniform_buffer.write()?;
*write = shaders::plain::SceneData {
model: model.to_cols_array_2d(),
view: view.to_cols_array_2d(),
projection: projection.to_cols_array_2d(),
};
}
let descriptor_layout = &self.pipeline.layout().set_layouts()[0];
let set0 = DescriptorSet::new(
self.allocators.descriptor_set.clone(),
descriptor_layout.clone(),
[WriteDescriptorSet::buffer(0, uniform_buffer)],
[],
)?;
let mut builder = AutoCommandBufferBuilder::primary(
self.allocators.command_buffer.clone(),
self.queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)?;
builder.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![None, Some([0.3, 0.5, 0.9, 1.0].into()), Some(1.0.into())],
..RenderPassBeginInfo::framebuffer(framebuffer.clone())
},
SubpassBeginInfo {
contents: SubpassContents::Inline,
..Default::default()
},
)?;
builder
.bind_pipeline_graphics(self.pipeline.clone())?
.bind_descriptor_sets(
PipelineBindPoint::Graphics,
self.pipeline.layout().clone(),
0,
set0.offsets([0]),
)?
.bind_vertex_buffers(0, self.vertex_buffer.clone())?;
unsafe {
builder.draw(self.vertex_buffer.len() as _, 1, 0, 0)?;
}
builder.end_render_pass(SubpassEndInfo::default())?;
let command_buffer = builder.build()?;
sync::now(self.device.clone())
.join(acquire_future)
.then_execute(self.queue.clone(), command_buffer)?
.then_swapchain_present(
self.queue.clone(),
SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index),
)
.then_signal_fence_and_flush()?
.wait(None)?;
Ok(())
}
pub fn refresh_swapchain(&mut self) -> Result<(), Error> {
if !self.swapchain_dirty {
return Ok(());
}
log::info!("Recreate swapchain");
let dimensions = self.window.inner_size();
let create_info = self.swapchain.create_info();
let create_info = SwapchainCreateInfo {
image_extent: dimensions.into(),
..create_info
};
let (new_swapchain, new_swapchain_images) = self.swapchain.recreate(create_info)?;
self.viewport = Viewport::from_window(&self.window);
self.swapchain = new_swapchain;
self.swapchain_images = new_swapchain_images;
self.rebuild_pipelines()?;
self.framebuffers = util::create_3d_framebuffers(
&self.allocators,
&self.render_pass,
&self.swapchain_images,
DEPTH_FORMAT,
)?;
self.swapchain_dirty = false;
Ok(())
}
fn rebuild_pipelines(&mut self) -> Result<(), Error> {
self.pipeline = shaders::create_two_stage_3d_pipeline::<InputVertex>(
&self.device,
&self.render_pass,
self.viewport.clone(),
&self.vertex_shader,
&self.fragment_shader,
)?;
Ok(())
}
pub fn set_swapchain_dirty(&mut self) {
self.swapchain_dirty = true;
}
}
+103
View File
@@ -0,0 +1,103 @@
use std::sync::Arc;
use vulkano::{
descriptor_set::layout::DescriptorType,
device::Device,
pipeline::{
graphics::{
color_blend::{ColorBlendAttachmentState, ColorBlendState},
depth_stencil::{DepthState, DepthStencilState},
input_assembly::InputAssemblyState,
multisample::MultisampleState,
rasterization::RasterizationState,
vertex_input::{Vertex, VertexDefinition},
viewport::{Viewport, ViewportState},
GraphicsPipelineCreateInfo,
},
layout::PipelineDescriptorSetLayoutCreateInfo,
GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
},
render_pass::{RenderPass, Subpass},
shader::EntryPoint,
};
use crate::error::Error;
pub mod plain {
pub mod vs {
vulkano_shaders::shader! {
ty: "vertex",
path: "src/render/shaders/plain.vert",
}
}
pub mod fs {
vulkano_shaders::shader! {
ty: "fragment",
path: "src/render/shaders/plain.frag",
}
}
pub use vs::SceneData;
}
pub fn create_two_stage_3d_pipeline<V: Vertex>(
device: &Arc<Device>,
render_pass: &Arc<RenderPass>,
viewport: Viewport,
vertex_shader: &EntryPoint,
fragment_shader: &EntryPoint,
) -> Result<Arc<GraphicsPipeline>, Error> {
let vertex_input_state = V::per_vertex().definition(vertex_shader)?;
let stages = [
PipelineShaderStageCreateInfo::new(vertex_shader.clone()),
PipelineShaderStageCreateInfo::new(fragment_shader.clone()),
];
let layout = {
let mut layout_create_info = PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages);
layout_create_info.set_layouts[0]
.bindings
.get_mut(&0)
.unwrap()
.descriptor_type = DescriptorType::UniformBufferDynamic;
PipelineLayout::new(
device.clone(),
layout_create_info.into_pipeline_layout_create_info(device.clone())?,
)?
};
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
let pipeline = GraphicsPipeline::new(
device.clone(),
None,
GraphicsPipelineCreateInfo {
stages: stages.into_iter().collect(),
vertex_input_state: Some(vertex_input_state),
input_assembly_state: Some(InputAssemblyState::default()),
viewport_state: Some(ViewportState {
viewports: [viewport].into_iter().collect(),
..Default::default()
}),
rasterization_state: Some(RasterizationState::default()),
multisample_state: Some(MultisampleState {
rasterization_samples: subpass.num_samples().unwrap(),
..Default::default()
}),
color_blend_state: Some(ColorBlendState::with_attachment_states(
subpass.num_color_attachments(),
ColorBlendAttachmentState::default(),
)),
depth_stencil_state: Some(DepthStencilState {
depth: Some(DepthState::simple()),
..Default::default()
}),
subpass: Some(subpass.into()),
..GraphicsPipelineCreateInfo::layout(layout)
},
)?;
Ok(pipeline)
}
+9
View File
@@ -0,0 +1,9 @@
#version 460
layout(location = 0) in vec3 i_color;
layout(location = 0) out vec4 o_color;
void main() {
o_color = vec4(i_color, 1.0);
}
+20
View File
@@ -0,0 +1,20 @@
#version 460
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_color;
layout(location = 0) out vec3 o_color;
layout(set = 0, binding = 0) uniform SceneData {
mat4 model;
mat4 view;
mat4 projection;
} u_scene;
void main() {
vec4 m_position = vec4(i_position, 1.0);
vec4 s_position = u_scene.projection * u_scene.view * m_position;
gl_Position = s_position;
o_color = i_color;
}
+207
View File
@@ -0,0 +1,207 @@
use std::sync::Arc;
use vulkano::{
command_buffer::allocator::{CommandBufferAllocator, StandardCommandBufferAllocator},
descriptor_set::allocator::{DescriptorSetAllocator, StandardDescriptorSetAllocator},
device::{
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Queue,
QueueCreateInfo, QueueFlags,
},
format::Format,
image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage, SampleCount},
instance::{Instance, InstanceCreateInfo},
memory::allocator::StandardMemoryAllocator,
pipeline::graphics::viewport::Viewport,
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass},
swapchain::{Surface, Swapchain, SwapchainCreateInfo},
VulkanLibrary,
};
use winit::{dpi::PhysicalSize, window::Window};
use crate::error::Error;
pub struct Allocators {
pub memory: Arc<StandardMemoryAllocator>,
pub command_buffer: Arc<dyn CommandBufferAllocator>,
pub descriptor_set: Arc<dyn DescriptorSetAllocator>,
}
pub trait ViewportExt {
fn from_window(window: &Arc<Window>) -> Self;
}
impl ViewportExt for Viewport {
fn from_window(window: &Arc<Window>) -> Self {
let dimensions = window.inner_size();
Self {
offset: [0.0, dimensions.height as _],
extent: [dimensions.width as _, -(dimensions.height as f32)],
depth_range: 0.0..=1.0,
}
}
}
impl Allocators {
pub fn new_default(device: Arc<Device>) -> Result<Arc<Self>, Error> {
let memory = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
let command_buffer = Arc::new(StandardCommandBufferAllocator::new(
device.clone(),
Default::default(),
));
let descriptor_set = Arc::new(StandardDescriptorSetAllocator::new(
device.clone(),
Default::default(),
));
Ok(Arc::new(Self {
memory,
command_buffer,
descriptor_set,
}))
}
}
pub fn create_instance(info: InstanceCreateInfo) -> Result<Arc<Instance>, Error> {
let library = VulkanLibrary::new()?;
let instance = Instance::new(library, info)?;
Ok(instance)
}
pub fn create_device(
instance: &Arc<Instance>,
required_extensions: DeviceExtensions,
surface: &Arc<Surface>,
) -> Result<(Arc<Device>, Arc<Queue>), Error> {
let (physical_device, queue_family_index) = instance
.enumerate_physical_devices()?
.filter(|device| device.supported_extensions().contains(&required_extensions))
.filter_map(|device| {
let queue_family_index = device
.queue_family_properties()
.iter()
.enumerate()
.position(|(index, qf)| {
qf.queue_flags.contains(QueueFlags::GRAPHICS)
&& device
.surface_support(index as u32, surface)
.unwrap_or(false)
})?;
Some((device, queue_family_index as u32))
})
.min_by_key(|(device, _)| match device.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1,
PhysicalDeviceType::VirtualGpu => 2,
PhysicalDeviceType::Cpu => 3,
_ => 3,
})
.ok_or(Error::NoValidDevice)?;
let (device, mut queues) = Device::new(
physical_device,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queue_family_index,
..Default::default()
}],
enabled_extensions: required_extensions,
..Default::default()
},
)?;
let queue = queues.next().unwrap();
Ok((device, queue))
}
pub fn create_swapchain(
device: &Arc<Device>,
surface: &Arc<Surface>,
dimensions: PhysicalSize<u32>,
) -> Result<(Arc<Swapchain>, Vec<Arc<Image>>), Error> {
let physical_device = device.physical_device();
let caps = physical_device.surface_capabilities(surface, Default::default())?;
let composite_alpha = caps
.supported_composite_alpha
.into_iter()
.next()
.expect("a supported composite alpha");
let image_format = physical_device.surface_formats(surface, Default::default())?[0].0;
let (swapchain, images) = Swapchain::new(
device.clone(),
surface.clone(),
SwapchainCreateInfo {
min_image_count: caps.min_image_count + 1,
image_format,
image_extent: dimensions.into(),
image_usage: ImageUsage::COLOR_ATTACHMENT
| ImageUsage::TRANSFER_DST
| ImageUsage::STORAGE,
composite_alpha,
..Default::default()
},
)?;
Ok((swapchain, images))
}
pub fn create_3d_framebuffers(
allocators: &Allocators,
render_pass: &Arc<RenderPass>,
swapchain_images: &[Arc<Image>],
depth_format: Format,
) -> Result<Vec<Arc<Framebuffer>>, Error> {
let msaa_image = Image::new(
allocators.memory.clone(),
ImageCreateInfo {
image_type: ImageType::Dim2d,
format: swapchain_images[0].format(),
extent: swapchain_images[0].extent(),
usage: ImageUsage::COLOR_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
samples: SampleCount::Sample4,
..Default::default()
},
Default::default(),
)?;
let depth_image = Image::new(
allocators.memory.clone(),
ImageCreateInfo {
format: depth_format,
image_type: ImageType::Dim2d,
extent: swapchain_images[0].extent(),
usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
samples: SampleCount::Sample4,
..Default::default()
},
Default::default(),
)?;
let msaa_attachment = ImageView::new_default(msaa_image)?;
let depth_attachment = ImageView::new_default(depth_image)?;
let framebuffers = swapchain_images
.iter()
.map(|image| {
let color_attachment = ImageView::new_default(image.clone())?;
let framebuffer = Framebuffer::new(
render_pass.clone(),
FramebufferCreateInfo {
attachments: vec![
color_attachment,
msaa_attachment.clone(),
depth_attachment.clone(),
],
..Default::default()
},
)?;
Ok(framebuffer)
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(framebuffers)
}
+12
View File
@@ -0,0 +1,12 @@
use glam::Vec3;
use vulkano::{buffer::BufferContents, pipeline::graphics::vertex_input::Vertex};
#[derive(Debug, Clone, Copy, BufferContents, Vertex)]
#[repr(C)]
pub struct InputVertex {
#[format(R32G32B32_SFLOAT)]
pub i_position: Vec3,
#[format(R32G32B32_SFLOAT)]
pub i_color: Vec3,
}