fixed performance issue on windows. got profiling with visual studio working, found that calling canvas.fill_rect() every pixel was way too slow. now writing pixels to one raw RGB buffer, and between frames, updating a texture with that buffer and copying it to the canvas.
This commit is contained in:
parent
4edcbdd73c
commit
28b371a5ae
|
@ -7,3 +7,6 @@ edition = "2018"
|
|||
[dependencies]
|
||||
sdl2 = "0.32"
|
||||
cpuprofiler = "0.0.3"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -9,24 +9,33 @@ mod input;
|
|||
use cpu::Cpu;
|
||||
use ppu::Ppu;
|
||||
use cartridge::Cartridge;
|
||||
use screen::Screen;
|
||||
use screen::{init_window, draw_pixel, draw_to_window};
|
||||
use input::poll_buttons;
|
||||
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::pixels::PixelFormatEnum;
|
||||
|
||||
// use cpuprofiler::PROFILER;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
// Set up screen
|
||||
let sdl_context = sdl2::init()?;
|
||||
let mut event_pump = sdl_context.event_pump()?;
|
||||
let mut screen = Screen::new(&sdl_context)?;
|
||||
let (mut canvas, texture_creator) = init_window(&sdl_context).expect("Could not create window");
|
||||
let mut texture = texture_creator.create_texture_streaming(
|
||||
PixelFormatEnum::RGB24, 256*screen::SCALE_FACTOR as u32, 240*screen::SCALE_FACTOR as u32)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let byte_width = 256 * 3 * screen::SCALE_FACTOR; // 256 NES pixels, 3 bytes for each pixel (RGB 24-bit), and NES-to-SDL scale factor
|
||||
let byte_height = 240 * screen::SCALE_FACTOR; // NES image is 240 pixels tall, multiply by scale factor for total number of rows needed
|
||||
let mut screen_buffer = vec![0; byte_width * byte_height]; // contains raw RGB data for the screen
|
||||
|
||||
// Initialize hardware components
|
||||
let cart = Cartridge::new();
|
||||
|
||||
let ppu = Ppu::new(&cart);
|
||||
let mut cpu = Cpu::new(&cart, ppu);
|
||||
|
||||
// For throttling to 60 FPS
|
||||
let mut timer = Instant::now();
|
||||
let mut fps_timer = Instant::now();
|
||||
let mut fps = 0;
|
||||
|
@ -39,12 +48,12 @@ fn main() -> Result<(), String> {
|
|||
for _i in 0..num_cycles * 3 {
|
||||
let (pixel, end_of_frame) = cpu.ppu.step();
|
||||
match pixel {
|
||||
Some((x, y, color)) => screen.draw_pixel(x, y, color),
|
||||
None => Ok(()),
|
||||
}?;
|
||||
Some((x, y, color)) => draw_pixel(&mut screen_buffer, x, y, color),
|
||||
None => (),
|
||||
};
|
||||
if end_of_frame {
|
||||
fps += 1;
|
||||
screen.canvas.present();
|
||||
draw_to_window(&mut texture, &mut canvas, &screen_buffer)?;
|
||||
let now = Instant::now();
|
||||
if now < timer + Duration::from_millis(1000/60) {
|
||||
std::thread::sleep(timer + Duration::from_millis(1000/60) - now);
|
||||
|
|
|
@ -2,38 +2,50 @@ extern crate sdl2;
|
|||
|
||||
use sdl2::Sdl;
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::Canvas;
|
||||
use sdl2::video::Window;
|
||||
use sdl2::render::{Canvas, Texture, TextureCreator};
|
||||
use sdl2::video::{Window, WindowContext};
|
||||
|
||||
const SCALE_FACTOR: usize = 4;
|
||||
pub const SCALE_FACTOR: usize = 3;
|
||||
const BYTES_IN_COL: usize = SCALE_FACTOR * 3; // 3 bytes per pixel in RGB24. This represents a thick, SCALE_FACTOR-pixel-wide column.
|
||||
const BYTES_IN_ROW: usize = BYTES_IN_COL * 256; // 256 = screen width in NES pixels. This represents a thin, one-SDL-pixel-tall row.
|
||||
|
||||
type RGBColor = (u8, u8, u8);
|
||||
|
||||
pub struct Screen {
|
||||
pub canvas: Canvas<Window>,
|
||||
pub fn init_window(context: &Sdl) -> Result<(Canvas<Window>, TextureCreator<WindowContext>), String> {
|
||||
let video_subsystem = context.video()?;
|
||||
let window = video_subsystem.window("NESTUR", (256 * SCALE_FACTOR) as u32, (240 * SCALE_FACTOR) as u32)
|
||||
.position_centered()
|
||||
.opengl()
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||
let texture_creator = canvas.texture_creator();
|
||||
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||
canvas.clear();
|
||||
canvas.present();
|
||||
Ok((canvas, texture_creator))
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
pub fn new(context: &Sdl) -> Result<Self, String> {
|
||||
let video_subsystem = context.video()?;
|
||||
let window = video_subsystem.window("NESTUR", (256 * SCALE_FACTOR) as u32, (240 * SCALE_FACTOR) as u32)
|
||||
.position_centered()
|
||||
.opengl()
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||
canvas.clear();
|
||||
canvas.present();
|
||||
Ok(Screen{canvas})
|
||||
pub fn draw_pixel(buffer: &mut Vec<u8>, x: usize, y: usize, color: RGBColor) {
|
||||
let (r, g, b) = color;
|
||||
let nes_y_offset = y * BYTES_IN_ROW * SCALE_FACTOR; // find offset for thick, SCALE_FACTOR-pixel tall row
|
||||
for sdl_row_num in 0..SCALE_FACTOR { // looping over one-pixel tall rows up to SCALE_FACTOR
|
||||
let row_offset = nes_y_offset + (sdl_row_num * BYTES_IN_ROW); // row_offset is the offset within buffer of the thin row we're on
|
||||
let nes_x_offset = x * BYTES_IN_COL; // find horizontal offset within row (in byte terms) of NES x-coordinate
|
||||
for sdl_col_num in 0..SCALE_FACTOR { // for pixels up to SCALE_FACTOR, moving horizontally
|
||||
let col_offset = nes_x_offset + (sdl_col_num * 3); // skip 3 bytes at a time, R/G/B for each pixel
|
||||
let offset = row_offset + col_offset;
|
||||
buffer[offset + 0] = r;
|
||||
buffer[offset + 1] = g;
|
||||
buffer[offset + 2] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_pixel(&mut self, x: usize, y: usize, color: RGBColor) -> Result<(), String> {
|
||||
let (r, g, b) = color;
|
||||
self.canvas.set_draw_color(Color::RGB(r, g, b));
|
||||
self.canvas.fill_rect(Rect::new((x * SCALE_FACTOR) as i32, (y * SCALE_FACTOR) as i32,
|
||||
SCALE_FACTOR as u32, SCALE_FACTOR as u32))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub fn draw_to_window(texture: &mut Texture, canvas: &mut Canvas<Window>, buffer: &Vec<u8>) -> Result<(), String> {
|
||||
texture.update(None, buffer, 256*3*SCALE_FACTOR)
|
||||
.map_err(|e| e.to_string())?;
|
||||
canvas.copy(&texture, None, None)?;
|
||||
canvas.present();
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue