feat: switch from BGR555 to RGBA8888
This commit is contained in:
parent
39ab363afa
commit
3d61c0dba4
|
@ -72,7 +72,7 @@ pub fn main() anyerror!void {
|
||||||
var cpu = Arm7tdmi.init(&scheduler, &bus);
|
var cpu = Arm7tdmi.init(&scheduler, &bus);
|
||||||
cpu.fastBoot();
|
cpu.fastBoot();
|
||||||
|
|
||||||
var log_file: ?File = undefined;
|
var log_file: ?File = null;
|
||||||
if (enable_logging) {
|
if (enable_logging) {
|
||||||
const file_name: []const u8 = if (is_binary) "zba.bin" else "zba.log";
|
const file_name: []const u8 = if (is_binary) "zba.bin" else "zba.log";
|
||||||
const file = try std.fs.cwd().createFile(file_name, .{});
|
const file = try std.fs.cwd().createFile(file_name, .{});
|
||||||
|
@ -112,7 +112,7 @@ pub fn main() anyerror!void {
|
||||||
var renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse sdlPanic();
|
var renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse sdlPanic();
|
||||||
defer SDL.SDL_DestroyRenderer(renderer);
|
defer SDL.SDL_DestroyRenderer(renderer);
|
||||||
|
|
||||||
const texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_BGR555, SDL.SDL_TEXTUREACCESS_STREAMING, 240, 160) orelse sdlPanic();
|
const texture = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_RGBA8888, SDL.SDL_TEXTUREACCESS_STREAMING, 240, 160) orelse sdlPanic();
|
||||||
defer SDL.SDL_DestroyTexture(texture);
|
defer SDL.SDL_DestroyTexture(texture);
|
||||||
|
|
||||||
// Init FPS Timer
|
// Init FPS Timer
|
||||||
|
|
79
src/ppu.zig
79
src/ppu.zig
|
@ -11,10 +11,14 @@ const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.PPU);
|
const log = std.log.scoped(.PPU);
|
||||||
const pollBlankingDma = @import("bus/dma.zig").pollBlankingDma;
|
const pollBlankingDma = @import("bus/dma.zig").pollBlankingDma;
|
||||||
|
const intToBytes = @import("util.zig").intToBytes;
|
||||||
|
|
||||||
|
/// This is used to generate byuu / Talurabi's Color Correction algorithm
|
||||||
|
// const COLOUR_LUT = genColourLut();
|
||||||
|
|
||||||
pub const width = 240;
|
pub const width = 240;
|
||||||
pub const height = 160;
|
pub const height = 160;
|
||||||
pub const framebuf_pitch = width * @sizeOf(u16);
|
pub const framebuf_pitch = width * @sizeOf(u32);
|
||||||
|
|
||||||
pub const Ppu = struct {
|
pub const Ppu = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -274,8 +278,7 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
switch (bg_mode) {
|
switch (bg_mode) {
|
||||||
0x0 => {
|
0x0 => {
|
||||||
const start = framebuf_pitch * @as(usize, scanline);
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
|
|
||||||
if (obj_enable) self.fetchSprites();
|
if (obj_enable) self.fetchSprites();
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -292,9 +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, j| {
|
for (self.scanline_buf) |maybe_px, j| {
|
||||||
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 + j * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
self.framebuf[(start + j * 2 + 1)] = @truncate(u8, bgr555 >> 8);
|
|
||||||
self.framebuf[(start + j * 2 + 0)] = @truncate(u8, bgr555);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Scanline Buffer
|
// Reset Scanline Buffer
|
||||||
|
@ -303,23 +304,26 @@ pub const Ppu = struct {
|
||||||
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
std.mem.set(?Sprite, &self.scanline_sprites, null);
|
||||||
},
|
},
|
||||||
0x3 => {
|
0x3 => {
|
||||||
const start = framebuf_pitch * @as(usize, scanline);
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
std.mem.copy(u8, self.framebuf[start..][0..framebuf_pitch], self.vram.buf[start..][0..framebuf_pitch]);
|
const vram_base = width * @sizeOf(u16) * @as(usize, scanline);
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < width) : (i += 1) {
|
||||||
|
const bgr555 = self.vram.get16(vram_base + i * @sizeOf(u16));
|
||||||
|
std.mem.copy(u8, self.framebuf[fb_base + i * @sizeOf(u32) ..][0..4], &intToBytes(u32, toRgba8888(bgr555)));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0x4 => {
|
0x4 => {
|
||||||
const select = self.dispcnt.frame_select.read();
|
const select = self.dispcnt.frame_select.read();
|
||||||
const vram_start = width * @as(usize, scanline);
|
const fb_base = framebuf_pitch * @as(usize, scanline);
|
||||||
const buf_start = vram_start * @sizeOf(u16);
|
const vram_base = width * @as(usize, scanline) + if (select) 0xA000 else @as(usize, 0);
|
||||||
|
|
||||||
const start = vram_start + if (select) 0xA000 else @as(usize, 0);
|
|
||||||
const end = start + width; // Each Entry is only a byte long
|
|
||||||
|
|
||||||
// Render Current Scanline
|
// Render Current Scanline
|
||||||
for (self.vram.buf[start..end]) |byte, i| {
|
for (self.vram.buf[vram_base .. vram_base + width]) |byte, i| {
|
||||||
const id = @as(u16, byte) * 2;
|
const pal_id = @as(u16, byte) * @sizeOf(u16);
|
||||||
const j = i * @sizeOf(u16);
|
const bgr555 = self.palette.get16(pal_id);
|
||||||
|
|
||||||
std.mem.copy(u8, self.framebuf[(buf_start + j)..][0..2], self.palette.buf[id..][0..2]);
|
std.mem.copy(u8, self.framebuf[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}),
|
||||||
|
@ -684,3 +688,44 @@ fn spriteDimensions(shape: u2, size: u2) [2]u8 {
|
||||||
else => std.debug.panic("{} is an invalid sprite shape", .{shape}),
|
else => std.debug.panic("{} is an invalid sprite shape", .{shape}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toRgba8888(bgr555: u16) u32 {
|
||||||
|
const b = @as(u32, bgr555 >> 10 & 0x1F);
|
||||||
|
const g = @as(u32, bgr555 >> 5 & 0x1F);
|
||||||
|
const r = @as(u32, bgr555 & 0x1F);
|
||||||
|
|
||||||
|
return (r << 3 | r >> 2) << 24 | (g << 3 | g >> 2) << 16 | (b << 3 | b >> 2) << 8 | 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn genColourLut() [0x8000]u32 {
|
||||||
|
return comptime {
|
||||||
|
@setEvalBranchQuota(std.math.maxInt(u32));
|
||||||
|
|
||||||
|
var lut: [0x8000]u32 = undefined;
|
||||||
|
for (lut) |*px, i| px.* = toRgba8888Talarubi(i);
|
||||||
|
return lut;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: The implementation is incorrect and using it in the LUT crashes the compiler (OOM)
|
||||||
|
/// Implementation courtesy of byuu and Talarubi at https://near.sh/articles/video/color-emulation
|
||||||
|
fn toRgba8888Talarubi(bgr555: u16) u32 {
|
||||||
|
@setRuntimeSafety(false);
|
||||||
|
|
||||||
|
const lcd_gamma: f64 = 4;
|
||||||
|
const out_gamma: f64 = 2.2;
|
||||||
|
|
||||||
|
const b = @as(u32, bgr555 >> 10 & 0x1F);
|
||||||
|
const g = @as(u32, bgr555 >> 5 & 0x1F);
|
||||||
|
const r = @as(u32, bgr555 & 0x1F);
|
||||||
|
|
||||||
|
const lb = std.math.pow(f64, @intToFloat(f64, b << 3 | b >> 2) / 31, lcd_gamma);
|
||||||
|
const lg = std.math.pow(f64, @intToFloat(f64, g << 3 | g >> 2) / 31, lcd_gamma);
|
||||||
|
const lr = std.math.pow(f64, @intToFloat(f64, r << 3 | r >> 2) / 31, lcd_gamma);
|
||||||
|
|
||||||
|
const out_b = std.math.pow(f64, (220 * lb + 10 * lg + 50 * lr) / 255, 1 / out_gamma);
|
||||||
|
const out_g = std.math.pow(f64, (30 * lb + 230 * lg + 10 * lr) / 255, 1 / out_gamma);
|
||||||
|
const out_r = std.math.pow(f64, (0 * lb + 50 * lg + 255 * lr) / 255, 1 / out_gamma);
|
||||||
|
|
||||||
|
return @floatToInt(u32, out_r) << 24 | @floatToInt(u32, out_g) << 16 | @floatToInt(u32, out_b) << 8 | 0xFF;
|
||||||
|
}
|
||||||
|
|
11
src/util.zig
11
src/util.zig
|
@ -42,3 +42,14 @@ pub const FpsAverage = struct {
|
||||||
self.sample_count = 1;
|
self.sample_count = 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn intToBytes(comptime T: type, value: anytype) [@sizeOf(T)]u8 {
|
||||||
|
comptime std.debug.assert(@typeInfo(T) == .Int);
|
||||||
|
|
||||||
|
var result: [@sizeOf(T)]u8 = undefined;
|
||||||
|
|
||||||
|
var i: Log2Int(T) = 0;
|
||||||
|
while (i < result.len) : (i += 1) result[i] = @truncate(u8, value >> i * @bitSizeOf(u8));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue