development-vulkan #1
13 changed files with 2484 additions and 530 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,3 +5,6 @@
|
|||
/target/
|
||||
/.vscode/
|
||||
/.idea/
|
||||
|
||||
/bindgen/
|
||||
/docs/
|
1352
Cargo.lock
generated
1352
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
58
Cargo.toml
58
Cargo.toml
|
@ -40,43 +40,55 @@ members = []
|
|||
# namespace = { path = "path/to/thing" }
|
||||
|
||||
[build-dependencies]
|
||||
shaderc = "0.8.3"
|
||||
num_cpus = "1.16.0"
|
||||
jobserver = "0.1.32"
|
||||
shaderc = "0.8.3" # APACHE 2.0
|
||||
num_cpus = "1.16.0" # MIT
|
||||
jobserver = "0.1.32" # MIT
|
||||
gl_generator = "0.14" # APACHE 2.0
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1.41", features = [
|
||||
"max_level_debug",
|
||||
"release_max_level_warn",
|
||||
] }
|
||||
tracing-subscriber = "0.3.19"
|
||||
log = "0.4.25"
|
||||
] } # MIT
|
||||
tracing-subscriber = "0.3.19" # MIT
|
||||
log = "0.4.25" # MIT
|
||||
|
||||
anyhow = "1.0.95"
|
||||
thiserror = "2.0.11"
|
||||
anyhow = "1.0.95" # MIT
|
||||
thiserror = "2.0.11" # MIT
|
||||
|
||||
cgmath = "0.18.0"
|
||||
png = "0.17.16"
|
||||
tobj = { version = "4.0.2", features = ["log"] }
|
||||
nalgebra = "0.33.2" # APACHE 2.0
|
||||
cgmath = "0.18.0" # APACHE 2.0
|
||||
image = "0.25.5" # MIT
|
||||
# png = "0.17.16" # MIT
|
||||
tobj = { version = "4.0.2", features = ["log"] } # MIT
|
||||
|
||||
[target.'cfg(any(target_os = "windows",target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
# vulkan
|
||||
vulkanalia = { version = "0.26.0", features = [
|
||||
"libloading",
|
||||
"provisional",
|
||||
"window",
|
||||
] }
|
||||
winit = "0.29" # dont update, many many breaking changes with no guides
|
||||
ctrlc = "3.4.5"
|
||||
tokio = { version = "1.43.0", features = ["full"] }
|
||||
] } # APACHE 2.0
|
||||
old_winit = { package = "winit", version = "0.29" } # APACHE 2.0 # dont update, many many breaking changes with no guides
|
||||
# end vulkan
|
||||
# opengl
|
||||
glutin = "0.32.2" # APACHE 2.0
|
||||
glutin-winit = "0.5.0" # MIT
|
||||
raw-window-handle = "0.6" # MIT
|
||||
winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] } # APACHE 2.0
|
||||
drm = { version = "0.14.1", optional = true } # MIT
|
||||
# end opengl
|
||||
ctrlc = "3.4.5" # MIT
|
||||
tokio = { version = "1.43.0", features = ["full"] } # MIT
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
wgpu = "24.0.0"
|
||||
wgpu = "24.0.0" # MIT
|
||||
|
||||
[target.'cfg(target_os = "vita")'.dependencies]
|
||||
vitasdk-sys = { version = "0.3.3", features = ["all-stubs"] }
|
||||
concat-idents = "1.1.5"
|
||||
libc = "0.2.153"
|
||||
rand = "0.8.5"
|
||||
vitasdk-sys = { version = "0.3.3", features = ["all-stubs"] } # MIT and VITASDK themselves use MIT as well
|
||||
concat-idents = "1.1.5" # MIT
|
||||
libc = "0.2.153" # MIT
|
||||
rand = "0.9.0" # MIT
|
||||
tokio = { version = "1.36.0", features = [
|
||||
"fs",
|
||||
"macros",
|
||||
|
@ -88,12 +100,12 @@ tokio = { version = "1.36.0", features = [
|
|||
"net",
|
||||
"tracing",
|
||||
"time",
|
||||
] }
|
||||
] } # MIT
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
metal = "0.31.0"
|
||||
objc = "0.2.7"
|
||||
metal = "0.31.0" # MIT
|
||||
objc = "0.2.7" # MIT
|
||||
|
|
138
build.rs
138
build.rs
|
@ -1,26 +1,46 @@
|
|||
// Requires crates: shaderc, num_cpus, jobserver
|
||||
// Requires crates: shaderc, num_cpus, jobserver, gl_generator
|
||||
|
||||
// This file is licensed to anyone who obtains a copy by lever1209 under the CC BY 4.0 license available here: https://creativecommons.org/licenses/by/4.0/
|
||||
|
||||
const SHADER_SRC_ROOT:&'static str = "shaders/";
|
||||
const SUPPORTED_SHADER_FORMAT_EXTENSIONS_GLSL:[&'static str; 1] = ["glsl"];
|
||||
const SUPPORTED_SHADER_FORMAT_EXTENSIONS_HLSL:[&'static str; 2] = ["cg", "hlsl"];
|
||||
const COMPILED_SHADER_EXTENSION:&'static str = ".spv";
|
||||
const FRAGMENT_EXTENSIONS:[&'static str; 1] = ["frag"];
|
||||
const VERTEX_EXTENSIONS:[&'static str; 1] = ["vert"];
|
||||
const GLSL_PREFIXES:[&'static str; 1] = ["glsl"];
|
||||
const HLSL_PREFIXES:[&'static str; 2] = ["cg", "hlsl"];
|
||||
static MAX_THREAD_COUNT:std::sync::LazyLock<usize> = std::sync::LazyLock::new(|| num_cpus::get());
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
setup_gl_bindings()?;
|
||||
|
||||
compile_shaders()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_gl_bindings() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = std::path::PathBuf::from(&std::env::var("OUT_DIR")?).join("gl_bindings.rs");
|
||||
|
||||
// Skip generation if the file is already there
|
||||
if !path.exists() {
|
||||
let mut file = std::fs::File::create(path)?;
|
||||
gl_generator::Registry::new(gl_generator::Api::Gles2, (3, 0), gl_generator::Profile::Core, gl_generator::Fallbacks::All, [])
|
||||
.write_bindings(gl_generator::StructGenerator, &mut file)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_shaders() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("cargo:rerun-if-changed={SHADER_SRC_ROOT}");
|
||||
|
||||
let client = unsafe { jobserver::Client::from_env() }.unwrap_or({
|
||||
println!("cargo:warning=Could not connect to the gnu jobserver. Assuming max supported threads.");
|
||||
jobserver::Client::new(num_cpus::get())?
|
||||
println!(
|
||||
"cargo:warning=Could not connect to the gnu jobserver. Using defined max thread count: {}.",
|
||||
*MAX_THREAD_COUNT
|
||||
);
|
||||
jobserver::Client::new(*MAX_THREAD_COUNT)?
|
||||
});
|
||||
|
||||
let out_dir = std::env::var("OUT_DIR").map_err(|err| format!("Could not read environment variable OUT_DIR: `{err}`"))?;
|
||||
|
@ -31,7 +51,7 @@ fn compile_shaders() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let mut shader_source_files = vec![];
|
||||
if let Err(err) = walk_dir(SHADER_SRC_ROOT.into(), &mut shader_source_files) {
|
||||
println!("cargo:warning=There was an error walking the shader source directory: `{err}`");
|
||||
return Err(format!("There was an error walking the shader source directory: `{err}`").into());
|
||||
};
|
||||
|
||||
for input_path in shader_source_files {
|
||||
|
@ -41,44 +61,58 @@ fn compile_shaders() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let shader_out_dir = shader_out_dir.clone();
|
||||
|
||||
running_tasks.push((input_path.clone(), std::thread::spawn(move || -> Result<(), Box<dyn std::error::Error + Sync + Send>> {
|
||||
running_tasks.push((
|
||||
// Store a copy here in case we panic and need to know which file caused the panic (realistically should never happen but whatever)
|
||||
input_path.clone(),
|
||||
std::thread::spawn(move || -> Result<(), Box<dyn std::error::Error+Sync+Send>> {
|
||||
let _aquired = aquired;
|
||||
|
||||
let input_file_stem = match input_path.file_stem().and_then(|f| <&str>::try_from(f).ok()) {
|
||||
Some(stem) => stem.to_owned(),
|
||||
None => return Err(format!("Could not read file stem of file: {input_path:?}").into()),
|
||||
};
|
||||
let output_path = shader_out_dir.join(&input_path.file_name().unwrap()); // Guaranteed to be a file because of walk_dir
|
||||
|
||||
let shader_kind = match &input_file_stem[0..=1] {
|
||||
"f_" => shaderc::ShaderKind::Fragment,
|
||||
"v_" => shaderc::ShaderKind::Vertex,
|
||||
_ => return Err(format!("Shader folder contains a file that is neither a vertex or fragment shader: {input_path:?}").into()),
|
||||
};
|
||||
|
||||
let new_file_name = input_file_stem + COMPILED_SHADER_EXTENSION;
|
||||
let output_path = shader_out_dir.join(&new_file_name);
|
||||
|
||||
if let Err(error) = compile_shader(&input_path, &output_path, shader_kind) {
|
||||
println!("cargo:warning=Compilation process for input file: {input_path:?} was not successful: `{error}`");
|
||||
println!("cargo:warning=I cant be assed to fix this myself but cargo cuts off newlines in warning messages, so if the above error gets cut off youre going to need to find the text file or re run cargo with the -vv flag");
|
||||
}
|
||||
|
||||
drop(aquired);
|
||||
Ok(())
|
||||
})));
|
||||
if let Err(error) = compile_shader(&input_path, &output_path) {
|
||||
return Err(format!("Compilation process for input file: {input_path:?} was not successful: `{error}`")
|
||||
.replace('\n', "\t")
|
||||
.into());
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
let mut has_error = false;
|
||||
|
||||
for (input_file, task) in running_tasks {
|
||||
match task.join() {
|
||||
Ok(Ok(())) => (),
|
||||
Ok(Err(err)) => {
|
||||
println!("cargo:warning=There was an error running a task: `{err}`");
|
||||
has_error = true;
|
||||
},
|
||||
Err(_) => {
|
||||
println!("cargo:warning=A thread panicked while processing this file!: {input_file:?}",);
|
||||
has_error = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
if has_error {
|
||||
Err("Cannot proceed with build process. One or more shaders failed to compile.".into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_stem_and_ext(path:&std::path::PathBuf) -> Result<(String, String), Box<dyn std::error::Error>> {
|
||||
Ok((
|
||||
match path.file_stem().and_then(|f| <&str>::try_from(f).ok()) {
|
||||
Some(stem) => stem.to_owned(),
|
||||
None => return Err(format!("Could not read file stem of file: {path:?}").into()),
|
||||
},
|
||||
match path.extension().and_then(|f| <&str>::try_from(f).ok()) {
|
||||
Some(stem) => stem.to_owned(),
|
||||
None => return Err(format!("Could not read extension of file: {path:?}").into()),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn walk_dir(path:std::path::PathBuf, file_paths_buf:&mut Vec<std::path::PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
@ -94,22 +128,48 @@ fn walk_dir(path:std::path::PathBuf, file_paths_buf:&mut Vec<std::path::PathBuf>
|
|||
}
|
||||
}
|
||||
|
||||
fn compile_shader(input_path:&std::path::PathBuf, output_path:&std::path::PathBuf, shader_kind:shaderc::ShaderKind) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn compile_shader(input_path:&std::path::PathBuf, output_path:&std::path::PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let compiler = match shaderc::Compiler::new() {
|
||||
Some(compiler) => compiler,
|
||||
None => return Err(format!("Could not initialize shaderc compiler.").into()),
|
||||
};
|
||||
|
||||
let target = if let Some(extension) = input_path.extension().and_then(|s| <&str>::try_from(s).ok()) {
|
||||
if SUPPORTED_SHADER_FORMAT_EXTENSIONS_GLSL.contains(&extension) {
|
||||
shaderc::SourceLanguage::GLSL
|
||||
} else if SUPPORTED_SHADER_FORMAT_EXTENSIONS_HLSL.contains(&extension) {
|
||||
shaderc::SourceLanguage::HLSL
|
||||
} else {
|
||||
return Err(format!("Could not determine compile target! This file has an invalid extension.").into());
|
||||
let (stem, ext) = get_stem_and_ext(&input_path)?;
|
||||
|
||||
let mut target = None;
|
||||
for glsl_prefix in GLSL_PREFIXES {
|
||||
if stem.starts_with(glsl_prefix) {
|
||||
target = Some(shaderc::SourceLanguage::GLSL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for hlsl_prefix in HLSL_PREFIXES {
|
||||
if stem.starts_with(hlsl_prefix) {
|
||||
target = Some(shaderc::SourceLanguage::HLSL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let target = if let Some(target) = target {
|
||||
target
|
||||
} else {
|
||||
return Err(format!("Could not determine compile target! This file is missing an extension.").into());
|
||||
return Err(format!("This file is neither HLSL or GLSL!").into());
|
||||
};
|
||||
|
||||
let mut shader_kind = None;
|
||||
for vertex_extension in VERTEX_EXTENSIONS {
|
||||
if ext == vertex_extension {
|
||||
shader_kind = Some(shaderc::ShaderKind::Vertex)
|
||||
}
|
||||
}
|
||||
for fragment_extension in FRAGMENT_EXTENSIONS {
|
||||
if ext == fragment_extension {
|
||||
shader_kind = Some(shaderc::ShaderKind::Fragment)
|
||||
}
|
||||
}
|
||||
let shader_kind = if let Some(shader_kind) = shader_kind {
|
||||
shader_kind
|
||||
} else {
|
||||
return Err(format!("This file is neither a fragment shader or vertex shader!").into());
|
||||
};
|
||||
|
||||
let options = match shaderc::CompileOptions::new() {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
24
shaders/glsl_default.frag
Normal file
24
shaders/glsl_default.frag
Normal file
|
@ -0,0 +1,24 @@
|
|||
#version 450
|
||||
|
||||
layout(binding = 1) uniform sampler2D texSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTexCoord;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
// outColor = texture(texSampler, fragTexCoord);
|
||||
outColor = vec4(fragColor, 1.0) * texture(texSampler, fragTexCoord).rgba;
|
||||
}
|
||||
|
||||
// #version 450
|
||||
|
||||
// layout(location = 0) in vec3 fragColor;
|
||||
// layout(location = 1) in vec2 fragTexCoord;
|
||||
|
||||
// layout(location = 0) out vec4 outColor;
|
||||
|
||||
// void main() {
|
||||
// outColor = vec4(fragTexCoord, 0.0, 1.0);
|
||||
// }
|
|
@ -8,10 +8,13 @@ layout(binding = 0) uniform UniformBufferObject {
|
|||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
fragTexCoord = inTexCoord;
|
||||
}
|
|
@ -1 +1,447 @@
|
|||
use std::error::Error;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
|
||||
use gl::types::GLfloat;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::KeyEvent;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::keyboard::Key;
|
||||
use winit::keyboard::NamedKey;
|
||||
use winit::raw_window_handle::HasWindowHandle;
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowAttributes;
|
||||
|
||||
use glutin::config::Config;
|
||||
use glutin::config::ConfigTemplateBuilder;
|
||||
use glutin::config::GetGlConfig;
|
||||
use glutin::context::ContextApi;
|
||||
use glutin::context::ContextAttributesBuilder;
|
||||
use glutin::context::NotCurrentContext;
|
||||
use glutin::context::PossiblyCurrentContext;
|
||||
use glutin::context::Version;
|
||||
use glutin::display::GetGlDisplay;
|
||||
use glutin::prelude::*;
|
||||
use glutin::surface::Surface;
|
||||
use glutin::surface::SwapInterval;
|
||||
use glutin::surface::WindowSurface;
|
||||
|
||||
use glutin_winit::DisplayBuilder;
|
||||
use glutin_winit::GlWindow;
|
||||
|
||||
pub mod gl {
|
||||
// #![allow(clippy::all)]
|
||||
|
||||
pub use Gles2 as Gl;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
}
|
||||
|
||||
pub fn main(event_loop:winit::event_loop::EventLoop<()>) -> Result<(), Box<dyn Error>> {
|
||||
// The template will match only the configurations supporting rendering
|
||||
// to windows.
|
||||
//
|
||||
// XXX We force transparency only on macOS, given that EGL on X11 doesn't
|
||||
// have it, but we still want to show window. The macOS situation is like
|
||||
// that, because we can query only one config at a time on it, but all
|
||||
// normal platforms will return multiple configs, so we can find the config
|
||||
// with transparency ourselves inside the `reduce`.
|
||||
let template = ConfigTemplateBuilder::new().with_alpha_size(8).with_transparency(cfg!(cgl_backend));
|
||||
|
||||
let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes()));
|
||||
|
||||
let mut app = App::new(template, display_builder);
|
||||
event_loop.run_app(&mut app)?;
|
||||
|
||||
app.exit_state
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop:&ActiveEventLoop) {
|
||||
let (window, gl_config) = match &self.gl_display {
|
||||
// We just created the event loop, so initialize the display, pick the config, and
|
||||
// create the context.
|
||||
GlDisplayCreationState::Builder(display_builder) => {
|
||||
let (window, gl_config) = match display_builder.clone().build(event_loop, self.template.clone(), gl_config_picker) {
|
||||
Ok((window, gl_config)) => (window.unwrap(), gl_config),
|
||||
Err(err) => {
|
||||
self.exit_state = Err(err);
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
println!("Picked a config with {} samples", gl_config.num_samples());
|
||||
|
||||
// Mark the display as initialized to not recreate it on resume, since the
|
||||
// display is valid until we explicitly destroy it.
|
||||
self.gl_display = GlDisplayCreationState::Init;
|
||||
|
||||
// Create gl context.
|
||||
self.gl_context = Some(create_gl_context(&window, &gl_config).treat_as_possibly_current());
|
||||
|
||||
(window, gl_config)
|
||||
},
|
||||
GlDisplayCreationState::Init => {
|
||||
println!("Recreating window in `resumed`");
|
||||
// Pick the config which we already use for the context.
|
||||
let gl_config = self.gl_context.as_ref().unwrap().config();
|
||||
match glutin_winit::finalize_window(event_loop, window_attributes(), &gl_config) {
|
||||
Ok(window) => (window, gl_config),
|
||||
Err(err) => {
|
||||
self.exit_state = Err(err.into());
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let attrs = window.build_surface_attributes(Default::default()).expect("Failed to build surface attributes");
|
||||
let gl_surface = unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };
|
||||
|
||||
// The context needs to be current for the Renderer to set up shaders and
|
||||
// buffers. It also performs function loading, which needs a current context on
|
||||
// WGL.
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
gl_context.make_current(&gl_surface).unwrap();
|
||||
|
||||
self.renderer.get_or_insert_with(|| Renderer::new(&gl_config.display()));
|
||||
|
||||
// Try setting vsync.
|
||||
if let Err(res) = gl_surface.set_swap_interval(gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) {
|
||||
eprintln!("Error setting vsync: {res:?}");
|
||||
}
|
||||
|
||||
assert!(self.state.replace(AppState { gl_surface, window }).is_none());
|
||||
}
|
||||
|
||||
fn suspended(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
// This event is only raised on Android, where the backing NativeWindow for a GL
|
||||
// Surface can appear and disappear at any moment.
|
||||
println!("Android window removed");
|
||||
|
||||
// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
|
||||
// the window back to the system.
|
||||
self.state = None;
|
||||
|
||||
// Make context not current.
|
||||
self.gl_context = Some(self.gl_context.take().unwrap().make_not_current().unwrap().treat_as_possibly_current());
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop:&ActiveEventLoop, _window_id:winit::window::WindowId, event:WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => {
|
||||
// Some platforms like EGL require resizing GL surface to update the size
|
||||
// Notable platforms here are Wayland and macOS, other don't require it
|
||||
// and the function is no-op, but it's wise to resize it for portability
|
||||
// reasons.
|
||||
if let Some(AppState { gl_surface, window: _ }) = self.state.as_ref() {
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
gl_surface.resize(gl_context, NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap());
|
||||
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.resize(size.width as i32, size.height as i32);
|
||||
}
|
||||
},
|
||||
WindowEvent::CloseRequested |
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => event_loop.exit(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn exiting(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
// NOTE: The handling below is only needed due to nvidia on Wayland to not crash
|
||||
// on exit due to nvidia driver touching the Wayland display from on
|
||||
// `exit` hook.
|
||||
let _gl_display = self.gl_context.take().unwrap().display();
|
||||
|
||||
// Clear the window.
|
||||
self.state = None;
|
||||
#[cfg(egl_backend)]
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
if let glutin::display::Display::Egl(display) = _gl_display {
|
||||
unsafe {
|
||||
display.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop:&ActiveEventLoop) {
|
||||
if let Some(AppState { gl_surface, window }) = self.state.as_ref() {
|
||||
let gl_context = self.gl_context.as_ref().unwrap();
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.draw();
|
||||
window.request_redraw();
|
||||
|
||||
gl_surface.swap_buffers(gl_context).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_gl_context(window:&Window, gl_config:&Config) -> NotCurrentContext {
|
||||
let raw_window_handle = window.window_handle().ok().map(|wh| wh.as_raw());
|
||||
|
||||
// The context creation part.
|
||||
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
|
||||
|
||||
// Since glutin by default tries to create OpenGL core context, which may not be
|
||||
// present we should try gles.
|
||||
let fallback_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::Gles(None))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// There are also some old devices that support neither modern OpenGL nor GLES.
|
||||
// To support these we can try and create a 2.1 context.
|
||||
let legacy_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// Reuse the uncurrented context from a suspended() call if it exists, otherwise
|
||||
// this is the first time resumed() is called, where the context still
|
||||
// has to be created.
|
||||
let gl_display = gl_config.display();
|
||||
|
||||
unsafe {
|
||||
gl_display.create_context(gl_config, &context_attributes).unwrap_or_else(|_| {
|
||||
gl_display.create_context(gl_config, &fallback_context_attributes).unwrap_or_else(|_| {
|
||||
gl_display
|
||||
.create_context(gl_config, &legacy_context_attributes)
|
||||
.expect("failed to create context")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn window_attributes() -> WindowAttributes {
|
||||
Window::default_attributes()
|
||||
.with_transparent(true)
|
||||
.with_title("Glutin triangle gradient example (press Escape to exit)")
|
||||
}
|
||||
|
||||
enum GlDisplayCreationState {
|
||||
/// The display was not build yet.
|
||||
Builder(DisplayBuilder),
|
||||
/// The display was already created for the application.
|
||||
Init,
|
||||
}
|
||||
|
||||
struct App {
|
||||
template: ConfigTemplateBuilder,
|
||||
renderer: Option<Renderer>,
|
||||
// NOTE: `AppState` carries the `Window`, thus it should be dropped after everything else.
|
||||
state: Option<AppState>,
|
||||
gl_context:Option<PossiblyCurrentContext>,
|
||||
gl_display:GlDisplayCreationState,
|
||||
exit_state:Result<(), Box<dyn Error>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(template:ConfigTemplateBuilder, display_builder:DisplayBuilder) -> Self {
|
||||
Self {
|
||||
template,
|
||||
gl_display:GlDisplayCreationState::Builder(display_builder),
|
||||
exit_state:Ok(()),
|
||||
gl_context:None,
|
||||
state:None,
|
||||
renderer:None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
gl_surface:Surface<WindowSurface>,
|
||||
// NOTE: Window should be dropped after all resources created using its
|
||||
// raw-window-handle.
|
||||
window: Window,
|
||||
}
|
||||
|
||||
// Find the config with the maximum number of samples, so our triangle will be
|
||||
// smooth.
|
||||
pub fn gl_config_picker(configs:Box<dyn Iterator<Item=Config>+'_>) -> Config {
|
||||
configs
|
||||
.reduce(|accum, config| {
|
||||
let transparency_check = config.supports_transparency().unwrap_or(false) & !accum.supports_transparency().unwrap_or(false);
|
||||
|
||||
if transparency_check || config.num_samples() > accum.num_samples() {
|
||||
config
|
||||
} else {
|
||||
accum
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
program:gl::types::GLuint,
|
||||
vao: gl::types::GLuint,
|
||||
vbo: gl::types::GLuint,
|
||||
gl: gl::Gl,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new<D:GlDisplay>(gl_display:&D) -> Self {
|
||||
unsafe {
|
||||
let gl = gl::Gl::load_with(|symbol| {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
gl_display.get_proc_address(symbol.as_c_str()).cast()
|
||||
});
|
||||
|
||||
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
|
||||
println!("Running on {}", renderer.to_string_lossy());
|
||||
}
|
||||
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
|
||||
println!("OpenGL Version {}", version.to_string_lossy());
|
||||
}
|
||||
|
||||
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
|
||||
println!("Shaders version on {}", shaders_version.to_string_lossy());
|
||||
}
|
||||
|
||||
let vertex_shader = create_shader(&gl, gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE);
|
||||
let fragment_shader = create_shader(&gl, gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);
|
||||
|
||||
let program = gl.CreateProgram();
|
||||
|
||||
gl.AttachShader(program, vertex_shader);
|
||||
gl.AttachShader(program, fragment_shader);
|
||||
|
||||
gl.LinkProgram(program);
|
||||
|
||||
gl.UseProgram(program);
|
||||
|
||||
gl.DeleteShader(vertex_shader);
|
||||
gl.DeleteShader(fragment_shader);
|
||||
|
||||
let mut vao = std::mem::zeroed();
|
||||
gl.GenVertexArrays(1, &mut vao);
|
||||
gl.BindVertexArray(vao);
|
||||
|
||||
let mut vbo = std::mem::zeroed();
|
||||
gl.GenBuffers(1, &mut vbo);
|
||||
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
|
||||
gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
(VERTEX_DATA.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
|
||||
VERTEX_DATA.as_ptr() as *const _,
|
||||
gl::STATIC_DRAW,
|
||||
);
|
||||
|
||||
let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _);
|
||||
let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _);
|
||||
gl.VertexAttribPointer(
|
||||
pos_attrib as gl::types::GLuint,
|
||||
2,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.VertexAttribPointer(
|
||||
color_attrib as gl::types::GLuint,
|
||||
3,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
(2 * std::mem::size_of::<f32>()) as *const () as *const _,
|
||||
);
|
||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
||||
|
||||
Self { program, vao, vbo, gl }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self) { self.draw_with_clear_color(0.1, 0.1, 0.1, 0.9) }
|
||||
|
||||
pub fn draw_with_clear_color(&self, red:GLfloat, green:GLfloat, blue:GLfloat, alpha:GLfloat) {
|
||||
unsafe {
|
||||
self.gl.UseProgram(self.program);
|
||||
|
||||
self.gl.BindVertexArray(self.vao);
|
||||
self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
||||
|
||||
self.gl.ClearColor(red, green, blue, alpha);
|
||||
self.gl.Clear(gl::COLOR_BUFFER_BIT);
|
||||
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&self, width:i32, height:i32) {
|
||||
unsafe {
|
||||
self.gl.Viewport(0, 0, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Renderer {
|
||||
type Target = gl::Gl;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.gl }
|
||||
}
|
||||
|
||||
impl Drop for Renderer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.gl.DeleteProgram(self.program);
|
||||
self.gl.DeleteBuffers(1, &self.vbo);
|
||||
self.gl.DeleteVertexArrays(1, &self.vao);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_shader(gl:&gl::Gl, shader:gl::types::GLenum, source:&[u8]) -> gl::types::GLuint {
|
||||
let shader = gl.CreateShader(shader);
|
||||
gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null());
|
||||
gl.CompileShader(shader);
|
||||
shader
|
||||
}
|
||||
|
||||
fn get_gl_string(gl:&gl::Gl, variant:gl::types::GLenum) -> Option<&'static CStr> {
|
||||
unsafe {
|
||||
let s = gl.GetString(variant);
|
||||
(!s.is_null()).then(|| CStr::from_ptr(s.cast()))
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
static VERTEX_DATA: [f32; 15] = [
|
||||
-0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, -0.5, 0.0, 0.0, 1.0,
|
||||
];
|
||||
|
||||
const VERTEX_SHADER_SOURCE:&[u8] = b"
|
||||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec3 color;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
v_color = color;
|
||||
}
|
||||
\0";
|
||||
|
||||
const FRAGMENT_SHADER_SOURCE:&[u8] = b"
|
||||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(v_color, 1.0);
|
||||
}
|
||||
\0";
|
||||
|
|
|
@ -10,7 +10,6 @@ use log::*;
|
|||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::mem::size_of;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr::copy_nonoverlapping as memcpy;
|
||||
|
@ -29,7 +28,7 @@ use vulkanalia::vk::ExtDebugUtilsExtension;
|
|||
use vulkanalia::vk::KhrSurfaceExtension;
|
||||
use vulkanalia::vk::KhrSwapchainExtension;
|
||||
|
||||
use winit::window::Window;
|
||||
use old_winit::window::Window;
|
||||
|
||||
type Vec2f = cgmath::Vector2<f32>;
|
||||
type Vec3f = cgmath::Vector3<f32>;
|
||||
|
@ -63,18 +62,30 @@ const MAX_FRAMES_IN_FLIGHT:usize = 2;
|
|||
// Vertex::new(vec2(-0.5, 0.5), vec3(0.3, 0.0, 1.0)),
|
||||
// ];
|
||||
|
||||
static VERTICES:[Vertex; 4] = [
|
||||
Vertex::new(vec2(-0.5, -0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(0.5, -0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(0.5, 0.5), vec3(0.3, 0.0, 1.0)),
|
||||
Vertex::new(vec2(-0.5, 0.5), vec3(0.3, 0.0, 1.0)),
|
||||
// Vertex::new(vec2(-1.0, -1.0),vec3(0.3, 0.0, 1.0)),
|
||||
// Vertex::new(vec2(1.0, -1.0), vec3(0.3, 0.0, 1.0)),
|
||||
// Vertex::new(vec2(1.0, 1.0), vec3(0.3, 0.0, 1.0)),
|
||||
// Vertex::new(vec2(-1.0, 1.0), vec3(0.3, 0.0, 1.0)),
|
||||
#[rustfmt::skip]
|
||||
static VERTICES:&[Vertex] = &[
|
||||
// Vertex::new(vec2(-0.5, -0.5), vec3(1.0, 1.0, 1.0), vec2(1.0, 0.0)),
|
||||
// Vertex::new(vec2(0.5, -0.5), vec3(1.0, 1.0, 1.0), vec2(0.0, 0.0)),
|
||||
// Vertex::new(vec2(0.5, 0.5), vec3(1.0, 1.0, 1.0), vec2(0.0, 1.0)),
|
||||
// Vertex::new(vec2(-0.5, 0.5), vec3(1.0, 1.0, 1.0), vec2(1.0, 1.0)),
|
||||
|
||||
Vertex::new(vec3(-0.5, -0.5, 0.0), vec3(1.0, 0.0, 0.0), vec2(1.0, 0.0)),
|
||||
Vertex::new(vec3( 0.5, -0.5, 0.0), vec3(0.0, 1.0, 0.0), vec2(0.0, 0.0)),
|
||||
Vertex::new(vec3( 0.5, 0.5, 0.0), vec3(0.0, 0.0, 1.0), vec2(0.0, 1.0)),
|
||||
Vertex::new(vec3(-0.5, 0.5, 0.0), vec3(1.0, 1.0, 1.0), vec2(1.0, 1.0)),
|
||||
Vertex::new(vec3(-0.5, -0.5, -0.5), vec3(1.0, 0.0, 0.0), vec2(1.0, 0.0)),
|
||||
Vertex::new(vec3( 0.5, -0.5, -0.5), vec3(0.0, 1.0, 0.0), vec2(0.0, 0.0)),
|
||||
Vertex::new(vec3( 0.5, 0.5, -0.5), vec3(0.0, 0.0, 1.0), vec2(0.0, 1.0)),
|
||||
Vertex::new(vec3(-0.5, 0.5, -0.5), vec3(1.0, 1.0, 1.0), vec2(1.0, 1.0)),
|
||||
];
|
||||
const INDICES:&[u16] = &[0, 1, 2, 2, 3, 0];
|
||||
|
||||
#[rustfmt::skip]
|
||||
const INDICES:&[u16] = &[
|
||||
0, 1, 2, 2, 3, 0,
|
||||
4, 5, 6, 6, 7, 4,
|
||||
];
|
||||
|
||||
/// The shaders compiled by build.rs keep the same name and relative path as the source file exactly
|
||||
macro_rules! const_shaders {
|
||||
{ $($vis:vis $identifier:ident = $string:literal; )* } => {
|
||||
$(
|
||||
|
@ -88,6 +99,7 @@ macro_rules! const_shaders {
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct App {
|
||||
/// Vulkan entrypoint
|
||||
#[allow(unused)]
|
||||
entry: Entry,
|
||||
instance: Instance,
|
||||
data: AppData,
|
||||
|
@ -137,7 +149,12 @@ impl App {
|
|||
|
||||
/// Renders a frame for our Vulkan app.
|
||||
pub unsafe fn render(&mut self, window:&Window) -> Result<()> {
|
||||
self.device.wait_for_fences(&[self.data.in_flight_fences[self.frame]], true, u64::MAX)?;
|
||||
// self.device
|
||||
// .wait_for_fences(&[self.data.in_flight_fences[self.frame]], true, u64::MAX)?;
|
||||
|
||||
let in_flight_fence = self.data.in_flight_fences[self.frame];
|
||||
|
||||
self.device.wait_for_fences(&[in_flight_fence], true, u64::MAX)?;
|
||||
|
||||
let result = self.device.acquire_next_image_khr(
|
||||
self.data.swapchain,
|
||||
|
@ -152,12 +169,22 @@ impl App {
|
|||
Err(e) => return Err(anyhow!(e)),
|
||||
};
|
||||
|
||||
if !self.data.images_in_flight[image_index as usize].is_null() {
|
||||
self.device
|
||||
.wait_for_fences(&[self.data.images_in_flight[image_index as usize]], true, u64::MAX)?;
|
||||
// if !self.data.images_in_flight[image_index].is_null() {
|
||||
// self.device.wait_for_fences(
|
||||
// &[self.data.images_in_flight[image_index as usize]],
|
||||
// true,
|
||||
// u64::MAX,
|
||||
// )?;
|
||||
// }
|
||||
|
||||
// self.data.images_in_flight[image_index] = self.data.in_flight_fences[self.frame];
|
||||
|
||||
let image_in_flight = self.data.images_in_flight[image_index];
|
||||
if !image_in_flight.is_null() {
|
||||
self.device.wait_for_fences(&[image_in_flight], true, u64::MAX)?;
|
||||
}
|
||||
|
||||
self.data.images_in_flight[image_index as usize] = self.data.in_flight_fences[self.frame];
|
||||
self.data.images_in_flight[image_index] = in_flight_fence;
|
||||
|
||||
self.update_uniform_buffer(image_index)?;
|
||||
|
||||
|
@ -206,17 +233,24 @@ impl App {
|
|||
|
||||
let model = Mat4f::from_axis_angle(vec3(0.0, 0.0, 1.0), Deg(45.0) * time);
|
||||
|
||||
let view = Mat4f::look_at_rh(point3(2.0, 5.0, 2.0), point3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0));
|
||||
let view = Mat4f::look_at_rh(point3(2.0, 2.0, 2.0), point3(0.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0));
|
||||
|
||||
let mut proj = cgmath::perspective(
|
||||
Deg(45.0),
|
||||
self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32,
|
||||
0.1,
|
||||
10.0,
|
||||
#[rustfmt::skip]
|
||||
let correction = Mat4f::new(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
// We're also flipping the Y-axis with this line's `-1.0` because cgmath was made with opengl in mind
|
||||
0.0, -1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0 / 2.0, 0.0,
|
||||
0.0, 0.0, 1.0 / 2.0, 1.0,
|
||||
);
|
||||
|
||||
// cgmath was made with opengl in mind, this fixes it to work with vulkan
|
||||
proj[1][1] *= -1.0;
|
||||
let proj = correction *
|
||||
cgmath::perspective(
|
||||
Deg(45.0),
|
||||
self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32,
|
||||
0.1,
|
||||
10.0,
|
||||
);
|
||||
|
||||
let ubo = UniformBufferObject { model, view, proj };
|
||||
|
||||
|
@ -273,9 +307,11 @@ impl App {
|
|||
self.device.free_memory(self.data.vertex_buffer_memory, None);
|
||||
self.device.destroy_buffer(self.data.vertex_buffer, None);
|
||||
|
||||
self.device.destroy_sampler(self.data.texture_sampler, None);
|
||||
self.device.destroy_image_view(self.data.texture_image_view, None);
|
||||
|
||||
self.device.free_memory(self.data.texture_image_memory, None);
|
||||
self.device.destroy_image(self.data.texture_image, None);
|
||||
self.device.destroy_image_view(self.data.texture_image_view, None);
|
||||
|
||||
self.device.destroy_command_pool(self.data.command_pool, None);
|
||||
|
||||
|
@ -343,6 +379,7 @@ struct AppData {
|
|||
texture_image: vk::Image,
|
||||
texture_image_memory: vk::DeviceMemory,
|
||||
texture_image_view: vk::ImageView,
|
||||
texture_sampler: vk::Sampler,
|
||||
}
|
||||
|
||||
unsafe fn create_instance(window:&Window, entry:&Entry, data:&mut AppData) -> Result<Instance> {
|
||||
|
@ -351,14 +388,14 @@ unsafe fn create_instance(window:&Window, entry:&Entry, data:&mut AppData) -> Re
|
|||
// Lets hope one day we will be on this list
|
||||
|
||||
let application_info = vk::ApplicationInfo::builder()
|
||||
// Our application information
|
||||
.application_name(VK_APPLICATION_NAME)
|
||||
.application_version(vk::make_version(0, 1, 0))
|
||||
// Our engine information
|
||||
.engine_name(VK_ENGINE_NAME)
|
||||
.engine_version(vk::make_version(0, 1, 0))
|
||||
// Vulkan api version target
|
||||
.api_version(vk::make_version(1, 0, 0));
|
||||
// Our application information
|
||||
.application_name(VK_APPLICATION_NAME)
|
||||
.application_version(vk::make_version(0, 1, 0))
|
||||
// Our engine information
|
||||
.engine_name(VK_ENGINE_NAME)
|
||||
.engine_version(vk::make_version(0, 1, 0))
|
||||
// Vulkan api version target
|
||||
.api_version(vk::make_version(1, 0, 0));
|
||||
|
||||
// Get the names of available layers
|
||||
let available_layers = entry
|
||||
|
@ -385,8 +422,8 @@ unsafe fn create_instance(window:&Window, entry:&Entry, data:&mut AppData) -> Re
|
|||
// Required by Vulkan SDK on macOS since 1.3.216.
|
||||
let flags = if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION {
|
||||
info!("Enabling extensions for macOS portability.");
|
||||
// already present above
|
||||
// extensions.push(
|
||||
// // already present above
|
||||
// vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name.as_ptr(),
|
||||
// );
|
||||
extensions.push(vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr());
|
||||
|
@ -471,6 +508,11 @@ unsafe fn check_physical_device(instance:&Instance, data:&AppData, physical_devi
|
|||
return Err(anyhow!(SuitabilityError("Insufficient swapchain support.")));
|
||||
}
|
||||
|
||||
let features = instance.get_physical_device_features(physical_device);
|
||||
if features.sampler_anisotropy != vk::TRUE {
|
||||
return Err(anyhow!(SuitabilityError("No sampler anisotropy.")));
|
||||
}
|
||||
|
||||
// // TODO handle this like the other one?
|
||||
// let properties = instance.get_physical_device_properties(physical_device);
|
||||
// if properties.device_type != vk::PhysicalDeviceType::DISCRETE_GPU {
|
||||
|
@ -531,7 +573,7 @@ unsafe fn create_logical_device(entry:&Entry, instance:&Instance, data:&mut AppD
|
|||
extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr());
|
||||
}
|
||||
|
||||
let features = vk::PhysicalDeviceFeatures::builder();
|
||||
let features = vk::PhysicalDeviceFeatures::builder().sampler_anisotropy(true);
|
||||
|
||||
let info = vk::DeviceCreateInfo::builder()
|
||||
.queue_create_infos(&queue_infos)
|
||||
|
@ -697,7 +739,13 @@ unsafe fn create_descriptor_set_layout(device:&Device, data:&mut AppData) -> Res
|
|||
.descriptor_count(1)
|
||||
.stage_flags(vk::ShaderStageFlags::VERTEX);
|
||||
|
||||
let bindings = &[ubo_binding];
|
||||
let sampler_binding = vk::DescriptorSetLayoutBinding::builder()
|
||||
.binding(1)
|
||||
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||
.descriptor_count(1)
|
||||
.stage_flags(vk::ShaderStageFlags::FRAGMENT);
|
||||
|
||||
let bindings = &[ubo_binding, sampler_binding];
|
||||
let info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(bindings);
|
||||
|
||||
data.descriptor_set_layout = device.create_descriptor_set_layout(&info, None)?;
|
||||
|
@ -707,8 +755,8 @@ unsafe fn create_descriptor_set_layout(device:&Device, data:&mut AppData) -> Res
|
|||
|
||||
unsafe fn create_pipeline(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
const_shaders! {
|
||||
frag = "f_default.spv";
|
||||
vert = "v_default.spv";
|
||||
frag = "glsl_default.frag";
|
||||
vert = "glsl_default.vert";
|
||||
}
|
||||
|
||||
let vert_shader_module = create_shader_module(device, &vert[..])?;
|
||||
|
@ -763,14 +811,14 @@ unsafe fn create_pipeline(device:&Device, data:&mut AppData) -> Result<()> {
|
|||
|
||||
let attachment = vk::PipelineColorBlendAttachmentState::builder()
|
||||
.color_write_mask(vk::ColorComponentFlags::all())
|
||||
.blend_enable(false);
|
||||
// .blend_enable(true)
|
||||
// .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
|
||||
// .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
// .color_blend_op(vk::BlendOp::ADD)
|
||||
// .src_alpha_blend_factor(vk::BlendFactor::ONE)
|
||||
// .dst_alpha_blend_factor(vk::BlendFactor::ZERO)
|
||||
// .alpha_blend_op(vk::BlendOp::ADD);
|
||||
// .blend_enable(false);
|
||||
.blend_enable(true)
|
||||
.src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
|
||||
.dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
.color_blend_op(vk::BlendOp::ADD)
|
||||
.src_alpha_blend_factor(vk::BlendFactor::ONE)
|
||||
.dst_alpha_blend_factor(vk::BlendFactor::ZERO)
|
||||
.alpha_blend_op(vk::BlendOp::ADD);
|
||||
|
||||
let attachments = &[attachment];
|
||||
let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder()
|
||||
|
@ -837,8 +885,8 @@ unsafe fn create_command_pool(instance:&Instance, device:&Device, data:&mut AppD
|
|||
let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?;
|
||||
|
||||
let info = vk::CommandPoolCreateInfo::builder()
|
||||
.flags(vk::CommandPoolCreateFlags::empty()) // Optional.
|
||||
.queue_family_index(indices.graphics);
|
||||
.flags(vk::CommandPoolCreateFlags::empty()) // Optional.
|
||||
.queue_family_index(indices.graphics);
|
||||
|
||||
data.command_pool = device.create_command_pool(&info, None)?;
|
||||
|
||||
|
@ -846,16 +894,18 @@ unsafe fn create_command_pool(instance:&Instance, device:&Device, data:&mut AppD
|
|||
}
|
||||
|
||||
unsafe fn create_texture_image(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let image = File::open("assets/common/archlogo.512.png")?;
|
||||
let image = include_bytes!("../../../assets/common/archlogo.512.png");
|
||||
|
||||
let decoder = png::Decoder::new(image);
|
||||
let mut reader = decoder.read_info()?;
|
||||
let mut image = image::ImageReader::new(std::io::Cursor::new(image.to_vec()));
|
||||
image.set_format(image::ImageFormat::Png);
|
||||
let image = image.decode()?;
|
||||
|
||||
let mut pixels = vec![0; reader.info().raw_bytes()];
|
||||
reader.next_frame(&mut pixels)?; // what format is this reading to?
|
||||
let pixelcollection:Vec<(u32, u32, image::Rgba<u8>)> = image::GenericImageView::pixels(&image).collect(); //.view(0, 0, image.width(), image.height());
|
||||
|
||||
let size = reader.info().raw_bytes() as u64;
|
||||
let (width, height) = reader.info().size();
|
||||
let rawpixeldata = Vec::from_iter(pixelcollection.iter().map(|(pox, poy, dat)| dat.0));
|
||||
|
||||
let size = (rawpixeldata.len() * 4) as u64;
|
||||
let (width, height) = image::GenericImageView::dimensions(&image);
|
||||
|
||||
let (staging_buffer, staging_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
|
@ -868,7 +918,7 @@ unsafe fn create_texture_image(instance:&Instance, device:&Device, data:&mut App
|
|||
|
||||
let memory = device.map_memory(staging_buffer_memory, 0, size, vk::MemoryMapFlags::empty())?;
|
||||
|
||||
memcpy(pixels.as_ptr(), memory.cast(), pixels.len());
|
||||
memcpy(rawpixeldata.as_ptr(), memory.cast(), rawpixeldata.len());
|
||||
|
||||
device.unmap_memory(staging_buffer_memory);
|
||||
|
||||
|
@ -911,6 +961,203 @@ unsafe fn create_texture_image(instance:&Instance, device:&Device, data:&mut App
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_texture_image_view(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.texture_image_view = create_image_view(device, data.texture_image, vk::Format::R8G8B8A8_SRGB)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_texture_sampler(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
// let mut ccolor = vk::SamplerCustomBorderColorCreateInfoEXT::builder()
|
||||
// .format(vk::Format::R8G8B8A8_SRGB)
|
||||
// .custom_border_color(vk::ClearColorValue {
|
||||
// float32:[0.0125, 0.001, 0.007, 1.0],
|
||||
// // float32:[1.0, 1.0, 1.0, 1.0],
|
||||
// })
|
||||
// .build();
|
||||
|
||||
let info = vk::SamplerCreateInfo::builder()
|
||||
.mag_filter(vk::Filter::LINEAR)
|
||||
.min_filter(vk::Filter::LINEAR)
|
||||
.address_mode_u(vk::SamplerAddressMode::CLAMP_TO_BORDER)
|
||||
.address_mode_v(vk::SamplerAddressMode::CLAMP_TO_BORDER)
|
||||
.address_mode_w(vk::SamplerAddressMode::CLAMP_TO_BORDER)
|
||||
.anisotropy_enable(true)
|
||||
.max_anisotropy(16.0)
|
||||
.border_color(vk::BorderColor::INT_OPAQUE_WHITE)
|
||||
// .border_color(vk::BorderColor::FLOAT_CUSTOM_EXT)
|
||||
// .push_next(&mut ccolor)
|
||||
.unnormalized_coordinates(false)
|
||||
.compare_enable(false)
|
||||
.compare_op(vk::CompareOp::ALWAYS)
|
||||
.mipmap_mode(vk::SamplerMipmapMode::LINEAR)
|
||||
.mip_lod_bias(0.0)
|
||||
.min_lod(0.0)
|
||||
.max_lod(0.0);
|
||||
|
||||
data.texture_sampler = device.create_sampler(&info, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_vertex_buffer(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let size = (size_of::<Vertex>() * VERTICES.len()) as u64;
|
||||
|
||||
let (staging_buffer, staging_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
|
||||
let memory = device.map_memory(staging_buffer_memory, 0, size, vk::MemoryMapFlags::empty())?;
|
||||
|
||||
memcpy(VERTICES.as_ptr(), memory.cast(), VERTICES.len());
|
||||
|
||||
device.unmap_memory(staging_buffer_memory);
|
||||
|
||||
let (vertex_buffer, vertex_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
)?;
|
||||
|
||||
data.vertex_buffer = vertex_buffer;
|
||||
data.vertex_buffer_memory = vertex_buffer_memory;
|
||||
|
||||
copy_buffer(device, data, staging_buffer, vertex_buffer, size)?;
|
||||
|
||||
device.destroy_buffer(staging_buffer, None);
|
||||
device.free_memory(staging_buffer_memory, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_index_buffer(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let size = (size_of::<u16>() * INDICES.len()) as u64;
|
||||
|
||||
let (staging_buffer, staging_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
|
||||
let memory = device.map_memory(staging_buffer_memory, 0, size, vk::MemoryMapFlags::empty())?;
|
||||
|
||||
memcpy(INDICES.as_ptr(), memory.cast(), INDICES.len());
|
||||
|
||||
device.unmap_memory(staging_buffer_memory);
|
||||
|
||||
let (index_buffer, index_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
)?;
|
||||
|
||||
data.index_buffer = index_buffer;
|
||||
data.index_buffer_memory = index_buffer_memory;
|
||||
|
||||
copy_buffer(device, data, staging_buffer, index_buffer, size)?;
|
||||
|
||||
device.destroy_buffer(staging_buffer, None);
|
||||
device.free_memory(staging_buffer_memory, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_uniform_buffers(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.uniform_buffers.clear();
|
||||
data.uniform_buffers_memory.clear();
|
||||
|
||||
for _ in 0..data.swapchain_images.len() {
|
||||
let (uniform_buffer, uniform_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size_of::<UniformBufferObject>() as u64,
|
||||
vk::BufferUsageFlags::UNIFORM_BUFFER,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
|
||||
data.uniform_buffers.push(uniform_buffer);
|
||||
data.uniform_buffers_memory.push(uniform_buffer_memory);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_descriptor_pool(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let ubo_size = vk::DescriptorPoolSize::builder()
|
||||
.type_(vk::DescriptorType::UNIFORM_BUFFER)
|
||||
.descriptor_count(data.swapchain_images.len() as u32);
|
||||
|
||||
let sampler_size = vk::DescriptorPoolSize::builder()
|
||||
.type_(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||
.descriptor_count(data.swapchain_images.len() as u32);
|
||||
|
||||
let pool_sizes = &[ubo_size, sampler_size];
|
||||
let info = vk::DescriptorPoolCreateInfo::builder()
|
||||
.pool_sizes(pool_sizes)
|
||||
.max_sets(data.swapchain_images.len() as u32);
|
||||
|
||||
data.descriptor_pool = device.create_descriptor_pool(&info, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_descriptor_sets(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let layouts = vec![data.descriptor_set_layout; data.swapchain_images.len()];
|
||||
let info = vk::DescriptorSetAllocateInfo::builder()
|
||||
.descriptor_pool(data.descriptor_pool)
|
||||
.set_layouts(&layouts);
|
||||
|
||||
data.descriptor_sets = device.allocate_descriptor_sets(&info)?;
|
||||
|
||||
for i in 0..data.swapchain_images.len() {
|
||||
let info = vk::DescriptorBufferInfo::builder()
|
||||
.buffer(data.uniform_buffers[i])
|
||||
.offset(0)
|
||||
.range(size_of::<UniformBufferObject>() as u64);
|
||||
|
||||
let buffer_info = &[info];
|
||||
let ubo_write = vk::WriteDescriptorSet::builder()
|
||||
.dst_set(data.descriptor_sets[i])
|
||||
.dst_binding(0)
|
||||
.dst_array_element(0)
|
||||
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
|
||||
.buffer_info(buffer_info);
|
||||
|
||||
let info = vk::DescriptorImageInfo::builder()
|
||||
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
|
||||
.image_view(data.texture_image_view)
|
||||
.sampler(data.texture_sampler);
|
||||
|
||||
let image_info = &[info];
|
||||
let sampler_write = vk::WriteDescriptorSet::builder()
|
||||
.dst_set(data.descriptor_sets[i])
|
||||
.dst_binding(1)
|
||||
.dst_array_element(0)
|
||||
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||
.image_info(image_info);
|
||||
|
||||
device.update_descriptor_sets(&[ubo_write, sampler_write], &[] as &[vk::CopyDescriptorSet]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_command_buffers(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let allocate_info = vk::CommandBufferAllocateInfo::builder()
|
||||
.command_pool(data.command_pool)
|
||||
|
@ -921,10 +1168,9 @@ unsafe fn create_command_buffers(device:&Device, data:&mut AppData) -> Result<()
|
|||
|
||||
for (i, command_buffer) in data.command_buffers.iter().enumerate() {
|
||||
let inheritance = vk::CommandBufferInheritanceInfo::builder();
|
||||
|
||||
let info = vk::CommandBufferBeginInfo::builder()
|
||||
.flags(vk::CommandBufferUsageFlags::empty()) // Optional.
|
||||
.inheritance_info(&inheritance); // Optional.
|
||||
.flags(vk::CommandBufferUsageFlags::empty()) // Optional.
|
||||
.inheritance_info(&inheritance); // Optional.
|
||||
|
||||
device.begin_command_buffer(*command_buffer, &info)?;
|
||||
|
||||
|
@ -933,6 +1179,7 @@ unsafe fn create_command_buffers(device:&Device, data:&mut AppData) -> Result<()
|
|||
let color_clear_value = vk::ClearValue {
|
||||
color:vk::ClearColorValue {
|
||||
float32:[0.0125, 0.0094, 0.0071, 1.0],
|
||||
// float32: [0.0, 0.0, 0.0, 1.0],
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1031,15 +1278,24 @@ impl SwapchainSupport {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct UniformBufferObject {
|
||||
model:Mat4f,
|
||||
view: Mat4f,
|
||||
proj: Mat4f,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Vertex {
|
||||
pos: Vec2f,
|
||||
color:Vec3f,
|
||||
pos: Vec3f,
|
||||
color: Vec3f,
|
||||
tex_coord:Vec2f,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
const fn new(pos:Vec2f, color:Vec3f) -> Self { Self { pos, color } }
|
||||
const fn new(pos:Vec3f, color:Vec3f, tex_coord:Vec2f) -> Self { Self { pos, color, tex_coord } }
|
||||
|
||||
fn binding_description() -> vk::VertexInputBindingDescription {
|
||||
vk::VertexInputBindingDescription::builder()
|
||||
|
@ -1049,11 +1305,11 @@ impl Vertex {
|
|||
.build()
|
||||
}
|
||||
|
||||
fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] {
|
||||
fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] {
|
||||
let pos = vk::VertexInputAttributeDescription::builder()
|
||||
.binding(0)
|
||||
.location(0)
|
||||
.format(vk::Format::R32G32_SFLOAT)
|
||||
.format(vk::Format::R32G32B32_SFLOAT)
|
||||
.offset(0)
|
||||
.build();
|
||||
|
||||
|
@ -1064,71 +1320,17 @@ impl Vertex {
|
|||
.offset(size_of::<Vec2f>() as u32)
|
||||
.build();
|
||||
|
||||
[pos, color]
|
||||
let tex_coord = vk::VertexInputAttributeDescription::builder()
|
||||
.binding(0)
|
||||
.location(2)
|
||||
.format(vk::Format::R32G32_SFLOAT)
|
||||
.offset((size_of::<Vec2f>() + size_of::<Vec3f>()) as u32)
|
||||
.build();
|
||||
|
||||
[pos, color, tex_coord]
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_vertex_buffer(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let size = (size_of::<Vertex>() * VERTICES.len()) as u64;
|
||||
|
||||
let (staging_buffer, staging_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
|
||||
let memory = device.map_memory(staging_buffer_memory, 0, size, vk::MemoryMapFlags::empty())?;
|
||||
|
||||
memcpy(VERTICES.as_ptr(), memory.cast(), VERTICES.len());
|
||||
|
||||
device.unmap_memory(staging_buffer_memory);
|
||||
|
||||
let (vertex_buffer, vertex_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
)?;
|
||||
|
||||
data.vertex_buffer = vertex_buffer;
|
||||
data.vertex_buffer_memory = vertex_buffer_memory;
|
||||
|
||||
copy_buffer(device, data, staging_buffer, vertex_buffer, size)?;
|
||||
|
||||
device.destroy_buffer(staging_buffer, None);
|
||||
device.free_memory(staging_buffer_memory, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn copy_buffer(device:&Device, data:&AppData, source:vk::Buffer, destination:vk::Buffer, size:vk::DeviceSize) -> Result<()> {
|
||||
let command_buffer = begin_single_time_commands(device, data)?;
|
||||
|
||||
let regions = vk::BufferCopy::builder().size(size);
|
||||
device.cmd_copy_buffer(command_buffer, source, destination, &[regions]);
|
||||
|
||||
end_single_time_commands(device, data, command_buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn get_memory_type_index(instance:&Instance, data:&AppData, properties:vk::MemoryPropertyFlags, requirements:vk::MemoryRequirements) -> Result<u32> {
|
||||
let memory = instance.get_physical_device_memory_properties(data.physical_device);
|
||||
|
||||
(0..memory.memory_type_count)
|
||||
.find(|i| {
|
||||
let suitable = (requirements.memory_type_bits & (1 << i)) != 0;
|
||||
let memory_type = memory.memory_types[*i as usize];
|
||||
suitable && memory_type.property_flags.contains(properties)
|
||||
})
|
||||
.ok_or_else(|| anyhow!("Failed to find suitable memory type."))
|
||||
}
|
||||
|
||||
unsafe fn create_buffer(
|
||||
instance:&Instance, device:&Device, data:&AppData, size:vk::DeviceSize, usage:vk::BufferUsageFlags, properties:vk::MemoryPropertyFlags,
|
||||
) -> Result<(vk::Buffer, vk::DeviceMemory)> {
|
||||
|
@ -1149,112 +1351,13 @@ unsafe fn create_buffer(
|
|||
Ok((buffer, buffer_memory))
|
||||
}
|
||||
|
||||
unsafe fn create_index_buffer(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let size = (size_of::<u16>() * INDICES.len()) as u64;
|
||||
unsafe fn copy_buffer(device:&Device, data:&AppData, source:vk::Buffer, destination:vk::Buffer, size:vk::DeviceSize) -> Result<()> {
|
||||
let command_buffer = begin_single_time_commands(device, data)?;
|
||||
|
||||
let (staging_buffer, staging_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
let regions = vk::BufferCopy::builder().size(size);
|
||||
device.cmd_copy_buffer(command_buffer, source, destination, &[regions]);
|
||||
|
||||
let memory = device.map_memory(staging_buffer_memory, 0, size, vk::MemoryMapFlags::empty())?;
|
||||
|
||||
memcpy(INDICES.as_ptr(), memory.cast(), INDICES.len());
|
||||
|
||||
device.unmap_memory(staging_buffer_memory);
|
||||
|
||||
let (index_buffer, index_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size,
|
||||
vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
)?;
|
||||
|
||||
data.index_buffer = index_buffer;
|
||||
data.index_buffer_memory = index_buffer_memory;
|
||||
|
||||
copy_buffer(device, data, staging_buffer, index_buffer, size)?;
|
||||
|
||||
device.destroy_buffer(staging_buffer, None);
|
||||
device.free_memory(staging_buffer_memory, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct UniformBufferObject {
|
||||
model:Mat4f,
|
||||
view: Mat4f,
|
||||
proj: Mat4f,
|
||||
}
|
||||
|
||||
unsafe fn create_uniform_buffers(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.uniform_buffers.clear();
|
||||
data.uniform_buffers_memory.clear();
|
||||
|
||||
for _ in 0..data.swapchain_images.len() {
|
||||
let (uniform_buffer, uniform_buffer_memory) = create_buffer(
|
||||
instance,
|
||||
device,
|
||||
data,
|
||||
size_of::<UniformBufferObject>() as u64,
|
||||
vk::BufferUsageFlags::UNIFORM_BUFFER,
|
||||
vk::MemoryPropertyFlags::HOST_COHERENT | vk::MemoryPropertyFlags::HOST_VISIBLE,
|
||||
)?;
|
||||
|
||||
data.uniform_buffers.push(uniform_buffer);
|
||||
data.uniform_buffers_memory.push(uniform_buffer_memory);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_descriptor_pool(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let ubo_size = vk::DescriptorPoolSize::builder()
|
||||
.type_(vk::DescriptorType::UNIFORM_BUFFER)
|
||||
.descriptor_count(data.swapchain_images.len() as u32);
|
||||
|
||||
let pool_sizes = &[ubo_size];
|
||||
let info = vk::DescriptorPoolCreateInfo::builder()
|
||||
.pool_sizes(pool_sizes)
|
||||
.max_sets(data.swapchain_images.len() as u32);
|
||||
|
||||
data.descriptor_pool = device.create_descriptor_pool(&info, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_descriptor_sets(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let layouts = vec![data.descriptor_set_layout; data.swapchain_images.len()];
|
||||
let info = vk::DescriptorSetAllocateInfo::builder()
|
||||
.descriptor_pool(data.descriptor_pool)
|
||||
.set_layouts(&layouts);
|
||||
|
||||
data.descriptor_sets = device.allocate_descriptor_sets(&info)?;
|
||||
|
||||
for i in 0..data.swapchain_images.len() {
|
||||
let info = vk::DescriptorBufferInfo::builder()
|
||||
.buffer(data.uniform_buffers[i])
|
||||
.offset(0)
|
||||
.range(size_of::<UniformBufferObject>() as u64);
|
||||
|
||||
let buffer_info = &[info];
|
||||
let ubo_write = vk::WriteDescriptorSet::builder()
|
||||
.dst_set(data.descriptor_sets[i])
|
||||
.dst_binding(0)
|
||||
.dst_array_element(0)
|
||||
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
|
||||
.buffer_info(buffer_info);
|
||||
|
||||
device.update_descriptor_sets(&[ubo_write], &[] as &[vk::CopyDescriptorSet]);
|
||||
}
|
||||
end_single_time_commands(device, data, command_buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1272,8 +1375,8 @@ unsafe fn create_image(
|
|||
.tiling(tiling)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED)
|
||||
.usage(usage)
|
||||
.samples(vk::SampleCountFlags::_1)
|
||||
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
||||
.sharing_mode(vk::SharingMode::EXCLUSIVE)
|
||||
.samples(vk::SampleCountFlags::_1);
|
||||
|
||||
let image = device.create_image(&info, None)?;
|
||||
|
||||
|
@ -1290,47 +1393,26 @@ unsafe fn create_image(
|
|||
Ok((image, image_memory))
|
||||
}
|
||||
|
||||
unsafe fn begin_single_time_commands(device:&Device, data:&AppData) -> Result<vk::CommandBuffer> {
|
||||
let info = vk::CommandBufferAllocateInfo::builder()
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_pool(data.command_pool)
|
||||
.command_buffer_count(1);
|
||||
|
||||
let command_buffer = device.allocate_command_buffers(&info)?[0];
|
||||
|
||||
let info = vk::CommandBufferBeginInfo::builder().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
|
||||
device.begin_command_buffer(command_buffer, &info)?;
|
||||
|
||||
Ok(command_buffer)
|
||||
}
|
||||
|
||||
unsafe fn end_single_time_commands(device:&Device, data:&AppData, command_buffer:vk::CommandBuffer) -> Result<()> {
|
||||
device.end_command_buffer(command_buffer)?;
|
||||
|
||||
let command_buffers = &[command_buffer];
|
||||
let info = vk::SubmitInfo::builder().command_buffers(command_buffers);
|
||||
|
||||
device.queue_submit(data.graphics_queue, &[info], vk::Fence::null())?;
|
||||
device.queue_wait_idle(data.graphics_queue)?;
|
||||
|
||||
device.free_command_buffers(data.command_pool, &[command_buffer]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn transition_image_layout(
|
||||
device:&Device, data:&AppData, image:vk::Image, format:vk::Format, old_layout:vk::ImageLayout, new_layout:vk::ImageLayout,
|
||||
) -> Result<()> {
|
||||
let command_buffer = begin_single_time_commands(device, data)?;
|
||||
|
||||
let subresource = vk::ImageSubresourceRange::builder()
|
||||
unsafe fn create_image_view(device:&Device, image:vk::Image, format:vk::Format) -> Result<vk::ImageView> {
|
||||
let subresource_range = vk::ImageSubresourceRange::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.base_mip_level(0)
|
||||
.level_count(1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1);
|
||||
|
||||
let info = vk::ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.view_type(vk::ImageViewType::_2D)
|
||||
.format(format)
|
||||
.subresource_range(subresource_range);
|
||||
|
||||
Ok(device.create_image_view(&info, None)?)
|
||||
}
|
||||
|
||||
unsafe fn transition_image_layout(
|
||||
device:&Device, data:&AppData, image:vk::Image, format:vk::Format, old_layout:vk::ImageLayout, new_layout:vk::ImageLayout,
|
||||
) -> Result<()> {
|
||||
let (src_access_mask, dst_access_mask, src_stage_mask, dst_stage_mask) = match (old_layout, new_layout) {
|
||||
(vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL) => (
|
||||
vk::AccessFlags::empty(),
|
||||
|
@ -1347,6 +1429,15 @@ unsafe fn transition_image_layout(
|
|||
_ => return Err(anyhow!("Unsupported image layout transition!")),
|
||||
};
|
||||
|
||||
let command_buffer = begin_single_time_commands(device, data)?;
|
||||
|
||||
let subresource = vk::ImageSubresourceRange::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.base_mip_level(0)
|
||||
.level_count(1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1);
|
||||
|
||||
let barrier = vk::ImageMemoryBarrier::builder()
|
||||
.old_layout(old_layout)
|
||||
.new_layout(new_layout)
|
||||
|
@ -1396,27 +1487,43 @@ unsafe fn copy_buffer_to_image(device:&Device, data:&AppData, buffer:vk::Buffer,
|
|||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_texture_image_view(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.texture_image_view = create_image_view(device, data.texture_image, vk::Format::R8G8B8A8_SRGB)?;
|
||||
|
||||
unsafe fn get_memory_type_index(instance:&Instance, data:&AppData, properties:vk::MemoryPropertyFlags, requirements:vk::MemoryRequirements) -> Result<u32> {
|
||||
let memory = instance.get_physical_device_memory_properties(data.physical_device);
|
||||
|
||||
(0..memory.memory_type_count)
|
||||
.find(|i| {
|
||||
let suitable = (requirements.memory_type_bits & (1 << i)) != 0;
|
||||
let memory_type = memory.memory_types[*i as usize];
|
||||
suitable && memory_type.property_flags.contains(properties)
|
||||
})
|
||||
.ok_or_else(|| anyhow!("Failed to find suitable memory type."))
|
||||
}
|
||||
|
||||
unsafe fn begin_single_time_commands(device:&Device, data:&AppData) -> Result<vk::CommandBuffer> {
|
||||
let info = vk::CommandBufferAllocateInfo::builder()
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_pool(data.command_pool)
|
||||
.command_buffer_count(1);
|
||||
|
||||
let command_buffer = device.allocate_command_buffers(&info)?[0];
|
||||
|
||||
let info = vk::CommandBufferBeginInfo::builder().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
|
||||
device.begin_command_buffer(command_buffer, &info)?;
|
||||
|
||||
Ok(command_buffer)
|
||||
}
|
||||
|
||||
unsafe fn end_single_time_commands(device:&Device, data:&AppData, command_buffer:vk::CommandBuffer) -> Result<()> {
|
||||
device.end_command_buffer(command_buffer)?;
|
||||
|
||||
let command_buffers = &[command_buffer];
|
||||
let info = vk::SubmitInfo::builder().command_buffers(command_buffers);
|
||||
|
||||
device.queue_submit(data.graphics_queue, &[info], vk::Fence::null())?;
|
||||
device.queue_wait_idle(data.graphics_queue)?;
|
||||
|
||||
device.free_command_buffers(data.command_pool, &[command_buffer]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_image_view(device:&Device, image:vk::Image, format:vk::Format) -> Result<vk::ImageView> {
|
||||
let subresource_range = vk::ImageSubresourceRange::builder()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.base_mip_level(0)
|
||||
.level_count(1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1);
|
||||
|
||||
let info = vk::ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.view_type(vk::ImageViewType::_2D)
|
||||
.format(format)
|
||||
.subresource_range(subresource_range);
|
||||
|
||||
Ok(device.create_image_view(&info, None)?)
|
||||
}
|
||||
|
||||
unsafe fn create_texture_sampler(device:&Device, data:&mut AppData) -> Result<()> { Ok(()) }
|
||||
|
|
132
src/linux.rs
132
src/linux.rs
|
@ -4,15 +4,16 @@ use log::*;
|
|||
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::Event;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
use crate::graphics_engines;
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
const MODE:GMode = GMode::Vulkan;
|
||||
|
||||
pub enum GMode {
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
super::init_logging();
|
||||
|
@ -30,65 +31,82 @@ pub fn main() -> Result<()> {
|
|||
|
||||
// Window
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
match MODE {
|
||||
GMode::Vulkan => {
|
||||
use old_winit::dpi::LogicalSize;
|
||||
use old_winit::event::Event;
|
||||
use old_winit::event::WindowEvent;
|
||||
use old_winit::event_loop::EventLoop;
|
||||
use old_winit::window::WindowBuilder;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
// App
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
// App
|
||||
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
},
|
||||
GMode::OpenGL => {
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
if let Err(err) = graphics_engines::opengl::main(EventLoop::new().unwrap()) {
|
||||
error!("An error occured in the opengl backend: {err}");
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
info!("Exiting program.");
|
||||
|
||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -1,4 +1,4 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
// #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![allow(
|
||||
// dead_code,
|
||||
|
@ -78,18 +78,18 @@ mod graphics_engines;
|
|||
|
||||
fn init_logging() {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_ansi(true)
|
||||
.with_level(true)
|
||||
// .with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.with_max_level(if cfg!(debug_assertions) {
|
||||
tracing::level_filters::LevelFilter::DEBUG
|
||||
} else {
|
||||
tracing::level_filters::LevelFilter::INFO
|
||||
})
|
||||
.init();
|
||||
.compact()
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_ansi(true)
|
||||
.with_level(true)
|
||||
// .with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.with_max_level(if cfg!(debug_assertions) {
|
||||
tracing::level_filters::LevelFilter::DEBUG
|
||||
} else {
|
||||
tracing::level_filters::LevelFilter::INFO
|
||||
})
|
||||
.init();
|
||||
|
||||
// pretty_env_logger::init_timed();
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn main() {
|
|||
std::panic::set_hook(Box::new(custom_panic_hook));
|
||||
super::init_logging();
|
||||
|
||||
panic!("Vista system target is not supported currently.");
|
||||
panic!("Vita system target is not supported currently.");
|
||||
|
||||
// for _ in 0..3 {
|
||||
// raw_rainbow();
|
||||
|
|
134
src/windows.rs
134
src/windows.rs
|
@ -4,22 +4,23 @@ use log::*;
|
|||
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::Event;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
use crate::graphics_engines;
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
const MODE:GMode = GMode::Vulkan;
|
||||
|
||||
pub enum GMode {
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
super::init_logging();
|
||||
|
||||
info!("Registering CTRLC hook.");
|
||||
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel::<()>();
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
shutdown_tx.send(()).expect("Failed to send shutdown signal");
|
||||
|
@ -30,65 +31,82 @@ pub fn main() -> Result<()> {
|
|||
|
||||
// Window
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
match MODE {
|
||||
GMode::Vulkan => {
|
||||
use old_winit::dpi::LogicalSize;
|
||||
use old_winit::event::Event;
|
||||
use old_winit::event::WindowEvent;
|
||||
use old_winit::event_loop::EventLoop;
|
||||
use old_winit::window::WindowBuilder;
|
||||
|
||||
info!("Creating app and starting event loop.");
|
||||
let event_loop = EventLoop::new()?;
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(WINDOW_TITLE)
|
||||
.with_inner_size(LogicalSize::new(1024, 768))
|
||||
.build(&event_loop)?;
|
||||
|
||||
// App
|
||||
info!("Creating app and starting event loop.");
|
||||
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
// App
|
||||
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
let mut app = unsafe { App::create(&window)? };
|
||||
let mut minimized = false;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
|
||||
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
event_loop.run(move |event, elwt| {
|
||||
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
if let Some(receiver) = shutdown_rx_guard.as_mut() {
|
||||
if receiver.try_recv().is_ok() {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
match event {
|
||||
// Request a redraw when all events were processed.
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
// Render a frame if our Vulkan app is not being destroyed.
|
||||
WindowEvent::RedrawRequested if !elwt.exiting() && !minimized => {
|
||||
unsafe { app.render(&window) }.unwrap();
|
||||
},
|
||||
WindowEvent::Resized(size) =>
|
||||
if size.width == 0 || size.height == 0 {
|
||||
minimized = true;
|
||||
} else {
|
||||
minimized = false;
|
||||
app.resized = true;
|
||||
},
|
||||
// Destroy our Vulkan app.
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Closing event loop and destroying Vulkan instance.");
|
||||
elwt.exit();
|
||||
unsafe {
|
||||
app.device.device_wait_idle().unwrap();
|
||||
app.destroy();
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
})?;
|
||||
},
|
||||
GMode::OpenGL => {
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
if let Err(err) = graphics_engines::opengl::main(EventLoop::new().unwrap()) {
|
||||
error!("An error occured in the opengl backend: {err}");
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
info!("Exiting program.");
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue