Compare commits

..

7 Commits

Author SHA1 Message Date
Avery c9fdcfe008
Deduplicate setup_dest_layout
10 months ago
Avery 32aa330b33
Add doc comments to various things
10 months ago
Avery b09eedafab
Fix pushing constants not using specified flags and offset, rendering now works
10 months ago
Avery dbce32f625
Fix `VK_ERROR_SURFACE_LOST_KHR`, `VK_ERROR_OUT_OF_POOL_MEMORY`, typos in shader names
10 months ago
Avery 2f15842ca4
Reorganise and split `main.rs`
10 months ago
Avery 8168eebf7b
Reflective cube
10 months ago
Avery 3da43d5a69
Seperate shader for skybox, shader and cube in one scene
10 months ago

1
.gitignore vendored

@ -1 +1,2 @@
/target
/shaders/*.spv

@ -1,6 +1,6 @@
BIN = target/debug/mineclone
SHADERS = shaders/vert.spv shaders/frag.spv
SHADERS = $(shell find shaders -name '*.frag' -or -name '*.vert' | sed -e 's/\.frag/_frag.spv/' -e 's/\.vert/_vert.spv/')
SOURCES = $(shell find src -name '*.rs')
all: $(SHADERS) $(BIN)
@ -11,8 +11,8 @@ run: all
$(BIN): $(SOURCES)
cargo build
%.spv: %.vert
%_vert.spv: %.vert
glslc $< -o $@
%.spv: %.frag
%_frag.spv: %.frag
glslc $< -o $@

@ -26,6 +26,7 @@
vulkan-headers
vulkan-loader
vulkan-validation-layers # maybe?
shaderc
# glm and whatnot …
];
};

@ -1,19 +1,22 @@
#version 450
// vim: ft=c
//
// clang-format off
layout(binding = 1) uniform samplerCube combined_image;
layout(location = 0) out vec4 outColor;
layout(location = 0) in vec4 normal;
layout(location = 1) in vec4 pos_pre;
layout(location = 2) in vec4 view_orig;
layout(push_constant, std430) uniform pc {
layout(offset=48) vec4 data;
};
void main() {
outColor = texture(combined_image, vec3(pos_pre));
vec4 ray = pos_pre - view_orig;
outColor = texture(combined_image, vec3(reflect(ray, normal)));
// outColor = vec4(data.rgb*(1.0+dot(normal.xyz, normalize(vec3(-0.7, -0.5, -0.1))))/2.0, 1.0);
//if(pos_post.z <= 0.0) {
// outColor = vec4(1.0);

@ -1,5 +1,6 @@
#version 450
// vim: ft=c
// clang-format off
struct PosNorm {
vec4 pos;
@ -18,6 +19,7 @@ layout(push_constant, std430) uniform pc {
layout (location = 0) out vec4 normal;
layout (location = 1) out vec4 pos_pre;
layout (location = 2) out vec4 view_orig;
const float PI = 3.14159;
// Forgive me for I have sinned
@ -27,6 +29,7 @@ void main() {
// assign outs
normal = pos.posnrm[gl_VertexIndex].norm;
pos_pre = pos.posnrm[gl_VertexIndex].pos;
view_orig = cam_orig;
// define constants
const float zFar = 100.0;
@ -91,7 +94,7 @@ void main() {
// apply view's origin transformation
//gl_Position.xyz += cam_orig.xyz;
// gl_Position *= view_orig;
gl_Position *= view_orig;
// apply view's xz rotation
//mat2 xz_rot;

Binary file not shown.

@ -0,0 +1,10 @@
#version 450
// vim: ft=c
layout(binding = 0) uniform samplerCube combined_image;
layout(location = 0) out vec4 outColor;
layout(location = 0) in vec4 pos_pre;
void main() { outColor = texture(combined_image, vec3(pos_pre)); }

@ -0,0 +1,157 @@
#version 450
// vim: ft=c
// clang-format off
const vec4 positions[36] = vec4[36](
// BOTTOM
vec4(-0.5, 0.5, -0.5, 1.0),
vec4(0.5, 0.5, 0.5, 1.0),
vec4(-0.5, 0.5, 0.5, 1.0),
vec4(0.5, 0.5, -0.5, 1.0),
vec4(0.5, 0.5, 0.5, 1.0),
vec4(-0.5, 0.5, -0.5, 1.0),
// TOP
vec4(0.5, -0.5, 0.5, 1.0),
vec4(-0.5, -0.5, -0.5, 1.0),
vec4(-0.5, -0.5, 0.5, 1.0),
vec4(0.5, -0.5, 0.5, 1.0),
vec4(0.5, -0.5, -0.5, 1.0),
vec4(-0.5, -0.5, -0.5, 1.0),
// FRONT
vec4(-0.5, -0.5, -0.5, 1.0),
vec4(0.5, 0.5, -0.5, 1.0),
vec4(-0.5, 0.5, -0.5, 1.0),
vec4(0.5, -0.5, -0.5, 1.0),
vec4(0.5, 0.5, -0.5, 1.0),
vec4(-0.5, -0.5, -0.5, 1.0),
// BACK
vec4(0.5, 0.5, 0.5, 1.0),
vec4(-0.5, -0.5, 0.5, 1.0),
vec4(-0.5, 0.5, 0.5, 1.0),
vec4(0.5, 0.5, 0.5, 1.0),
vec4(0.5, -0.5, 0.5, 1.0),
vec4(-0.5, -0.5, 0.5, 1.0),
// LEFT
vec4(-0.5, -0.5, -0.5, 1.0),
vec4(-0.5, 0.5, 0.5, 1.0),
vec4(-0.5, -0.5, 0.5, 1.0),
vec4(-0.5, 0.5, -0.5, 1.0),
vec4(-0.5, 0.5, 0.5, 1.0),
vec4(-0.5, -0.5, -0.5, 1.0),
// RIGHT
vec4(0.5, 0.5, 0.5, 1.0),
vec4(0.5, -0.5, -0.5, 1.0),
vec4(0.5, -0.5, 0.5, 1.0),
vec4(0.5, 0.5, 0.5, 1.0),
vec4(0.5, 0.5, -0.5, 1.0),
vec4(0.5, -0.5, -0.5, 1.0)
);
layout(push_constant, std430) uniform pc {
layout(offset=0) vec4 cam_orig;
layout(offset=16) vec4 cam_rot;
layout(offset=32) uvec2 screen_res;
};
layout (location = 0) out vec4 pos_pre;
const float PI = 3.14159;
// Forgive me for I have sinned
const float TAU = PI*2.0;
void main() {
// assign outs
pos_pre = positions[gl_VertexIndex];
// define constants
const float zFar = 100.0;
const float zNear = 0.1;
// assign the transformee
gl_Position = positions[gl_VertexIndex];
mat4 fix_coordinates = mat4(
-1.0, 0.0, 0.0, 0.0,
0.0, -1.0, 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 view_rot_xz = mat4(
cos(cam_rot.y), 0.0, sin(cam_rot.y), 0.0,
0.0, 1.0, 0.0, 0.0,
-sin(cam_rot.y), 0.0, cos(cam_rot.y), 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 view_rot_yz = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, cos(cam_rot.x), sin(cam_rot.x), 0.0,
0.0, -sin(cam_rot.x), cos(cam_rot.x), 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 project_aspect = mat4(
(float(screen_res.y)/float(screen_res.x)), 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 project_znear = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, -zNear,
0.0, 0.0, 0.0, 1.0
);
mat4 project_div = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, tan((70.0/2.0)/360.0*TAU), 0.0
);
mat4 project_normal = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0/zFar, 0.0,
0.0, 0.0, 0.0, 1.0
);
// vulkan has inverted screen coordinates
// but we want regular mesh coordinates
// gl_Position.xyz *= -1.0
gl_Position *= fix_coordinates;
// apply view's xz rotation
//mat2 xz_rot;
//xz_rot[0] = vec2(cos(cam_rot.y), -sin(cam_rot.y));
//xz_rot[1] = vec2(sin(cam_rot.y), cos(cam_rot.y));
//gl_Position.xz *= inverse(xz_rot);
gl_Position *= view_rot_xz;
// apply view's yz rotation
//mat2 yz_rot;
//yz_rot[0] = vec2(cos(cam_rot.x), -sin(cam_rot.x));
//yz_rot[1] = vec2(sin(cam_rot.x), cos(cam_rot.x));
//gl_Position.yz *= inverse(yz_rot);
gl_Position *= view_rot_yz;
// aspect correction
//gl_Position.x *= (1080.0/1920.0);
gl_Position *= project_aspect;
// z near correction
//gl_Position.z -= zNear;
gl_Position *= project_znear;
// division by z
// has to be assigned by w so that the hardware performs the division AFTER clipping.
//gl_Position.w = (gl_Position.z*sin(140.0/360.0*TAU));
gl_Position *= project_div;
// z normalization
//gl_Position.z /= zFar;
gl_Position *= project_normal;
}

Binary file not shown.

@ -0,0 +1,100 @@
use std::{
process, thread,
time::{Duration, Instant},
};
use sdl2::{
event::{Event, WindowEvent},
keyboard::Keycode,
};
use crate::{Game, InWorld, RenderAvailable, input::InputHandler};
#[derive(Debug, Default)]
pub struct Vector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Debug, Default)]
pub struct Camera {
pub origin: Vector3,
pub rotation: Vector3,
}
impl Game<InWorld> {
/// The main gameloop, this will exit the program is the game is requested to close,
/// or return to the [`RenderAvailable`] state if the world is closed and we want to
/// return to a menu.
pub fn main_loop(mut self) -> Game<RenderAvailable> {
println!("Starting main loop");
let mut event_pump = self
.state
.sdl_context
.event_pump()
.expect("Failed to get event pump");
let mouse = self.state.sdl_context.mouse();
mouse.set_relative_mouse_mode(true);
let mut running = true;
while running {
let now = Instant::now();
for event in event_pump.poll_iter() {
match event {
// Setting running to false just returns to
// RenderAvailable state (and then main menu)
Event::Quit { .. } => process::exit(0),
Event::Window {
win_event: WindowEvent::Resized(w, h),
..
} => {
self.window_resize((w as u32, h as u32));
}
Event::KeyDown {
keycode: Some(keycode),
keymod,
repeat,
..
} => self.handle_keydown(keycode, keymod, repeat),
Event::KeyUp {
keycode: Some(keycode),
keymod,
repeat,
..
} => self.handle_keyup(keycode, keymod, repeat),
Event::MouseMotion {
mousestate,
x,
y,
xrel,
yrel,
..
} => self.handle_mouse_motion(mousestate, (x, y), (xrel, yrel)),
_ => {}
}
}
event_pump
.keyboard_state()
.pressed_scancodes()
.filter_map(Keycode::from_scancode)
.for_each(|k| self.handle_cont_key(k));
self.render_frame();
let elapsed = now.elapsed();
let delay = (1_000_000u128 / 60).saturating_sub(elapsed.as_micros());
thread::sleep(Duration::from_micros(delay as u64));
}
Game {
state: RenderAvailable {
sdl_context: self.state.sdl_context,
window: self.state.window,
ctx: self.state.ctx,
},
}
}
}

@ -0,0 +1,67 @@
use sdl2::{
keyboard::{Keycode, Mod},
mouse::MouseState,
};
use crate::{Game, InWorld};
use super::InputHandler;
const MOV_STEP: f32 = 0.05;
const ROT_MOUSE_SCALE: f32 = 0.001;
impl InputHandler for Game<InWorld> {
fn handle_cont_key(&mut self, keycode: Keycode) {
let Self {
state: InWorld { camera, .. },
} = self;
match keycode {
Keycode::W => {
camera.origin.x -= -camera.rotation.y.sin() * MOV_STEP;
camera.origin.z -= camera.rotation.y.cos() * MOV_STEP;
}
Keycode::S => {
camera.origin.x += -camera.rotation.y.sin() * MOV_STEP;
camera.origin.z += camera.rotation.y.cos() * MOV_STEP;
}
Keycode::A => {
camera.origin.x += camera.rotation.y.cos() * MOV_STEP;
camera.origin.z += camera.rotation.y.sin() * MOV_STEP;
}
Keycode::D => {
camera.origin.z -= camera.rotation.y.sin() * MOV_STEP;
camera.origin.x -= camera.rotation.y.cos() * MOV_STEP;
}
Keycode::Space => {
camera.origin.y += MOV_STEP;
}
Keycode::LShift => {
camera.origin.y -= MOV_STEP;
}
_ => {}
}
}
fn handle_keydown(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) {
match keycode {
Keycode::Escape => {
let mouse = self.state.sdl_context.mouse();
mouse.set_relative_mouse_mode(!mouse.relative_mouse_mode());
}
_ => {}
}
}
fn handle_mouse_motion(
&mut self,
state: MouseState,
abs: (i32, i32),
(xrel, yrel): (i32, i32),
) {
if self.state.sdl_context.mouse().relative_mouse_mode() {
self.state.camera.rotation.y -= xrel as f32 * ROT_MOUSE_SCALE;
self.state.camera.rotation.x -= yrel as f32 * ROT_MOUSE_SCALE;
}
}
}

@ -0,0 +1,22 @@
mod ingame_input;
use sdl2::{
keyboard::{Keycode, Mod},
mouse::MouseState,
};
/// The generic interface for input handling, this is supposed to be implemented for
/// the game in a specific state, see [`ingame_input`] for an example.
/// More methods to be added for other forms of input
pub trait InputHandler {
/// Handle keys which are meant to be held down and repeated every frame, ex movement keys
fn handle_cont_key(&mut self, keycode: Keycode) {}
/// Handle single key presses or keys held down after the repear delay
fn handle_keydown(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) {}
/// Handle single key releases
fn handle_keyup(&mut self, keycode: Keycode, modifier: Mod, repeat: bool) {}
/// Handle mouse movements
fn handle_mouse_motion(&mut self, state: MouseState, abs: (i32, i32), rel: (i32, i32)) {}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,397 @@
use std::collections::HashMap;
use ash::vk;
use crate::{Game, LoadingWorld, RenderCtx, render::MemType};
use super::loading_world::WorldComponent;
pub struct Cube {
mesh_buf: vk::Buffer,
pipeline: vk::Pipeline,
pipe_layout: vk::PipelineLayout,
}
const MESH_SIZE: u64 = 36 * 2 * 4 * 4;
#[rustfmt::skip]
const POSITIONS: [f32; 36 * 2 * 4] = [
// BOTTOM
-0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
0.0, 1.0, 0.0, 0.0,
// TOP
0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
0.5, -0.5, 2.0, 1.0,
0.0, -1.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, -1.0, 0.0, 0.0,
// FRONT
-0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
0.0, 0.0, -1.0, 0.0,
// BACK
0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
0.5, 0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
0.0, 0.0, 1.0, 0.0,
// LEFT
-0.5, -0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, -0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, 0.5, 2.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
-0.5, -0.5, 1.0, 1.0,
-1.0, 0.0, 0.0, 0.0,
// RIGHT
0.5, 0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, 0.5, 2.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
0.5, -0.5, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0,
];
impl WorldComponent for Cube {
fn descriptors(&self) -> std::collections::HashMap<vk::DescriptorType, u32> {
let mut map = HashMap::new();
map.insert(vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 1);
map.insert(vk::DescriptorType::STORAGE_BUFFER, 1);
map
}
fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout {
setup_desc_layout(ctx)
}
fn write_desc_set(
&self,
ctx: &RenderCtx,
desc_set: vk::DescriptorSet,
skybox: (vk::ImageView, vk::Sampler),
) {
let buf_info = vk::DescriptorBufferInfo::default()
.buffer(self.mesh_buf)
.offset(0)
.range(vk::WHOLE_SIZE);
let buf_infos = [buf_info];
let buf_desc = vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(0)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.buffer_info(&buf_infos);
let img_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::GENERAL)
.image_view(skybox.0)
.sampler(skybox.1);
let img_infos = &[img_info];
let img_desc = vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(1)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(img_infos);
unsafe {
ctx.dev.update_descriptor_sets(&[buf_desc, img_desc], &[]);
}
}
fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) {
(self.pipeline, self.pipe_layout)
}
fn push_constants(
&self,
camera: &crate::game::Camera,
res: [u32; 2],
base_color: [f32; 4],
) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)> {
let mut constants = Vec::new();
let cam_data: Vec<u8> = (Vec::<f32>::from([
camera.origin.x,
camera.origin.y,
camera.origin.z,
0.0,
camera.rotation.x,
camera.rotation.y,
camera.rotation.z,
]))
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data));
let screen_res: Vec<u8> = res
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res));
let base_color: Vec<u8> = (Vec::<f32>::from([0.0, 1.0, 1.0, 1.0]))
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::FRAGMENT, 48, base_color));
constants
}
}
impl RenderCtx {
fn create_cube(&self, pass: vk::RenderPass) -> Cube {
let mesh_mem = self.mem_alloc(MemType::HostVisibile, MESH_SIZE);
let mesh_buf = self.alloc_buf(
mesh_mem,
MESH_SIZE,
vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER,
);
let mesh_mem = self.buf_to_ptr(mesh_mem, mesh_buf);
unsafe {
std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len());
}
let pipe_layout = setup_pipe_layout(self);
let pipeline = setup_pipeline(self, &pass, pipe_layout);
Cube {
mesh_buf,
pipeline,
pipe_layout,
}
}
}
impl Game<LoadingWorld> {
/// Create a cube to be rendered in world, this can then be
/// added using [`Game<LoadingWorld>::add_component`]
pub fn create_cube(&self) -> Cube {
let Self {
state: LoadingWorld { ctx, pass, .. },
} = self;
ctx.create_cube(*pass)
}
}
fn load_cube_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) {
let shader_vert_shader = ctx.shader_mod_from_file(
"shaders/cube_vert.spv",
vk::ShaderModuleCreateFlags::empty(),
);
let shader_frag_shader = ctx.shader_mod_from_file(
"shaders/cube_frag.spv",
vk::ShaderModuleCreateFlags::empty(),
);
(shader_vert_shader, shader_frag_shader)
}
fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout {
let storage_binding = vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::VERTEX);
let image_binding = vk::DescriptorSetLayoutBinding::default()
.binding(1)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT);
let layouts = [storage_binding, image_binding];
let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts);
let layout = unsafe {
ctx.dev
.create_descriptor_set_layout(&layout_info, None)
.expect("Failed to create descriptor set layout")
};
layout
}
fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout {
let layout = setup_desc_layout(ctx);
let push_range: [vk::PushConstantRange; 2] = [
vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::VERTEX)
.offset(0)
.size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res
vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::FRAGMENT)
.offset(48)
.size(16), // vec4 base_color
];
let layouts = &[layout];
let pipe_layout = vk::PipelineLayoutCreateInfo::default()
.set_layouts(layouts.as_ref())
.push_constant_ranges(&push_range);
let pipe_layout = unsafe {
ctx.dev
.create_pipeline_layout(&pipe_layout, None)
.expect("Failed to create pipeline layout")
};
pipe_layout
}
fn setup_pipeline(
ctx: &RenderCtx,
pass: &vk::RenderPass,
layout: vk::PipelineLayout,
) -> vk::Pipeline {
let (vert_shader, frag_shader) = load_cube_shaders(ctx);
let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader);
let vert_input = vk::PipelineVertexInputStateCreateInfo::default();
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let rasterization = vk::PipelineRasterizationStateCreateInfo::default()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::BACK)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.line_width(1.0);
let multisample = vk::PipelineMultisampleStateCreateInfo::default()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
.depth_test_enable(true)
.depth_write_enable(true)
.depth_compare_op(vk::CompareOp::LESS)
.depth_bounds_test_enable(true)
.stencil_test_enable(false)
.min_depth_bounds(0.0)
.max_depth_bounds(1.0);
let blend = vk::PipelineColorBlendAttachmentState::default()
.src_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.color_blend_op(vk::BlendOp::ADD)
.src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask(
vk::ColorComponentFlags::R
| vk::ColorComponentFlags::G
| vk::ColorComponentFlags::B
| vk::ColorComponentFlags::A,
);
let blend_attachments = [blend];
let color_blend = vk::PipelineColorBlendStateCreateInfo::default()
.attachments(blend_attachments.as_ref())
.logic_op_enable(false)
.logic_op(vk::LogicOp::COPY)
.blend_constants([1.0, 1.0, 1.0, 1.0]);
let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[
vk::DynamicState::VIEWPORT_WITH_COUNT,
vk::DynamicState::SCISSOR_WITH_COUNT,
]);
let pipe_info = vk::GraphicsPipelineCreateInfo::default()
.stages(&shader_stages)
.vertex_input_state(&vert_input)
.input_assembly_state(&input_assembly)
.rasterization_state(&rasterization)
.multisample_state(&multisample)
.depth_stencil_state(&depth_stencil)
.color_blend_state(&color_blend)
.layout(layout)
.dynamic_state(&dynamic)
.render_pass(*pass)
.subpass(0);
let pipe = unsafe {
ctx.dev
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None)
.expect("Failed to create graphics pipeline")
.remove(0)
};
println!("Created pipeline");
pipe
}

@ -0,0 +1,282 @@
use ash::vk::{self};
use crate::{Game, InWorld, RenderCtx, Window};
use super::{
SwapchainCtx,
loading_world::{Component, WorldComponent as _},
swapchain::{
make_depth_img, make_depth_view, make_framebufs, make_swap_images, make_swap_views,
setup_swapchain,
},
};
impl Game<InWorld> {
/// Rebuild the swapchain in response to a resize of the window
pub fn window_resize(&mut self, size: (u32, u32)) {
let Self {
state:
InWorld {
sdl_context,
window,
ctx,
skybox,
components,
sem_avail,
sem_finish,
fence_flight,
swapchain:
SwapchainCtx {
surface_loader,
swapchain_loader,
swap_images,
swap_views,
depth_mem,
depth_image,
depth_view,
framebufs,
swapchain,
},
pass,
camera,
},
} = self;
window.width = size.0;
window.height = size.1;
unsafe {
ctx.dev
.device_wait_idle()
.expect("Failed to wait for device idle");
swapchain_loader.destroy_swapchain(*swapchain, None);
}
(*swapchain, _) = setup_swapchain(
&ctx,
&surface_loader,
&swapchain_loader,
window.width,
window.height,
);
*swap_images = make_swap_images(*swapchain, swapchain_loader);
*swap_views = make_swap_views(&ctx, &swap_images, vk::Format::B8G8R8A8_UNORM);
*depth_image = make_depth_img(&ctx, window.width, window.height, *depth_mem);
*depth_view = make_depth_view(&ctx, *depth_image, vk::Format::D32_SFLOAT);
*framebufs = make_framebufs(
&ctx,
&swap_views,
&depth_view,
&pass,
window.width,
window.height,
);
}
/// Render a single frame
pub fn render_frame(&self) {
let Self {
state:
InWorld {
sdl_context,
window,
ctx,
skybox,
components,
sem_avail,
sem_finish,
fence_flight,
swapchain,
pass,
camera,
},
} = self;
ctx.wait_for_fence(*fence_flight);
ctx.reset_fence(*fence_flight);
let img_idx = ack_next_img(swapchain, sem_avail);
ctx.start_cmd_buf();
self.record_commands(img_idx as usize);
ctx.end_cmd_buf();
ctx.submit_queue(*sem_avail, *sem_finish, *fence_flight);
ctx.queue_present(swapchain, img_idx, *sem_finish);
}
fn record_commands(&self, img_idx: usize) {
let Self {
state:
InWorld {
sdl_context,
window: Window { width, height, .. },
ctx: RenderCtx { dev, cmd_buf, .. },
skybox,
components,
swapchain,
pass,
camera,
..
},
} = self;
let clear_vals = [
vk::ClearValue {
color: vk::ClearColorValue {
float32: [0.0, 0.0, 0.0, 1.0],
},
},
vk::ClearValue {
depth_stencil: vk::ClearDepthStencilValue {
depth: 1.0,
stencil: 0,
},
},
];
let render_pass_info = vk::RenderPassBeginInfo::default()
.render_pass(*pass)
.framebuffer(swapchain.framebufs[img_idx])
.render_area(
vk::Rect2D::default()
.offset(vk::Offset2D::default())
.extent(vk::Extent2D {
width: *width,
height: *height,
}),
)
.clear_values(&clear_vals);
unsafe {
let regions = (0..6)
.map(|i| {
vk::BufferImageCopy::default()
.buffer_offset(3 * 2048 * 2048 * i)
.buffer_row_length(0)
.buffer_image_height(0)
.image_offset(vk::Offset3D::default())
.image_extent(vk::Extent3D::default().width(2048).height(2048).depth(1))
.image_subresource(
vk::ImageSubresourceLayers::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.mip_level(0)
.layer_count(1)
.base_array_layer(i as u32),
)
})
.collect::<Vec<_>>();
dev.cmd_copy_buffer_to_image(
*cmd_buf,
skybox.0.buf,
skybox.0.image,
vk::ImageLayout::GENERAL,
&regions,
);
dev.cmd_begin_render_pass(*cmd_buf, &render_pass_info, vk::SubpassContents::INLINE);
let base_color = [0.0, 1.0, 1.0, 1.0];
for Component { inner, desc_set } in components {
let (pipe, layout) = inner.pipeline();
dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, pipe);
let desc_sets = [*desc_set];
dev.cmd_bind_descriptor_sets(
*cmd_buf,
vk::PipelineBindPoint::GRAPHICS,
layout,
0,
desc_sets.as_ref(),
&[],
);
dev.cmd_set_viewport_with_count(
*cmd_buf,
&[vk::Viewport::default()
.width(*width as f32)
.height(*height as f32)],
);
dev.cmd_set_scissor_with_count(
*cmd_buf,
&[vk::Rect2D::default().extent(vk::Extent2D {
width: *width,
height: *height,
})],
);
inner
.push_constants(camera, [*width, *height], base_color)
.into_iter()
.for_each(|(f, o, d)| {
dev.cmd_push_constants(*cmd_buf, layout, f, o, d.as_ref());
});
dev.cmd_draw(*cmd_buf, 36, 1, 0, 0);
}
{
let (pipe, layout) = skybox.0.pipeline();
dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, pipe);
let desc_sets = [skybox.1];
dev.cmd_bind_descriptor_sets(
*cmd_buf,
vk::PipelineBindPoint::GRAPHICS,
layout,
0,
desc_sets.as_ref(),
&[],
);
dev.cmd_set_viewport_with_count(
*cmd_buf,
&[vk::Viewport::default()
.width(*width as f32)
.height(*height as f32)],
);
dev.cmd_set_scissor_with_count(
*cmd_buf,
&[vk::Rect2D::default().extent(vk::Extent2D {
width: *width,
height: *height,
})],
);
skybox
.0
.push_constants(camera, [*width, *height], base_color)
.into_iter()
.for_each(|(f, o, d)| {
dev.cmd_push_constants(
*cmd_buf,
layout,
vk::ShaderStageFlags::VERTEX,
0,
d.as_ref(),
);
});
// FIXME: We currently get away with hardcoding these number
// because all we draw are cubes, but not for long I suspect
dev.cmd_draw(*cmd_buf, 36, 1, 0, 0);
}
dev.cmd_end_render_pass(*cmd_buf);
}
}
}
fn ack_next_img(swapchain: &SwapchainCtx, sem_avail: &vk::Semaphore) -> u32 {
unsafe {
let (img_inx, _) = swapchain
.swapchain_loader
.acquire_next_image(swapchain.swapchain, u64::MAX, *sem_avail, vk::Fence::null())
.expect("Failed to acquire next image");
img_inx
}
}

@ -0,0 +1,250 @@
use std::ffi::{CStr, CString};
use ash::{
Device, Entry, Instance,
vk::{self},
};
use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};
use sdl2::{Sdl, video::Window};
use crate::{APP_NAME, Game, RenderAvailable, RenderCtx, Unstarted};
impl Game<Unstarted> {
pub fn init_render(self, sdl_context: Sdl, window: Window) -> Game<RenderAvailable> {
let (width, height) = window.size();
let entry = Entry::linked();
let instance = create_instance(&window, &entry);
let surface = make_surface(&entry, &instance, &window);
let pdev = find_pdev_dgpu(&instance);
let queue_idx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found");
let dev = make_dev(
&instance,
&pdev,
queue_idx,
vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"],
);
let queue = unsafe { dev.get_device_queue(queue_idx, 0) };
let cmd_pool = make_command_pool(&dev, queue_idx);
let cmd_buf = alloc_cmd_buf(&dev, cmd_pool);
let host_vis_idx =
find_host_visible(&instance, &pdev).expect("No host visible memory found");
let host_invis_idx =
find_host_invisible(&instance, &pdev).expect("No host invisible memory found");
Game {
state: RenderAvailable {
sdl_context,
window: crate::Window {
width,
height,
_sdl: window,
},
ctx: RenderCtx {
entry,
instance,
pdev,
dev,
surface,
queue,
queue_idx,
cmd_buf,
host_vis_idx,
host_invis_idx,
},
},
}
}
}
fn create_instance(window: &Window, entry: &Entry) -> Instance {
let n_app_name = CString::new(APP_NAME).unwrap();
let n_engine_name = CString::new(format!("{} Engine", APP_NAME)).unwrap();
let appinfo = vk::ApplicationInfo::default()
.application_name(n_app_name.as_c_str())
.application_version(0)
.engine_name(n_engine_name.as_c_str())
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 3, 0));
let default_exts = window
.vulkan_instance_extensions()
.expect("Failed to get list of required extensions from SDL2");
let mut exts = vec![];
exts.extend(default_exts);
let exts: Vec<*const i8> = exts
.iter()
.map(|s| s.as_ptr() as *const i8)
.collect::<Vec<_>>();
let exts: &[*const i8] = exts.as_slice();
let insinfo = vk::InstanceCreateInfo::default()
.application_info(&appinfo)
.enabled_extension_names(exts);
let instance =
unsafe { entry.create_instance(&insinfo, None) }.expect("Failed to create instance");
println!("Created instance");
instance
}
fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR {
let surface = unsafe {
ash_window::create_surface(
&entry,
&instance,
window.display_handle().unwrap().as_raw(),
window.window_handle().unwrap().as_raw(),
None,
)
}
.expect("Failed to create surface");
println!("Created surface");
surface
}
fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice {
let mut pdevs =
unsafe { instance.enumerate_physical_devices() }.expect("Failed to enumerate devices");
for (i, d) in pdevs.iter().enumerate() {
let props = unsafe { instance.get_physical_device_properties(*d) };
if props.device_type == vk::PhysicalDeviceType::DISCRETE_GPU {
// TODO this assumes that the first discrete GPU will have an RCS queue family
// This is not guaranteed by the spec (example, a compute-only GPU)
// Fix.
println!(
"Found discrete GPU: {}",
unsafe { std::str::from_utf8(std::mem::transmute(props.device_name.as_slice())) }
.unwrap()
);
return pdevs.remove(i);
}
}
println!("No discrete GPU found");
assert!(pdevs.len() > 0, "No GPU found");
return pdevs.remove(0);
}
// Using Intel GPU hardware terminology 🥴
fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let qfams = unsafe { instance.get_physical_device_queue_family_properties(*pdev) };
for (inx, qfam) in qfams.iter().enumerate() {
if qfam
.queue_flags
.contains(vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE)
{
println!("Found RCS queue at index {}", inx);
return Some(inx as u32);
}
}
println!("No RCS queue found");
return None;
}
fn make_dev(
instance: &Instance,
pdev: &vk::PhysicalDevice,
rcs_queue_inx: u32,
exts: Vec<&CStr>,
) -> Device {
unsafe {
let dev_queue = vec![
vk::DeviceQueueCreateInfo::default()
.queue_family_index(rcs_queue_inx)
.queue_priorities(&[1.0]),
];
let exts = exts.iter().map(|&s| s.as_ptr()).collect::<Vec<_>>();
let exts = exts.as_slice();
// Enable all features
let features = instance.get_physical_device_features(*pdev);
let dev_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&dev_queue)
.enabled_extension_names(exts)
.enabled_features(&features);
let dev = instance
.create_device(*pdev, &dev_info, None)
.expect("Failed to create device");
println!("Created device");
dev
}
}
fn make_command_pool(dev: &Device, rcs_queue_inx: u32) -> vk::CommandPool {
unsafe {
let pool_info = vk::CommandPoolCreateInfo::default()
.queue_family_index(rcs_queue_inx)
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
let pool = dev
.create_command_pool(&pool_info, None)
.expect("Failed to create command pool");
println!("Created command pool");
pool
}
}
fn alloc_cmd_buf(dev: &Device, pool: vk::CommandPool) -> vk::CommandBuffer {
unsafe {
let alloc_info = vk::CommandBufferAllocateInfo::default()
.command_pool(pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(1);
let cmd_buf = dev
.allocate_command_buffers(&alloc_info)
.expect("Failed to allocate command buffer")
.remove(0);
println!("Allocated command buffer");
cmd_buf
}
}
fn find_host_visible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) };
for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
if mem_type.property_flags.contains(
vk::MemoryPropertyFlags::DEVICE_LOCAL
| vk::MemoryPropertyFlags::HOST_VISIBLE
| vk::MemoryPropertyFlags::HOST_COHERENT,
) {
println!("Found host visible memory at index {}", i);
return Some(i as u32);
}
}
println!("No host visible memory found");
return None;
}
fn find_host_invisible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
let mem_props = unsafe { instance.get_physical_device_memory_properties(*pdev) };
for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
if mem_type
.property_flags
.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL)
&& !mem_type.property_flags.contains(
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)
{
println!("Found host invisible memory at index {}", i);
return Some(i as u32);
}
}
println!("No host invisible memory found");
return None;
}

@ -0,0 +1,361 @@
use std::{collections::HashMap, iter};
use ash::{
khr::{surface, swapchain},
vk::{self},
};
use crate::{
Game, InWorld, LoadingWorld, RenderAvailable, RenderCtx, game::Camera, render::MemType,
};
use super::{
MAX_HEIGHT, MAX_WIDTH, SwapchainCtx,
swapchain::{
make_depth_img, make_depth_view, make_framebufs, make_swap_images, make_swap_views,
setup_swapchain,
},
};
/// Anything can be rendered in world
pub trait WorldComponent {
/// Return the types and number of descriptors this component wants to use
fn descriptors(&self) -> HashMap<vk::DescriptorType, u32>;
/// Return the layout of the descriptor set this component wants to used,
/// the used descriptors should match [`WorldComponent::descriptors`]
fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout;
/// Write to this components descriptor set, the passed descriptor set
/// is layed out like was requested in [`WorldComponent::desc_layout`]
///
/// Skybox is currently passed in for reflections
///
/// Note:
/// This api might be changed to having this functions return the
/// [`vk::WriteDescriptorSet`]s it wants, so that the render context
/// does not have to be passed in
fn write_desc_set(
&self,
ctx: &RenderCtx,
desc_set: vk::DescriptorSet,
skybox: (vk::ImageView, vk::Sampler),
);
/// Return the pipeline and pipeline layout this component wants to use,
/// this function is called every frame, so the component should not
/// contruct the pipeline in this function, but merely return an earlier
/// contructed pipeline
fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout);
/// Return the constants that must be pushed to the shader,
/// camera, screen resolution and base color are provided
/// as they are relevant for most shaders.
/// Return in `(stage, offset, data)`
fn push_constants(
&self,
camera: &Camera,
res: [u32; 2],
base_color: [f32; 4],
) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)>;
}
/// A [`WorldComponent`] and its associated [`vk::DescriptorSet`]
pub struct Component {
pub inner: Box<dyn WorldComponent>,
pub desc_set: vk::DescriptorSet,
}
impl Game<RenderAvailable> {
/// Start the loading of a world
///
/// FIXME: This should not remain in [`crate::render`],
/// the function here should be replaced with one for render
/// specifically and this should be moved to [`crate::game`]
/// and call the render specific function from there
pub fn start_load_world(self) -> Game<LoadingWorld> {
let Self {
state:
RenderAvailable {
sdl_context,
ctx,
window,
},
} = self;
let depth_mem = ctx.mem_alloc(MemType::HostInvisible, (MAX_WIDTH * MAX_HEIGHT * 8) as u64);
let pass = setup_render_pass(&ctx);
let surface_loader = surface::Instance::new(&ctx.entry, &ctx.instance);
let swapchain_loader = swapchain::Device::new(&ctx.instance, &ctx.dev);
let (swapchain, extents) = setup_swapchain(
&ctx,
&surface_loader,
&swapchain_loader,
window.width,
window.height,
);
let depth_img = make_depth_img(&ctx, window.width, window.height, depth_mem);
let swap_images = make_swap_images(swapchain, &swapchain_loader);
let swap_views = make_swap_views(&ctx, &swap_images, vk::Format::B8G8R8A8_UNORM);
let depth_view = make_depth_view(&ctx, depth_img, vk::Format::D32_SFLOAT);
let framebufs = make_framebufs(
&ctx,
&swap_views,
&depth_view,
&pass,
window.width,
window.height,
);
let skybox = ctx.create_skybox(pass);
let requested_descriptors = skybox.descriptors();
Game {
state: LoadingWorld {
sdl_context,
ctx,
window,
requested_descriptors,
swapchain: SwapchainCtx {
surface_loader,
swapchain_loader,
swap_images,
swap_views,
depth_mem,
depth_image: depth_img,
depth_view,
framebufs,
swapchain,
},
pass,
skybox,
components: Vec::new(),
},
}
}
}
impl Game<LoadingWorld> {
/// Add a component to be rendered to the world, see [`crate::render::cube`]
/// for an example for such a component
pub fn add_component<C: WorldComponent + 'static>(&mut self, component: C) {
let Self {
state:
LoadingWorld {
requested_descriptors,
components,
..
},
} = self;
for (desc, n) in component.descriptors() {
match requested_descriptors.remove(&desc) {
Some(m) => requested_descriptors.insert(desc, n + m),
None => requested_descriptors.insert(desc, n),
};
}
components.push(Box::new(component));
}
/// Finish loading of a world, it is now ready for play
///
/// FIXME: This should not remain in [`crate::render`],
/// the function here should be replaced with one for render
/// specifically and this should be moved to [`crate::game`]
/// and call the render specific function from there
pub fn start_world(self) -> Game<InWorld> {
println!("Starting world");
let Self {
state:
LoadingWorld {
sdl_context,
window,
ctx,
requested_descriptors,
swapchain,
pass,
skybox,
components,
},
} = self;
let desc_pool = setup_desc_pool(&ctx, requested_descriptors, (components.len() + 1) as u32);
let mut desc_sets = make_desc_sets(
&ctx,
&desc_pool,
&components
.iter()
.map(|c| c.desc_layout(&ctx))
.chain(iter::once(skybox.desc_layout(&ctx)))
.collect::<Vec<_>>(),
);
let skybox_descriptor = desc_sets.pop().unwrap();
let components = components
.into_iter()
.zip(desc_sets.into_iter())
.map(|(c, d)| Component {
inner: c,
desc_set: d,
})
.collect::<Vec<_>>();
for comp in &components {
let Component { inner, desc_set } = comp;
inner.write_desc_set(&ctx, *desc_set, (skybox.imageview, skybox.sampler));
}
skybox.write_desc_set(&ctx, skybox_descriptor, (skybox.imageview, skybox.sampler));
let sem_avail = ctx.make_sem();
let sem_finish = ctx.make_sem();
let fence_flight = ctx.make_fence();
println!("Starting world");
Game {
state: InWorld {
sdl_context,
window,
ctx,
skybox: (skybox, skybox_descriptor),
components,
sem_avail,
sem_finish,
fence_flight,
swapchain,
pass,
camera: Camera::default(),
},
}
}
}
fn setup_render_pass(ctx: &RenderCtx) -> vk::RenderPass {
let color_attach = vk::AttachmentDescription::default()
.format(vk::Format::B8G8R8A8_UNORM)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::STORE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
let depth_attach = vk::AttachmentDescription::default()
.format(vk::Format::D32_SFLOAT)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::DONT_CARE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
let color_ref = vk::AttachmentReference::default()
.attachment(0)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let depth_ref = vk::AttachmentReference::default()
.attachment(1)
.layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
let color_refs = [color_ref];
let depth_refs = [depth_ref];
let subpass = vk::SubpassDescription::default()
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.color_attachments(color_refs.as_ref())
.depth_stencil_attachment(&depth_ref);
let subpass_dep = vk::SubpassDependency::default()
.src_subpass(vk::SUBPASS_EXTERNAL)
.dst_subpass(0)
.src_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS,
)
.dst_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS,
)
.src_access_mask(vk::AccessFlags::empty())
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
);
let attach_desc = [color_attach, depth_attach];
let subpasses = [subpass];
let deps = [subpass_dep];
let pass_info = vk::RenderPassCreateInfo::default()
.attachments(&attach_desc)
.subpasses(subpasses.as_ref())
.dependencies(deps.as_ref());
let pass = unsafe {
ctx.dev
.create_render_pass(&pass_info, None)
.expect("Failed to create render pass")
};
pass
}
fn setup_desc_pool(
ctx: &RenderCtx,
requested_descriptors: HashMap<vk::DescriptorType, u32>,
num_sets: u32,
) -> vk::DescriptorPool {
let pool_sizes = requested_descriptors
.into_iter()
.map(|(desc, count)| {
vk::DescriptorPoolSize::default()
.ty(desc)
.descriptor_count(count)
})
.collect::<Vec<_>>();
let pool_info = vk::DescriptorPoolCreateInfo::default()
.max_sets(num_sets)
.pool_sizes(&pool_sizes);
let pool = unsafe {
ctx.dev
.create_descriptor_pool(&pool_info, None)
.expect("Failed to create descriptor pool")
};
pool
}
fn make_desc_sets(
ctx: &RenderCtx,
desc_pool: &vk::DescriptorPool,
layouts: &[vk::DescriptorSetLayout],
) -> Vec<vk::DescriptorSet> {
let alloc_info = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(*desc_pool)
.set_layouts(&layouts);
let sets = unsafe {
ctx.dev
.allocate_descriptor_sets(&alloc_info)
.expect("Failed to allocate descriptor sets")
};
sets
}

@ -0,0 +1,210 @@
use ash::{
khr::{surface, swapchain as khr_swapchain},
vk,
};
use crate::RenderCtx;
mod cube;
pub mod in_world;
mod init;
pub mod loading_world;
pub mod skybox;
mod swapchain;
const MAX_WIDTH: u32 = 3440;
const MAX_HEIGHT: u32 = 1440;
pub struct SwapchainCtx {
surface_loader: surface::Instance,
swapchain_loader: khr_swapchain::Device,
swap_images: Vec<vk::Image>,
swap_views: Vec<vk::ImageView>,
depth_mem: vk::DeviceMemory,
depth_image: vk::Image,
depth_view: vk::ImageView,
framebufs: Vec<vk::Framebuffer>,
swapchain: vk::SwapchainKHR,
}
pub enum MemType {
HostVisibile,
HostInvisible,
}
impl RenderCtx {
fn mem_alloc(&self, typ: MemType, size: u64) -> vk::DeviceMemory {
let mem_info = vk::MemoryAllocateInfo::default()
.allocation_size(size)
.memory_type_index(match typ {
MemType::HostVisibile => self.host_vis_idx,
MemType::HostInvisible => self.host_invis_idx,
});
let mem = unsafe { self.dev.allocate_memory(&mem_info, None) }
.expect("Failed to allocate memory");
println!("Allocated memory");
mem
}
fn alloc_buf(
&self,
mem: vk::DeviceMemory,
size: vk::DeviceSize,
usage: vk::BufferUsageFlags,
) -> vk::Buffer {
unsafe {
let buf_info = vk::BufferCreateInfo::default()
.size(size)
.usage(usage)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buf = self
.dev
.create_buffer(&buf_info, None)
.expect("Failed to create buffer");
self.dev
.bind_buffer_memory(buf, mem, 0)
.expect("Failed to bind buffer memory");
println!("Created buffer");
buf
}
}
fn buf_to_ptr(&self, mem: vk::DeviceMemory, buf: vk::Buffer) -> *mut std::ffi::c_void {
let ptr = unsafe {
self.dev
.map_memory(mem, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty())
}
.expect("Failed to map memory");
println!("Mapped memory");
ptr
}
fn shader_mod_from_file(
&self,
file: &str,
flags: vk::ShaderModuleCreateFlags,
) -> vk::ShaderModule {
let shader_bin = std::fs::read(file).unwrap();
unsafe {
self.dev.create_shader_module(
&vk::ShaderModuleCreateInfo {
code_size: shader_bin.len(),
p_code: shader_bin.as_ptr() as *const u32,
flags,
..Default::default()
},
None,
)
}
.expect(&format!("Failed to create shader module form file {file}"))
}
fn setup_simple_shader_stage(
&self,
vert_shader: vk::ShaderModule,
frag_shader: vk::ShaderModule,
) -> [vk::PipelineShaderStageCreateInfo; 2] {
let vert_stage = vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::VERTEX)
.module(vert_shader)
.name(c"main");
let frag_stage = vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(frag_shader)
.name(c"main");
[vert_stage, frag_stage]
}
fn make_sem(&self) -> vk::Semaphore {
let sem_info = vk::SemaphoreCreateInfo::default();
let sem = unsafe {
self.dev
.create_semaphore(&sem_info, None)
.expect("Failed to create semaphore")
};
sem
}
fn make_fence(&self) -> vk::Fence {
let fence_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED);
let fence = unsafe {
self.dev
.create_fence(&fence_info, None)
.expect("Failed to create fence")
};
fence
}
fn wait_for_fence(&self, fence: vk::Fence) {
unsafe {
let fence = [fence];
self.dev
.wait_for_fences(&fence, true, u64::MAX)
.expect("Failed to wait for fence");
}
}
fn reset_fence(&self, fence: vk::Fence) {
unsafe {
let fence = [fence];
self.dev
.reset_fences(&fence)
.expect("Failed to reset fence");
}
}
fn start_cmd_buf(&self) {
unsafe {
self.dev
.reset_command_buffer(self.cmd_buf, vk::CommandBufferResetFlags::empty())
.expect("Failed to reset command buffer");
let begin_info =
vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::empty());
self.dev
.begin_command_buffer(self.cmd_buf, &begin_info)
.expect("Failed to begin command buffer");
}
}
fn end_cmd_buf(&self) {
unsafe {
self.dev
.end_command_buffer(self.cmd_buf)
.expect("Failed to end command buffer");
}
}
fn submit_queue(&self, sem_avail: vk::Semaphore, sem_finish: vk::Semaphore, fence: vk::Fence) {
let cmd_bufs = [self.cmd_buf];
let wait_sems = [sem_avail];
let signal_sems = [sem_finish];
let submit_info = vk::SubmitInfo::default()
.command_buffers(&cmd_bufs)
.wait_semaphores(&wait_sems)
.wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT])
.signal_semaphores(&signal_sems);
unsafe {
self.dev
.queue_submit(self.queue, &[submit_info], fence)
.expect("Failed to submit queue");
}
}
fn queue_present(&self, swapchain: &SwapchainCtx, img_inx: u32, sem_finish: vk::Semaphore) {
let swapchains = [swapchain.swapchain];
let img_inxs = [img_inx];
let wait_sems = [sem_finish];
let present_info = vk::PresentInfoKHR::default()
.swapchains(&swapchains)
.image_indices(&img_inxs)
.wait_semaphores(&wait_sems);
unsafe {
swapchain
.swapchain_loader
.queue_present(self.queue, &present_info)
.expect("Failed to present queue");
}
}
}

@ -0,0 +1,305 @@
use std::collections::HashMap;
use ash::vk::{self};
use crate::{RenderCtx, decode_rif, game::Camera};
use super::{MemType, loading_world::WorldComponent};
pub struct Skybox {
pub buf: vk::Buffer,
pub image: vk::Image,
pub imageview: vk::ImageView,
pub sampler: vk::Sampler,
pipeline: vk::Pipeline,
pipe_layout: vk::PipelineLayout,
}
impl WorldComponent for Skybox {
fn descriptors(&self) -> HashMap<vk::DescriptorType, u32> {
let mut map = HashMap::new();
map.insert(vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 1);
map
}
fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout {
setup_desc_layout(ctx)
}
fn write_desc_set(
&self,
ctx: &RenderCtx,
desc_set: vk::DescriptorSet,
_: (vk::ImageView, vk::Sampler),
) {
let img_info = vk::DescriptorImageInfo::default()
.image_layout(vk::ImageLayout::GENERAL)
.image_view(self.imageview)
.sampler(self.sampler);
let img_infos = &[img_info];
let img_desc = vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(0)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(img_infos);
unsafe {
ctx.dev.update_descriptor_sets(&[img_desc], &[]);
}
}
fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) {
(self.pipeline, self.pipe_layout)
}
fn push_constants(
&self,
camera: &Camera,
res: [u32; 2],
base_color: [f32; 4],
) -> Vec<(vk::ShaderStageFlags, u32, Vec<u8>)> {
let mut constants = Vec::new();
let cam_data: Vec<u8> = (Vec::<f32>::from([
camera.origin.x,
camera.origin.y,
camera.origin.z,
0.0,
camera.rotation.x,
camera.rotation.y,
camera.rotation.z,
]))
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data));
let screen_res: Vec<u8> = res
.iter()
.map(|f| f.to_ne_bytes())
.collect::<Vec<_>>()
.into_flattened();
constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res));
constants
}
}
impl Skybox {}
impl RenderCtx {
pub(super) fn create_skybox(&self, pass: vk::RenderPass) -> Skybox {
let skybox_mem = self.mem_alloc(MemType::HostVisibile, 3 * 2048 * 2048 * 6);
let skybox_buf = self.alloc_buf(
skybox_mem,
3 * 2048 * 2048 * 6,
vk::BufferUsageFlags::TRANSFER_SRC,
);
let skybox_ptr = self.buf_to_ptr(skybox_mem, skybox_buf);
let img = std::fs::read("resources/skybox.rif").unwrap();
let data = decode_rif(&img);
unsafe {
std::ptr::copy(data.as_ptr(), skybox_ptr as *mut u8, data.len());
}
let image = make_skybox_image(self);
let imageview = make_skybox_image_view(self, image);
let sampler = make_skybox_sampler(self);
let pipe_layout = setup_pipe_layout(self);
let pipeline = setup_pipeline(self, pass, pipe_layout);
Skybox {
buf: skybox_buf,
image,
imageview,
sampler,
pipeline,
pipe_layout,
}
}
}
fn make_skybox_image(ctx: &RenderCtx) -> vk::Image {
let qf_idxs = [ctx.queue_idx];
let create_info = vk::ImageCreateInfo::default()
.flags(vk::ImageCreateFlags::CUBE_COMPATIBLE)
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::R8G8B8_UNORM)
.extent(vk::Extent3D::default().width(2048).height(2048).depth(1))
.mip_levels(1)
.array_layers(6)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED)
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.queue_family_indices(&qf_idxs);
let image =
unsafe { ctx.dev.create_image(&create_info, None) }.expect("Failed to create image");
let skybox_mem_size: vk::MemoryRequirements =
unsafe { ctx.dev.get_image_memory_requirements(image) };
let skybox_dev_mem = ctx.mem_alloc(MemType::HostInvisible, skybox_mem_size.size);
unsafe { ctx.dev.bind_image_memory(image, skybox_dev_mem, 0) }
.expect("Failed to bind image memory");
image
}
fn make_skybox_image_view(ctx: &RenderCtx, image: vk::Image) -> vk::ImageView {
let create_info = vk::ImageViewCreateInfo::default()
.image(image)
.view_type(vk::ImageViewType::CUBE)
.format(vk::Format::R8G8B8_UNORM)
.components(vk::ComponentMapping::default())
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(6),
);
unsafe { ctx.dev.create_image_view(&create_info, None) }.expect("Failed to create image view")
}
fn make_skybox_sampler(ctx: &RenderCtx) -> vk::Sampler {
let create_info = vk::SamplerCreateInfo::default()
.mag_filter(vk::Filter::LINEAR)
.min_filter(vk::Filter::LINEAR)
.mipmap_mode(vk::SamplerMipmapMode::LINEAR)
.address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE)
.address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE)
.address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE)
.max_anisotropy(1.0)
.max_lod(1.0)
.border_color(vk::BorderColor::FLOAT_OPAQUE_WHITE);
unsafe { ctx.dev.create_sampler(&create_info, None) }.expect("Failed to create sampler")
}
fn load_skybox_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) {
let skybox_vert_shader = ctx.shader_mod_from_file(
"shaders/skybox_vert.spv",
vk::ShaderModuleCreateFlags::empty(),
);
let skybox_frag_shader = ctx.shader_mod_from_file(
"shaders/skybox_frag.spv",
vk::ShaderModuleCreateFlags::empty(),
);
(skybox_vert_shader, skybox_frag_shader)
}
fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout {
let image_binding = vk::DescriptorSetLayoutBinding::default()
.binding(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::FRAGMENT);
let layouts = [image_binding];
let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts);
let layout = unsafe { ctx.dev.create_descriptor_set_layout(&layout_info, None) }
.expect("Failed to create descriptor set layout");
layout
}
fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout {
let layout = setup_desc_layout(ctx);
let push_range: [vk::PushConstantRange; 1] = [
vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::VERTEX)
.offset(0)
.size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res
];
let layouts = &[layout];
let pipe_layout = vk::PipelineLayoutCreateInfo::default()
.set_layouts(layouts.as_ref())
.push_constant_ranges(&push_range);
let pipe_layout = unsafe {
ctx.dev
.create_pipeline_layout(&pipe_layout, None)
.expect("Failed to create pipeline layout")
};
pipe_layout
}
fn setup_pipeline(
ctx: &RenderCtx,
pass: vk::RenderPass,
layout: vk::PipelineLayout,
) -> vk::Pipeline {
let (vert_shader, frag_shader) = load_skybox_shaders(ctx);
let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader);
let vert_input = vk::PipelineVertexInputStateCreateInfo::default();
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST);
let rasterization = vk::PipelineRasterizationStateCreateInfo::default()
.polygon_mode(vk::PolygonMode::FILL)
.cull_mode(vk::CullModeFlags::NONE)
.front_face(vk::FrontFace::CLOCKWISE)
.line_width(1.0);
let multisample = vk::PipelineMultisampleStateCreateInfo::default()
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
.depth_test_enable(true)
.depth_write_enable(true)
.depth_compare_op(vk::CompareOp::LESS)
.depth_bounds_test_enable(true)
.stencil_test_enable(false)
.min_depth_bounds(0.0)
.max_depth_bounds(1.0);
let blend = vk::PipelineColorBlendAttachmentState::default()
.src_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_color_blend_factor(vk::BlendFactor::SRC_COLOR)
.color_blend_op(vk::BlendOp::ADD)
.src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
.alpha_blend_op(vk::BlendOp::ADD)
.color_write_mask(
vk::ColorComponentFlags::R
| vk::ColorComponentFlags::G
| vk::ColorComponentFlags::B
| vk::ColorComponentFlags::A,
);
let blend_attachments = [blend];
let color_blend = vk::PipelineColorBlendStateCreateInfo::default()
.attachments(blend_attachments.as_ref())
.logic_op_enable(false)
.logic_op(vk::LogicOp::COPY)
.blend_constants([1.0, 1.0, 1.0, 1.0]);
let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[
vk::DynamicState::VIEWPORT_WITH_COUNT,
vk::DynamicState::SCISSOR_WITH_COUNT,
]);
let pipe_info = vk::GraphicsPipelineCreateInfo::default()
.stages(&shader_stages)
.vertex_input_state(&vert_input)
.input_assembly_state(&input_assembly)
.rasterization_state(&rasterization)
.multisample_state(&multisample)
.depth_stencil_state(&depth_stencil)
.color_blend_state(&color_blend)
.layout(layout)
.dynamic_state(&dynamic)
.render_pass(pass)
.subpass(0);
let pipe = unsafe {
ctx.dev
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None)
.expect("Failed to create graphics pipeline")
.remove(0)
};
println!("Created pipeline");
pipe
}

@ -0,0 +1,203 @@
use std::cmp;
use ash::{
khr::{surface, swapchain as khr_swapchain},
vk,
};
use crate::{
RenderCtx,
render::{MAX_HEIGHT, MAX_WIDTH},
};
pub fn setup_swapchain(
ctx: &RenderCtx,
surface_loader: &surface::Instance,
swapchain_loader: &khr_swapchain::Device,
width: u32,
height: u32,
) -> (vk::SwapchainKHR, vk::Extent2D) {
let caps = unsafe {
surface_loader
.get_physical_device_surface_capabilities(ctx.pdev, ctx.surface)
.expect("Failed to get surface capabilities")
};
let formats = unsafe {
surface_loader
.get_physical_device_surface_formats(ctx.pdev, ctx.surface)
.expect("Failed to get surface formats")
};
let format = (formats)
.iter()
.filter(|f| {
f.format == vk::Format::B8G8R8A8_UNORM
&& f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
})
.next()
.expect("No suitable format found");
// When the window is resized fast, then the caps goes out of sync of the target extents
// Thus we are using the passed-in (target) extents instead of the cap extents.
// assert_eq!(caps.current_extent.width, width);
// assert_eq!(caps.current_extent.height, height);
let swap_create = vk::SwapchainCreateInfoKHR::default()
.surface(ctx.surface)
.min_image_count(caps.min_image_count)
.image_format(format.format)
.image_color_space(format.color_space)
.image_extent(vk::Extent2D {
width: cmp::min(cmp::max(caps.current_extent.width, width), MAX_WIDTH),
height: cmp::min(cmp::max(caps.current_extent.height, height), MAX_HEIGHT),
})
.image_array_layers(1)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.present_mode(vk::PresentModeKHR::MAILBOX)
.clipped(true);
let swapchain = unsafe {
swapchain_loader
.create_swapchain(&swap_create, None)
.expect("Failed to create swapchain")
};
println!("Created swapchain");
(swapchain, caps.current_extent)
}
pub fn make_swap_images(
swapchain: vk::SwapchainKHR,
swapchain_loader: &khr_swapchain::Device,
) -> Vec<vk::Image> {
unsafe {
let images = swapchain_loader
.get_swapchain_images(swapchain)
.expect("Failed to get swapchain images");
println!("Fetched swapchain images");
images
}
}
pub fn make_swap_views(
ctx: &RenderCtx,
swap_images: &[vk::Image],
format: vk::Format,
) -> Vec<vk::ImageView> {
let mut views = Vec::new();
for img in swap_images.iter() {
let view_info = vk::ImageViewCreateInfo::default()
.image(*img)
.view_type(vk::ImageViewType::TYPE_2D)
.format(format)
.components(vk::ComponentMapping::default())
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1),
);
let view = unsafe {
ctx.dev
.create_image_view(&view_info, None)
.expect("Failed to create image view")
};
views.push(view);
}
views
}
pub fn make_depth_img(
ctx: &RenderCtx,
width: u32,
height: u32,
mem: vk::DeviceMemory,
) -> vk::Image {
let queue_fams = [ctx.queue_idx];
let img_info = vk::ImageCreateInfo::default()
.flags(vk::ImageCreateFlags::empty())
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::D32_SFLOAT)
.extent(vk::Extent3D {
width,
height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.queue_family_indices(&queue_fams)
.initial_layout(vk::ImageLayout::UNDEFINED);
let img = unsafe {
ctx.dev
.create_image(&img_info, None)
.expect("Failed to create image")
};
println!("Created image");
unsafe {
ctx.dev
.bind_image_memory(img, mem, 0)
.expect("Failed to bind image memory")
};
img
}
pub fn make_depth_view(ctx: &RenderCtx, depth_img: vk::Image, format: vk::Format) -> vk::ImageView {
let view_info = vk::ImageViewCreateInfo::default()
.image(depth_img)
.view_type(vk::ImageViewType::TYPE_2D)
.format(format)
.components(vk::ComponentMapping::default())
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(vk::ImageAspectFlags::DEPTH)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1),
);
let view = unsafe {
ctx.dev
.create_image_view(&view_info, None)
.expect("Failed to create image view")
};
view
}
pub fn make_framebufs(
ctx: &RenderCtx,
swap_views: &Vec<vk::ImageView>,
depth_view: &vk::ImageView,
pass: &vk::RenderPass,
width: u32,
height: u32,
) -> Vec<vk::Framebuffer> {
let mut framebufs = Vec::new();
for view in swap_views.iter() {
let attachments = [*view, *depth_view];
let framebuf_info = vk::FramebufferCreateInfo::default()
.render_pass(*pass)
.attachments(attachments.as_ref())
.width(width)
.height(height)
.layers(1);
let framebuf = unsafe {
ctx.dev
.create_framebuffer(&framebuf_info, None)
.expect("Failed to create framebuffer")
};
framebufs.push(framebuf);
}
framebufs
}
Loading…
Cancel
Save