Compare commits

...
Sign in to create a new pull request.

7 commits
main ... u1-dev

Author SHA1 Message Date
7ac84da35a
Merge branch 'opengl-dev' into jelly-dev 2025-02-08 09:52:14 -04:00
0f6cde2b52 Merge pull request 'development-vulkan' (#1) from development-vulkan into jelly-dev
Reviewed-on: #1
2025-02-08 13:11:31 +00:00
4b70b6f59f
sync 2025-02-07 18:50:49 -04:00
4c0fa9e6ee
sync 2025-02-05 11:34:55 -04:00
fd8b343683
using an opengl tutorial from glutin 2025-02-02 10:22:00 -04:00
dbfc0f5538
sync 2025-01-31 18:13:51 -04:00
e35f5130ae
finished implementing textures and texture sampling
fixed some issues with build.rs and made it a bit more generic
2025-01-31 16:46:42 -04:00
35 changed files with 4663 additions and 805 deletions

3
.gitignore vendored
View file

@ -5,3 +5,6 @@
/target/
/.vscode/
/.idea/
/bindgen/
/docs/

1364
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
name = "minemod"
version = "0.1.0"
edition = "2024"
default-run = "minemod"
[package.metadata.vita]
title_id = "PKDC10002"
@ -9,7 +10,7 @@ title_name = "MineMod"
# assets = "somewhere" # TODO make this a build.rs output folder so we can use build.rs to manage vita assets
[profile.release]
opt-level = 2
opt-level = 3
lto = true
codegen-units = 1
# panic = "abort" # Vita platform doesnt support abort currently
@ -40,43 +41,71 @@ 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
glam = "0.29.2"
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
rand = "0.9.0"
#image_full = { package = "image", version = "0.25.5" }
#image = { version = "0.25.5", default-features = false }
#image_dds = "0.7.1"
tbc = "0.3.0"
[target.'cfg(any(target_os = "windows",target_os = "linux",target_os = "macos"))'.dependencies]
tokio = { version = "1.43.0", features = ["full"] } # MIT
ctrlc = "3.4.5" # MIT
glfw = "0.59.0"
#glfw = {version = "0.59.0", default-features = false, features = ["raw-window-handle-v0-6"]} # we literally just need the window management parts # nevermind this crashes immediately
## 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
# kept for now so the vulkan demo works (also the tutorial is using it so once the tutorial is done and i fully understand how vulkan works i will be tossing winit fully and reworking the vulkan structure to better suit the engine)
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 # no winit >:(
raw-window-handle = "0.6.2" # MIT
# winit is hereby banished from this realm because it pissed me off >:(
# its designed around mobile and desktop, but its mobile side sticks around even when its only being used for desktop and theres no way to cut it out
# on top of its piss poor documentation around the breaking changes in update 0.3
#winit = { version = "0.30.0", features = ["rwh_06"] }
# drm = { version = "0.14.1", optional = true } # MIT
## end opengl
[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 +117,14 @@ tokio = { version = "1.36.0", features = [
"net",
"tracing",
"time",
] }
] } # MIT
[target.'cfg(target_os = "windows")'.dependencies]
gfx-backend-dx12 = "0.9.1"
gfx-backend-dx11 = "0.9.0"
[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

132
build.rs
View file

@ -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");
generate_opengl_bindings()?;
compile_shaders()?;
Ok(())
}
fn generate_opengl_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");
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());
}
drop(aquired);
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;
},
}
}
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() {

View file

@ -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
View 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);
// }

View file

@ -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;
}

21
src/bin/bcntool.rs Normal file
View file

@ -0,0 +1,21 @@
use log::*;
// This tool will be used to convert any common image format to BCn of a specified level or BC1 by default.
fn main() {
tracing_subscriber::fmt()
.compact()
.with_timer(tracing_subscriber::fmt::time::uptime())
.with_ansi(true)
.with_level(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::formatted_timed_builder().filter_level(LevelFilter::Info).init();
info!("We support BC1, BC3, and BC4 via the tbc crate.");
info!("Unfortunately this tool is not currently implemented.");
unimplemented!();
}

View file

@ -1,16 +1,114 @@
pub(crate) trait GraphicsCommander {}
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
use glam::Vec3;
use resources::*;
//use crate::graphics_engines::Renderable;
pub mod resources;
#[derive(Debug)]
pub struct Cataclysm {
game_objects:Vec<GameObject>,
pub creation_time:Instant,
textures: Arc<ResourceTable<GraphicsTexture>>,
vertex_objects: Arc<ResourceTable<GraphicsVBO>>,
shader_objects: Arc<ResourceTable<GraphicsShader>>,
sounds: Arc<ResourceTable<AudioSFX>>,
music: Arc<ResourceTable<AudioMusic>>,
pub chunks: HashMap<(u32, u32, u32), CataclysmChunk>,
}
pub struct GameObject {
pos:(f64, f64, f64),
#[derive(Debug, Clone)]
pub struct CataclysmChunk {
pub simple_blocks:HashMap<(u8, u8, u8), SimpleBlock>,
pub fancy_blocks: HashMap<Vec3, FancyBlock>,
pub smart_blocks: HashMap<Vec3, SmartBlock>,
pub entities: Vec<Entity>,
}
pub enum ModelKind {
Cube,
Complex(),
impl Cataclysm {
pub const CHUNK_SIZE:u8 = 32;
pub const ENTITY_RUN_LIMIT:u8 = 5;
pub const WORLD_SIZE:(u8, u8) = (2, 2);
pub fn new() -> Self {
Self {
creation_time: Instant::now(),
textures: ResourceTable::new(),
vertex_objects:ResourceTable::new(),
shader_objects:ResourceTable::new(),
sounds: ResourceTable::new(),
music: ResourceTable::new(),
chunks: HashMap::new(),
}
}
}
pub struct ResourceReference {}
impl CataclysmChunk {
pub fn new() -> Self {
Self {
simple_blocks:HashMap::new(),
fancy_blocks: HashMap::new(),
smart_blocks: HashMap::new(),
entities: vec![],
}
}
}
#[derive(Debug, Clone)]
pub struct SimpleBlock {}
//impl Renderable for SimpleBlock {
#[rustfmt::skip]
fn vertext_data<'a>() -> (&'a [f32], &'a [u32]) {
(&[
-1.0, -1.0, 0.5, //0
1.0, -1.0, 0.5, //1
-1.0, 1.0, 0.5, //2
1.0, 1.0, 0.5, //3
-1.0, -1.0, -0.5, //4
1.0, -1.0, -0.5, //5
-1.0, 1.0, -0.5, //6
1.0, 1.0, -0.5 //7
],
&[
//Top
2, 6, 7,
2, 3, 7,
//Bottom
0, 4, 5,
0, 1, 5,
//Left
0, 2, 6,
0, 4, 6,
//Right
1, 3, 7,
1, 5, 7,
//Front
0, 2, 3,
0, 1, 3,
//Back
4, 6, 7,
4, 5, 7
]
)
}
//}
#[derive(Debug, Clone)]
pub struct FancyBlock {
pub pos:Vec3,
}
#[derive(Debug, Clone)]
pub struct SmartBlock {
pub pos:Vec3,
}
#[derive(Debug, Clone)]
pub struct Entity {
pub pos:Vec3,
}

View file

@ -0,0 +1,172 @@
use ::std::collections::HashMap;
use ::std::sync::Arc;
use ::std::sync::Weak;
use ::std::sync::atomic::AtomicBool;
/// You should prefer cloning a resource out and replacing the resource when it becomes stale
#[derive(Debug)]
pub struct ResourceTable<T> {
resources:HashMap<String, Resource<T>>,
}
pub const DEFAULT_RESOURCE_NAME:&&'static str = &"resource.default";
impl<T> ResourceTable<T> {
pub fn add_resource(self: &Arc<Self>, name:&dyn ToString, thing:T) {
let resource = Resource {
inner:Arc::new(ResourceInner::new(thing)),
name: Arc::new(name.to_string()),
table:Arc::downgrade(&self),
};
}
// could be a macro? id prefer if it could work on self like it does for add_resource
pub fn upgrade(table:&Weak<Self>) -> Arc<Self> {
if let Some(table) = table.upgrade() {
table
} else {
panic!(
"A resource replace was attempted but it appears the owning table doesnt exist? Please ensure the owning table is not dropped, this is not expected to occur."
);
}
}
///
pub fn get_resource(&self, name:&dyn ToString) -> Option<Resource<T>> { self.resources.get(&name.to_string()).cloned() }
///
pub fn get_resource_or_default(&self, name:&dyn ToString) -> Resource<T> {
let resource = self.resources.get(&name.to_string());
// we do this instead of .unwrap_or(T) because unwrap_or is not lazy
// and will calculate T immediately whether it is needed or not
if let Some(resource) = resource {
resource.clone()
} else {
if let Some(resource) = self.get_resource(DEFAULT_RESOURCE_NAME) {
resource
} else {
unreachable!(
"Since resource tables get created with the `{DEFAULT_RESOURCE_NAME}` resource and you cannot remove it, this situation is impossible to occur."
)
}
}
}
}
impl<T:Default> ResourceTable<T> {
pub fn new() -> Arc<Self> {
let table = Arc::new(Self { resources:HashMap::new() });
table.add_resource(&"default", T::default());
table
}
}
// make an enum when mutable resources pop up?
// mutable resources are very strange since they are global
// instead any mutable *instances* of resource should be just that, an instance
// there is currently no valid reason for a mutable resource
// game logic has no business here
#[derive(Debug)]
struct Resource<T> {
inner: Arc<ResourceInner<T>>,
name: Arc<String>,
/// Fast access to the table the resource belongs to
pub table:Weak<ResourceTable<T>>,
}
impl<T> Resource<T> {
pub fn is_stale(&self) -> bool { self.inner.stale.load(std::sync::atomic::Ordering::SeqCst) }
// This permanently marks this resource as stale and all usages of it should be upgraded via the replace function
pub fn expire(&mut self) {
//self.table
self.inner.stale.store(true, std::sync::atomic::Ordering::SeqCst);
}
// This function will fetch the new resource under the same name, or will return itself as an error if there is none
// Typically you should prefer replace_or_default unless you have a specific reason to handle the failure/need to keep using the original on failure
pub fn replace(self) -> Result<Self, Self> {
let table = ResourceTable::upgrade(&self.table);
if let Some(resource) = table.get_resource(&self.name) {
Ok(resource)
} else {
Err(self)
}
}
}
impl<T:Default> Resource<T> {
pub fn replace_or_default(self) -> Self {
if let Some(table) = self.table.upgrade() {
table.get_resource_or_default(&self.name)
} else {
panic!(
"A resource replace was attempted but it appears the owning table doesnt exist? Please ensure the owning table is not dropped, this is not expected to occur."
);
}
}
}
impl<T> Clone for Resource<T> {
fn clone(&self) -> Self {
Self {
inner:self.inner.clone(),
name: self.name.clone(),
table:self.table.clone(),
}
}
}
#[derive(Debug)]
struct ResourceInner<T> {
stale:AtomicBool,
thing:T,
}
impl<T> ResourceInner<T> {
fn new(thing:T) -> Self {
Self {
stale:AtomicBool::new(false),
thing,
}
}
}
// should contain both source and compiled version?
#[derive(Debug)]
pub struct GraphicsShader {}
impl Default for GraphicsShader {
fn default() -> Self { todo!() }
}
#[derive(Debug)]
pub struct GraphicsTexture {}
impl Default for GraphicsTexture {
fn default() -> Self { todo!() }
}
#[derive(Debug)]
pub struct GraphicsVBO {}
impl Default for GraphicsVBO {
fn default() -> Self { todo!() }
}
#[derive(Debug)]
pub struct AudioSFX {}
impl Default for AudioSFX {
fn default() -> Self { todo!() }
}
#[derive(Debug)]
pub struct AudioMusic {}
impl Default for AudioMusic {
fn default() -> Self { todo!() }
}

View file

@ -1,6 +1,15 @@
use anyhow::Result;
use glfw::PWindow;
use crate::cataclysm::Cataclysm;
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub mod vulkan;
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub mod vulkan_gc;
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub mod opengl;
@ -11,3 +20,91 @@ pub mod opengl;
#[cfg(target_os = "windows")] pub mod directx;
#[cfg(target_os = "vita")] pub mod gxm;
type Vec2f = cgmath::Vector2<f32>;
type Vec3f = cgmath::Vector3<f32>;
type Vec4f = cgmath::Vector4<f32>;
type Vec2d = cgmath::Vector2<f64>;
type Vec3d = cgmath::Vector3<f64>;
type Vec4d = cgmath::Vector4<f64>;
type Mat2f = cgmath::Matrix2<f32>;
type Mat3f = cgmath::Matrix3<f32>;
type Mat4f = cgmath::Matrix4<f32>;
type Mat2d = cgmath::Matrix2<f64>;
type Mat3d = cgmath::Matrix3<f64>;
type Mat4d = cgmath::Matrix4<f64>;
#[repr(C)]
#[derive(Debug, Clone, Default)]
pub struct VertexData {
pub verts: Box<[Vertex]>,
pub indices:Box<[u32]>,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Vertex {
pub pos: Vec2f,
pub color:Vec3f,
}
impl Vertex {
pub const fn new(pos:Vec2f, color:Vec3f) -> Self { Self { pos, color } }
}
#[allow(unused)]
pub trait GraphicsCommander {
/// Used to halt all render calls and calculations
/// May deallocate graphics memory
fn suspend_rendering(&mut self);
/// Used to resume all render calls and calculations
/// May allocate graphics memory
fn resume_rendering(&mut self);
/// The first called function, used to register self to window and collect needed info from the system
fn initialize_controller_system(&mut self, window: &mut PWindow);
/// Returns a formatted multiline string detailing various aspects of the render system, meant to be immediately presented to the user through the engine
fn get_renderer_information(&self) -> String;
/// Used to load and potentially compile a shader
fn load_shader_program(&mut self);
// TODO cataclysm render object
// will need a reference to game engine objects
// replace with generic draw call? draw call will draw instructions to image, image may be framebuffer or not, allows gpu accelerated dynamic texture stuff
fn render(&mut self, window:&mut PWindow, cataclysm:&mut Cataclysm) -> Result<()>;
fn create_vxos(&mut self, data:&[VertexData]);
fn destroy_vxos(&mut self);
// make generic for assets?
fn register_texture(&mut self);
fn destroy_texture(&mut self);
// cleans up all data from memory, used when exiting or switching graphics backends
//fn cleanup(&mut self);
fn exit(&mut self);
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
struct UniformBufferObject {
model:Mat4f,
view: Mat4f,
proj: Mat4f,
}
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum GMode {
Vulkan,
OpenGL,
#[cfg(target_os = "macos")]
Metal,
#[cfg(target_os = "windows")]
DirectX,
}

View file

@ -1 +1,313 @@
use crate::cataclysm::Cataclysm;
use crate::graphics_engines::Vertex;
use super::GraphicsCommander;
use super::Mat4f;
use super::UniformBufferObject;
use anyhow::Result;
use cgmath::point3;
use cgmath::vec3;
use cgmath::Deg;
use glfw::Context;
use glfw::PWindow;
use log::*;
pub mod gl {
#![allow(clippy::all)]
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
pub use Gles2 as Gl;
}
impl GraphicsCommander for CataclysmOGL {
fn suspend_rendering(&mut self) {
}
fn resume_rendering(&mut self) {
}
fn render(&mut self, window:&mut glfw::PWindow, cataclysm:&mut Cataclysm) -> anyhow::Result<()> {
// let model = Mat4f::from_axis_angle(vec3(0.0, 0.0, 1.0), Deg(45.0) * cataclysm.creation_time.elapsed().as_secs_f32());
// 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 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,
// );
// // cgmath was made with opengl in mind, this fixes it to work with vulkan
// proj[1][1] *= -1.0;
// let ubo = UniformBufferObject { model, view, proj };
//let memory = self.device.map_memory(
// self.data.uniform_buffers_memory[image_index],
// 0,
// size_of::<UniformBufferObject>() as u64,
// vk::MemoryMapFlags::empty(),
//)?;
//memcpy(&ubo, memory.cast(), 1);
//self.device.unmap_memory(self.data.uniform_buffers_memory[image_index]);
let (red, green, blue, alpha) = (0.0125, 0.0094, 0.0071, 1.0);
unsafe {
self.gl.UseProgram(self.program);
//info!("Clearing screen with color {red}|{green}|{blue}|{alpha}.");
self.gl.ClearColor(red, green, blue, alpha);
self.gl.Clear(gl::COLOR_BUFFER_BIT);
for vxo in &self.vxos {
//info!("Bind.");
self.gl.BindVertexArray(vxo.0);
for vbo in &vxo.1 {
self.gl.BindBuffer(gl::ARRAY_BUFFER, *vbo);
//info!("DrawArrays.");
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
}
}
}
window.swap_buffers();
Ok(())
}
fn register_texture(&mut self) { todo!() }
fn destroy_texture(&mut self) { todo!() }
fn exit(&mut self) {
info!("Destroying OpenGL instance.");
unsafe {
self.gl.DeleteProgram(self.program);
self.destroy_vxos();
}
}
fn create_vxos(&mut self, data:&[super::VertexData]) {
for data in data {
info!("Creating OpenGL VAO.");
let vao = unsafe {
let mut vao = std::mem::zeroed();
self.gl.GenVertexArrays(1, &mut vao);
self.gl.BindVertexArray(vao);
vao
};
info!("Creating OpenGL VBO for vertices.");
let vbo = unsafe {
let mut vbo = std::mem::zeroed();
self.gl.GenBuffers(1, &mut vbo);
self.gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
self.gl.BufferData(
gl::ARRAY_BUFFER,
dbg!(data.verts.len() * std::mem::size_of::<Vertex>()) as gl::types::GLsizeiptr,
data.verts.as_ptr() as *const _,
gl::STATIC_DRAW,
);
vbo
};
let (pos, col) = unsafe {
info!("Applying shader attributes.");
let pos_attrib = self.gl.GetAttribLocation(self.program, b"position\0".as_ptr() as *const _);
self.gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
self.gl.VertexAttribPointer(
pos_attrib as gl::types::GLuint,
2,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<f32>() as gl::types::GLsizei,
std::ptr::null(),
);
let color_attrib = self.gl.GetAttribLocation(self.program, b"color\0".as_ptr() as *const _);
self.gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
self.gl.VertexAttribPointer(
color_attrib as gl::types::GLuint,
3,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<f32>() as gl::types::GLsizei,
(2 * std::mem::size_of::<f32>()) as *const () as *const _,
);
(pos_attrib, color_attrib)
};
self.vxos.push((vao, vec![vbo]));
}
}
fn destroy_vxos(&mut self) {
unsafe {
for _ in 0..self.vxos.len() {
let vxo = self.vxos.remove(0);
self.gl.DeleteVertexArrays(1, &vxo.0);
for vbo in vxo.1.iter().take(vxo.1.len()) {
self.gl.DeleteBuffers(1, vbo);
}
}
}
}
fn initialize_controller_system(&mut self, window: &mut PWindow) {
todo!()
}
fn get_renderer_information(&self) -> String {
todo!()
}
fn load_shader_program(&mut self) {
todo!()
}
}
use std::ffi::CStr;
use std::ops::Deref;
type VAO = gl::types::GLuint;
type VBO = gl::types::GLuint;
pub struct CataclysmOGL {
program:gl::types::GLuint,
vxos: Vec<(VAO, Vec<VBO>)>,
gl: gl::Gl,
}
impl CataclysmOGL {
pub fn create(window:&mut PWindow) -> Result<Self> {
info!("Creating OpenGL renderer instance.");
unsafe {
let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol));
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
info!("Running on {}", renderer.to_string_lossy());
}
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
info!("OpenGL Version {}", version.to_string_lossy());
}
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
info!("Shaders version on {}", shaders_version.to_string_lossy());
}
info!("Loading shaders.");
let vertex_shader = create_shader(&gl, gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE);
let fragment_shader = create_shader(&gl, gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);
info!("Creating OpenGL shader program.");
let program = gl.CreateProgram();
gl.AttachShader(program, vertex_shader);
gl.AttachShader(program, fragment_shader);
info!("Deleting shader objects.");
gl.DeleteShader(vertex_shader);
gl.DeleteShader(fragment_shader);
info!("Linking shader program.");
gl.LinkProgram(program);
info!("OpenGL renderer instance created.");
Ok(Self {
program,
vxos:vec![/*(vao, vec![vbo])*/],
gl,
})
}
}
pub fn resize(&self, width:i32, height:i32) {
unsafe {
self.gl.Viewport(0, 0, width, height);
}
}
}
impl Deref for CataclysmOGL {
type Target = gl::Gl;
fn deref(&self) -> &Self::Target { &self.gl }
}
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()))
}
}
const VERTEX_SHADER_SOURCE2:&[u8] = b"
#version 450
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
fragColor = inColor;
}
\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_SOURCE2:&[u8] = b"
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}
\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";

View file

@ -7,10 +7,14 @@ use cgmath::vec3;
use cgmath::Deg;
use log::*;
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;
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 +33,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 +67,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)),
];
const INDICES:&[u16] = &[0, 1, 2, 2, 3, 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)),
];
#[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; )* } => {
$(
@ -84,10 +100,78 @@ macro_rules! const_shaders {
};
}
pub fn start() -> Result<()> {
info!("Initializing event loop and winit window instance.");
// Window
let event_loop = EventLoop::new()?;
let window = WindowBuilder::new()
.with_title("WHAT")
.with_inner_size(LogicalSize::new(1024, 768))
.build(&event_loop)?;
info!("Creating app and starting event loop.");
// App
let mut app = unsafe { App::create(&window)? };
let mut minimized = false;
//let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
event_loop.run(move |event, elwt| {
//let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
//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();
}
},
_ => {},
},
_ => {},
}
})?;
Ok(())
}
/// Our Vulkan app.
#[derive(Clone, Debug)]
pub(crate) struct App {
/// Vulkan entrypoint
#[allow(unused)]
entry: Entry,
instance: Instance,
data: AppData,
@ -137,7 +221,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 +241,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,18 +305,25 @@ 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(
#[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,
);
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,
);
// cgmath was made with opengl in mind, this fixes it to work with vulkan
proj[1][1] *= -1.0;
let ubo = UniformBufferObject { model, view, proj };
let memory = self.device.map_memory(
@ -273,9 +379,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 +451,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> {
@ -385,8 +494,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 +580,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 +645,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 +811,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 +827,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 +883,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()
@ -846,16 +966,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 +990,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 +1033,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,7 +1240,6 @@ 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.
@ -933,6 +1251,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 +1350,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 +1377,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 +1392,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 +1423,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 +1447,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 +1465,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 +1501,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 +1559,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(()) }

File diff suppressed because it is too large Load diff

View file

@ -1,96 +0,0 @@
use anyhow::Result;
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::vulkan::App;
const WINDOW_TITLE:&'static str = "MineMod";
pub fn main() -> Result<()> {
super::init_logging();
info!("Registering CTRLC hook.");
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
ctrlc::set_handler(move || {
shutdown_tx.send(()).expect("Failed to send shutdown signal");
})
.expect("Error setting Ctrl-C handler");
info!("Initializing event loop and winit window instance.");
// Window
let event_loop = EventLoop::new()?;
let window = WindowBuilder::new()
.with_title(WINDOW_TITLE)
.with_inner_size(LogicalSize::new(1024, 768))
.build(&event_loop)?;
info!("Creating app and starting event loop.");
// App
let mut app = unsafe { App::create(&window)? };
let mut minimized = false;
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
event_loop.run(move |event, elwt| {
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
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();
}
},
_ => {},
},
_ => {},
}
})?;
info!("Exiting program.");
Ok(())
}

View file

@ -1,96 +0,0 @@
use anyhow::Result;
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::vulkan::App;
const WINDOW_TITLE:&'static str = "MineMod";
pub fn main() -> Result<()> {
super::init_logging();
info!("Registering CTRLC hook.");
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
ctrlc::set_handler(move || {
shutdown_tx.send(()).expect("Failed to send shutdown signal");
})
.expect("Error setting Ctrl-C handler");
info!("Initializing event loop and winit window instance.");
// Window
let event_loop = EventLoop::new()?;
let window = WindowBuilder::new()
.with_title(WINDOW_TITLE)
.with_inner_size(LogicalSize::new(1024, 768))
.build(&event_loop)?;
info!("Creating app and starting event loop.");
// App
let mut app = unsafe { App::create(&window)? };
let mut minimized = false;
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
event_loop.run(move |event, elwt| {
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
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();
}
},
_ => {},
},
_ => {},
}
})?;
info!("Exiting program.");
Ok(())
}

View file

@ -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,
@ -8,8 +8,33 @@
unsafe_op_in_unsafe_fn
)]
mod audio_engines;
mod cataclysm;
mod graphics_engines;
mod input_systems;
mod targets;
/// i hope you will forgive me for these crimes, but as far as i can tell this is the best way
/// to handle all these platforms with wildly different requirements and capabilities
pub use targets::*;
// Logging is currently common across all platforms so it may live here
fn init_logging() {
tracing_subscriber::fmt()
.compact()
.with_timer(tracing_subscriber::fmt::time::uptime())
.with_ansi(true)
.with_level(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();
}
// NOTE use crate:: for things in engine that depend on the target platform
// this allows an incredibly easy way to handle gui and others per platform
@ -39,57 +64,4 @@ mod graphics_engines;
// research why texture atlases exist, it seems like an easy way to reduce the allocation calls since they are limited, and it seems to provide better usage of memory overall
// Planned system targets
#[cfg(target_os = "linux")] mod linux;
#[cfg(target_os = "linux")] pub use linux::*;
#[cfg(target_os = "windows")] mod windows;
#[cfg(target_os = "windows")] pub use windows::*;
#[cfg(target_os = "macos")] mod mac;
#[cfg(target_os = "macos")] pub use mac::*;
#[cfg(target_family = "wasm")] mod wasm;
#[cfg(target_family = "wasm")] pub use wasm::*;
#[cfg(target_os = "vita")] mod vita;
#[cfg(target_os = "vita")] pub use vita::*;
// Potential system targets
// bindgen, cc, https://github.com/rust-console
// switch // c lib: libnx, crate: rust-switch, toolchain: devkitPro
// #[cfg(target_os = "horizon")]
// mod switch;
// #[cfg(target_os = "horizon")]
// pub use switch::*;
// ps3 // sdk: PSL1GHT, target: powerpc64-unknown-linux-gnu
// #[cfg(target_os = "psl1ght")]
// mod ps3;
// #[cfg(target_os = "psl1ght")]
// pub use ps3::*;
// x360 // c lib: libxenon, make custom spec for xenon target
// #[cfg(target_os = "xenon")]
// mod x369;
// #[cfg(target_os = "xenon")]
// pub use x360::*;
// wii // toolchain: devkitPPC, target: powerpc-unknown-eabi, c lib: libogc
// #[cfg(target_os = "wii")]
// mod wii;
// #[cfg(target_os = "wii")]
// pub use wii::*;
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();
// pretty_env_logger::init_timed();
}
// multirender system where both vulkan and opengl are active at once, each rendering half of the screen, can use to compare the two systems while working on them

View file

@ -0,0 +1,13 @@
//use anyhow::Result;
//use log::*;
//use winit::event_loop::EventLoop;
//pub fn main() -> Result<()> {
// crate::init_logging();
// Ok(())
//}

View file

@ -0,0 +1,96 @@
//use anyhow::Result;
//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::vulkan::App;
//const WINDOW_TITLE:&'static str = "MineMod";
//pub fn main() -> Result<()> {
// super::init_logging();
// info!("Registering CTRLC hook.");
// let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
// ctrlc::set_handler(move || {
// shutdown_tx.send(()).expect("Failed to send shutdown signal");
// })
// .expect("Error setting Ctrl-C handler");
// info!("Initializing event loop and winit window instance.");
// // Window
// let event_loop = EventLoop::new()?;
// let window = WindowBuilder::new()
// .with_title(WINDOW_TITLE)
// .with_inner_size(LogicalSize::new(1024, 768))
// .build(&event_loop)?;
// info!("Creating app and starting event loop.");
// // App
// let mut app = unsafe { App::create(&window)? };
// let mut minimized = false;
// let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
// event_loop.run(move |event, elwt| {
// let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
// 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();
// }
// },
// _ => {},
// },
// _ => {},
// }
// })?;
// info!("Exiting program.");
// Ok(())
//}

288
src/targets/desktop/mod.rs Normal file
View file

@ -0,0 +1,288 @@
use anyhow::Result;
use cgmath::vec2;
use cgmath::vec3;
use glfw::Action;
use glfw::Context;
use glfw::Key;
use log::*;
use std::collections::HashMap;
use crate::cataclysm::Cataclysm;
use crate::cataclysm::CataclysmChunk;
use crate::cataclysm::SimpleBlock;
use crate::graphics_engines::GMode;
use crate::graphics_engines::GraphicsCommander;
use crate::graphics_engines::Vertex;
use crate::graphics_engines::VertexData;
//#[cfg(target_os = "linux")] mod linux;
//#[cfg(target_os = "linux")] pub use linux::*;
//#[cfg(target_os = "windows")] mod windows;
//#[cfg(target_os = "windows")] pub use windows::*;
//#[cfg(target_os = "macos")] mod mac;
//#[cfg(target_os = "macos")] pub use mac::*;
pub const WINDOW_TITLE:&'static str = "MineMod";
pub const WINDOW_DECORATION_LEFT:bool = false;
pub fn shutdown_handler(sender:std::sync::mpsc::Sender<()>) {
static SHUTDOWN_COUNT:std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(1);
if SHUTDOWN_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst) >= 3 {
warn!("3 exit events have piled up. Forcing exit.");
std::process::exit(-1);
}
sender.send(()).expect("Failed to send shutdown signal");
}
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
crate::init_logging();
info!("Registering ^C hook.");
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
{
let tx = shutdown_tx.clone();
ctrlc::set_handler(move || shutdown_handler(tx.clone())).expect("Error setting Ctrl-C handler");
}
//std::thread::sleep(std::time::Duration::from_secs(10));
let mut glfw = glfw::init(|err, string| match err {
// we dont give a shit because we supply our own render system
glfw::Error::NoWindowContext => warn!("GLFW Error: {err} {string}"),
_ => panic!("GLFW Error! {err}, {string}"),
})?;
let mut graphics_mode = GMode::OpenGL;
// if we do not want to use opengl, disable it
if graphics_mode != GMode::OpenGL {
glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
}
let (mut window, events) = if let Some(glfw) = glfw.create_window(1024, 768, &WINDOW_TITLE, glfw::WindowMode::Windowed) {
glfw
} else {
return Err("Could not create glfw window!".into());
};
window.set_key_polling(true);
window.make_current();
window.set_close_callback(move |_| shutdown_handler(shutdown_tx.clone()));
let mut graphics_commander = make_graphics_commander(graphics_mode, &mut window)?;
let mut opengl_cache = None;
// Register models
let verts:Box<[Vertex]> = Box::new([
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)),
]);
graphics_commander.create_vxos(&[VertexData { verts, ..Default::default() }]);
// create game world
let mut game_world = Cataclysm::new();
//let mut inners = vec![];
//let mut mids = vec![];
//let mut outers = vec![];
let mut threads = vec![];
let (sender, recv) = std::sync::mpsc::channel();
let instant = std::time::Instant::now();
//let mut count = 0;
const RANGE:std::ops::Range<u32> = 0..1;
for x in RANGE {
'ml: loop {
if threads.len() >= 4 {
//info!("MAX");
recv.recv().unwrap();
break 'ml;
} else {
break 'ml;
}
}
let sender = sender.clone();
threads.push(std::thread::spawn(move || {
info!("Spawned");
let mut chunks:HashMap<(u32, u32, u32), CataclysmChunk> = HashMap::new();
let instant = std::time::Instant::now();
for y in RANGE {
//let instant = std::time::Instant::now();
for z in RANGE {
//let instant = std::time::Instant::now();
chunks.insert((x, y, z), CataclysmChunk::new());
for bx in 0..Cataclysm::CHUNK_SIZE {
for by in 0..Cataclysm::CHUNK_SIZE {
for bz in 0..Cataclysm::CHUNK_SIZE {
chunks.get_mut(&(x, y, z)).unwrap().simple_blocks.insert((bx, by, bz), SimpleBlock {});
//count += 1;
}
}
}
//inners.push(std::time::Instant::now() - instant);
}
//mids.push(std::time::Instant::now() - instant);
}
//outers.push(std::time::Instant::now() - instant);
sender.send(()).unwrap();
chunks
}));
}
//dbg!(count);
let mut master_chunkmap:HashMap<(u32, u32, u32), CataclysmChunk> = HashMap::new();
for t in threads {
let chunks = t.join().unwrap();
for (_id, (key, value)) in chunks.iter().enumerate() {
if let Some(val) = master_chunkmap.insert(*key, value.clone()) {
warn!("Existing value present! {:?}", val);
}
}
}
game_world.chunks = master_chunkmap;
//{
// let mut avg = 0.0;
// for i in &inners {
// avg += i.as_secs_f32();
// }
// avg = avg / inners.len() as f32;
// info!("inners:{avg}");
// avg = 0.0;
// for i in &mids {
// avg += i.as_secs_f32();
// }
// avg = avg / mids.len() as f32;
// info!("mids:{avg}");
// avg = 0.0;
// for i in &outers {
// avg += i.as_secs_f32();
// }
// avg = avg / outers.len() as f32;
// info!("outers:{avg}");
//}
info!("MASTER: {}", (std::time::Instant::now() - instant).as_secs_f32());
//return Ok(());
let mut graphics_commander = Some(graphics_commander);
//'mainloop: while !window.should_close() {
loop {
if shutdown_rx.try_recv().is_ok() {
info!("^C triggered.");
window.set_should_close(true);
}
// dont flood the logs
//std::thread::sleep(std::time::Duration::from_secs(1));
trace!("Polling GLFW events.");
// Poll for and process events
glfw.poll_events();
for (e, event) in glfw::flush_messages(&events) {
info!("GLFW Event: {:?}", event);
match event {
//glfw::WindowEvent::FramebufferSize(x, y) => {},
// minimize? doesnt appear to be
glfw::WindowEvent::Iconify(e) =>
if let Some(graphics_commander) = graphics_commander.as_mut() {
if e {
graphics_commander.suspend_rendering();
} else {
graphics_commander.resume_rendering();
}
},
glfw::WindowEvent::Key(key, _, Action::Press, _) => match key {
Key::Escape | Key::Q => window.set_should_close(true),
Key::E => {
match graphics_mode {
GMode::Vulkan => {
info!("v1 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
if let Some(mut graphics_commander) = graphics_commander.take() {
graphics_commander.exit();
}
info!("v2 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
graphics_mode = GMode::OpenGL;
graphics_commander = opengl_cache.take();
if graphics_commander.is_none() {
error!("OpenGL was expected but not present");
graphics_commander = Some(make_graphics_commander(graphics_mode, &mut window)?);
}
info!("v3 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
},
GMode::OpenGL => {
info!("o1 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
//graphics_commander.clean();
opengl_cache = graphics_commander.take();
info!("o2 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
graphics_mode = GMode::Vulkan;
graphics_commander = Some(make_graphics_commander(graphics_mode, &mut window)?);
info!("o3 mode{:?} cmd{} cache{}", graphics_mode, graphics_commander.is_some(), opengl_cache.is_some());
},
#[cfg(target_os = "macos")]
GMode::Metal => todo!(),
#[cfg(target_os = "windows")]
GMode::DirectX => todo!(),
}
},
_ => (),
},
_ => (),
}
}
if window.should_close() {
info!("Breaking loop.");
break;
}
if let Some(graphics_commander) = graphics_commander.as_mut() {
trace!("Rendering.");
//cmd.render_simple_blocks(game_world.chunks.get_mut(&(0, 0, 0)).unwrap().simple_blocks);
graphics_commander.render(&mut window, &mut game_world)?;
}
}
if let Some(graphics_commander) = graphics_commander.as_mut() {
info!("Closing GC instance.");
graphics_commander.exit();
}
info!("Exiting.");
Ok(())
}
fn make_graphics_commander(graphics_mode:GMode, window:&mut glfw::PWindow) -> Result<Box<dyn GraphicsCommander>> {
Ok(match graphics_mode {
GMode::Vulkan => Box::new(unsafe { crate::graphics_engines::vulkan_gc::App::create(&window)? }),
GMode::OpenGL => Box::new(crate::graphics_engines::opengl::CataclysmOGL::create(window)?),
#[cfg(target_os = "macos")]
GMode::Metal => todo!(),
#[cfg(target_os = "windows")]
GMode::DirectX => todo!(),
})
}
//fn make_glfw() -> Result<glfw::Glfw, Box<dyn std::error::Error>> {
// Ok(glfw)
//}

View file

@ -0,0 +1,96 @@
//use anyhow::Result;
//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::vulkan::App;
//const WINDOW_TITLE:&'static str = "MineMod";
//pub fn main() -> Result<()> {
// super::init_logging();
// info!("Registering CTRLC hook.");
// let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel::<()>();
// ctrlc::set_handler(move || {
// shutdown_tx.send(()).expect("Failed to send shutdown signal");
// })
// .expect("Error setting Ctrl-C handler");
// info!("Initializing event loop and winit window instance.");
// // Window
// let event_loop = EventLoop::new()?;
// let window = WindowBuilder::new()
// .with_title(WINDOW_TITLE)
// .with_inner_size(LogicalSize::new(1024, 768))
// .build(&event_loop)?;
// info!("Creating app and starting event loop.");
// // App
// let mut app = unsafe { App::create(&window)? };
// let mut minimized = false;
// let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
// event_loop.run(move |event, elwt| {
// let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
// 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();
// }
// },
// _ => {},
// },
// _ => {},
// }
// })?;
// info!("Exiting program.");
// Ok(())
//}

37
src/targets/mod.rs Normal file
View file

@ -0,0 +1,37 @@
// Planned system targets
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
mod desktop;
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub use desktop::*;
// wasm may count for desktop? it does not need windowing however
//#[cfg(target_family = "wasm")] mod wasm;
//#[cfg(target_family = "wasm")] pub use wasm::*;
//#[cfg(target_os = "vita")] mod vita;
//#[cfg(target_os = "vita")] pub use vita::*;
// Potential system targets
// bindgen, cc, https://github.com/rust-console
// switch // c lib: libnx, crate: rust-switch, toolchain: devkitPro
// #[cfg(target_os = "horizon")]
// mod switch;
// #[cfg(target_os = "horizon")]
// pub use switch::*;
// ps3 // sdk: PSL1GHT, target: powerpc64-unknown-linux-gnu
// #[cfg(target_os = "psl1ght")]
// mod ps3;
// #[cfg(target_os = "psl1ght")]
// pub use ps3::*;
// x360 // c lib: libxenon, make custom spec for xenon target
// #[cfg(target_os = "xenon")]
// mod x369;
// #[cfg(target_os = "xenon")]
// pub use x360::*;
// wii // toolchain: devkitPPC, target: powerpc-unknown-eabi, c lib: libogc
// #[cfg(target_os = "wii")]
// mod wii;
// #[cfg(target_os = "wii")]
// pub use wii::*;

View file

@ -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();

View file

@ -1,96 +0,0 @@
use anyhow::Result;
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::vulkan::App;
const WINDOW_TITLE:&'static str = "MineMod";
pub fn main() -> Result<()> {
super::init_logging();
info!("Registering CTRLC hook.");
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel::<()>();
ctrlc::set_handler(move || {
shutdown_tx.send(()).expect("Failed to send shutdown signal");
})
.expect("Error setting Ctrl-C handler");
info!("Initializing event loop and winit window instance.");
// Window
let event_loop = EventLoop::new()?;
let window = WindowBuilder::new()
.with_title(WINDOW_TITLE)
.with_inner_size(LogicalSize::new(1024, 768))
.build(&event_loop)?;
info!("Creating app and starting event loop.");
// App
let mut app = unsafe { App::create(&window)? };
let mut minimized = false;
let shutdown_rx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_rx)));
event_loop.run(move |event, elwt| {
let mut shutdown_rx_guard = shutdown_rx.lock().unwrap();
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();
}
},
_ => {},
},
_ => {},
}
})?;
info!("Exiting program.");
Ok(())
}