Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
|
c9fdcfe008 | 1 day ago |
|
32aa330b33 | 2 days ago |
|
b09eedafab | 2 days ago |
|
dbce32f625 | 2 days ago |
|
2f15842ca4 | 2 days ago |
|
8168eebf7b | 2 days ago |
|
3da43d5a69 | 2 days ago |
@ -1 +1,2 @@
|
||||
/target
|
||||
/shaders/*.spv
|
||||
|
@ -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);
|
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,
|
||||
®ions,
|
||||
);
|
||||
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…
Reference in new issue