dev sync
This commit is contained in:
parent
51556284bb
commit
d4b2d14fb7
22 changed files with 1775 additions and 1004 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -910,7 +910,9 @@ dependencies = [
|
|||
"jobserver",
|
||||
"libc",
|
||||
"log",
|
||||
"metal 0.31.0",
|
||||
"num_cpus",
|
||||
"objc",
|
||||
"png",
|
||||
"rand",
|
||||
"shaderc",
|
||||
|
|
|
@ -12,9 +12,14 @@ title_name = "MineMod"
|
|||
opt-level = 2
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
# panic = "abort" # Vita platform doesnt support abort currently
|
||||
strip = "debuginfo"
|
||||
|
||||
# this should be working but isnt
|
||||
# [target.'cfg(target_os = "vita")']
|
||||
# [target.'armv7-sony-vita-newlibeabihf']
|
||||
# rustflags = ["-C","panic=unwind"]
|
||||
|
||||
[profile.release.package."*"]
|
||||
opt-level = 3
|
||||
codegen-units = 1
|
||||
|
@ -90,3 +95,5 @@ tokio = { version = "1.36.0", features = [
|
|||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
metal = "0.31.0"
|
||||
objc = "0.2.7"
|
||||
|
|
BIN
assets/common/archlogo.512.png
Normal file
BIN
assets/common/archlogo.512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -1,4 +1,3 @@
|
|||
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
@ -7,4 +6,4 @@ layout(location = 0) out vec4 outColor;
|
|||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
||||
}
|
|
@ -1,20 +1,17 @@
|
|||
#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;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
}
|
||||
|
|
0
src/audio_engines/mod.rs
Normal file
0
src/audio_engines/mod.rs
Normal file
16
src/cataclysm/mod.rs
Normal file
16
src/cataclysm/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
pub(crate) trait GraphicsCommander {}
|
||||
|
||||
pub struct Cataclysm {
|
||||
game_objects:Vec<GameObject>,
|
||||
}
|
||||
|
||||
pub struct GameObject {
|
||||
pos:(f64, f64, f64),
|
||||
}
|
||||
|
||||
pub enum ModelKind {
|
||||
Cube,
|
||||
Complex(),
|
||||
}
|
||||
|
||||
pub struct ResourceReference {}
|
1
src/graphics_engines/directx/mod.rs
Normal file
1
src/graphics_engines/directx/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
src/graphics_engines/gxm/mod.rs
Normal file
1
src/graphics_engines/gxm/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
src/graphics_engines/metal/mod.rs
Normal file
1
src/graphics_engines/metal/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
13
src/graphics_engines/mod.rs
Normal file
13
src/graphics_engines/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
#[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 opengl;
|
||||
|
||||
#[cfg(target_family = "wasm")] pub mod webgpu;
|
||||
|
||||
#[cfg(target_os = "macos")] pub mod metal;
|
||||
|
||||
#[cfg(target_os = "windows")] pub mod directx;
|
||||
|
||||
#[cfg(target_os = "vita")] pub mod gxm;
|
1
src/graphics_engines/opengl/mod.rs
Normal file
1
src/graphics_engines/opengl/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
1422
src/graphics_engines/vulkan/mod.rs
Normal file
1422
src/graphics_engines/vulkan/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
1
src/graphics_engines/webgpu/mod.rs
Normal file
1
src/graphics_engines/webgpu/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
848
src/linux.rs
848
src/linux.rs
|
@ -1,87 +1,21 @@
|
|||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
|
||||
use cgmath::vec2;
|
||||
use cgmath::vec3;
|
||||
|
||||
use log::*;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::size_of;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use vulkanalia::Version;
|
||||
use vulkanalia::bytecode::Bytecode;
|
||||
use vulkanalia::loader::LIBRARY;
|
||||
use vulkanalia::loader::LibloadingLoader;
|
||||
use vulkanalia::prelude::v1_0::*;
|
||||
use vulkanalia::window as vk_window;
|
||||
|
||||
use vulkanalia::vk::ExtDebugUtilsExtension;
|
||||
use vulkanalia::vk::KhrSurfaceExtension;
|
||||
use vulkanalia::vk::KhrSwapchainExtension;
|
||||
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::Event;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
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>;
|
||||
|
||||
const VALIDATION_ENABLED:bool = cfg!(debug_assertions); // add env support?
|
||||
const VALIDATION_LAYER:vk::ExtensionName = vk::ExtensionName::from_bytes(b"VK_LAYER_KHRONOS_validation");
|
||||
|
||||
const DEVICE_EXTENSIONS:&[vk::ExtensionName] = &[vk::KHR_SWAPCHAIN_EXTENSION.name];
|
||||
const PORTABILITY_MACOS_VERSION:Version = Version::new(1, 3, 216);
|
||||
use crate::graphics_engines::vulkan::App;
|
||||
|
||||
const WINDOW_TITLE:&'static str = "MineMod";
|
||||
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;
|
||||
|
||||
macro_rules! const_shaders {
|
||||
{ $($vis:vis $identifier:ident = $string:literal; )* } => {
|
||||
$(
|
||||
#[allow(non_upper_case_globals)]
|
||||
$vis static $identifier: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders/", $string));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
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();
|
||||
super::init_logging();
|
||||
|
||||
info!("Registering CTRLC hook.");
|
||||
|
||||
|
@ -160,781 +94,3 @@ pub fn main() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Our Vulkan app.
|
||||
#[derive(Clone, Debug)]
|
||||
struct App {
|
||||
entry: Entry,
|
||||
instance:Instance,
|
||||
data: AppData,
|
||||
device: Device,
|
||||
frame: usize, // current frame
|
||||
resized: bool,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Creates our Vulkan app.
|
||||
unsafe fn create(window:&Window) -> Result<Self> {
|
||||
let loader = LibloadingLoader::new(LIBRARY)?;
|
||||
let entry = Entry::new(loader).map_err(|b| anyhow!("{}", b))?;
|
||||
let mut data = AppData::default();
|
||||
let instance = create_instance(window, &entry, &mut data)?;
|
||||
data.surface = vk_window::create_surface(&instance, &window, &window)?;
|
||||
pick_physical_device(&instance, &mut data)?;
|
||||
let device = create_logical_device(&entry, &instance, &mut data)?;
|
||||
create_swapchain(window, &instance, &device, &mut data)?;
|
||||
create_swapchain_image_views(&device, &mut data)?;
|
||||
create_render_pass(&instance, &device, &mut data)?;
|
||||
create_pipeline(&device, &mut data)?;
|
||||
create_framebuffers(&device, &mut data)?;
|
||||
create_command_pool(&instance, &device, &mut data)?;
|
||||
create_command_buffers(&device, &mut data)?;
|
||||
create_sync_objects(&device, &mut data)?;
|
||||
Ok(Self {
|
||||
entry,
|
||||
instance,
|
||||
data,
|
||||
device,
|
||||
frame:0,
|
||||
resized:false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Renders a frame for our Vulkan app.
|
||||
unsafe fn render(&mut self, window:&Window) -> Result<()> {
|
||||
self.device.wait_for_fences(&[self.data.in_flight_fences[self.frame]], true, u64::MAX)?;
|
||||
|
||||
let result = self.device.acquire_next_image_khr(
|
||||
self.data.swapchain,
|
||||
u64::MAX,
|
||||
self.data.image_available_semaphores[self.frame],
|
||||
vk::Fence::null(),
|
||||
);
|
||||
|
||||
let image_index = match result {
|
||||
Ok((image_index, _)) => image_index as usize,
|
||||
Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return self.recreate_swapchain(window),
|
||||
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)?;
|
||||
}
|
||||
|
||||
self.data.images_in_flight[image_index as usize] = self.data.in_flight_fences[self.frame];
|
||||
|
||||
let wait_semaphores = &[self.data.image_available_semaphores[self.frame]];
|
||||
let wait_stages = &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
|
||||
let command_buffers = &[self.data.command_buffers[image_index as usize]];
|
||||
let signal_semaphores = &[self.data.render_finished_semaphores[self.frame]];
|
||||
let submit_info = vk::SubmitInfo::builder()
|
||||
.wait_semaphores(wait_semaphores)
|
||||
.wait_dst_stage_mask(wait_stages)
|
||||
.command_buffers(command_buffers)
|
||||
.signal_semaphores(signal_semaphores);
|
||||
|
||||
self.device.reset_fences(&[self.data.in_flight_fences[self.frame]])?;
|
||||
|
||||
self.device
|
||||
.queue_submit(self.data.graphics_queue, &[submit_info], self.data.in_flight_fences[self.frame])?;
|
||||
|
||||
let swapchains = &[self.data.swapchain];
|
||||
let image_indices = &[image_index as u32];
|
||||
let present_info = vk::PresentInfoKHR::builder()
|
||||
.wait_semaphores(signal_semaphores)
|
||||
.swapchains(swapchains)
|
||||
.image_indices(image_indices);
|
||||
|
||||
let result = self.device.queue_present_khr(self.data.present_queue, &present_info);
|
||||
|
||||
let changed = result == Ok(vk::SuccessCode::SUBOPTIMAL_KHR) || result == Err(vk::ErrorCode::OUT_OF_DATE_KHR);
|
||||
|
||||
if self.resized || changed {
|
||||
self.resized = false;
|
||||
self.recreate_swapchain(window)?;
|
||||
} else if let Err(e) = result {
|
||||
return Err(anyhow!(e));
|
||||
}
|
||||
|
||||
// self.device.queue_wait_idle(self.data.present_queue)?;
|
||||
|
||||
self.frame = (self.frame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn recreate_swapchain(&mut self, window:&Window) -> Result<()> {
|
||||
self.device.device_wait_idle()?;
|
||||
self.destroy_swapchain();
|
||||
create_swapchain(window, &self.instance, &self.device, &mut self.data)?;
|
||||
create_swapchain_image_views(&self.device, &mut self.data)?;
|
||||
create_render_pass(&self.instance, &self.device, &mut self.data)?;
|
||||
create_pipeline(&self.device, &mut self.data)?;
|
||||
create_framebuffers(&self.device, &mut self.data)?;
|
||||
create_command_buffers(&self.device, &mut self.data)?;
|
||||
self.data.images_in_flight.resize(self.data.swapchain_images.len(), vk::Fence::null());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn destroy_swapchain(&mut self) {
|
||||
self.data.framebuffers.iter().for_each(|f| self.device.destroy_framebuffer(*f, None));
|
||||
self.device.free_command_buffers(self.data.command_pool, &self.data.command_buffers);
|
||||
self.device.destroy_pipeline(self.data.pipeline, None);
|
||||
self.device.destroy_pipeline_layout(self.data.pipeline_layout, None);
|
||||
self.device.destroy_render_pass(self.data.render_pass, None);
|
||||
self.data.swapchain_image_views.iter().for_each(|v| self.device.destroy_image_view(*v, None));
|
||||
self.device.destroy_swapchain_khr(self.data.swapchain, None);
|
||||
}
|
||||
|
||||
/// Destroys our Vulkan app.
|
||||
unsafe fn destroy(&mut self) {
|
||||
self.destroy_swapchain();
|
||||
|
||||
self.data.in_flight_fences.iter().for_each(|f| self.device.destroy_fence(*f, None));
|
||||
self.data
|
||||
.render_finished_semaphores
|
||||
.iter()
|
||||
.for_each(|s| self.device.destroy_semaphore(*s, None));
|
||||
self.data
|
||||
.image_available_semaphores
|
||||
.iter()
|
||||
.for_each(|s| self.device.destroy_semaphore(*s, None));
|
||||
|
||||
self.device.destroy_command_pool(self.data.command_pool, None);
|
||||
self.device.destroy_device(None);
|
||||
self.instance.destroy_surface_khr(self.data.surface, None);
|
||||
|
||||
if VALIDATION_ENABLED {
|
||||
self.instance.destroy_debug_utils_messenger_ext(self.data.messenger, None);
|
||||
}
|
||||
|
||||
self.instance.destroy_instance(None);
|
||||
}
|
||||
}
|
||||
|
||||
/// The Vulkan handles and associated properties used by our Vulkan app.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct AppData {
|
||||
messenger: vk::DebugUtilsMessengerEXT,
|
||||
surface: vk::SurfaceKHR,
|
||||
physical_device: vk::PhysicalDevice,
|
||||
graphics_queue: vk::Queue,
|
||||
present_queue: vk::Queue,
|
||||
swapchain_format: vk::Format,
|
||||
swapchain_extent: vk::Extent2D,
|
||||
swapchain: vk::SwapchainKHR,
|
||||
swapchain_images: Vec<vk::Image>,
|
||||
swapchain_image_views: Vec<vk::ImageView>,
|
||||
render_pass: vk::RenderPass,
|
||||
pipeline_layout: vk::PipelineLayout,
|
||||
pipeline: vk::Pipeline,
|
||||
framebuffers: Vec<vk::Framebuffer>,
|
||||
command_pool: vk::CommandPool,
|
||||
command_buffers: Vec<vk::CommandBuffer>,
|
||||
image_available_semaphores:Vec<vk::Semaphore>,
|
||||
render_finished_semaphores:Vec<vk::Semaphore>,
|
||||
in_flight_fences: Vec<vk::Fence>,
|
||||
images_in_flight: Vec<vk::Fence>,
|
||||
}
|
||||
|
||||
unsafe fn create_instance(window:&Window, entry:&Entry, data:&mut AppData) -> Result<Instance> {
|
||||
let application_info = vk::ApplicationInfo::builder()
|
||||
.application_name(VK_APPLICATION_NAME)
|
||||
.application_version(vk::make_version(0, 1, 0))
|
||||
.engine_name(VK_ENGINE_NAME)
|
||||
.engine_version(vk::make_version(0, 1, 0))
|
||||
.api_version(vk::make_version(1, 0, 0));
|
||||
|
||||
let available_layers = entry
|
||||
.enumerate_instance_layer_properties()?
|
||||
.iter()
|
||||
.map(|l| l.layer_name)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
if VALIDATION_ENABLED && !available_layers.contains(&VALIDATION_LAYER) {
|
||||
return Err(anyhow!("Validation layer requested but not supported."));
|
||||
}
|
||||
|
||||
let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { Vec::new() };
|
||||
|
||||
let mut extensions = vk_window::get_required_instance_extensions(window)
|
||||
.iter()
|
||||
.map(|e| e.as_ptr())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// needed for semaphores, unix only?
|
||||
extensions.push(vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name.as_ptr());
|
||||
|
||||
// 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.");
|
||||
// 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());
|
||||
vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR
|
||||
} else {
|
||||
vk::InstanceCreateFlags::empty()
|
||||
};
|
||||
if VALIDATION_ENABLED {
|
||||
extensions.push(vk::EXT_DEBUG_UTILS_EXTENSION.name.as_ptr());
|
||||
}
|
||||
|
||||
let mut info = vk::InstanceCreateInfo::builder()
|
||||
.application_info(&application_info)
|
||||
.enabled_layer_names(&layers)
|
||||
.enabled_extension_names(&extensions)
|
||||
.flags(flags);
|
||||
|
||||
let mut debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder()
|
||||
.message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all())
|
||||
.message_type(
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
|
||||
)
|
||||
.user_callback(Some(debug_callback));
|
||||
|
||||
if VALIDATION_ENABLED {
|
||||
info = info.push_next(&mut debug_info);
|
||||
}
|
||||
|
||||
let instance = entry.create_instance(&info, None)?;
|
||||
|
||||
if VALIDATION_ENABLED {
|
||||
data.messenger = instance.create_debug_utils_messenger_ext(&debug_info, None)?;
|
||||
}
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
extern "system" fn debug_callback(
|
||||
severity:vk::DebugUtilsMessageSeverityFlagsEXT, type_:vk::DebugUtilsMessageTypeFlagsEXT, data:*const vk::DebugUtilsMessengerCallbackDataEXT, _:*mut c_void,
|
||||
) -> vk::Bool32 {
|
||||
let data = unsafe { *data };
|
||||
let message = unsafe { CStr::from_ptr(data.message) }.to_string_lossy();
|
||||
|
||||
if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::ERROR {
|
||||
error!("({:?}) {}", type_, message);
|
||||
} else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING {
|
||||
warn!("({:?}) {}", type_, message);
|
||||
} else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::INFO {
|
||||
debug!("({:?}) {}", type_, message);
|
||||
} else {
|
||||
trace!("({:?}) {}", type_, message);
|
||||
}
|
||||
|
||||
vk::FALSE
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("{0}")]
|
||||
pub struct SuitabilityError(pub &'static str);
|
||||
|
||||
unsafe fn pick_physical_device(instance:&Instance, data:&mut AppData) -> Result<()> {
|
||||
for physical_device in instance.enumerate_physical_devices()? {
|
||||
let properties = instance.get_physical_device_properties(physical_device);
|
||||
|
||||
if let Err(error) = check_physical_device(instance, data, physical_device) {
|
||||
warn!("Skipping physical device (`{}`): {}", properties.device_name, error);
|
||||
} else {
|
||||
info!("Selected physical device (`{}`).", properties.device_name);
|
||||
data.physical_device = physical_device;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("Failed to find suitable physical device."))
|
||||
}
|
||||
unsafe fn check_physical_device(instance:&Instance, data:&AppData, physical_device:vk::PhysicalDevice) -> Result<()> {
|
||||
QueueFamilyIndices::get(instance, data, physical_device)?;
|
||||
check_physical_device_extensions(instance, physical_device)?;
|
||||
|
||||
let support = SwapchainSupport::get(instance, data, physical_device)?;
|
||||
if support.formats.is_empty() || support.present_modes.is_empty() {
|
||||
return Err(anyhow!(SuitabilityError("Insufficient swapchain support.")));
|
||||
}
|
||||
|
||||
// // TODO handle this like the other one?
|
||||
// let properties = instance.get_physical_device_properties(physical_device);
|
||||
// if properties.device_type != vk::PhysicalDeviceType::DISCRETE_GPU {
|
||||
// return Err(anyhow!(SuitabilityError(
|
||||
// "Only discrete GPUs are supported."
|
||||
// )));
|
||||
// }
|
||||
|
||||
// let features = instance.get_physical_device_features(physical_device);
|
||||
// let required_features = [
|
||||
// (features.geometry_shader, "Missing geometry shader support."),
|
||||
// // ( // needed for vr
|
||||
// // features.multi_viewport,
|
||||
// // "Missing support for multiple viewports.",
|
||||
// // ),
|
||||
// ];
|
||||
|
||||
// for (feature, string) in required_features {
|
||||
// if feature != vk::TRUE {
|
||||
// return Err(anyhow!(SuitabilityError(string)));
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn check_physical_device_extensions(instance:&Instance, physical_device:vk::PhysicalDevice) -> Result<()> {
|
||||
let extensions = instance
|
||||
.enumerate_device_extension_properties(physical_device, None)?
|
||||
.iter()
|
||||
.map(|e| e.extension_name)
|
||||
.collect::<HashSet<_>>();
|
||||
if DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(SuitabilityError("Missing required device extensions.")))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_logical_device(entry:&Entry, instance:&Instance, data:&mut AppData) -> Result<Device> {
|
||||
let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?;
|
||||
|
||||
let mut unique_indices = HashSet::new();
|
||||
unique_indices.insert(indices.graphics);
|
||||
unique_indices.insert(indices.present);
|
||||
|
||||
let queue_priorities = &[1.0];
|
||||
let queue_infos = unique_indices
|
||||
.iter()
|
||||
.map(|i| vk::DeviceQueueCreateInfo::builder().queue_family_index(*i).queue_priorities(queue_priorities))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { vec![] };
|
||||
|
||||
let mut extensions = DEVICE_EXTENSIONS.iter().map(|n| n.as_ptr()).collect::<Vec<_>>();
|
||||
|
||||
// Required by Vulkan SDK on macOS since 1.3.216.
|
||||
if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION {
|
||||
extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr());
|
||||
}
|
||||
|
||||
let features = vk::PhysicalDeviceFeatures::builder();
|
||||
|
||||
let info = vk::DeviceCreateInfo::builder()
|
||||
.queue_create_infos(&queue_infos)
|
||||
.enabled_layer_names(&layers)
|
||||
.enabled_extension_names(&extensions)
|
||||
.enabled_features(&features);
|
||||
|
||||
let device = instance.create_device(data.physical_device, &info, None)?;
|
||||
|
||||
data.graphics_queue = device.get_device_queue(indices.graphics, 0);
|
||||
data.present_queue = device.get_device_queue(indices.present, 0);
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
unsafe fn create_swapchain(
|
||||
window:&Window,
|
||||
instance:&Instance,
|
||||
device:&Device,
|
||||
data:&mut AppData,
|
||||
// old_swapchain: Option<vk::SwapchainKHR>
|
||||
) -> Result<()> {
|
||||
let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?;
|
||||
let support = SwapchainSupport::get(instance, data, data.physical_device)?;
|
||||
|
||||
let surface_format = get_swapchain_surface_format(&support.formats);
|
||||
let present_mode = get_swapchain_present_mode(&support.present_modes);
|
||||
let extent = get_swapchain_extent(window, support.capabilities);
|
||||
|
||||
data.swapchain_format = surface_format.format;
|
||||
data.swapchain_extent = extent;
|
||||
|
||||
let mut image_count = support.capabilities.min_image_count + 1;
|
||||
if support.capabilities.max_image_count != 0 && image_count > support.capabilities.max_image_count {
|
||||
image_count = support.capabilities.max_image_count;
|
||||
}
|
||||
|
||||
let mut queue_family_indices = vec![];
|
||||
let image_sharing_mode = if indices.graphics != indices.present {
|
||||
queue_family_indices.push(indices.graphics);
|
||||
queue_family_indices.push(indices.present);
|
||||
vk::SharingMode::CONCURRENT
|
||||
} else {
|
||||
vk::SharingMode::EXCLUSIVE
|
||||
};
|
||||
|
||||
let info = vk::SwapchainCreateInfoKHR::builder()
|
||||
.surface(data.surface)
|
||||
.min_image_count(image_count)
|
||||
.image_format(surface_format.format)
|
||||
.image_color_space(surface_format.color_space)
|
||||
.image_extent(extent)
|
||||
.image_array_layers(1)
|
||||
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
|
||||
.image_sharing_mode(image_sharing_mode)
|
||||
.queue_family_indices(&queue_family_indices)
|
||||
.pre_transform(support.capabilities.current_transform)
|
||||
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
|
||||
.present_mode(present_mode)
|
||||
.clipped(true)
|
||||
.old_swapchain(vk::SwapchainKHR::null());
|
||||
// .old_swapchain(data.swapchain); // TODO if experiencing issues replace with vk::SwapchainKHR::null()
|
||||
|
||||
data.swapchain = device.create_swapchain_khr(&info, None)?;
|
||||
|
||||
data.swapchain_images = device.get_swapchain_images_khr(data.swapchain)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_swapchain_surface_format(formats:&[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
|
||||
formats
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|f| f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR)
|
||||
.unwrap_or_else(|| formats[0])
|
||||
}
|
||||
|
||||
fn get_swapchain_present_mode(present_modes:&[vk::PresentModeKHR]) -> vk::PresentModeKHR {
|
||||
present_modes
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|m| *m == vk::PresentModeKHR::MAILBOX)
|
||||
.unwrap_or(vk::PresentModeKHR::FIFO)
|
||||
}
|
||||
|
||||
fn get_swapchain_extent(window:&Window, capabilities:vk::SurfaceCapabilitiesKHR) -> vk::Extent2D {
|
||||
if capabilities.current_extent.width != u32::MAX {
|
||||
capabilities.current_extent
|
||||
} else {
|
||||
vk::Extent2D::builder()
|
||||
.width(
|
||||
window
|
||||
.inner_size()
|
||||
.width
|
||||
.clamp(capabilities.min_image_extent.width, capabilities.max_image_extent.width),
|
||||
)
|
||||
.height(
|
||||
window
|
||||
.inner_size()
|
||||
.height
|
||||
.clamp(capabilities.min_image_extent.height, capabilities.max_image_extent.height),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_swapchain_image_views(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.swapchain_image_views = data
|
||||
.swapchain_images
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let components = vk::ComponentMapping::builder()
|
||||
.r(vk::ComponentSwizzle::IDENTITY)
|
||||
.g(vk::ComponentSwizzle::IDENTITY)
|
||||
.b(vk::ComponentSwizzle::IDENTITY)
|
||||
.a(vk::ComponentSwizzle::IDENTITY);
|
||||
|
||||
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(*i)
|
||||
.view_type(vk::ImageViewType::_2D)
|
||||
.format(data.swapchain_format)
|
||||
.components(components)
|
||||
.subresource_range(subresource_range);
|
||||
|
||||
device.create_image_view(&info, None)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_render_pass(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let color_attachment = vk::AttachmentDescription::builder()
|
||||
.format(data.swapchain_format)
|
||||
.samples(vk::SampleCountFlags::_1)
|
||||
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||
.store_op(vk::AttachmentStoreOp::STORE)
|
||||
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
|
||||
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED)
|
||||
.final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
|
||||
|
||||
let color_attachment_ref = vk::AttachmentReference::builder()
|
||||
.attachment(0)
|
||||
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
let color_attachments = &[color_attachment_ref];
|
||||
let subpass = vk::SubpassDescription::builder()
|
||||
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
|
||||
.color_attachments(color_attachments);
|
||||
|
||||
let dependency = vk::SubpassDependency::builder()
|
||||
.src_subpass(vk::SUBPASS_EXTERNAL)
|
||||
.dst_subpass(0)
|
||||
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
|
||||
.src_access_mask(vk::AccessFlags::empty())
|
||||
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
|
||||
.dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
|
||||
|
||||
let attachments = &[color_attachment];
|
||||
let subpasses = &[subpass];
|
||||
let dependencies = &[dependency];
|
||||
let info = vk::RenderPassCreateInfo::builder()
|
||||
.attachments(attachments)
|
||||
.subpasses(subpasses)
|
||||
.dependencies(dependencies);
|
||||
|
||||
data.render_pass = device.create_render_pass(&info, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
const_shaders! {
|
||||
frag = "f_default.spv";
|
||||
vert = "v_default.spv";
|
||||
}
|
||||
|
||||
let vert_shader_module = create_shader_module(device, &vert[..])?;
|
||||
let frag_shader_module = create_shader_module(device, &frag[..])?;
|
||||
|
||||
let vert_stage = vk::PipelineShaderStageCreateInfo::builder()
|
||||
.stage(vk::ShaderStageFlags::VERTEX)
|
||||
.module(vert_shader_module)
|
||||
.name(b"main\0"); // keep specialization_info in mind for pipeline creation optimizations that dont happen at render time
|
||||
|
||||
let frag_stage = vk::PipelineShaderStageCreateInfo::builder()
|
||||
.stage(vk::ShaderStageFlags::FRAGMENT)
|
||||
.module(frag_shader_module)
|
||||
.name(b"main\0");
|
||||
|
||||
let vertex_input_state = vk::PipelineVertexInputStateCreateInfo::builder();
|
||||
|
||||
let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::builder()
|
||||
.topology(vk::PrimitiveTopology::TRIANGLE_LIST)
|
||||
.primitive_restart_enable(false);
|
||||
|
||||
let viewport = vk::Viewport::builder()
|
||||
.x(0.0)
|
||||
.y(0.0)
|
||||
.width(data.swapchain_extent.width as f32)
|
||||
.height(data.swapchain_extent.height as f32)
|
||||
.min_depth(0.0)
|
||||
.max_depth(1.0);
|
||||
|
||||
let scissor = vk::Rect2D::builder().offset(vk::Offset2D { x:0, y:0 }).extent(data.swapchain_extent);
|
||||
|
||||
let viewports = &[viewport];
|
||||
let scissors = &[scissor];
|
||||
let viewport_state = vk::PipelineViewportStateCreateInfo::builder().viewports(viewports).scissors(scissors);
|
||||
|
||||
let rasterization_state = vk::PipelineRasterizationStateCreateInfo::builder()
|
||||
.depth_clamp_enable(false)
|
||||
.rasterizer_discard_enable(false)
|
||||
.polygon_mode(vk::PolygonMode::FILL)
|
||||
.line_width(1.0)
|
||||
.cull_mode(vk::CullModeFlags::BACK)
|
||||
.front_face(vk::FrontFace::CLOCKWISE)
|
||||
.depth_bias_enable(false);
|
||||
|
||||
let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder()
|
||||
.sample_shading_enable(false)
|
||||
.rasterization_samples(vk::SampleCountFlags::_1);
|
||||
|
||||
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);
|
||||
|
||||
let attachments = &[attachment];
|
||||
let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder()
|
||||
.logic_op_enable(false)
|
||||
.logic_op(vk::LogicOp::COPY)
|
||||
.attachments(attachments)
|
||||
.blend_constants([0.0, 0.0, 0.0, 0.0]);
|
||||
|
||||
let layout_info = vk::PipelineLayoutCreateInfo::builder();
|
||||
|
||||
data.pipeline_layout = device.create_pipeline_layout(&layout_info, None)?;
|
||||
|
||||
let stages = &[vert_stage, frag_stage];
|
||||
let info = vk::GraphicsPipelineCreateInfo::builder()
|
||||
.stages(stages)
|
||||
.vertex_input_state(&vertex_input_state)
|
||||
.input_assembly_state(&input_assembly_state)
|
||||
.viewport_state(&viewport_state)
|
||||
.rasterization_state(&rasterization_state)
|
||||
.multisample_state(&multisample_state)
|
||||
.color_blend_state(&color_blend_state)
|
||||
.layout(data.pipeline_layout)
|
||||
.render_pass(data.render_pass)
|
||||
.subpass(0)
|
||||
.base_pipeline_handle(vk::Pipeline::null()) // Optional.
|
||||
.base_pipeline_index(-1); // Optional.
|
||||
|
||||
data.pipeline = device.create_graphics_pipelines(vk::PipelineCache::null(), &[info], None)?.0[0];
|
||||
|
||||
device.destroy_shader_module(vert_shader_module, None);
|
||||
device.destroy_shader_module(frag_shader_module, None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_shader_module(device:&Device, bytecode:&[u8]) -> Result<vk::ShaderModule> {
|
||||
let bytecode = Bytecode::new(bytecode).unwrap();
|
||||
|
||||
let info = vk::ShaderModuleCreateInfo::builder().code_size(bytecode.code_size()).code(bytecode.code());
|
||||
|
||||
Ok(device.create_shader_module(&info, None)?)
|
||||
}
|
||||
|
||||
unsafe fn create_framebuffers(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
data.framebuffers = data
|
||||
.swapchain_image_views
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let attachments = &[*i];
|
||||
let create_info = vk::FramebufferCreateInfo::builder()
|
||||
.render_pass(data.render_pass)
|
||||
.attachments(attachments)
|
||||
.width(data.swapchain_extent.width)
|
||||
.height(data.swapchain_extent.height)
|
||||
.layers(1);
|
||||
|
||||
device.create_framebuffer(&create_info, None)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn create_command_pool(instance:&Instance, device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let indices = QueueFamilyIndices::get(instance, data, data.physical_device)?;
|
||||
|
||||
let info = vk::CommandPoolCreateInfo::builder()
|
||||
.flags(vk::CommandPoolCreateFlags::empty()) // Optional.
|
||||
.queue_family_index(indices.graphics);
|
||||
|
||||
data.command_pool = device.create_command_pool(&info, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_command_buffers(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let allocate_info = vk::CommandBufferAllocateInfo::builder()
|
||||
.command_pool(data.command_pool)
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_buffer_count(data.framebuffers.len() as u32);
|
||||
|
||||
data.command_buffers = device.allocate_command_buffers(&allocate_info)?;
|
||||
|
||||
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.
|
||||
|
||||
device.begin_command_buffer(*command_buffer, &info)?;
|
||||
|
||||
let render_area = vk::Rect2D::builder().offset(vk::Offset2D::default()).extent(data.swapchain_extent);
|
||||
|
||||
let color_clear_value = vk::ClearValue {
|
||||
color:vk::ClearColorValue { float32:[0.0, 0.0, 0.0, 1.0] },
|
||||
};
|
||||
|
||||
let clear_values = &[color_clear_value];
|
||||
let info = vk::RenderPassBeginInfo::builder()
|
||||
.render_pass(data.render_pass)
|
||||
.framebuffer(data.framebuffers[i])
|
||||
.render_area(render_area)
|
||||
.clear_values(clear_values);
|
||||
|
||||
device.cmd_begin_render_pass(*command_buffer, &info, vk::SubpassContents::INLINE);
|
||||
device.cmd_bind_pipeline(*command_buffer, vk::PipelineBindPoint::GRAPHICS, data.pipeline);
|
||||
device.cmd_draw(*command_buffer, 3, 1, 0, 0);
|
||||
|
||||
device.cmd_end_render_pass(*command_buffer);
|
||||
device.end_command_buffer(*command_buffer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_sync_objects(device:&Device, data:&mut AppData) -> Result<()> {
|
||||
let semaphore_info = vk::SemaphoreCreateInfo::builder();
|
||||
let fence_info = vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
|
||||
|
||||
for _ in 0..MAX_FRAMES_IN_FLIGHT {
|
||||
data.image_available_semaphores.push(device.create_semaphore(&semaphore_info, None)?);
|
||||
data.render_finished_semaphores.push(device.create_semaphore(&semaphore_info, None)?);
|
||||
|
||||
data.in_flight_fences.push(device.create_fence(&fence_info, None)?);
|
||||
}
|
||||
|
||||
data.images_in_flight = data.swapchain_images.iter().map(|_| vk::Fence::null()).collect();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct QueueFamilyIndices {
|
||||
graphics:u32,
|
||||
present: u32,
|
||||
}
|
||||
|
||||
impl QueueFamilyIndices {
|
||||
unsafe fn get(instance:&Instance, data:&AppData, physical_device:vk::PhysicalDevice) -> Result<Self> {
|
||||
let properties = instance.get_physical_device_queue_family_properties(physical_device);
|
||||
|
||||
let graphics = properties
|
||||
.iter()
|
||||
.position(|p| p.queue_flags.contains(vk::QueueFlags::GRAPHICS))
|
||||
.map(|i| i as u32);
|
||||
|
||||
let mut present = None;
|
||||
for (index, properties) in properties.iter().enumerate() {
|
||||
if instance.get_physical_device_surface_support_khr(physical_device, index as u32, data.surface)? {
|
||||
present = Some(index as u32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that it's very likely that these end up being the same queue family after all, but throughout the program we will treat them as if they were separate queues for a uniform approach. Nevertheless, you could add logic to explicitly prefer a physical device that supports drawing and presentation in the same queue for improved performance.
|
||||
if let (Some(graphics), Some(present)) = (graphics, present) {
|
||||
Ok(Self { graphics, present })
|
||||
} else {
|
||||
Err(anyhow!(SuitabilityError("Missing required queue families.")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SwapchainSupport {
|
||||
capabilities: vk::SurfaceCapabilitiesKHR,
|
||||
formats: Vec<vk::SurfaceFormatKHR>,
|
||||
present_modes:Vec<vk::PresentModeKHR>,
|
||||
}
|
||||
|
||||
impl SwapchainSupport {
|
||||
unsafe fn get(instance:&Instance, data:&AppData, physical_device:vk::PhysicalDevice) -> Result<Self> {
|
||||
Ok(Self {
|
||||
capabilities: instance.get_physical_device_surface_capabilities_khr(physical_device, data.surface)?,
|
||||
formats: instance.get_physical_device_surface_formats_khr(physical_device, data.surface)?,
|
||||
present_modes:instance.get_physical_device_surface_present_modes_khr(physical_device, data.surface)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
96
src/mac.rs
Normal file
96
src/mac.rs
Normal 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(())
|
||||
}
|
0
src/macos/metal_bindgen/metal.rs
Normal file
0
src/macos/metal_bindgen/metal.rs
Normal file
53
src/main.rs
53
src/main.rs
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![allow(
|
||||
// dead_code,
|
||||
|
@ -7,8 +8,38 @@
|
|||
unsafe_op_in_unsafe_fn
|
||||
)]
|
||||
|
||||
// planned system targets
|
||||
mod cataclysm;
|
||||
mod graphics_engines;
|
||||
|
||||
// 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
|
||||
// without needing a ton of conditional compile targets in the engine code
|
||||
|
||||
// NOTE player save data will behave like so:
|
||||
// playerA
|
||||
// playerA/worldA
|
||||
// playerB
|
||||
// playerB/worldB
|
||||
// playerB/worldC
|
||||
// playerB/world...
|
||||
// playerC
|
||||
// playerC/worldD
|
||||
|
||||
// Make sure to date save data and potentially version it so changes may be rolled back to a set date
|
||||
|
||||
// quotes from the first link in thanks.txt
|
||||
//
|
||||
// It should be noted that in a real world application, you're not supposed to actually call allocate_memory for every individual buffer. The maximum number of simultaneous memory allocations is limited by the max_memory_allocation_count physical device limit, which may be as low as 4096 even on high end hardware like an NVIDIA GTX 1080. The right way to allocate memory for a large number of objects at the same time is to create a custom allocator that splits up a single allocation among many different objects by using the offset parameters that we've seen in many functions.
|
||||
// The previous chapter already mentioned that you should allocate multiple resources like buffers from a single memory allocation, but in fact you should go a step further. Driver developers recommend that you also store multiple buffers, like the vertex and index buffer, into a single vk::Buffer and use offsets in commands like cmd_bind_vertex_buffers. The advantage is that your data is more cache friendly in that case, because it's closer together. It is even possible to reuse the same chunk of memory for multiple resources if they are not used during the same render operations, provided that their data is refreshed, of course. This is known as aliasing and some Vulkan functions have explicit flags to specify that you want to do this.
|
||||
//
|
||||
// https://docs.vulkan.org/spec/latest/chapters/descriptorsets.html#interfaces-resources-layout
|
||||
//
|
||||
|
||||
// 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
|
||||
|
||||
// Planned system targets
|
||||
#[cfg(target_os = "linux")] mod linux;
|
||||
#[cfg(target_os = "linux")] pub use linux::*;
|
||||
#[cfg(target_os = "windows")] mod windows;
|
||||
|
@ -20,7 +51,7 @@
|
|||
#[cfg(target_os = "vita")] mod vita;
|
||||
#[cfg(target_os = "vita")] pub use vita::*;
|
||||
|
||||
// unconfirmed system targets
|
||||
// Potential system targets
|
||||
|
||||
// bindgen, cc, https://github.com/rust-console
|
||||
|
||||
|
@ -44,3 +75,21 @@
|
|||
// 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();
|
||||
}
|
||||
|
|
172
src/vita/mod.rs
172
src/vita/mod.rs
|
@ -17,166 +17,66 @@ pub const NOT_CDRAM_ALIGNMENT:u32 = (4 * 1024) - 1;
|
|||
#[inline]
|
||||
pub const fn align(data:u32, alignment:u32) -> u32 { (data + alignment) & !alignment }
|
||||
|
||||
// these should probably be macros?
|
||||
#[inline]
|
||||
pub fn abs(x:i8) -> i8 { if x > 0 { x } else { x * -1 } }
|
||||
// #[inline]
|
||||
// pub fn abs(x:i8) -> i8 { if x > 0 { x } else { x * -1 } }
|
||||
|
||||
// tracing based logger
|
||||
// fn rainbow_color(t:f32) -> (u32, u32, u32) {
|
||||
// let hue = t * 360.0; // Convert time to hue (0-360 degrees)
|
||||
// let saturation = 1.0; // Maximum saturation
|
||||
// let value = 1.0; // Maximum value (brightness)
|
||||
|
||||
fn rainbow_color(t:f32) -> (u32, u32, u32) {
|
||||
let hue = t * 360.0; // Convert time to hue (0-360 degrees)
|
||||
let saturation = 1.0; // Maximum saturation
|
||||
let value = 1.0; // Maximum value (brightness)
|
||||
// // Convert HSV to RGB
|
||||
// let c = value * saturation;
|
||||
// let x = c * (1.0 - (((hue / 60.0) % 2.0) - 1.0).abs());
|
||||
// let m = value - c;
|
||||
// let (r, g, b) = match hue {
|
||||
// 0.0..=60.0 => (c, x, 0.0),
|
||||
// 60.0..=120.0 => (x, c, 0.0),
|
||||
// 120.0..=180.0 => (0.0, c, x),
|
||||
// 180.0..=240.0 => (0.0, x, c),
|
||||
// 240.0..=300.0 => (x, 0.0, c),
|
||||
// _ => (c, 0.0, x),
|
||||
// };
|
||||
|
||||
// Convert HSV to RGB
|
||||
let c = value * saturation;
|
||||
let x = c * (1.0 - (((hue / 60.0) % 2.0) - 1.0).abs());
|
||||
let m = value - c;
|
||||
let (r, g, b) = match hue {
|
||||
0.0..=60.0 => (c, x, 0.0),
|
||||
60.0..=120.0 => (x, c, 0.0),
|
||||
120.0..=180.0 => (0.0, c, x),
|
||||
180.0..=240.0 => (0.0, x, c),
|
||||
240.0..=300.0 => (x, 0.0, c),
|
||||
_ => (c, 0.0, x),
|
||||
};
|
||||
// // Scale RGB values to 8-bit integers
|
||||
// let r = (255.0 * (r + m)) as u32;
|
||||
// let g = (255.0 * (g + m)) as u32;
|
||||
// let b = (255.0 * (b + m)) as u32;
|
||||
|
||||
// Scale RGB values to 8-bit integers
|
||||
let r = (255.0 * (r + m)) as u32;
|
||||
let g = (255.0 * (g + m)) as u32;
|
||||
let b = (255.0 * (b + m)) as u32;
|
||||
// (r, g, b)
|
||||
// }
|
||||
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
fn ilerp(min:f32, max:f32, input:f32) -> f32 { (input - min) / (max - min) }
|
||||
// fn ilerp(min:f32, max:f32, input:f32) -> f32 { (input - min) / (max - min) }
|
||||
|
||||
// #[tokio::main]
|
||||
pub fn main() {
|
||||
std::panic::set_hook(Box::new(custom_panic_hook));
|
||||
super::init_logging();
|
||||
|
||||
println!("Panic hook set");
|
||||
|
||||
// SCE_GXM_ERROR_INVALID_POINTER
|
||||
|
||||
// const thing1: u32 = 0x80024b05;
|
||||
// const thing2: u32 = 0x80024309;
|
||||
|
||||
// unsafe { sceClibPrintf("whar?".as_ptr() as *const i8); }
|
||||
|
||||
// text_screen();
|
||||
|
||||
// std::thread::sleep(Duration::from_secs(3));
|
||||
panic!("Vista system target is not supported currently.");
|
||||
|
||||
// for _ in 0..3 {
|
||||
// raw_rainbow();
|
||||
// }
|
||||
|
||||
// std::thread::sleep(Duration::from_secs(3));
|
||||
|
||||
// new_text_screen();
|
||||
|
||||
// std::thread::sleep(Duration::from_secs(3));
|
||||
|
||||
// unsafe {
|
||||
// cube::cube();
|
||||
// cube2::cube();
|
||||
// cube3::cube();
|
||||
// }
|
||||
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
|
||||
panic!("Test panic!");
|
||||
|
||||
// loop {
|
||||
// std::thread::sleep(Duration::from_secs(10));
|
||||
// }
|
||||
|
||||
unsafe { sceGxmTerminate() };
|
||||
}
|
||||
|
||||
// fn new_text_screen() {
|
||||
// let mut screen = gui::TextScreen::predef();
|
||||
// screen.enable();
|
||||
// fn raw_rainbow() {
|
||||
// let mut raw_buffer = gui::framebuffer::Framebuffer::new();
|
||||
// raw_buffer.set_display();
|
||||
|
||||
// // for i in 336..446 {
|
||||
// // screen.framebuffer.set(i, 0xFF_FF_FF_FF);
|
||||
// // }
|
||||
// for index_width in 0..raw_buffer.screen_width {
|
||||
// thread::sleep(Duration::from_micros(20));
|
||||
|
||||
// // screen.set_cursor(4, 0);
|
||||
// // write!(screen, "\n").ok();
|
||||
// // screen.update_screen_raw();
|
||||
// write!(screen, "Goodbye Void!\n").ok();
|
||||
// screen.update_screen_raw();
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// write!(screen, "Goodbye Void!\n").ok();
|
||||
// screen.update_screen_raw();
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// write!(screen, "Goodbye Void!\n").ok();
|
||||
// screen.update_screen_raw();
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// let (r, g, b) = rainbow_color(ilerp(0.0, raw_buffer.screen_width as f32, index_width as f32));
|
||||
|
||||
// screen.set_cursor(0, 3);
|
||||
// for _ in 0..25 {
|
||||
// write!(screen, "Goodbye Void!\n").ok();
|
||||
// // screen.update_screen_raw();
|
||||
// for index_height in 0..raw_buffer.screen_height {
|
||||
// raw_buffer.set(index_width + (index_height * raw_buffer.screen_width), 0 as u32 | b << 16 | g << 8 | r); // VITA USES ABGR
|
||||
// }
|
||||
// }
|
||||
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// screen.clear();
|
||||
// screen.update_screen_raw();
|
||||
|
||||
// // screen.set_foreground(255,255,0);
|
||||
|
||||
// // for x in 0..24 {
|
||||
// // for y in 0..24 {
|
||||
// // mono_screen.set_cursor(24 + x, 24 + y);
|
||||
// // write!((219 as char).to_string());
|
||||
// // thread::sleep(Duration::from_millis(16));
|
||||
// // }
|
||||
// // }
|
||||
// // mono_screen.set_cursor(0,1);
|
||||
// // mono_screen.set_foreground(0,0,0);
|
||||
|
||||
// // drop(screen);
|
||||
|
||||
// // let mut screen = gui::TextScreen::new();
|
||||
// // screen.set_display();
|
||||
// // loop {
|
||||
// // screen.clear();
|
||||
// // screen.update_screen_raw();
|
||||
// // std::thread::sleep(Duration::from_millis(100));
|
||||
// // }
|
||||
// }
|
||||
|
||||
// fn text_screen() {
|
||||
// let mut screen = debug::framebuffer::DebugScreen::new();
|
||||
// writeln!(screen, "crusty").ok();
|
||||
// // thread::sleep(Duration::from_secs(2));
|
||||
// writeln!(screen, "newline").ok();
|
||||
// // thread::sleep(Duration::from_secs(2));
|
||||
|
||||
// let random_numbers: Vec<u8> = (0..8).map(|_i| rand::random::<u8>()).collect();
|
||||
// writeln!(screen, "random numbers: {:?}", random_numbers).ok();
|
||||
|
||||
// // writeln!(screen, "I know where you sleep :3").ok();
|
||||
// }
|
||||
|
||||
fn raw_rainbow() {
|
||||
let mut raw_buffer = gui::framebuffer::Framebuffer::new();
|
||||
raw_buffer.set_display();
|
||||
|
||||
for index_width in 0..raw_buffer.screen_width {
|
||||
thread::sleep(Duration::from_micros(20));
|
||||
|
||||
let (r, g, b) = rainbow_color(ilerp(0.0, raw_buffer.screen_width as f32, index_width as f32));
|
||||
|
||||
for index_height in 0..raw_buffer.screen_height {
|
||||
raw_buffer.set(index_width + (index_height * raw_buffer.screen_width), 0 as u32 | b << 16 | g << 8 | r); // VITA USES ABGR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn custom_panic_hook(info:&PanicHookInfo<'_>) {
|
||||
// The current implementation always returns `Some`.
|
||||
let location = info.location().unwrap();
|
||||
|
|
6
src/wasm.rs
Normal file
6
src/wasm.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use log::*;
|
||||
|
||||
pub fn main() {
|
||||
super::init_logging();
|
||||
error!("Sorry, but wasm currently isnt setup in this engine!");
|
||||
}
|
|
@ -1 +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(())
|
||||
}
|
||||
|
|
12
thanks.txt
12
thanks.txt
|
@ -1,10 +1,18 @@
|
|||
# list of resources ive used in the making of this project
|
||||
# thank you to everyone here for allowing me to learn what was required to make this project
|
||||
# List of resources ive used in the making of this project
|
||||
# Thank you to everyone here for allowing me to learn what was required to make this project
|
||||
|
||||
# Certain items may not be immediately related to the project but are still listed because they helped in another way
|
||||
|
||||
# The entire vulkan 1.0 implementation is based off of this guide, currently just copied and pasted with slight modification
|
||||
https://kylemayes.github.io/vulkanalia/introduction.html
|
||||
|
||||
# javidx9 code it yourself console game engine tutorial
|
||||
# I like this series because it explains much of the math behind 3d rendering
|
||||
https://www.youtube.com/watch?v=ih20l3pJoeU
|
||||
https://www.youtube.com/watch?v=XgMWc6LumG4
|
||||
https://www.youtube.com/watch?v=HXSuNxpCzdM
|
||||
https://www.youtube.com/watch?v=nBzCS-Y0FcY
|
||||
|
||||
# Very interesting shader project i will be using to test my engines shader capabilities later on
|
||||
https://www.youtube.com/watch?v=HPqGaIMVuLs
|
||||
https://github.com/runevision/Dither3D
|
||||
|
|
Loading…
Add table
Reference in a new issue