diff --git a/Cargo.toml b/Cargo.toml index 83ca2a8..5c84b26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,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 diff --git a/src/cataclysm/mod.rs b/src/cataclysm/mod.rs index c2f7a00..381ca6b 100644 --- a/src/cataclysm/mod.rs +++ b/src/cataclysm/mod.rs @@ -1,13 +1,26 @@ use std::collections::HashMap; +use std::sync::Arc; +use std::time::Instant; use glam::Vec3; +use resources::*; -use crate::graphics_engines::Renderable; +//use crate::graphics_engines::Renderable; +pub mod resources; + +#[derive(Debug)] pub struct Cataclysm { - pub chunks:HashMap<(u32, u32, u32), CataclysmChunk>, + pub creation_time:Instant, + textures: Arc>, + vertex_objects: Arc>, + shader_objects: Arc>, + sounds: Arc>, + music: Arc>, + pub chunks: HashMap<(u32, u32, u32), CataclysmChunk>, } +#[derive(Debug, Clone)] pub struct CataclysmChunk { pub simple_blocks:HashMap<(u8, u8, u8), SimpleBlock>, pub fancy_blocks: HashMap, @@ -22,7 +35,13 @@ impl Cataclysm { pub fn new() -> Self { Self { - chunks: HashMap::new() + creation_time: Instant::now(), + textures: ResourceTable::new(), + vertex_objects:ResourceTable::new(), + shader_objects:ResourceTable::new(), + sounds: ResourceTable::new(), + music: ResourceTable::new(), + chunks: HashMap::new(), } } } @@ -30,33 +49,31 @@ impl Cataclysm { impl CataclysmChunk { pub fn new() -> Self { Self { - simple_blocks: HashMap::new(), + simple_blocks:HashMap::new(), fancy_blocks: HashMap::new(), smart_blocks: HashMap::new(), entities: vec![], } } } +#[derive(Debug, Clone)] +pub struct SimpleBlock {} -pub struct SimpleBlock { - -} - -impl Renderable for SimpleBlock { - #[rustfmt::skip] - fn vertext_data<'a>() -> (&'a [f32], &'a [u32]) { +//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, //5 -1.0, 1.0, -0.5, //6 1.0, 1.0, -0.5 //7 - ], - &[ - //Top + ], + &[ + //Top 2, 6, 7, 2, 3, 7, @@ -80,25 +97,18 @@ impl Renderable for SimpleBlock { 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, } - -//pub enum ModelKind { -// Cube, -// Complex(), -//} - -//pub struct ResourceReference {} diff --git a/src/cataclysm/resources/mod.rs b/src/cataclysm/resources/mod.rs new file mode 100644 index 0000000..8a11aac --- /dev/null +++ b/src/cataclysm/resources/mod.rs @@ -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 { + resources:HashMap>, +} + +pub const DEFAULT_RESOURCE_NAME:&&'static str = &"resource.default"; + +impl ResourceTable { + pub fn add_resource(self: &Arc, 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) -> Arc { + 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> { self.resources.get(&name.to_string()).cloned() } + + /// + pub fn get_resource_or_default(&self, name:&dyn ToString) -> Resource { + 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 ResourceTable { + pub fn new() -> Arc { + 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 { + inner: Arc>, + name: Arc, + /// Fast access to the table the resource belongs to + pub table:Weak>, +} + +impl Resource { + 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 { + let table = ResourceTable::upgrade(&self.table); + if let Some(resource) = table.get_resource(&self.name) { + Ok(resource) + } else { + Err(self) + } + } +} + +impl Resource { + 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 Clone for Resource { + fn clone(&self) -> Self { + Self { + inner:self.inner.clone(), + name: self.name.clone(), + table:self.table.clone(), + } + } +} + +#[derive(Debug)] +struct ResourceInner { + stale:AtomicBool, + thing:T, +} + +impl ResourceInner { + 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!() } +} diff --git a/src/graphics_engines/mod.rs b/src/graphics_engines/mod.rs index cc3a92c..bfc2458 100644 --- a/src/graphics_engines/mod.rs +++ b/src/graphics_engines/mod.rs @@ -21,28 +21,66 @@ pub mod opengl; #[cfg(target_os = "vita")] pub mod gxm; -pub trait Renderable { - fn vertext_data<'a>() -> (&'a [f32], &'a [u32]); -} +type Vec2f = cgmath::Vector2; +type Vec3f = cgmath::Vector3; +type Vec4f = cgmath::Vector4; + +type Vec2d = cgmath::Vector2; +type Vec3d = cgmath::Vector3; +type Vec4d = cgmath::Vector4; + +type Mat2f = cgmath::Matrix2; +type Mat3f = cgmath::Matrix3; +type Mat4f = cgmath::Matrix4; + +type Mat2d = cgmath::Matrix2; +type Mat3d = cgmath::Matrix3; +type Mat4d = cgmath::Matrix4; #[repr(C)] #[derive(Debug, Clone, Default)] pub struct VertexData { - verts: Box<[f32]>, - indices:Box<[u32]>, + 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<()>; - /// This function will clear all existing vbos and replace them with its own. - fn register_vbos(&mut self, data:&[VertexData]); + 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); @@ -52,6 +90,14 @@ pub trait GraphicsCommander { 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 { diff --git a/src/graphics_engines/opengl/mod.rs b/src/graphics_engines/opengl/mod.rs index 37a6257..6cf13d0 100644 --- a/src/graphics_engines/opengl/mod.rs +++ b/src/graphics_engines/opengl/mod.rs @@ -1,14 +1,15 @@ use crate::cataclysm::Cataclysm; +use crate::graphics_engines::Vertex; use super::GraphicsCommander; +use super::Mat4f; +use super::UniformBufferObject; use anyhow::Result; -use glam::vec2; -use glam::vec3; - -use glam::Vec2; -use glam::Vec3; +use cgmath::point3; +use cgmath::vec3; +use cgmath::Deg; use glfw::Context; use glfw::PWindow; @@ -21,7 +22,7 @@ pub mod gl { pub use Gles2 as Gl; } -impl GraphicsCommander for Renderer { +impl GraphicsCommander for CataclysmOGL { fn suspend_rendering(&mut self) { } @@ -29,23 +30,51 @@ impl GraphicsCommander for Renderer { } 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::() 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); - trace!("Clearing screen with color {red}|{green}|{blue}|{alpha}."); unsafe { self.gl.UseProgram(self.program); - trace!("Bind."); - self.gl.BindVertexArray(self.vao); - self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo); - - trace!("ClearColor."); + //info!("Clearing screen with color {red}|{green}|{blue}|{alpha}."); self.gl.ClearColor(red, green, blue, alpha); - trace!("Clear."); self.gl.Clear(gl::COLOR_BUFFER_BIT); - trace!("DrawArrays."); - self.gl.DrawArrays(gl::TRIANGLES, 0, 3); + + 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); + } + } } - trace!("Done clearing screen."); window.swap_buffers(); @@ -56,62 +85,75 @@ impl GraphicsCommander for Renderer { fn destroy_texture(&mut self) { todo!() } - //fn cleanup(&mut self) { todo!() } - fn exit(&mut self) { - trace!("Destroying OpenGL instance."); + info!("Destroying OpenGL instance."); unsafe { self.gl.DeleteProgram(self.program); - for vbo in self.vbos.iter().take(self.vbos.len()) { - self.gl.DeleteBuffers(1, vbo); - } - self.gl.DeleteVertexArrays(1, &self.vao); + self.destroy_vxos(); } } - fn register_vbos(&mut self, data:&[super::VertexData]) { + fn create_vxos(&mut self, data:&[super::VertexData]) { for data in data { - trace!("Creating OpenGL VAO."); - unsafe { + info!("Creating OpenGL VAO."); + let vao = unsafe { let mut vao = std::mem::zeroed(); self.gl.GenVertexArrays(1, &mut vao); self.gl.BindVertexArray(vao); - } - trace!("Creating OpenGL VBO."); - unsafe { + 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, - (VERTEX_DATA.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, - VERTEX_DATA.as_ptr() as *const _, + dbg!(data.verts.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, + data.verts.as_ptr() as *const _, gl::STATIC_DRAW, ); - } + vbo + }; - unsafe { - trace!("Applying shader attributes."); - let pos_attrib = self.gl.GetAttribLocation( self.program, b"position\0".as_ptr() as *const _); - let color_attrib = self.gl.GetAttribLocation(self.program, b"color\0".as_ptr() as *const _); + 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, - 0, - std::mem::size_of::() as gl::types::GLsizei, + gl::FALSE, + std::mem::size_of::() 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, - 0, - 5 * std::mem::size_of::() as gl::types::GLsizei, + gl::FALSE, + std::mem::size_of::() as gl::types::GLsizei, (2 * std::mem::size_of::()) as *const () as *const _, ); - self.gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint); - self.gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint); + (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); + } } } } @@ -120,17 +162,18 @@ impl GraphicsCommander for Renderer { use std::ffi::CStr; use std::ops::Deref; -pub struct Renderer { +type VAO = gl::types::GLuint; +type VBO = gl::types::GLuint; + +pub struct CataclysmOGL { program:gl::types::GLuint, - vao: gl::types::GLuint, - vbos: Vec, - //vbo: gl::types::GLuint, + vxos: Vec<(VAO, Vec)>, gl: gl::Gl, } -impl Renderer { +impl CataclysmOGL { pub fn create(window:&mut PWindow) -> Result { - trace!("Creating OpenGL renderer instance."); + info!("Creating OpenGL renderer instance."); unsafe { let gl = gl::Gl::load_with(|symbol| window.get_proc_address(symbol)); @@ -146,26 +189,29 @@ impl Renderer { info!("Shaders version on {}", shaders_version.to_string_lossy()); } - trace!("Loading shaders."); + 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); - trace!("Creating OpenGL shader program."); + info!("Creating OpenGL shader program."); let program = gl.CreateProgram(); gl.AttachShader(program, vertex_shader); gl.AttachShader(program, fragment_shader); - gl.LinkProgram(program); - - gl.UseProgram(program); - - trace!("Deleting shader objects."); + info!("Deleting shader objects."); gl.DeleteShader(vertex_shader); gl.DeleteShader(fragment_shader); - trace!("OpenGL renderer instance created."); - Ok(Self { program, vao, vbos:vec![], gl }) + info!("Linking shader program."); + gl.LinkProgram(program); + + info!("OpenGL renderer instance created."); + Ok(Self { + program, + vxos:vec![/*(vao, vec![vbo])*/], + gl, + }) } } @@ -176,7 +222,7 @@ impl Renderer { } } -impl Deref for Renderer { +impl Deref for CataclysmOGL { type Target = gl::Gl; fn deref(&self) -> &Self::Target { &self.gl } @@ -196,53 +242,6 @@ fn get_gl_string(gl:&gl::Gl, variant:gl::types::GLenum) -> Option<&'static CStr> } } -#[repr(C)] -#[derive(Copy, Clone, Debug)] -struct Vertex { - pos: Vec2, - color:Vec3, -} - -#[rustfmt::skip] -static VERTEX_DATA: [Vertex; 3] = [ - //-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, - 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)), -]; -impl Vertex { - const fn new(pos:Vec2, color:Vec3) -> Self { Self { pos, color } } - - //fn binding_description() -> vk::VertexInputBindingDescription { - // vk::VertexInputBindingDescription::builder() - // .binding(0) - // .stride(size_of::() as u32) - // .input_rate(vk::VertexInputRate::VERTEX) - // .build() - //} - - //fn attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { - // let pos = vk::VertexInputAttributeDescription::builder() - // .binding(0) - // .location(0) - // .format(vk::Format::R32G32_SFLOAT) - // .offset(0) - // .build(); - - // let color = vk::VertexInputAttributeDescription::builder() - // .binding(0) - // .location(1) - // .format(vk::Format::R32G32B32_SFLOAT) - // .offset(size_of::() as u32) - // .build(); - - // [pos, color] - //} -} - const VERTEX_SHADER_SOURCE2:&[u8] = b" #version 450 diff --git a/src/graphics_engines/vulkan_gc.rs b/src/graphics_engines/vulkan_gc.rs index 670df99..d32bff8 100644 --- a/src/graphics_engines/vulkan_gc.rs +++ b/src/graphics_engines/vulkan_gc.rs @@ -1,12 +1,13 @@ use anyhow::Result; use anyhow::anyhow; +use cgmath::Deg; use cgmath::point3; use cgmath::vec2; use cgmath::vec3; -use cgmath::Deg; use glfw::PWindow; + use log::*; use std::collections::HashSet; @@ -33,6 +34,9 @@ use vulkanalia::vk::KhrSwapchainExtension; use crate::cataclysm::Cataclysm; use super::GraphicsCommander; +use super::Mat4f; +use super::Vec2f; +use super::Vec3f; const VALIDATION_ENABLED:bool = cfg!(debug_assertions); // add env support? const VALIDATION_LAYER:vk::ExtensionName = vk::ExtensionName::from_bytes(b"VK_LAYER_KHRONOS_validation"); @@ -44,21 +48,11 @@ const VK_APPLICATION_NAME:&'static [u8; 8] = b"minemod\0"; const VK_ENGINE_NAME:&'static [u8; 10] = b"cataclysm\0"; const MAX_FRAMES_IN_FLIGHT:usize = 2; -// static VERTICES:[Vertex; 3] = [ -// Vertex::new(vec2(0.0, -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)), -// ]; - -static VERTICES:[Vertex; 4] = [ +const 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]; @@ -85,9 +79,13 @@ pub(crate) struct App { } impl GraphicsCommander for App { - fn suspend_rendering(&mut self) { info!("SUSPEND") } + fn suspend_rendering(&mut self) { + warn!("Unimplemented!"); + } - fn resume_rendering(&mut self) { info!("RESUME") } + fn resume_rendering(&mut self) { + warn!("Unimplemented!"); + } /// Renders a frame for our Vulkan app. fn render(&mut self, window:&mut PWindow, cataclysm:&mut Cataclysm) -> Result<()> { @@ -159,11 +157,21 @@ impl GraphicsCommander for App { } } - fn register_vbos(&mut self, data:&[super::VertexData]) { todo!() } + fn create_vxos(&mut self, data:&[super::VertexData]) { + warn!("Unimplemented!"); + } - fn register_texture(&mut self) { todo!() } + fn destroy_vxos(&mut self) { + warn!("Unimplemented!"); + } - fn destroy_texture(&mut self) { todo!() } + fn register_texture(&mut self) { + warn!("Unimplemented!"); + } + + fn destroy_texture(&mut self) { + warn!("Unimplemented!"); + } fn exit(&mut self) { info!("Destroying Vulkan instance."); diff --git a/src/main.rs b/src/main.rs index 69409c6..2a5e0a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,3 +63,5 @@ fn init_logging() { // test BCn vs raw formats // 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 + +// 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 diff --git a/src/targets/desktop/mod.rs b/src/targets/desktop/mod.rs index 6199875..6989366 100644 --- a/src/targets/desktop/mod.rs +++ b/src/targets/desktop/mod.rs @@ -1,16 +1,24 @@ 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::*; @@ -22,6 +30,15 @@ use crate::graphics_engines::GraphicsCommander; 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> { crate::init_logging(); @@ -29,26 +46,25 @@ pub fn main() -> Result<(), Box> { 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"); + { + 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 /*| glfw::Error::NoError*/ => warn!("GLFW Error: {err} {string}"), + glfw::Error::NoWindowContext => warn!("GLFW Error: {err} {string}"), _ => panic!("GLFW Error! {err}, {string}"), })?; - let graphics_mode = GMode::OpenGL; + 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)); } - if let GMode::OpenGL = graphics_mode {} - let (mut window, events) = if let Some(glfw) = glfw.create_window(1024, 768, &WINDOW_TITLE, glfw::WindowMode::Windowed) { glfw } else { @@ -57,21 +73,115 @@ pub fn main() -> Result<(), Box> { window.set_key_polling(true); window.make_current(); + window.set_close_callback(move |_| shutdown_handler(shutdown_tx.clone())); - let mut cmd = /*Some(*/make_graphics_commander(graphics_mode, &mut window)?/*)*/; + 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(); - game_world.chunks.insert((0, 0, 0), CataclysmChunk::new()); + //let mut inners = vec![]; + //let mut mids = vec![]; + //let mut outers = vec![]; - for x in 0..Cataclysm::CHUNK_SIZE { - for y in 0..Cataclysm::CHUNK_SIZE { - for z in 0..Cataclysm::CHUNK_SIZE { - game_world.chunks.get_mut(&(0, 0, 0)).unwrap().simple_blocks.insert((x, y, z), SimpleBlock {}); + 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 = 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() { @@ -79,11 +189,8 @@ pub fn main() -> Result<(), Box> { window.set_should_close(true); } - // Swap front and back buffers - //window.swap_buffers(); // cannot be used - // dont flood the logs - //std::thread::sleep(Duration::from_secs(1)); + //std::thread::sleep(std::time::Duration::from_secs(1)); trace!("Polling GLFW events."); // Poll for and process events @@ -93,38 +200,53 @@ pub fn main() -> Result<(), Box> { info!("GLFW Event: {:?}", event); match event { //glfw::WindowEvent::FramebufferSize(x, y) => {}, - //glfw::WindowEvent::Close => window.set_should_close(true), // minimize? doesnt appear to be glfw::WindowEvent::Iconify(e) => - //if let Some(cmd) = graphics_commander.as_mut() { - if e { - cmd.suspend_rendering(); - } else { - cmd.resume_rendering(); + 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 => { - //if let Some(cmd) = graphics_commander.as_mut() { - // cmd.exit(); - //} - //let _gc = graphics_commander.take(); - //match graphics_mode { - // GMode::Vulkan => graphics_mode = GMode::OpenGL, - // GMode::OpenGL => graphics_mode = GMode::Vulkan, - // #[cfg(target_os = "macos")] - // GMode::Metal => todo!(), - // #[cfg(target_os = "windows")] - // GMode::DirectX => todo!(), - //} - //graphics_commander = Some(make_graphics_commander(graphics_mode, &mut window)?); - //}, + 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!(), + } + }, _ => (), }, - event => { - dbg!(event); - }, + _ => (), } } @@ -133,17 +255,17 @@ pub fn main() -> Result<(), Box> { break; } - //if let Some(cmd) = graphics_commander.as_mut() { - trace!("Rendering."); - //cmd.render_simple_blocks(game_world.chunks.get_mut(&(0, 0, 0)).unwrap().simple_blocks); - cmd.render(&mut window, &mut game_world)?; - //} + 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(cmd) = graphics_commander.as_mut() { - info!("Closing GC instance."); - cmd.exit(); - //} + if let Some(graphics_commander) = graphics_commander.as_mut() { + info!("Closing GC instance."); + graphics_commander.exit(); + } info!("Exiting."); Ok(()) @@ -152,7 +274,7 @@ pub fn main() -> Result<(), Box> { fn make_graphics_commander(graphics_mode:GMode, window:&mut glfw::PWindow) -> Result> { 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::Renderer::create(window)?), + GMode::OpenGL => Box::new(crate::graphics_engines::opengl::CataclysmOGL::create(window)?), #[cfg(target_os = "macos")] GMode::Metal => todo!(), #[cfg(target_os = "windows")]