project setup

This commit is contained in:
deepCurse 2025-01-20 06:19:34 -04:00
commit 51556284bb
Signed by: u1
GPG key ID: AD770D25A908AFF4
20 changed files with 4984 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
**/*.rs.bk
/debug/
/target/
/.vscode/
/.idea/

2665
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

92
Cargo.toml Normal file
View file

@ -0,0 +1,92 @@
[package]
name = "minemod"
version = "0.1.0"
edition = "2024"
[package.metadata.vita]
title_id = "PKDC10002"
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
lto = true
codegen-units = 1
panic = "abort"
strip = "debuginfo"
[profile.release.package."*"]
opt-level = 3
codegen-units = 1
strip = "debuginfo"
[profile.dev.package."*"]
opt-level = 3
[profile.wasm-release]
inherits = "release"
opt-level = "z"
strip = true
[workspace]
members = []
# [patch."ssh://user@domain.com/repository.git"]
# namespace = { path = "path/to/thing" }
[build-dependencies]
shaderc = "0.8.3"
num_cpus = "1.16.0"
jobserver = "0.1.32"
[dependencies]
tracing = { version = "0.1.41", features = [
"max_level_debug",
"release_max_level_warn",
] }
tracing-subscriber = "0.3.19"
log = "0.4.25"
anyhow = "1.0.95"
thiserror = "2.0.11"
cgmath = "0.18.0"
png = "0.17.16"
tobj = { version = "4.0.2", features = ["log"] }
[target.'cfg(any(target_os = "windows",target_os = "linux",target_os = "macos"))'.dependencies]
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"] }
[target.'cfg(target_family = "wasm")'.dependencies]
wgpu = "24.0.0"
[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"
tokio = { version = "1.36.0", features = [
"fs",
"macros",
"bytes",
"io-std",
"io-util",
"rt",
"rt-multi-thread",
"net",
"tracing",
"time",
] }
[target.'cfg(target_os = "windows")'.dependencies]
[target.'cfg(target_os = "linux")'.dependencies]
[target.'cfg(target_os = "macos")'.dependencies]

139
build.rs Normal file
View file

@ -0,0 +1,139 @@
// Requires crates: shaderc, num_cpus, jobserver
// 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";
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs");
compile_shaders()?;
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())?
});
let out_dir = std::env::var("OUT_DIR").map_err(|err| format!("Could not read environment variable OUT_DIR: `{err}`"))?;
let shader_out_dir = std::path::Path::new(&out_dir).join(SHADER_SRC_ROOT);
std::fs::create_dir_all(&shader_out_dir)?;
let mut running_tasks = vec![];
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}`");
};
for input_path in shader_source_files {
let aquired = client
.acquire()
.map_err(|err| format!("There was an error while aquiring a jobserver token: `{err}`"))?;
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>> {
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 shader_kind = match &input_file_stem[0..=1] {
"f_" => shaderc::ShaderKind::Fragment,
"v_" => shaderc::ShaderKind::Vertex,
_ => return Err(format!("Shader folder contains a file that is neither a vertex or fragment shader: {input_path:?}").into()),
};
let new_file_name = input_file_stem + COMPILED_SHADER_EXTENSION;
let output_path = shader_out_dir.join(&new_file_name);
if let Err(error) = compile_shader(&input_path, &output_path, shader_kind) {
println!("cargo:warning=Compilation process for input file: {input_path:?} was not successful: `{error}`");
println!("cargo:warning=I cant be assed to fix this myself but cargo cuts off newlines in warning messages, so if the above error gets cut off youre going to need to find the text file or re run cargo with the -vv flag");
}
drop(aquired);
Ok(())
})));
}
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}`");
},
Err(_) => {
println!("cargo:warning=A thread panicked while processing this file!: {input_file:?}",);
},
}
}
Ok(())
}
fn walk_dir(path:std::path::PathBuf, file_paths_buf:&mut Vec<std::path::PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
if std::path::PathBuf::from(&path).is_file() {
file_paths_buf.push(path);
return Ok(());
} else {
let dir = std::fs::read_dir(path)?;
for f in dir {
walk_dir(f?.path(), file_paths_buf)?;
}
return Ok(());
}
}
fn compile_shader(input_path:&std::path::PathBuf, output_path:&std::path::PathBuf, shader_kind:shaderc::ShaderKind) -> 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());
}
} else {
return Err(format!("Could not determine compile target! This file is missing an extension.").into());
};
let options = match shaderc::CompileOptions::new() {
Some(mut options) => {
options.set_optimization_level(shaderc::OptimizationLevel::Performance);
options.set_source_language(target);
options
},
None => return Err(format!("Could not initialize shaderc compiler options.").into()),
};
let source = std::fs::read_to_string(input_path)?;
let file_name = match input_path.file_name() {
Some(file_name) => file_name,
None => return Err("This is a directory!".into()),
};
let file_name = match file_name.to_str() {
Some(file_name) => file_name,
None => return Err("Could not load this file name.".into()),
};
let binary_result = compiler.compile_into_spirv(&source, shader_kind, file_name, "main", Some(&options))?;
std::fs::write(output_path, binary_result.as_binary_u8())?;
Ok(())
}

33
rustfmt.toml Normal file
View file

@ -0,0 +1,33 @@
# i use tabs so stop bugging me and go elsewhere already
hard_tabs=true
binop_separator="Back"
condense_wildcard_suffixes = true
empty_item_single_line = false
enum_discrim_align_threshold = 30
struct_field_align_threshold = 30
short_array_element_width_threshold = 30
inline_attribute_width = 50
fn_params_layout = "Compressed"
fn_single_line = true
format_code_in_doc_comments = true
format_macro_matchers = true
hex_literal_case = "Upper"
imports_indent = "Visual"
imports_layout = "Vertical"
# indent_style = "Visual"
match_arm_blocks = false
match_block_trailing_comma = true
max_width = 160
imports_granularity = "Item"
newline_style = "Unix"
normalize_doc_attributes = true
overflow_delimited_expr = true
reorder_impl_items = true
# group_imports = "StdExternalCrate"
space_after_colon = false
# trailing_comma = "Always"
type_punctuation_density = "Compressed"
use_field_init_shorthand = true
use_try_shorthand = true
where_single_line = true

10
shaders/f_default.glsl Normal file
View file

@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}

20
shaders/v_default.glsl Normal file
View file

@ -0,0 +1,20 @@
#version 450
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];
}

940
src/linux.rs Normal file
View file

@ -0,0 +1,940 @@
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);
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();
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(())
}
/// 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)?,
})
}
}

46
src/main.rs Normal file
View file

@ -0,0 +1,46 @@
#![deny(clippy::unwrap_used)]
#![allow(
// dead_code,
unused_variables,
// clippy::too_many_arguments,
// clippy::unnecessary_wraps,
unsafe_op_in_unsafe_fn
)]
// 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::*;
// unconfirmed 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::*;

Binary file not shown.

View file

@ -0,0 +1,19 @@
pub struct DebugFont {
pub glyphs:&'static [u8],
pub width: usize,
pub height:usize,
pub first: u8,
pub last: u8,
pub size_w:usize,
pub size_h:usize,
}
pub const DEBUG_FONT:DebugFont = DebugFont {
glyphs:include_bytes!("font.bin"),
width: 8,
height:8,
first: 0,
last: 255,
size_w:8,
size_h:8,
};

View file

@ -0,0 +1,11 @@
struct Font {
bits_per_row:u8,
row_count: u8,
glyphs: [u8; 255],
}
const COMIC_MONO_FONT:Font = Font {
bits_per_row:8,
row_count: 8,
glyphs: [0x00000000],
};

View file

@ -0,0 +1,191 @@
use core::ffi::c_void;
use core::fmt::Result;
use core::fmt::Write;
use core::mem::size_of;
use core::ptr;
use vitasdk_sys::SCE_DISPLAY_SETBUF_NEXTFRAME;
use vitasdk_sys::SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW;
use vitasdk_sys::SceDisplayFrameBuf;
use vitasdk_sys::SceUID;
use vitasdk_sys::sceDisplaySetFrameBuf;
use vitasdk_sys::sceKernelAllocMemBlock;
use vitasdk_sys::sceKernelFreeMemBlock;
use vitasdk_sys::sceKernelGetMemBlockBase;
use super::font::DEBUG_FONT;
const SCREEN_WIDTH:usize = 960;
const SCREEN_HEIGHT:usize = 544;
const SCREEN_PIXEL_COUNT:usize = SCREEN_WIDTH * SCREEN_HEIGHT;
const SCREEN_FB_WIDTH:usize = 960;
const SCREEN_FB_SIZE:usize = 2 * 1024 * 1024; // 2kb
const SCREEN_TAB_SIZE:usize = 4; // Tab size in number of characters
const SCREEN_TAB_W:usize = DEBUG_FONT.size_w * SCREEN_TAB_SIZE;
const DEFAULT_FG:u32 = 0xFFFFFFFF;
const DEFAULT_BG:u32 = 0xFF000000;
pub struct DebugScreen {
framebuffer:Framebuffer,
coord_x: usize,
coord_y: usize,
color_fg: u32,
color_bg: u32,
}
pub struct Framebuffer {
buf: *mut u32,
block_uid:SceUID,
}
impl Framebuffer {
pub fn new() -> Framebuffer {
// Allocate memory to use as display buffer
let mut base:*mut c_void = ::core::ptr::null_mut();
let block_uid = unsafe {
let block_uid:SceUID = sceKernelAllocMemBlock(
b"display\0".as_ptr() as *const i8, // a dereferenced memory position of the display
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
SCREEN_FB_SIZE as u32,
::core::ptr::null_mut(),
);
sceKernelGetMemBlockBase(block_uid, &mut base);
block_uid
};
Framebuffer {
buf:base as *mut u32,
block_uid,
}
}
pub fn set_display(&mut self) {
// Sets buffer as current display frame
let frame = SceDisplayFrameBuf {
size: size_of::<SceDisplayFrameBuf>() as u32,
base: self.buf as *mut c_void,
pitch: SCREEN_FB_WIDTH as u32,
pixelformat:0,
width: SCREEN_WIDTH as u32,
height: SCREEN_HEIGHT as u32,
};
unsafe {
sceDisplaySetFrameBuf(&frame, SCE_DISPLAY_SETBUF_NEXTFRAME);
}
}
#[allow(unused)]
pub fn get(&self, index:usize) -> u32 {
if index > SCREEN_PIXEL_COUNT {
panic!("Invalid framebuffer index");
}
unsafe { ptr::read_volatile(self.buf.offset(index.try_into().unwrap())) }
}
pub fn set(&mut self, index:usize, value:u32) {
if index > SCREEN_PIXEL_COUNT {
panic!("Invalid framebuffer index");
}
unsafe { ptr::write_volatile(self.buf.offset(index.try_into().unwrap()), value) }
}
}
impl Drop for Framebuffer {
fn drop(&mut self) { let _error_code = unsafe { sceKernelFreeMemBlock(self.block_uid) }; }
}
impl Write for DebugScreen {
fn write_str(&mut self, s:&str) -> Result {
self.puts(s.as_bytes());
Ok(())
}
}
impl DebugScreen {
pub fn new() -> Self {
let mut framebuffer = Framebuffer::new();
framebuffer.set_display();
Self {
framebuffer,
coord_x:0,
coord_y:0,
color_fg:DEFAULT_FG,
color_bg:DEFAULT_BG,
}
}
#[allow(unused)]
fn clear(&mut self, from_h:usize, to_h:usize, from_w:usize, to_w:usize) {
for h in from_h..to_h {
for w in from_w..to_w {
self.framebuffer.set(h * SCREEN_FB_WIDTH + w, self.color_bg);
}
}
}
fn puts(&mut self, text:&[u8]) {
let bytes_per_glyph = DEBUG_FONT.width * DEBUG_FONT.height / 8;
for &chr in text.iter() {
if chr == b'\t' {
self.coord_x += SCREEN_TAB_W - (self.coord_x % SCREEN_TAB_W);
continue;
}
// Go to next line at the end of the current line
if self.coord_x + DEBUG_FONT.width > SCREEN_WIDTH {
self.coord_y += DEBUG_FONT.size_h;
self.coord_x = 0;
}
// Go to screen top when at the bottom of the screen
if self.coord_y + DEBUG_FONT.height > SCREEN_HEIGHT {
self.coord_x = 0;
self.coord_y = 0;
}
if chr == b'\n' {
self.coord_x = 0;
self.coord_y += DEBUG_FONT.size_h;
continue;
} else if chr == b'\r' {
self.coord_x = 0;
continue;
}
let current_offset = self.coord_x + self.coord_y * SCREEN_FB_WIDTH;
let mut font = &DEBUG_FONT.glyphs[(chr - DEBUG_FONT.first) as usize * bytes_per_glyph..];
let mut mask = 1 << 7;
for row in 0..DEBUG_FONT.height {
for col in 0..DEBUG_FONT.width {
if mask == 0 {
font = &font[1..];
mask = 1 << 7;
}
self.framebuffer.set(
current_offset + row * SCREEN_FB_WIDTH + col,
if font[0] & mask == 0 { self.color_bg } else { self.color_fg },
);
mask >>= 1;
}
#[allow(clippy::reversed_empty_ranges)]
for col in DEBUG_FONT.width..DEBUG_FONT.size_w {
self.framebuffer.set(current_offset + row * SCREEN_FB_WIDTH + col, self.color_bg)
}
}
#[allow(clippy::reversed_empty_ranges)]
for row in DEBUG_FONT.height..DEBUG_FONT.size_h {
for col in 0..DEBUG_FONT.size_w {
self.framebuffer.set(current_offset + row * SCREEN_FB_WIDTH + col, self.color_bg)
}
}
self.coord_x += DEBUG_FONT.size_w;
}
}
}

View file

@ -0,0 +1,2 @@
pub mod font;
pub mod framebuffer;

BIN
src/vita/gui/font.bin Normal file

Binary file not shown.

View file

@ -0,0 +1,95 @@
use std::mem::size_of;
use std::ptr;
use vitasdk_sys::SCE_DISPLAY_SETBUF_NEXTFRAME;
use vitasdk_sys::SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW;
use vitasdk_sys::SceDisplayFrameBuf;
use vitasdk_sys::SceUID;
use vitasdk_sys::c_void;
use vitasdk_sys::sceDisplaySetFrameBuf;
use vitasdk_sys::sceKernelAllocMemBlock;
use vitasdk_sys::sceKernelFreeMemBlock;
use vitasdk_sys::sceKernelGetMemBlockBase;
use crate::CDRAM_ALIGNMENT;
use crate::align;
#[derive(Debug, Clone)]
pub struct Framebuffer {
pub buf: *mut u32,
pub block_uid: SceUID,
pub screen_height:u32,
pub screen_width: u32,
pub pixel_pitch: u32,
// pub pixel_count: u32,
pub pixel_format: u32,
}
impl Framebuffer {
pub fn new() -> Framebuffer {
const SCREEN_WIDTH:u32 = 960;
const SCREEN_HEIGHT:u32 = 544;
const PIXEL_PITCH:u32 = SCREEN_WIDTH;
const PIXEL_COUNT:u32 = SCREEN_WIDTH * SCREEN_HEIGHT;
const FRAMEBUFFER_SIZE:u32 = align(PIXEL_COUNT * size_of::<u32>() as u32, CDRAM_ALIGNMENT);
// Allocate memory to use as display buffer
let mut base:*mut c_void = ::core::ptr::null_mut();
let block_uid = unsafe {
let block_uid:SceUID = sceKernelAllocMemBlock(
b"display\0".as_ptr() as *const i8, // a dereferenced memory position of the display // WHY i8
SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW,
FRAMEBUFFER_SIZE,
::core::ptr::null_mut(),
);
sceKernelGetMemBlockBase(block_uid, &mut base);
block_uid
};
Framebuffer {
buf:base as *mut u32,
block_uid,
screen_height:SCREEN_HEIGHT,
screen_width:SCREEN_WIDTH,
pixel_pitch:PIXEL_PITCH,
pixel_format:0,
}
}
pub fn set_display(&mut self) {
// Sets buffer as current display frame
let frame = SceDisplayFrameBuf {
size: size_of::<SceDisplayFrameBuf>() as u32,
base: self.buf as *mut c_void,
pitch: self.pixel_pitch,
pixelformat:self.pixel_format,
width: self.screen_width,
height: self.screen_height,
};
unsafe {
sceDisplaySetFrameBuf(&frame, SCE_DISPLAY_SETBUF_NEXTFRAME);
}
}
pub fn get(&self, index:u32) -> u32 {
if index > self.screen_height * self.screen_width {
panic!("Invalid framebuffer index");
}
unsafe { ptr::read_volatile(self.buf.offset(index.try_into().unwrap())) }
}
pub fn set(&mut self, index:u32, value:u32) {
if index > self.screen_height * self.screen_width {
panic!("Invalid framebuffer index");
}
unsafe { ptr::write_volatile(self.buf.offset(index.try_into().unwrap()), value) }
}
pub fn clear(&mut self) {
for index in 0..(self.screen_height * self.screen_width) {
self.set(index, 0);
}
}
}
impl Drop for Framebuffer {
fn drop(&mut self) { let _error_code = unsafe { sceKernelFreeMemBlock(self.block_uid) }; }
}

492
src/vita/gui/mod.rs Normal file
View file

@ -0,0 +1,492 @@
use std::fmt::Write;
pub mod framebuffer;
pub const CHAR_BUF_SIZE_LIMIT:usize = 4 * 1024; // 4kb buf
pub const CHAR_BUF_RESET_SIZE:usize = 1 * 1024; // reset to 1kb buf
pub const TAB_WIDTH:u32 = 4;
#[derive(Debug, Clone, Copy)]
pub struct Font {
char_width: u32,
char_height: u32,
pix_size_width: u32,
pix_size_height:u32,
first: u32,
last: u32,
glyphs: &'static [u8],
}
pub const DEBUG_FONT:Font = Font {
char_height: 8,
char_width: 8,
pix_size_width: 8,
pix_size_height:8,
first: 0,
last: 255,
glyphs: include_bytes!("font.bin"),
};
#[derive(Debug, Clone)]
pub struct CharacterBuffer {
buffer: Vec<char>,
buffer_x: u32,
buffer_y: u32,
use_word_wrap:bool,
}
impl CharacterBuffer {
// TODO
pub fn get_sized_buf(&self, x:u32, y:u32, width:u32, height:u32) -> CharacterBuffer {
let buffer = vec![];
// let buffer = self.buffer.clone();
CharacterBuffer {
buffer,
buffer_x:0,
buffer_y:0,
use_word_wrap:self.use_word_wrap,
}
}
#[inline]
pub fn from_char_array(chars:&[char], use_word_wrap:bool) -> CharacterBuffer {
let mut cbuf = CharacterBuffer {
buffer:vec![],
buffer_x:0,
buffer_y:0,
use_word_wrap,
};
for char in chars {
cbuf.buffer.push(*char);
}
cbuf
}
pub fn to_pix(char:char, font:&Font, color_fg:u32, color_bg:u32) -> Vec<u32> {
let bytes_per_glyph = font.char_width * font.char_height / 8;
let mut font_bytes = &font.glyphs[(char as usize - font.first as usize) * bytes_per_glyph as usize..];
let mut revec = vec![0; (font.char_width * font.char_height) as usize];
let mut mask = 1 << 7;
for row in 0..font.char_height {
for col in 0..font.char_width {
if mask == 0 {
font_bytes = &font_bytes[1..];
mask = 1 << 7;
}
revec[(row * font.char_width + col) as usize] = if font_bytes[0] & mask == 0 { color_bg } else { color_fg };
// self.framebuffer.set(
// current_offset + row * self.column_count + col,
// if font[0] & mask == 0 {
// self.color_bg
// } else {
// self.color_fg
// },
// );
mask >>= 1;
}
}
// self.cursor_position_x += font.pix_size_width;
revec
}
}
#[derive(Debug, Clone)]
pub struct TextScreen {
framebuffer:framebuffer::Framebuffer,
font: Font,
color_bg: u32,
color_fg: u32,
// for tracking charbuf
char_buf: CharacterBuffer,
// TODO move into cbuf
row_count: u32,
column_count:u32,
// for tracking charbuf inside framebuffer
screen_vertical_position: u32,
screen_horizontal_position:u32,
}
impl TextScreen {
// pub fn new(
// font: Font,
// use_word_wrap: bool,
// cursor_position_x: u32,
// cursor_position_y: u32,
// screen_vertical_position: u32,
// screen_horizontal_position: u32,
// char_buf: CharacterBuffer,
// color_fg: u32,
// color_bg: u32,
// ) -> TextScreen {
// let framebuffer = framebuffer::Framebuffer::new();
// let row_count = framebuffer.screen_height - (framebuffer.screen_height % font.char_height);
// let column_count = framebuffer.screen_width - (framebuffer.screen_width % font.char_width);
// // let font = DEBUG_FONT;
// // let char_buf_vec_size = (framebuffer.screen_height / font.char_height)
// // * (framebuffer.screen_width / font.char_width);
// TextScreen {
// framebuffer,
// font,
// cursor_position_x,
// cursor_position_y,
// row_count,
// column_count,
// screen_vertical_position,
// screen_horizontal_position,
// char_buf, // vec![TextChar(' '); char_buf_vec_size as usize * 2],
// color_bg,
// color_fg,
// }
// }
pub fn predef() -> TextScreen {
let framebuffer = framebuffer::Framebuffer::new();
let row_count = framebuffer.screen_height - (framebuffer.screen_height % DEBUG_FONT.char_height);
let column_count = framebuffer.screen_width - (framebuffer.screen_width % DEBUG_FONT.char_width);
let font = DEBUG_FONT;
let char_buf_vec_size = (framebuffer.screen_height / font.char_height) * (framebuffer.screen_width / font.char_width);
TextScreen {
framebuffer,
font,
row_count,
column_count,
screen_vertical_position:0,
screen_horizontal_position:0,
char_buf:CharacterBuffer {
// buffer: vec![' '; char_buf_vec_size as usize * 2],
buffer: vec![' '; 32],
buffer_x: 0,
buffer_y: 0,
use_word_wrap:true,
},
color_bg:0x00_00_00_00, // ABGR
color_fg:0xFF_FF_FF_FF, // ABGR
}
}
pub fn set_cursor(&mut self, x:u32, y:u32) {
self.char_buf.buffer_x = x;
self.char_buf.buffer_y = y;
}
pub fn clear(&mut self) {
self.char_buf.buffer.clear();
self.char_buf.buffer_x = 0;
self.char_buf.buffer_y = 0;
// self.framebuffer.clear();
}
pub fn update_screen_raw(&mut self) {
let mut screen_x = 0;
let mut screen_y = 0;
// let screen_position = self.screen_vertical_position * self.font.char_height;
// if self.screen_vertical_position > self.screen_vertical_position + self.row_count {
// panic!("current screen pos");
// }
// for x in 300..400 {
// for y in 200..333 {
// self.framebuffer
// .set(x + y * self.framebuffer.screen_width, 0xFF_FF_FF_FF);
// }
// }
// let cbuf = self.char_buf.clone();
// self.char_buf.get_sized_buf(
// self.cursor_position_x,
// self.cursor_position_y,
// self.column_count,
// self.row_count,
// );
// cbuf.buffer = Vec::from_iter("Goodbye Void\n".chars());
// panic!("cbuf len: {:#?}", cbuf);
for char in &self.char_buf.buffer {
// TODO re check like this for all end pixels and return if next end pixel is true
let end_pos = (screen_x + self.font.char_width) + (screen_y + self.font.char_height) * self.column_count;
if end_pos > self.framebuffer.screen_height * self.framebuffer.screen_width {
todo!("shift entire framebuffer up by like 15 lines and update it");
}
match char {
'\n' => {
screen_x = 0;
screen_y += 1 * self.font.char_height;
continue;
},
'\t' => {
screen_x = screen_x + TAB_WIDTH * self.font.char_width;
continue;
},
_ => {
let pix = CharacterBuffer::to_pix(*char, &self.font, self.color_fg, self.color_bg);
for y in 0..self.font.char_height {
for x in 0..self.font.char_width {
self.framebuffer.set(
// 336 + x + y,
(screen_x + screen_y * self.framebuffer.screen_width) + (x + y * self.framebuffer.screen_width),
// ((self.cursor_position_x * self.font.char_width)
// + (self.cursor_position_y * self.font.char_height)
// * self.framebuffer.screen_width)
// + y * self.framebuffer.screen_width
// + x,
// 0xFF_FF_FF_FF,
pix[(x + y * self.font.char_width) as usize],
);
// self.framebuffer.set(
// x + y * self.framebuffer.screen_width,
// 255,
// //char_pix[(x + y) as usize],
// );
}
}
},
}
// if screen_x + self.font.char_width > self.framebuffer.screen_width {
// screen_x = 0;
// screen_y += self.font.char_height;
// } else {
screen_x += self.font.char_width;
// }
}
// for y in self.screen_vertical_position..self.screen_vertical_position + self.row_count {
// for x in
// self.screen_horizontal_position..self.screen_horizontal_position + self.column_count
// {
// }
// }
}
pub fn enable(&mut self) { self.framebuffer.set_display(); }
pub fn toss(&mut self) {
if self.char_buf.buffer.len() > CHAR_BUF_SIZE_LIMIT {
todo!();
self.char_buf.buffer = self.char_buf.buffer.split_off(CHAR_BUF_RESET_SIZE);
}
}
// pub fn draw_pos(&mut self, x: u32, y: u32) {
// let char = self.char_buf.buffer[(x + y * self.column_count) as usize];
// let char_pix = CharacterBuffer::to_pix(char, &self.font, self.color_fg, self.color_bg);
// for y in 0..self.font.char_height {
// for x in 0..self.font.char_width {
// if self.cursor_position_x * self.font.char_width + self.font.char_width
// > self.framebuffer.screen_width
// {
// // self.set_cursor(self.cursor_position_x, self.cursor_position_y + 1);
// }
// self.framebuffer.set(
// // 336 + x + y,
// // self.cursor_position_x + x + (self.cursor_position_y + y) * self.framebuffer.screen_width,
// ((self.cursor_position_x * self.font.char_width)
// + (self.cursor_position_y * self.font.char_height)
// * self.framebuffer.screen_width)
// + y * self.framebuffer.screen_width
// + x,
// // 0xFF_FF_FF_FF,
// char_pix[(x + y * self.font.char_width) as usize],
// );
// // self.framebuffer.set(
// // x + y * self.framebuffer.screen_width,
// // 255,
// // //char_pix[(x + y) as usize],
// // );
// }
// }
// // // horizontal spacing
// // #[allow(clippy::reversed_empty_ranges)]
// // for col in DEBUG_FONT.char_width..DEBUG_FONT.pix_size_width {
// // self.framebuffer.set(
// // current_offset + row * self.column_count + col,
// // self.color_bg,
// // )
// // }
// // // vertical spacing
// // #[allow(clippy::reversed_empty_ranges)]
// // for row in DEBUG_FONT.char_height..DEBUG_FONT.pix_size_height {
// // for col in 0..DEBUG_FONT.pix_size_width {
// // self.framebuffer.set(
// // current_offset + row * self.column_count + col,
// // self.color_bg,
// // )
// // }
// // }
// }
// pub fn draw_screen() {}
// #[deprecated]
// pub fn update_pos(&mut self, x: u32, y: u32) {
// let bytes_per_glyph = DEBUG_FONT.char_width * DEBUG_FONT.char_height / 8;
// // for &chr in self.text.iter() {
// // if chr == b'\t' {
// // self.text_position_x += SCREEN_TAB_W - (self.text_position_x % SCREEN_TAB_W);
// // continue;
// // }
// // Go to next line at the end of the current line
// // if self.text_position_x + DEBUG_FONT.char_width > SCREEN_WIDTH {
// // self.text_position_y += DEBUG_FONT.char_height;
// // self.text_position_x = 0;
// // }
// // Go to screen top when at the bottom of the screen
// // if self.text_position_y + DEBUG_FONT.char_height > SCREEN_HEIGHT {
// // self.text_position_x = 0;
// // self.text_position_y = 0;
// // // todo!();
// // }
// // if chr == b'\n' {
// // self.coord_x = 0;
// // self.coord_y += DEBUG_FONT.size_h;
// // continue;
// // } else if chr == b'\r' {
// // self.coord_x = 0;
// // continue;
// // }
// let current_offset = self.cursor_position_x + self.cursor_position_y * self.column_count;
// let mut font = &DEBUG_FONT.glyphs
// // [(chr - DEBUG_FONT.first as u8) as usize * bytes_per_glyph as usize..];
// [((x + y*self.row_count) as usize - DEBUG_FONT.first as usize) * bytes_per_glyph as usize..];
// let mut mask = 1 << 7;
// for row in 0..DEBUG_FONT.char_height {
// for col in 0..DEBUG_FONT.char_width {
// if mask == 0 {
// font = &font[1..];
// mask = 1 << 7;
// }
// self.framebuffer.set(
// current_offset + row * self.column_count + col,
// if font[0] & mask == 0 {
// self.color_bg
// } else {
// self.color_fg
// },
// );
// mask >>= 1;
// }
// #[allow(clippy::reversed_empty_ranges)]
// for col in DEBUG_FONT.char_width..DEBUG_FONT.pix_size_width {
// self.framebuffer.set(
// current_offset + row * self.column_count + col,
// self.color_bg,
// )
// }
// }
// #[allow(clippy::reversed_empty_ranges)]
// for row in DEBUG_FONT.char_height..DEBUG_FONT.pix_size_height {
// for col in 0..DEBUG_FONT.pix_size_width {
// self.framebuffer.set(
// current_offset + row * self.column_count + col,
// self.color_bg,
// )
// }
// }
// self.cursor_position_x += DEBUG_FONT.pix_size_width;
// // }
// }
// #[deprecated]
// pub fn update_screen(&mut self) {
// for x in 0..self.row_count {
// for y in 0..self.column_count {
// self.update_pos(x, y);
// }
// }
// }
}
impl Write for TextScreen {
fn write_str(&mut self, s:&str) -> std::fmt::Result {
for char in s.chars() {
let pos = self.char_buf.buffer_x + self.char_buf.buffer_y * self.column_count;
// if position is outside current buf, allocate more space
// but if we try to allocate more space than we should
// just cut the char_buf in half to prevent memory bloating
while pos as usize >= self.char_buf.buffer.len() {
self.toss();
// self.char_buf.buffer.extend([' '; 256]);
self.char_buf.buffer.extend([' '; 32]);
}
self.char_buf.buffer[pos as usize] = char;
self.char_buf.buffer_x += 1;
}
// let mut chars = s.chars();
// while let Some(char) = chars.next() {
// match char {
// '\n' => (), //self.set_cursor(self.cursor_position_x, self.cursor_position_y + 1),
// '\t' => (), // self.set_cursor(
// // self.cursor_position_x + TAB_WIDTH - (self.cursor_position_x % TAB_WIDTH),
// // self.cursor_position_y,
// // ),
// '\r' => (), //self.set_cursor(0, self.cursor_position_y),
// _ => {
// // let pos = self.cursor_position_x + self.cursor_position_y * self.row_count;
// // if position is outside current buf, allocate more space
// // but if we try to allocate more space than we should
// // just cut the char_buf in half to prevent memory bloating
// while pos as usize > self.char_buf.len() {
// if self.char_buf.len() > CHAR_BUF_SIZE_LIMIT {
// self.char_buf = self.char_buf.split_off(CHAR_BUF_RESET_SIZE);
// }
// self.char_buf.extend([TextChar(' '); 256]);
// }
// self.char_buf[pos as usize] = TextChar(char);
// // self.draw_pos(self.cursor_position_x, self.cursor_position_y);
// // self.set_cursor(self.cursor_position_x + 1, self.cursor_position_y);
// }
// }
// // std::thread::sleep(Duration::from_millis(200));
// }
Ok(())
}
}

211
src/vita/mod.rs Normal file
View file

@ -0,0 +1,211 @@
use std::fmt::Write;
use std::panic::PanicHookInfo;
use std::thread;
use std::time::Duration;
use vitasdk_sys::sceGxmTerminate;
mod debug_screen;
mod gui;
// pub const CDRAM_ALIGNMENT: u32 = 0x3FFFF;
/// 256 kb
pub const CDRAM_ALIGNMENT:u32 = (256 * 1024) - 1;
/// 4 kb
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 } }
// 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)
// 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;
(r, g, b)
}
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));
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));
// 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();
// // for i in 336..446 {
// // screen.framebuffer.set(i, 0xFF_FF_FF_FF);
// // }
// // 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));
// screen.set_cursor(0, 3);
// for _ in 0..25 {
// write!(screen, "Goodbye Void!\n").ok();
// // screen.update_screen_raw();
// }
// 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();
let msg = match info.payload().downcast_ref::<&'static str>() {
Some(s) => *s,
None => match info.payload().downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
},
};
let name = "unknown";
let mut screen = debug_screen::framebuffer::DebugScreen::new();
println!("thread '{}' panicked at '{}', {}", name, msg, location);
writeln!(screen, "thread '{}' panicked at '{}', {}", name, msg, location).ok();
// Give 2 seconds to see the error in case capturing the stack trace fails
// (capturing the stack trace allocates memory)
thread::sleep(Duration::from_secs(2));
let _ = std::panic::catch_unwind(move || {
// The backtrace is full of "unknown" for some reason
let backtrace = std::backtrace::Backtrace::force_capture();
writeln!(screen, "{}", backtrace).ok();
});
loop {
thread::sleep(Duration::from_secs(60));
}
}

1
src/windows.rs Normal file
View file

@ -0,0 +1 @@

10
thanks.txt Normal file
View file

@ -0,0 +1,10 @@
# 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
https://kylemayes.github.io/vulkanalia/introduction.html
# javidx9 code it yourself console game engine tutorial
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