Compare commits
3 Commits
36e46d3780
...
fc6516cbb2
Author | SHA1 | Date |
---|---|---|
Rekai Nyangadzayi Musuka | fc6516cbb2 | |
Rekai Nyangadzayi Musuka | 88404dbf35 | |
Rekai Nyangadzayi Musuka | 46a25df45d |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"matklad.rust-analyzer",
|
"rust-lang.rust-analyzer",
|
||||||
"tamasfe.even-better-toml",
|
"tamasfe.even-better-toml",
|
||||||
"serayuzgur.crates",
|
"serayuzgur.crates",
|
||||||
"vadimcn.vscode-lldb",
|
"vadimcn.vscode-lldb",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
|
@ -10,12 +10,9 @@ edition = "2021"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bitfield = "0.13"
|
bitfield = "0.13"
|
||||||
clap = { version = "3.1", features = ["cargo"] }
|
clap = { version = "3.1", features = ["cargo"] }
|
||||||
gilrs = "0.8"
|
gilrs = "0.9"
|
||||||
winit = "0.26"
|
egui-wgpu = "0.18"
|
||||||
egui = "0.15"
|
egui-winit = "0.18"
|
||||||
wgpu = "0.11"
|
|
||||||
egui_wgpu_backend = "0.14"
|
|
||||||
egui_winit_platform = "0.12"
|
|
||||||
pollster = "0.2"
|
pollster = "0.2"
|
||||||
rodio = "0.15"
|
rodio = "0.15"
|
||||||
rtrb = "0.2"
|
rtrb = "0.2"
|
||||||
|
@ -23,7 +20,7 @@ directories-next = "2.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
once_cell = "1.10"
|
once_cell = "1.12"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
|
@ -4,14 +4,14 @@ use crate::cartridge::Cartridge;
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
|
use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
|
||||||
use clap::crate_name;
|
use clap::crate_name;
|
||||||
|
use egui_winit::winit::event::KeyboardInput;
|
||||||
|
use egui_winit::winit::event_loop::ControlFlow;
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use winit::event::KeyboardInput;
|
|
||||||
use winit::event_loop::ControlFlow;
|
|
||||||
|
|
||||||
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_CLOCK_SPEED);
|
||||||
pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
|
pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
|
||||||
|
|
64
src/gui.rs
64
src/gui.rs
|
@ -1,6 +1,9 @@
|
||||||
use egui::{ClippedMesh, CtxRef, TextureId};
|
use egui_wgpu::wgpu;
|
||||||
use egui_wgpu_backend::{BackendError, RenderPass, ScreenDescriptor};
|
use egui_winit::egui;
|
||||||
use egui_winit_platform::Platform;
|
use egui_winit::winit;
|
||||||
|
|
||||||
|
use egui::{ClippedPrimitive, Context, TextureId};
|
||||||
|
use egui_wgpu::renderer::{RenderPass, ScreenDescriptor};
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Adapter, CommandEncoder, Device, Extent3d, FilterMode, Instance, Queue, RequestDeviceError,
|
Adapter, CommandEncoder, Device, Extent3d, FilterMode, Instance, Queue, RequestDeviceError,
|
||||||
Surface, SurfaceConfiguration, SurfaceTexture, Texture, TextureFormat, TextureUsages,
|
Surface, SurfaceConfiguration, SurfaceTexture, Texture, TextureFormat, TextureUsages,
|
||||||
|
@ -121,20 +124,6 @@ pub fn surface_config(window: &Window, format: TextureFormat) -> SurfaceConfigur
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn platform_desc(window: &Window) -> Platform {
|
|
||||||
use egui::FontDefinitions;
|
|
||||||
use egui_winit_platform::PlatformDescriptor;
|
|
||||||
|
|
||||||
let size = window.inner_size();
|
|
||||||
Platform::new(PlatformDescriptor {
|
|
||||||
physical_width: size.width as u32,
|
|
||||||
physical_height: size.height as u32,
|
|
||||||
scale_factor: window.scale_factor(),
|
|
||||||
font_definitions: FontDefinitions::default(),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn texture_size() -> Extent3d {
|
pub fn texture_size() -> Extent3d {
|
||||||
Extent3d {
|
Extent3d {
|
||||||
width: GB_WIDTH as u32,
|
width: GB_WIDTH as u32,
|
||||||
|
@ -184,14 +173,6 @@ pub fn write_to_texture(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expose_texture_to_egui(
|
|
||||||
render_pass: &mut RenderPass,
|
|
||||||
device: &Device,
|
|
||||||
texture: &Texture,
|
|
||||||
) -> TextureId {
|
|
||||||
render_pass.egui_texture_from_wgpu_texture(device, texture, FILTER_MODE)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_view(frame: &SurfaceTexture) -> TextureView {
|
pub fn create_view(frame: &SurfaceTexture) -> TextureView {
|
||||||
use wgpu::TextureViewDescriptor;
|
use wgpu::TextureViewDescriptor;
|
||||||
|
@ -208,31 +189,19 @@ pub fn create_command_encoder(device: &Device) -> CommandEncoder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
car#[inline]
|
||||||
pub fn create_screen_descriptor(
|
|
||||||
window: &Window,
|
|
||||||
config: &SurfaceConfiguration,
|
|
||||||
) -> ScreenDescriptor {
|
|
||||||
ScreenDescriptor {
|
|
||||||
physical_width: config.width,
|
|
||||||
physical_height: config.height,
|
|
||||||
scale_factor: window.scale_factor() as f32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn execute_render_pass(
|
pub fn execute_render_pass(
|
||||||
render_pass: &mut RenderPass,
|
render_pass: &mut RenderPass,
|
||||||
encoder: &mut CommandEncoder,
|
encoder: &mut CommandEncoder,
|
||||||
view: &TextureView,
|
view: &TextureView,
|
||||||
jobs: Vec<ClippedMesh>,
|
jobs: Vec<ClippedPrimitive>,
|
||||||
descriptor: &ScreenDescriptor,
|
descriptor: &ScreenDescriptor,
|
||||||
) -> Result<(), BackendError> {
|
) {
|
||||||
render_pass.execute(encoder, view, &jobs, descriptor, Some(wgpu::Color::BLACK))
|
render_pass.execute(encoder, view, &jobs, descriptor, Some(wgpu::Color::BLACK))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn draw_egui(cpu: &Cpu, app: &mut GuiState, ctx: &CtxRef, texture_id: TextureId) {
|
pub fn draw_egui(cpu: &Cpu, app: &mut GuiState, ctx: &Context, texture_id: TextureId) {
|
||||||
use crate::{cpu, instruction, ppu};
|
use crate::{cpu, instruction, ppu};
|
||||||
|
|
||||||
fn selectable_text(ui: &mut egui::Ui, mut text: &str) -> egui::Response {
|
fn selectable_text(ui: &mut egui::Ui, mut text: &str) -> egui::Response {
|
||||||
|
@ -240,7 +209,7 @@ pub fn draw_egui(cpu: &Cpu, app: &mut GuiState, ctx: &CtxRef, texture_id: Textur
|
||||||
}
|
}
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
egui::menu::menu(ui, "File", |ui| {
|
ui.menu_button("File", |ui| {
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button("Quit").clicked() {
|
||||||
app.quit = true;
|
app.quit = true;
|
||||||
}
|
}
|
||||||
|
@ -322,9 +291,12 @@ pub fn draw_egui(cpu: &Cpu, app: &mut GuiState, ctx: &CtxRef, texture_id: Textur
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let ie = cpu.int_enable();
|
let ie = cpu.int_enable();
|
||||||
|
|
||||||
let r_len = ctx.fonts().glyph_width(egui::TextStyle::Body, 'R');
|
let style = ctx.style();
|
||||||
let e_len = ctx.fonts().glyph_width(egui::TextStyle::Body, 'E');
|
let font_id = egui::TextStyle::Body.resolve(&style);
|
||||||
let q_len = ctx.fonts().glyph_width(egui::TextStyle::Body, 'Q');
|
|
||||||
|
let r_len = ctx.fonts().glyph_width(&font_id, 'R');
|
||||||
|
let e_len = ctx.fonts().glyph_width(&font_id, 'E');
|
||||||
|
let q_len = ctx.fonts().glyph_width(&font_id, 'Q');
|
||||||
|
|
||||||
ui.label("IE:");
|
ui.label("IE:");
|
||||||
ui.add_space(q_len - (e_len - r_len));
|
ui.add_space(q_len - (e_len - r_len));
|
||||||
|
@ -350,7 +322,7 @@ pub fn draw_egui(cpu: &Cpu, app: &mut GuiState, ctx: &CtxRef, texture_id: Textur
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod kbd {
|
pub mod kbd {
|
||||||
use winit::event::{ElementState, KeyboardInput, VirtualKeyCode};
|
use egui_winit::winit::event::{ElementState, KeyboardInput, VirtualKeyCode};
|
||||||
|
|
||||||
pub fn space_released(input: &KeyboardInput) -> bool {
|
pub fn space_released(input: &KeyboardInput) -> bool {
|
||||||
let keycode = input.virtual_keycode;
|
let keycode = input.virtual_keycode;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
use egui_winit::winit::event::{ElementState, KeyboardInput, VirtualKeyCode};
|
||||||
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
|
use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType};
|
||||||
use winit::event::{ElementState, KeyboardInput, VirtualKeyCode};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Joypad {
|
pub struct Joypad {
|
||||||
|
|
49
src/main.rs
49
src/main.rs
|
@ -1,15 +1,16 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, Command};
|
use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, Command};
|
||||||
use egui_wgpu_backend::RenderPass;
|
use egui_wgpu::renderer::RenderPass;
|
||||||
use gb::gui::EmuMode;
|
use gb::gui::EmuMode;
|
||||||
use gb::{emu, gui};
|
use gb::{emu, gui};
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use gui::GuiState;
|
use gui::GuiState;
|
||||||
use rodio::{OutputStream, Sink};
|
use rodio::{OutputStream, Sink};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use winit::event::{Event, WindowEvent};
|
|
||||||
use winit::event_loop::EventLoop;
|
use egui_winit::winit::event::{Event, WindowEvent};
|
||||||
|
use egui_winit::winit::event_loop::{EventLoop, EventLoopProxy};
|
||||||
|
|
||||||
const AUDIO_ENABLED: bool = true;
|
const AUDIO_ENABLED: bool = true;
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ fn main() {
|
||||||
|
|
||||||
// --Here lies a lot of winit + wgpu Boilerplate--
|
// --Here lies a lot of winit + wgpu Boilerplate--
|
||||||
let event_loop: EventLoop<Event<()>> = EventLoop::with_user_event();
|
let event_loop: EventLoop<Event<()>> = EventLoop::with_user_event();
|
||||||
let window = gui::build_window(&event_loop).expect("build window");
|
let window = gui::build_window(&event_loop).expect("create winit window");
|
||||||
|
|
||||||
let (instance, surface) = gui::create_surface(&window);
|
let (instance, surface) = gui::create_surface(&window);
|
||||||
let adapter = gui::request_adapter(&instance, &surface).expect("request adaptor");
|
let adapter = gui::request_adapter(&instance, &surface).expect("request adaptor");
|
||||||
|
@ -59,7 +60,7 @@ fn main() {
|
||||||
|
|
||||||
let mut config = gui::surface_config(&window, format);
|
let mut config = gui::surface_config(&window, format);
|
||||||
surface.configure(&device, &config);
|
surface.configure(&device, &config);
|
||||||
let mut platform = gui::platform_desc(&window);
|
let mut state = egui_winit::State::new(8192, &window);
|
||||||
let mut render_pass = RenderPass::new(&device, format, 1);
|
let mut render_pass = RenderPass::new(&device, format, 1);
|
||||||
|
|
||||||
// We interrupt your boiler plate to initialize the emulator so that
|
// We interrupt your boiler plate to initialize the emulator so that
|
||||||
|
@ -79,7 +80,9 @@ fn main() {
|
||||||
let texture_size = gui::texture_size();
|
let texture_size = gui::texture_size();
|
||||||
let texture = gui::create_texture(&device, texture_size);
|
let texture = gui::create_texture(&device, texture_size);
|
||||||
gui::write_to_texture(&queue, &texture, emu::pixel_buf(&cpu), texture_size);
|
gui::write_to_texture(&queue, &texture, emu::pixel_buf(&cpu), texture_size);
|
||||||
let texture_id = gui::expose_texture_to_egui(&mut render_pass, &device, &texture);
|
let texture_id = todo!("Expose Texture ID to egui");
|
||||||
|
|
||||||
|
render_pass.
|
||||||
|
|
||||||
// Load ROM if filepath was provided
|
// Load ROM if filepath was provided
|
||||||
if let Some(path) = m.value_of("rom") {
|
if let Some(path) = m.value_of("rom") {
|
||||||
|
@ -116,14 +119,13 @@ fn main() {
|
||||||
|
|
||||||
// Set up state for the Immediate-mode GUI
|
// Set up state for the Immediate-mode GUI
|
||||||
let mut app = GuiState::new(rom_title);
|
let mut app = GuiState::new(rom_title);
|
||||||
|
let mut ctx = egui_winit::egui::Context::default();
|
||||||
let mut last_key = gui::unused_key();
|
let mut last_key = gui::unused_key();
|
||||||
|
|
||||||
// used for egui animations
|
// used for egui animations
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
platform.handle_event(&event);
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
if app.quit {
|
if app.quit {
|
||||||
|
@ -147,8 +149,6 @@ fn main() {
|
||||||
window.request_redraw();
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
Event::RedrawRequested(..) => {
|
Event::RedrawRequested(..) => {
|
||||||
platform.update_time(start_time.elapsed().as_secs_f64());
|
|
||||||
|
|
||||||
let data = emu::pixel_buf(&cpu);
|
let data = emu::pixel_buf(&cpu);
|
||||||
gui::write_to_texture(&queue, &texture, data, texture_size);
|
gui::write_to_texture(&queue, &texture, data, texture_size);
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ fn main() {
|
||||||
let screen_descriptor = gui::create_screen_descriptor(&window, &config);
|
let screen_descriptor = gui::create_screen_descriptor(&window, &config);
|
||||||
|
|
||||||
// Upload all resources for the GPU.
|
// Upload all resources for the GPU.
|
||||||
render_pass.update_texture(&device, &queue, &platform.context().texture());
|
render_pass.update_texture(&device, &queue, todo!(), todo!());
|
||||||
render_pass.update_user_textures(&device, &queue);
|
render_pass.update_user_textures(&device, &queue);
|
||||||
render_pass.update_buffers(&device, &queue, &paint_jobs, &screen_descriptor);
|
render_pass.update_buffers(&device, &queue, &paint_jobs, &screen_descriptor);
|
||||||
|
|
||||||
|
@ -192,18 +192,23 @@ fn main() {
|
||||||
// Redraw egui
|
// Redraw egui
|
||||||
output_frame.present();
|
output_frame.present();
|
||||||
}
|
}
|
||||||
Event::WindowEvent { event, .. } => match event {
|
Event::WindowEvent { event, .. } => {
|
||||||
WindowEvent::Resized(size) => {
|
let exclusive_use = state.on_event(&ctx, &event);
|
||||||
config.width = size.width;
|
if !exclusive_use {
|
||||||
config.height = size.height;
|
match event {
|
||||||
surface.configure(&device, &config);
|
WindowEvent::Resized(size) => {
|
||||||
|
config.width = size.width;
|
||||||
|
config.height = size.height;
|
||||||
|
surface.configure(&device, &config);
|
||||||
|
}
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
emu::save_and_exit(&cpu, control_flow);
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { input, .. } => last_key = input,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::CloseRequested => {
|
}
|
||||||
emu::save_and_exit(&cpu, control_flow);
|
|
||||||
}
|
|
||||||
WindowEvent::KeyboardInput { input, .. } => last_key = input,
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue