feat: implement double buffering
This commit is contained in:
parent
628d4cfb68
commit
b97b66927f
|
@ -169,7 +169,7 @@ pub fn main() anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
// FIXME: Is it OK just to copy the Emulator's Frame Buffer to SDL?
|
||||||
const buf_ptr = cpu.bus.ppu.framebuf.ptr;
|
const buf_ptr = cpu.bus.ppu.framebuf.get(.Renderer).ptr;
|
||||||
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch);
|
_ = SDL.SDL_UpdateTexture(texture, null, buf_ptr, framebuf_pitch);
|
||||||
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
_ = SDL.SDL_RenderCopy(renderer, texture, null, null);
|
||||||
SDL.SDL_RenderPresent(renderer);
|
SDL.SDL_RenderPresent(renderer);
|
||||||
|
|
64
src/ppu.zig
64
src/ppu.zig
|
@ -35,7 +35,7 @@ pub const Ppu = struct {
|
||||||
palette: Palette,
|
palette: Palette,
|
||||||
oam: Oam,
|
oam: Oam,
|
||||||
sched: *Scheduler,
|
sched: *Scheduler,
|
||||||
framebuf: []u8,
|
framebuf: FrameBuffer,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
scanline_sprites: [128]?Sprite,
|
scanline_sprites: [128]?Sprite,
|
||||||
|
@ -45,15 +45,15 @@ pub const Ppu = struct {
|
||||||
// Queue first Hblank
|
// Queue first Hblank
|
||||||
sched.push(.Draw, sched.tick + (240 * 4));
|
sched.push(.Draw, sched.tick + (240 * 4));
|
||||||
|
|
||||||
const framebuf = try alloc.alloc(u8, framebuf_pitch * height);
|
const framebufs = try alloc.alloc(u8, (framebuf_pitch * height) * 2);
|
||||||
std.mem.set(u8, framebuf, 0);
|
std.mem.set(u8, framebufs, 0);
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.vram = try Vram.init(alloc),
|
.vram = try Vram.init(alloc),
|
||||||
.palette = try Palette.init(alloc),
|
.palette = try Palette.init(alloc),
|
||||||
.oam = try Oam.init(alloc),
|
.oam = try Oam.init(alloc),
|
||||||
.sched = sched,
|
.sched = sched,
|
||||||
.framebuf = framebuf,
|
.framebuf = FrameBuffer.init(framebufs),
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
|
@ -68,7 +68,7 @@ pub const Ppu = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: Self) void {
|
||||||
self.alloc.free(self.framebuf);
|
self.framebuf.deinit(self.alloc);
|
||||||
self.vram.deinit();
|
self.vram.deinit();
|
||||||
self.palette.deinit();
|
self.palette.deinit();
|
||||||
self.oam.deinit();
|
self.oam.deinit();
|
||||||
|
@ -295,7 +295,7 @@ pub const Ppu = struct {
|
||||||
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
for (self.scanline_buf) |maybe_px, i| {
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
@ -319,7 +319,7 @@ pub const Ppu = struct {
|
||||||
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
for (self.scanline_buf) |maybe_px, i| {
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
@ -341,7 +341,7 @@ pub const Ppu = struct {
|
||||||
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
// If there are any nulls present in self.scanline_buf it means that no background drew a pixel there, so draw backdrop
|
||||||
for (self.scanline_buf) |maybe_px, i| {
|
for (self.scanline_buf) |maybe_px, i| {
|
||||||
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
const bgr555 = if (maybe_px) |px| px else self.palette.getBackdrop();
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
// Reset Current Scanline Pixel Buffer and list of fetched sprites
|
||||||
|
@ -356,7 +356,7 @@ pub const Ppu = struct {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < width) : (i += 1) {
|
while (i < width) : (i += 1) {
|
||||||
const bgr555 = self.vram.read(u16, vram_base + i * @sizeOf(u16));
|
const bgr555 = self.vram.read(u16, vram_base + i * @sizeOf(u16));
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x4 => {
|
0x4 => {
|
||||||
|
@ -367,7 +367,7 @@ pub const Ppu = struct {
|
||||||
// Render Current Scanline
|
// Render Current Scanline
|
||||||
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
||||||
const bgr555 = self.palette.read(u16, @as(u16, byte) * @sizeOf(u16));
|
const bgr555 = self.palette.read(u16, @as(u16, byte) * @sizeOf(u16));
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x5 => {
|
0x5 => {
|
||||||
|
@ -384,7 +384,7 @@ pub const Ppu = struct {
|
||||||
const bgr555 =
|
const bgr555 =
|
||||||
if (scanline < m5_height and i < m5_width) self.vram.read(u16, vram_base + i * @sizeOf(u16)) else self.palette.getBackdrop();
|
if (scanline < m5_height and i < m5_width) self.vram.read(u16, vram_base + i * @sizeOf(u16)) else self.palette.getBackdrop();
|
||||||
|
|
||||||
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
std.mem.copy(u8, self.framebuf.get(.Emulator)[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
else => std.debug.panic("[PPU] TODO: Implement BG Mode {}", .{bg_mode}),
|
||||||
|
@ -456,6 +456,8 @@ pub const Ppu = struct {
|
||||||
} else {
|
} else {
|
||||||
// Transitioning to a Vblank
|
// Transitioning to a Vblank
|
||||||
if (scanline == 160) {
|
if (scanline == 160) {
|
||||||
|
self.framebuf.swap(); // Swap FrameBuffers
|
||||||
|
|
||||||
self.dispstat.vblank.set();
|
self.dispstat.vblank.set();
|
||||||
|
|
||||||
if (self.dispstat.vblank_irq.read()) {
|
if (self.dispstat.vblank_irq.read()) {
|
||||||
|
@ -884,3 +886,43 @@ fn toRgba8888Talarubi(bgr555: u16) u32 {
|
||||||
|
|
||||||
return @floatToInt(u32, out_r) << 24 | @floatToInt(u32, out_g) << 16 | @floatToInt(u32, out_b) << 8 | 0xFF;
|
return @floatToInt(u32, out_r) << 24 | @floatToInt(u32, out_g) << 16 | @floatToInt(u32, out_b) << 8 | 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double Buffering Implementation
|
||||||
|
const FrameBuffer = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
buf: [2][]u8,
|
||||||
|
original: []u8,
|
||||||
|
current: u1,
|
||||||
|
|
||||||
|
// TODO: Rename
|
||||||
|
const Device = enum {
|
||||||
|
Emulator,
|
||||||
|
Renderer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(bufs: []u8) Self {
|
||||||
|
std.debug.assert(bufs.len == framebuf_pitch * height * 2);
|
||||||
|
|
||||||
|
const front = bufs[0 .. framebuf_pitch * height];
|
||||||
|
const back = bufs[framebuf_pitch * height ..];
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.buf = [2][]u8{ front, back },
|
||||||
|
.original = bufs,
|
||||||
|
.current = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: Self, alloc: Allocator) void {
|
||||||
|
alloc.free(self.original);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap(self: *Self) void {
|
||||||
|
self.current = ~self.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *Self, comptime dev: Device) []u8 {
|
||||||
|
return self.buf[if (dev == .Emulator) self.current else ~self.current];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue