Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/core/graphics/gl_rectangle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use crate::core::graphics::gl_utils::{create_program, create_shader, shader_source};
use gl::types::GLuint;
use std::ptr;

pub struct GlRectangle {
rect_vertices: Vec<[f32; 8]>, // [x, y, r, g, b, a, unused1, unused2] for each vertex
rect_indices: Vec<[u16; 6]>,
rect_program: GLuint,
}

impl GlRectangle {
pub fn new() -> Self {
let rect_program = unsafe {
let vert_shader = create_shader("rect", shader_source!("rect_vert"), gl::VERTEX_SHADER).unwrap();
let frag_shader = create_shader("rect", shader_source!("rect_frag"), gl::FRAGMENT_SHADER).unwrap();
let program = create_program(&[vert_shader, frag_shader]).unwrap();
gl::DeleteShader(vert_shader);
gl::DeleteShader(frag_shader);

gl::UseProgram(program);

gl::BindAttribLocation(program, 0, "position\0".as_ptr() as _);
gl::BindAttribLocation(program, 1, "color\0".as_ptr() as _);

gl::UseProgram(0);

program
};

GlRectangle {
rect_vertices: Vec::new(),
rect_indices: Vec::new(),
rect_program,
}
}

// Helper method to render vertices
unsafe fn render_vertices(&mut self) {
gl::UseProgram(self.rect_program);

gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

gl::EnableVertexAttribArray(0);
gl::EnableVertexAttribArray(1);

// Position attribute (x, y)
gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, 8 * std::mem::size_of::<f32>() as i32, self.rect_vertices.as_ptr() as _);

// Color attribute (r, g, b, a) - offset by 2 floats
gl::VertexAttribPointer(1, 4, gl::FLOAT, gl::FALSE, 8 * std::mem::size_of::<f32>() as i32,
(self.rect_vertices.as_ptr() as *const u8).add(2 * std::mem::size_of::<f32>()) as _);

gl::DrawElements(gl::TRIANGLES, (6 * self.rect_indices.len()) as i32, gl::UNSIGNED_SHORT, self.rect_indices.as_ptr() as _);

gl::DisableVertexAttribArray(0);
gl::DisableVertexAttribArray(1);
gl::Disable(gl::BLEND);
gl::UseProgram(0);
}

// Draw a filled rectangle
pub unsafe fn draw_filled(&mut self, x: f32, y: f32, width: f32, height: f32, fill_color: (f32, f32, f32, f32)) {
// Clear previous vertices and indices
self.rect_vertices.clear();
self.rect_indices.clear();

// Create vertices for the filled rectangle (4 corners)
let (r, g, b, a) = fill_color;
let vertices = [
// Top left
[x, y + height, r, g, b, a, 0.0, 0.0],
// Top right
[x + width, y + height, r, g, b, a, 0.0, 0.0],
// Bottom right
[x + width, y, r, g, b, a, 0.0, 0.0],
// Bottom left
[x, y, r, g, b, a, 0.0, 0.0],
];

self.rect_vertices.extend_from_slice(&vertices);

// Create indices for two triangles forming the rectangle
let indices = [0, 1, 2, 0, 2, 3]; // Two triangles: (0,1,2) and (0,2,3)
self.rect_indices.push(indices);

self.render_vertices();
}

// Draw a stroke-only rectangle (unfilled)
pub unsafe fn draw_stroke(&mut self, x: f32, y: f32, width: f32, height: f32, stroke_color: (f32, f32, f32, f32), stroke_width: f32) {
// Clear previous vertices and indices
self.rect_vertices.clear();
self.rect_indices.clear();

let (r, g, b, a) = stroke_color;
let half_stroke = stroke_width / 2.0;

// Create vertices for 4 stroke rectangles (top, right, bottom, left)
let vertices = [
// Top stroke
[x - half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y + height - half_stroke, r, g, b, a, 0.0, 0.0],
[x - half_stroke, y + height - half_stroke, r, g, b, a, 0.0, 0.0],

// Right stroke
[x + width - half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],
[x + width - half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],

// Bottom stroke
[x - half_stroke, y + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y + half_stroke, r, g, b, a, 0.0, 0.0],
[x + width + half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],
[x - half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],

// Left stroke
[x - half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + half_stroke, y + height + half_stroke, r, g, b, a, 0.0, 0.0],
[x + half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],
[x - half_stroke, y - half_stroke, r, g, b, a, 0.0, 0.0],
];

self.rect_vertices.extend_from_slice(&vertices);

// Create indices for 4 rectangles (16 vertices = 4 rectangles * 4 vertices each)
for i in 0..4 {
let base = i * 4;
let indices = [base, base + 1, base + 2, base, base + 2, base + 3];
self.rect_indices.push(indices);
}

self.render_vertices();
}

// Draw a rectangle with both fill and stroke
pub unsafe fn draw_filled_stroke(&mut self, x: f32, y: f32, width: f32, height: f32, fill_color: (f32, f32, f32, f32), stroke_color: (f32, f32, f32, f32), stroke_width: f32) {
// First draw the fill
self.draw_filled(x, y, width, height, fill_color);
// Then draw the stroke
self.draw_stroke(x, y, width, height, stroke_color, stroke_width);
}
}
18 changes: 15 additions & 3 deletions src/core/graphics/gpu_renderer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::core::graphics::gl_glyph::GlGlyph;
use crate::core::graphics::gl_rectangle:: GlRectangle;
use crate::core::graphics::gpu::{PowCnt1, DISPLAY_HEIGHT, DISPLAY_WIDTH};
use crate::core::graphics::gpu_2d::registers_2d::Gpu2DRegisters;
use crate::core::graphics::gpu_2d::renderer_2d::Gpu2DRenderer;
Expand All @@ -7,7 +8,7 @@ use crate::core::graphics::gpu_3d::registers_3d::Gpu3DRegisters;
use crate::core::graphics::gpu_3d::renderer_3d::Gpu3DRenderer;
use crate::core::graphics::gpu_mem_buf::GpuMemBuf;
use crate::core::memory::mem::Memory;
use crate::presenter::{Presenter, PresenterScreen, PRESENTER_SCREEN_HEIGHT, PRESENTER_SCREEN_WIDTH, PRESENTER_SUB_REGULAR, PRESENTER_SUB_RESIZED, PRESENTER_SUB_ROTATED};
use crate::presenter::{Presenter, PresenterScreen, PRESENTER_SCREEN_HEIGHT, PRESENTER_SCREEN_WIDTH, PRESENTER_SUB_REGULAR, PRESENTER_SUB_RESIZED, PRESENTER_SUB_RESIZED_INV, PRESENTER_SUB_ROTATED, SWAP_ZONE_WIDTH, SWAP_ZONE_HEIGHT};
use crate::settings::{ScreenMode, Settings};
use gl::types::GLuint;
use std::intrinsics::unlikely;
Expand Down Expand Up @@ -41,6 +42,7 @@ pub struct GpuRenderer {

common: GpuRendererCommon,
gl_glyph: GlGlyph,
gl_rectangle: GlRectangle,

rendering: Mutex<bool>,
rendering_condvar: Condvar,
Expand All @@ -66,6 +68,7 @@ impl GpuRenderer {

common: GpuRendererCommon::new(),
gl_glyph: GlGlyph::new(),
gl_rectangle: GlRectangle::new(),

rendering: Mutex::new(false),
rendering_condvar: Condvar::new(),
Expand Down Expand Up @@ -128,7 +131,7 @@ impl GpuRenderer {
}
}

pub fn render_loop(&mut self, presenter: &mut Presenter, fps: &Arc<AtomicU16>, last_save_time: &Arc<Mutex<Option<(Instant, bool)>>>, settings: &Settings) {
pub fn render_loop(&mut self, presenter: &mut Presenter, fps: &Arc<AtomicU16>, last_save_time: &Arc<Mutex<Option<(Instant, bool)>>>, settings: &Settings, swap_sizes: bool) {
{
let rendering = self.rendering.lock().unwrap();
let _drawing = self.rendering_condvar.wait_while(rendering, |rendering| !*rendering).unwrap();
Expand Down Expand Up @@ -177,7 +180,7 @@ impl GpuRenderer {
let screen_topology = match settings.screenmode() {
ScreenMode::Regular => PRESENTER_SUB_REGULAR,
ScreenMode::Rotated => PRESENTER_SUB_ROTATED,
ScreenMode::Resized => PRESENTER_SUB_RESIZED,
ScreenMode::Resized => if swap_sizes { PRESENTER_SUB_RESIZED } else { PRESENTER_SUB_RESIZED_INV },
};
let used_fbo = match screen_topology.mode {
ScreenMode::Regular | ScreenMode::Resized => self.renderer_2d.common.blend_fbo.fbo,
Expand Down Expand Up @@ -232,6 +235,15 @@ impl GpuRenderer {

let arm7_emu: &str = settings.arm7_hle().into();
self.gl_glyph.draw(format!("{}ms {arm7_emu}\n{per}% ({fps}fps)\n{info_text}", self.average_render_time));

// Draw a UI element to represent the touch-region to swap screen sizes in resized screen mode
if settings.screenmode() == ScreenMode::Resized
{
let swap_zone_x = (PRESENTER_SCREEN_WIDTH - SWAP_ZONE_WIDTH - 4) as f32;
let swap_zone_y = 5 as f32;
self.gl_rectangle.draw_filled_stroke(swap_zone_x, swap_zone_y, (SWAP_ZONE_WIDTH) as f32, SWAP_ZONE_HEIGHT as f32,
(0.31, 0.31, 0.31, 1.0), (0.66, 0.66, 0.66, 1.0), 1.0);
}

#[cfg(feature = "profiling")]
gl::ReadPixels(
Expand Down
1 change: 1 addition & 0 deletions src/core/graphics/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod gl_glyph;
mod gl_rectangle;
mod gl_utils;
pub mod gpu;
pub mod gpu_2d;
Expand Down
5 changes: 5 additions & 0 deletions src/core/graphics/shaders/cg/rect_frag.cg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
float4 in fragColor : COLOR0;

void main(out float4 color : COLOR) {
color = fragColor;
}
7 changes: 7 additions & 0 deletions src/core/graphics/shaders/cg/rect_vert.cg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
float4 out gl_Position : POSITION;
float4 out fragColor : COLOR0;

void main(float4 position, float4 color) {
fragColor = color;
gl_Position = float4(position.x / 480.0 - 1.0, -position.y / 272.0 + 1.0, 0.0, 1.0);
}
11 changes: 11 additions & 0 deletions src/core/graphics/shaders/glsl/rect_frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version 300 es

precision mediump float;

layout(location = 0) out vec4 color;

in vec4 fragColor;

void main() {
color = fragColor;
}
10 changes: 10 additions & 0 deletions src/core/graphics/shaders/glsl/rect_vert.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#version 300 es

in vec4 position;
in vec4 color;
out vec4 fragColor;

void main() {
fragColor = color;
gl_Position = vec4(position.x / 480.0 - 1.0, -position.y / 272.0 + 1.0, 0.0, 1.0);
}
36 changes: 32 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use crate::jit::jit_asm::{JitAsm, MAX_STACK_DEPTH_SIZE};
use crate::jit::jit_memory::JitMemory;
use crate::logging::{debug_println, info_println};
use crate::mmap::{register_abort_handler, ArmContext, Mmap, PAGE_SIZE};
use crate::presenter::{PresentEvent, Presenter, PRESENTER_AUDIO_BUF_SIZE};
use crate::presenter::{PresentEvent, Presenter, PRESENTER_AUDIO_BUF_SIZE, PRESENTER_SUB_RESIZED, PRESENTER_SCREEN_WIDTH, SWAP_ZONE_WIDTH, SWAP_ZONE_HEIGHT};
use crate::profiling::{profiling_init, profiling_set_thread_name};
use crate::settings::{Arm7Emu, Settings};
use crate::settings::{Arm7Emu, ScreenMode, Settings};
use crate::utils::{const_str_equal, set_thread_prio_affinity, HeapMemU32, ThreadAffinity, ThreadPriority};
use std::cell::UnsafeCell;
use std::cmp::min;
Expand Down Expand Up @@ -299,6 +299,25 @@ fn execute_jit<const ARM7_HLE: bool>(emu: &mut UnsafeCell<Emu>) {
}
}

fn handle_touch_swap(
swap_sizes: &mut bool,
raw_touch: Option<(u16, u16)>,
prev_swap_touch: &mut bool,
) {
if let Some((tx, ty)) = raw_touch {
let (x, y) = (tx as u32, ty as u32);
let in_zone = x >= (PRESENTER_SCREEN_WIDTH as u32).saturating_sub(SWAP_ZONE_WIDTH) && y < SWAP_ZONE_HEIGHT;

if in_zone && !*prev_swap_touch {
*swap_sizes = !*swap_sizes;
}

*prev_swap_touch = in_zone;
} else {
*prev_swap_touch = false;
}
}

#[used]
#[export_name = "_newlib_heap_size_user"]
pub static _NEWLIB_HEAP_SIZE_USER: u32 = 256 * 1024 * 1024; // 256 MiB
Expand Down Expand Up @@ -468,13 +487,22 @@ pub fn actual_main() {
.unwrap();

let gpu_renderer = unsafe { gpu_renderer.get().as_mut().unwrap() };
while let PresentEvent::Inputs { keymap, touch } = presenter.poll_event(settings.screenmode()) {

// Bottom screen on the right bigger by default
let mut swap_sizes = true;
let mut prev_swap_touch = false;

while let PresentEvent::Inputs { keymap, touch, raw_touch } = presenter.poll_event(settings.screenmode(), swap_sizes) {
if let Some((x, y)) = touch {
touch_points.store(((y as u16) << 8) | (x as u16), Ordering::Relaxed);
}
key_map.store(keymap, Ordering::Relaxed);

gpu_renderer.render_loop(&mut presenter, &fps, &last_save_time, &settings);
if settings.screenmode() == ScreenMode::Resized {
handle_touch_swap(&mut swap_sizes, raw_touch, &mut prev_swap_touch);
}

gpu_renderer.render_loop(&mut presenter, &fps, &last_save_time, &settings, swap_sizes);
}

audio_thread.join().unwrap();
Expand Down
27 changes: 26 additions & 1 deletion src/presenter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ mod platform;

pub const PRESENTER_SCREEN_WIDTH: u32 = 960;
pub const PRESENTER_SCREEN_HEIGHT: u32 = 544;
pub const SWAP_ZONE_WIDTH: u32 = 87;
pub const SWAP_ZONE_HEIGHT: u32 = 71;

pub enum PresentEvent {
Inputs { keymap: u32, touch: Option<(u8, u8)> },
Inputs { keymap: u32, touch: Option<(u8, u8)>, raw_touch: Option<(u16, u16)> },
Quit,
}

Expand Down Expand Up @@ -85,6 +87,23 @@ pub const PRESENTER_SUB_RESIZED_BOTTOM_SCREEN: PresenterScreen = PresenterScreen
PRESENTER_SUB_RESIZED_SCREEN_HEIGHT_BOT,
);

pub const PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_TOP: u32 = DISPLAY_WIDTH as u32 * 2;
pub const PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_BOT: u32 = DISPLAY_WIDTH as u32;
pub const PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_TOP: u32 = DISPLAY_HEIGHT as u32 * 2;
pub const PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_BOT: u32 = DISPLAY_HEIGHT as u32;
pub const PRESENTER_SUB_RESIZED_INV_TOP_SCREEN: PresenterScreen = PresenterScreen::new(
(PRESENTER_SCREEN_WIDTH - PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_TOP - PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_BOT) / 2,
(PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_TOP) / 2,
PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_TOP,
PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_TOP,
);
pub const PRESENTER_SUB_RESIZED_INV_BOTTOM_SCREEN: PresenterScreen = PresenterScreen::new(
PRESENTER_SUB_RESIZED_INV_TOP_SCREEN.x + PRESENTER_SUB_RESIZED_INV_TOP_SCREEN.width,
(PRESENTER_SCREEN_HEIGHT - PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_BOT) / 2,
PRESENTER_SUB_RESIZED_INV_SCREEN_WIDTH_BOT,
PRESENTER_SUB_RESIZED_INV_SCREEN_HEIGHT_BOT,
);

pub const PRESENTER_SUB_REGULAR: ScreenTopology = ScreenTopology {
top: PRESENTER_SUB_TOP_SCREEN,
bottom: PRESENTER_SUB_BOTTOM_SCREEN,
Expand All @@ -102,3 +121,9 @@ pub const PRESENTER_SUB_RESIZED: ScreenTopology = ScreenTopology {
bottom: PRESENTER_SUB_RESIZED_BOTTOM_SCREEN,
mode: ScreenMode::Resized,
};

pub const PRESENTER_SUB_RESIZED_INV: ScreenTopology = ScreenTopology {
top: PRESENTER_SUB_RESIZED_INV_TOP_SCREEN,
bottom: PRESENTER_SUB_RESIZED_INV_BOTTOM_SCREEN,
mode: ScreenMode::Resized,
};
Loading