diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ecf7da6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,335 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "ash-window" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52bca67b61cb81e5553babde81b8211f713cb6db79766f80168f3e5f40ea6c82" +dependencies = [ + "ash", + "raw-window-handle", + "raw-window-metal", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags", + "core-foundation", + "libc", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "mineclone" +version = "0.1.0" +dependencies = [ + "ash", + "ash-window", + "raw-window-handle", + "sdl2", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e8caa82e31bb98fee12fa8f051c94a6aa36b07cddb03f0d4fc558988360ff1" +dependencies = [ + "cocoa", + "core-graphics", + "objc", + "raw-window-handle", +] + +[[package]] +name = "sdl2" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "raw-window-handle", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index eaf6834..4a8ee93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +ash = { version = "0.38.0", features = ["linked"] } +ash-window = "0.13.0" +raw-window-handle = "0.6.2" +sdl2 = { version = "0.37.0", features = ["raw-window-handle"] } diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..239f7f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +BIN = target/debug/mineclone + +SHADERS = shaders/vert.spv shaders/frag.spv +SOURCES = $(shell find src -name '*.rs') + +all: $(SHADERS) $(BIN) + +run: all + cargo run + +$(BIN): $(SOURCES) + cargo build + +%.spv: %.vert + glslc $< -o $@ + +%.spv: %.frag + glslc $< -o $@ diff --git a/shaders/frag.frag b/shaders/frag.frag new file mode 100644 index 0000000..7be9bb8 --- /dev/null +++ b/shaders/frag.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(location = 0) out vec4 outColor; + +layout(location = 0) in vec4 normal; +layout(location = 1) in vec4 pos_pre; + +layout(push_constant, std430) uniform pc { + layout(offset=32) vec4 data; +}; + +void main() { + 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); +//} + //outColor = normal.xyz; + //outColor = vec4(vec3(1.0-gl_FragCoord.z), 1.0); +} diff --git a/shaders/frag.spv b/shaders/frag.spv new file mode 100644 index 0000000..f52d8db Binary files /dev/null and b/shaders/frag.spv differ diff --git a/shaders/vert.spv b/shaders/vert.spv new file mode 100644 index 0000000..afe0599 Binary files /dev/null and b/shaders/vert.spv differ diff --git a/shaders/vert.vert b/shaders/vert.vert new file mode 100644 index 0000000..7a2a36b --- /dev/null +++ b/shaders/vert.vert @@ -0,0 +1,109 @@ +#version 450 +// vim: ft=c + +layout(std430, set = 0, binding = 0) buffer positions_buffer { + mat2x4 posnrm[]; +} pos; + +layout(push_constant, std430) uniform pc { + layout(offset=0) vec4 cam_orig; + layout(offset=16) vec4 cam_rot; +}; + +layout (location = 0) out vec4 normal; +layout (location = 1) 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 + normal = pos.posnrm[gl_VertexIndex][1]; + pos_pre = pos.posnrm[gl_VertexIndex][0]; + + // define constants + const float zFar = 100.0; + const float zNear = 0.1; + + // assign the transformee + gl_Position = pos.posnrm[gl_VertexIndex][0]; + + mat4 view_orig = mat4( + 1.0, 0.0, 0.0, cam_orig.x, + 0.0, 1.0, 0.0, cam_orig.y, + 0.0, 0.0, 1.0, cam_orig.z, + 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( + (1080.0/1920.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 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 + ); + + // apply view's origin transformation + //gl_Position.xyz += cam_orig.xyz; + gl_Position *= view_orig; + + // 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; +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..9504522 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,747 @@ +#![feature(inherent_str_constructors)] +#![allow(unused_variables, unused_mut)] + +use ash::Device; +use ash::Entry; +use ash::Instance; +use ash::khr::{surface, swapchain}; +use ash::vk; + +use std::ffi::CStr; +use std::ffi::CString; + +use raw_window_handle::HasDisplayHandle; +use raw_window_handle::HasWindowHandle; + +use sdl2::video::Window; + +const APP_NAME: &str = "MineClone"; + +const MESH_SIZE: u64 = 36 * 2 * 4 * 4; + +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, +]; + +fn create_instance(window: &Window, entry: &Entry) -> Instance { + unsafe { + 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::>(); + let exts: &[*const i8] = exts.as_slice(); + + let insinfo = vk::InstanceCreateInfo::default() + .application_info(&appinfo) + .enabled_extension_names(exts); + + let instance = entry + .create_instance(&insinfo, None) + .expect("Failed to create instance"); + println!("Created instance"); + instance + } +} + +fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice { + unsafe { + let mut pdevs = instance + .enumerate_physical_devices() + .expect("Failed to enumerate devices"); + for (i, d) in pdevs.iter().enumerate() { + let props = 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: {}", + 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); + } +} + +fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR { + unsafe { + let surface = 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 + } +} + +// Using Intel GPU hardware terminology 🥴 +fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option { + unsafe { + let qfams = 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::>(); + 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 { + unsafe { + let mem_props = 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 { + unsafe { + let mem_props = 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; + } +} + +fn mem_alloc(dev: &Device, mem_type_inx: u32, size: u64) -> vk::DeviceMemory { + unsafe { + let mem_info = vk::MemoryAllocateInfo::default() + .allocation_size(size) + .memory_type_index(mem_type_inx); + let mem = dev + .allocate_memory(&mem_info, None) + .expect("Failed to allocate memory"); + println!("Allocated memory"); + mem + } +} + +fn make_depth_img( + dev: &Device, + host_invisible_inx: u32, + width: u32, + height: u32, + mem: vk::DeviceMemory, + qfam: u32, +) -> vk::Image { + unsafe { + let queue_fams = [qfam]; + 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 = dev + .create_image(&img_info, None) + .expect("Failed to create image"); + println!("Created image"); + + dev.bind_image_memory(img, mem, 0) + .expect("Failed to bind image memory"); + + img + } +} + +fn alloc_buf( + dev: &Device, + mem: &vk::DeviceMemory, + size: u64, + usage: vk::BufferUsageFlags, +) -> vk::Buffer { + unsafe { + let buf_info = vk::BufferCreateInfo::default() + .size(size) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + let buf = dev + .create_buffer(&buf_info, None) + .expect("Failed to create buffer"); + + dev.bind_buffer_memory(buf, *mem, 0) + .expect("Failed to bind buffer memory"); + println!("Created buffer"); + buf + } +} + +fn buf_to_ptr(dev: &Device, mem: &vk::DeviceMemory, buf: &vk::Buffer) -> *mut std::ffi::c_void { + unsafe { + let ptr = dev + .map_memory(*mem, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty()) + .expect("Failed to map memory"); + println!("Mapped memory"); + ptr + } +} + +fn setup_desc_layout(dev: &Device) -> 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 layouts = [storage_binding]; + + let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); + let layout = unsafe { + dev.create_descriptor_set_layout(&layout_info, None) + .expect("Failed to create descriptor set layout") + }; + layout +} + +fn setup_render_pass(dev: &Device) -> 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 { + dev.create_render_pass(&pass_info, None) + .expect("Failed to create render pass") + }; + pass +} + +fn setup_pipe_layout(dev: &Device) -> vk::PipelineLayout { + let layout = setup_desc_layout(&dev); + let push_range: [vk::PushConstantRange; 2] = [ + vk::PushConstantRange::default() + .stage_flags(vk::ShaderStageFlags::VERTEX) + .offset(0) + .size(32), // vec4 camera_orig, camera_rot + vk::PushConstantRange::default() + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .offset(32) + .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 { + dev.create_pipeline_layout(&pipe_layout, None) + .expect("Failed to create pipeline layout") + }; + pipe_layout +} + +fn setup_shader_stage( + dev: &Device, + 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(CStr::from_bytes_with_nul(b"main\0").unwrap()); + let frag_stage = vk::PipelineShaderStageCreateInfo::default() + .stage(vk::ShaderStageFlags::FRAGMENT) + .module(frag_shader) + .name(CStr::from_bytes_with_nul(b"main\0").unwrap()); + [vert_stage, frag_stage] +} + +fn setup_pipeline( + dev: &Device, + vert_shader: &vk::ShaderModule, + frag_shader: &vk::ShaderModule, +) -> vk::Pipeline { + let pipe_layout = setup_pipe_layout(&dev); + let pass = setup_render_pass(&dev); + let shader_stages = setup_shader_stage(&dev, *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::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(pipe_layout) + .dynamic_state(&dynamic) + .render_pass(pass) + .subpass(0); + + let pipe = unsafe { + dev.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None) + .expect("Failed to create graphics pipeline") + .remove(0) + }; + println!("Created pipeline"); + pipe +} + +// TODO dynamic state for resizing +fn setup_swapchain( + pdev: &vk::PhysicalDevice, + dev: &Device, + surface: &vk::SurfaceKHR, + surface_loader: &surface::Instance, + swapchain_loader: &ash::khr::swapchain::Device, + instance: &Instance, +) -> vk::SwapchainKHR { + let caps = unsafe { + surface_loader + .get_physical_device_surface_capabilities(*pdev, *surface) + .expect("Failed to get surface capabilities") + }; + + let formats = unsafe { + surface_loader + .get_physical_device_surface_formats(*pdev, *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"); + + let swap_create = vk::SwapchainCreateInfoKHR::default() + .surface(*surface) + .min_image_count(caps.min_image_count) + .image_format(format.format) + .image_color_space(format.color_space) + .image_extent(caps.current_extent) + .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 +} + +fn setup_desc_pool(dev: &Device) -> vk::DescriptorPool { + let pool_size = vk::DescriptorPoolSize::default() + .ty(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1); + let pool_sizes = [pool_size]; + let pool_info = vk::DescriptorPoolCreateInfo::default() + .max_sets(1) + .pool_sizes(&pool_sizes); + let pool = unsafe { + dev.create_descriptor_pool(&pool_info, None) + .expect("Failed to create descriptor pool") + }; + pool +} + +fn make_swap_images( + dev: &Device, + swapchain: &vk::SwapchainKHR, + swapchain_loader: &swapchain::Device, +) -> Vec { + unsafe { + let images = swapchain_loader + .get_swapchain_images(*swapchain) + .expect("Failed to get swapchain images"); + println!("Fetched swapchain images"); + images + } +} + +fn make_swap_views( + dev: &Device, + swap_images: &Vec, + format: vk::Format, +) -> Vec { + 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 { + dev.create_image_view(&view_info, None) + .expect("Failed to create image view") + }; + views.push(view); + } + views +} + fn main() { - println!("Hello, world!"); + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let mut width = 1024; + let mut height = 768; + + let max_width = 3440; + let max_height = 1440; + + let window = video_subsystem + .window(APP_NAME, width, height) + .vulkan() + .build() + .unwrap(); + + let entry = Entry::linked(); + + let instance = create_instance(&window, &entry); + + let surface = make_surface(&entry, &instance, &window); + + let pdev = find_pdev_dgpu(&instance); + + let rcs_queue_inx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found"); + + let dev = make_dev(&instance, &pdev, rcs_queue_inx, vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"]); + + let queue = unsafe { dev.get_device_queue(rcs_queue_inx, 0) }; + + let cmd_pool = make_command_pool(&dev, rcs_queue_inx); + + let cmd_buf = alloc_cmd_buf(&dev, cmd_pool); + + let host_visible_inx = + find_host_visible(&instance, &pdev).expect("No host visible memory found"); + + let host_invisible_inx = + find_host_invisible(&instance, &pdev).expect("No host invisible memory found"); + + let mesh_mem = mem_alloc(&dev, host_visible_inx, MESH_SIZE); + // Max viewport size is 3440x1440 + let depth_mem = mem_alloc(&dev, host_invisible_inx, max_width * max_height * 8); + + let depth_img = make_depth_img( + &dev, + host_invisible_inx, + width, + height, + depth_mem, + rcs_queue_inx, + ); + + let mesh_buf = alloc_buf( + &dev, + &mesh_mem, + MESH_SIZE, + vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER, + ); + + let mesh_mem = buf_to_ptr(&dev, &mesh_mem, &mesh_buf); + unsafe { + std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len()); + } + + let vert_shader_bin = std::fs::read("shaders/vert.spv").unwrap(); + let frag_shader_bin = std::fs::read("shaders/frag.spv").unwrap(); + + let vert_shader = unsafe { + dev.create_shader_module( + &vk::ShaderModuleCreateInfo { + code_size: vert_shader_bin.len(), + p_code: vert_shader_bin.as_ptr() as *const u32, + ..Default::default() + }, + None, + ) + .expect("Failed to create vertex shader module") + }; + + let frag_shader = unsafe { + dev.create_shader_module( + &vk::ShaderModuleCreateInfo { + code_size: frag_shader_bin.len(), + p_code: frag_shader_bin.as_ptr() as *const u32, + ..Default::default() + }, + None, + ) + .expect("Failed to create fragment shader module") + }; + + let pipe = setup_pipeline(&dev, &vert_shader, &frag_shader); + + let surface_loader = surface::Instance::new(&entry, &instance); + let swapchain_loader = swapchain::Device::new(&instance, &dev); + + let swapchain = setup_swapchain( + &pdev, + &dev, + &surface, + &surface_loader, + &swapchain_loader, + &instance, + ); + + let desc_pool = setup_desc_pool(&dev); + + let swap_images = make_swap_images(&dev, &swapchain, &swapchain_loader); + + let swap_views = make_swap_views(&dev, &swap_images, vk::Format::B8G8R8A8_UNORM); }