Glyph Bank Domain Naming Contract
This commit is contained in:
parent
59101baf00
commit
54f65f58e6
@ -1,5 +1,5 @@
|
||||
#![allow(clippy::collapsible_if)]
|
||||
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
||||
use crate::memory_banks::{GlyphBankPoolInstaller, SoundBankPoolInstaller};
|
||||
use prometeu_hal::AssetBridge;
|
||||
use prometeu_hal::asset::{
|
||||
AssetCodec, AssetEntry, AssetId, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId,
|
||||
@ -7,19 +7,19 @@ use prometeu_hal::asset::{
|
||||
};
|
||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::glyph_bank::{GlyphBank, TileSize};
|
||||
use prometeu_hal::sample::Sample;
|
||||
use prometeu_hal::sound_bank::SoundBank;
|
||||
use prometeu_hal::tile_bank::{TileBank, TileSize};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::thread;
|
||||
use std::time::Instant;
|
||||
|
||||
const TILE_BANK_PALETTE_COUNT_V1: usize = 64;
|
||||
const TILE_BANK_COLORS_PER_PALETTE: usize = 16;
|
||||
const TILE_BANK_PALETTE_BYTES_V1: usize =
|
||||
TILE_BANK_PALETTE_COUNT_V1 * TILE_BANK_COLORS_PER_PALETTE * std::mem::size_of::<u16>();
|
||||
const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64;
|
||||
const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16;
|
||||
const GLYPH_BANK_PALETTE_BYTES_V1: usize =
|
||||
GLYPH_BANK_PALETTE_COUNT_V1 * GLYPH_BANK_COLORS_PER_PALETTE * std::mem::size_of::<u16>();
|
||||
|
||||
/// Resident metadata for a decoded/materialized asset inside a BankPolicy.
|
||||
#[derive(Debug)]
|
||||
@ -115,15 +115,15 @@ pub struct AssetManager {
|
||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||
|
||||
/// Narrow hardware interfaces
|
||||
gfx_installer: Arc<dyn TileBankPoolInstaller>,
|
||||
gfx_installer: Arc<dyn GlyphBankPoolInstaller>,
|
||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||
|
||||
/// Track what is installed in each hardware slot (for stats/info).
|
||||
gfx_slots: Arc<RwLock<[Option<AssetId>; 16]>>,
|
||||
sound_slots: Arc<RwLock<[Option<AssetId>; 16]>>,
|
||||
|
||||
/// Residency policy for GFX tile banks.
|
||||
gfx_policy: BankPolicy<TileBank>,
|
||||
/// Residency policy for GFX glyph banks.
|
||||
gfx_policy: BankPolicy<GlyphBank>,
|
||||
/// Residency policy for sound banks.
|
||||
sound_policy: BankPolicy<SoundBank>,
|
||||
|
||||
@ -179,10 +179,10 @@ impl AssetBridge for AssetManager {
|
||||
}
|
||||
|
||||
impl AssetManager {
|
||||
fn decode_tile_bank_layout(
|
||||
fn decode_glyph_bank_layout(
|
||||
entry: &AssetEntry,
|
||||
) -> Result<(TileSize, usize, usize, usize), String> {
|
||||
let meta = entry.metadata_as_tiles()?;
|
||||
let meta = entry.metadata_as_glyphs()?;
|
||||
|
||||
let tile_size = match meta.tile_size {
|
||||
8 => TileSize::Size8,
|
||||
@ -191,32 +191,32 @@ impl AssetManager {
|
||||
_ => return Err(format!("Invalid tile_size: {}", meta.tile_size)),
|
||||
};
|
||||
|
||||
if meta.palette_count as usize != TILE_BANK_PALETTE_COUNT_V1 {
|
||||
if meta.palette_count as usize != GLYPH_BANK_PALETTE_COUNT_V1 {
|
||||
return Err(format!("Invalid palette_count: {}", meta.palette_count));
|
||||
}
|
||||
|
||||
let width = meta.width as usize;
|
||||
let height = meta.height as usize;
|
||||
|
||||
let logical_pixels = width.checked_mul(height).ok_or("TileBank dimensions overflow")?;
|
||||
let logical_pixels = width.checked_mul(height).ok_or("GlyphBank dimensions overflow")?;
|
||||
let serialized_pixel_bytes = logical_pixels.div_ceil(2);
|
||||
let serialized_size = serialized_pixel_bytes
|
||||
.checked_add(TILE_BANK_PALETTE_BYTES_V1)
|
||||
.ok_or("TileBank serialized size overflow")?;
|
||||
.checked_add(GLYPH_BANK_PALETTE_BYTES_V1)
|
||||
.ok_or("GlyphBank serialized size overflow")?;
|
||||
let decoded_size = logical_pixels
|
||||
.checked_add(TILE_BANK_PALETTE_BYTES_V1)
|
||||
.ok_or("TileBank decoded size overflow")?;
|
||||
.checked_add(GLYPH_BANK_PALETTE_BYTES_V1)
|
||||
.ok_or("GlyphBank decoded size overflow")?;
|
||||
|
||||
if entry.size != serialized_size as u64 {
|
||||
return Err(format!(
|
||||
"Invalid TILEBANK serialized size: expected {}, got {}",
|
||||
"Invalid GLYPHBANK serialized size: expected {}, got {}",
|
||||
serialized_size, entry.size
|
||||
));
|
||||
}
|
||||
|
||||
if entry.decoded_size != decoded_size as u64 {
|
||||
return Err(format!(
|
||||
"Invalid TILEBANK decoded_size: expected {}, got {}",
|
||||
"Invalid GLYPHBANK decoded_size: expected {}, got {}",
|
||||
decoded_size, entry.decoded_size
|
||||
));
|
||||
}
|
||||
@ -224,7 +224,7 @@ impl AssetManager {
|
||||
Ok((tile_size, width, height, serialized_pixel_bytes))
|
||||
}
|
||||
|
||||
fn unpack_tile_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec<u8> {
|
||||
fn unpack_glyph_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec<u8> {
|
||||
let mut pixel_indices = Vec::with_capacity(logical_pixels);
|
||||
for &packed in packed_pixels {
|
||||
if pixel_indices.len() < logical_pixels {
|
||||
@ -239,7 +239,7 @@ impl AssetManager {
|
||||
|
||||
fn op_mode_for(entry: &AssetEntry) -> Result<AssetOpMode, String> {
|
||||
match (entry.bank_type, entry.codec) {
|
||||
(BankType::TILES, AssetCodec::None) => Ok(AssetOpMode::StageInMemory),
|
||||
(BankType::GLYPH, AssetCodec::None) => Ok(AssetOpMode::StageInMemory),
|
||||
(BankType::SOUNDS, AssetCodec::None) => Ok(AssetOpMode::DirectFromSlice),
|
||||
}
|
||||
}
|
||||
@ -247,7 +247,7 @@ impl AssetManager {
|
||||
pub fn new(
|
||||
assets: Vec<AssetEntry>,
|
||||
assets_data: AssetsPayloadSource,
|
||||
gfx_installer: Arc<dyn TileBankPoolInstaller>,
|
||||
gfx_installer: Arc<dyn GlyphBankPoolInstaller>,
|
||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||
) -> Self {
|
||||
let mut asset_map = HashMap::new();
|
||||
@ -296,9 +296,9 @@ impl AssetManager {
|
||||
if let Some(entry) = entry_opt {
|
||||
let slot_index = item.slot;
|
||||
match entry.bank_type {
|
||||
BankType::TILES => {
|
||||
BankType::GLYPH => {
|
||||
if let Ok(bank) =
|
||||
Self::perform_load_tile_bank(&entry, self.assets_data.clone())
|
||||
Self::perform_load_glyph_bank(&entry, self.assets_data.clone())
|
||||
{
|
||||
let bank_arc = Arc::new(bank);
|
||||
self.gfx_policy.put_resident(
|
||||
@ -306,7 +306,7 @@ impl AssetManager {
|
||||
Arc::clone(&bank_arc),
|
||||
entry.decoded_size as usize,
|
||||
);
|
||||
self.gfx_installer.install_tile_bank(slot_index, bank_arc);
|
||||
self.gfx_installer.install_glyph_bank(slot_index, bank_arc);
|
||||
let mut slots = self.gfx_slots.write().unwrap();
|
||||
if slot_index < slots.len() {
|
||||
slots[slot_index] = Some(entry.asset_id);
|
||||
@ -367,7 +367,7 @@ impl AssetManager {
|
||||
assets.get(&asset_id).ok_or(AssetLoadError::AssetNotFound)?.clone()
|
||||
};
|
||||
let slot = match entry.bank_type {
|
||||
BankType::TILES => SlotRef::gfx(slot_index),
|
||||
BankType::GLYPH => SlotRef::gfx(slot_index),
|
||||
BankType::SOUNDS => SlotRef::audio(slot_index),
|
||||
};
|
||||
|
||||
@ -377,7 +377,7 @@ impl AssetManager {
|
||||
|
||||
// Check if already resident (Dedup)
|
||||
let already_resident = match entry.bank_type {
|
||||
BankType::TILES => {
|
||||
BankType::GLYPH => {
|
||||
if let Some(bank) = self.gfx_policy.get_resident(asset_id) {
|
||||
self.gfx_policy.stage(handle_id, bank);
|
||||
true
|
||||
@ -435,8 +435,8 @@ impl AssetManager {
|
||||
}
|
||||
|
||||
match entry_clone.bank_type {
|
||||
BankType::TILES => {
|
||||
let result = Self::perform_load_tile_bank(&entry_clone, assets_data);
|
||||
BankType::GLYPH => {
|
||||
let result = Self::perform_load_glyph_bank(&entry_clone, assets_data);
|
||||
if let Ok(tilebank) = result {
|
||||
let bank_arc = Arc::new(tilebank);
|
||||
let resident_arc = {
|
||||
@ -507,10 +507,10 @@ impl AssetManager {
|
||||
Ok(handle_id)
|
||||
}
|
||||
|
||||
fn perform_load_tile_bank(
|
||||
fn perform_load_glyph_bank(
|
||||
entry: &AssetEntry,
|
||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||
) -> Result<TileBank, String> {
|
||||
) -> Result<GlyphBank, String> {
|
||||
let op_mode = Self::op_mode_for(entry)?;
|
||||
let slice = {
|
||||
let assets_data = assets_data.read().unwrap();
|
||||
@ -523,30 +523,33 @@ impl AssetManager {
|
||||
AssetOpMode::StageInMemory => {
|
||||
let buffer =
|
||||
slice.read_all().map_err(|_| "Asset payload read failed".to_string())?;
|
||||
Self::decode_tile_bank_from_buffer(entry, &buffer)
|
||||
Self::decode_glyph_bank_from_buffer(entry, &buffer)
|
||||
}
|
||||
AssetOpMode::DirectFromSlice => {
|
||||
let mut reader =
|
||||
slice.open_reader().map_err(|_| "Asset payload read failed".to_string())?;
|
||||
Self::decode_tile_bank_from_reader(entry, &mut reader)
|
||||
Self::decode_glyph_bank_from_reader(entry, &mut reader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_tile_bank_from_buffer(entry: &AssetEntry, buffer: &[u8]) -> Result<TileBank, String> {
|
||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_tile_bank_layout(entry)?;
|
||||
if buffer.len() < packed_pixel_bytes + TILE_BANK_PALETTE_BYTES_V1 {
|
||||
return Err("Buffer too small for TILEBANK".to_string());
|
||||
fn decode_glyph_bank_from_buffer(
|
||||
entry: &AssetEntry,
|
||||
buffer: &[u8],
|
||||
) -> Result<GlyphBank, String> {
|
||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?;
|
||||
if buffer.len() < packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1 {
|
||||
return Err("Buffer too small for GLYPHBANK".to_string());
|
||||
}
|
||||
|
||||
let logical_pixels = width * height;
|
||||
let packed_pixels = &buffer[0..packed_pixel_bytes];
|
||||
let pixel_indices = Self::unpack_tile_bank_pixels(packed_pixels, logical_pixels);
|
||||
let pixel_indices = Self::unpack_glyph_bank_pixels(packed_pixels, logical_pixels);
|
||||
let palette_data =
|
||||
&buffer[packed_pixel_bytes..packed_pixel_bytes + TILE_BANK_PALETTE_BYTES_V1];
|
||||
&buffer[packed_pixel_bytes..packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1];
|
||||
|
||||
let mut palettes =
|
||||
[[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1];
|
||||
[[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1];
|
||||
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||
for (c, slot) in pal.iter_mut().enumerate() {
|
||||
let offset = (p * 16 + c) * 2;
|
||||
@ -556,29 +559,29 @@ impl AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TileBank { tile_size, width, height, pixel_indices, palettes })
|
||||
Ok(GlyphBank { tile_size, width, height, pixel_indices, palettes })
|
||||
}
|
||||
|
||||
fn decode_tile_bank_from_reader(
|
||||
fn decode_glyph_bank_from_reader(
|
||||
entry: &AssetEntry,
|
||||
reader: &mut impl Read,
|
||||
) -> Result<TileBank, String> {
|
||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_tile_bank_layout(entry)?;
|
||||
) -> Result<GlyphBank, String> {
|
||||
let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?;
|
||||
let logical_pixels = width * height;
|
||||
let mut packed_pixels = vec![0_u8; packed_pixel_bytes];
|
||||
reader
|
||||
.read_exact(&mut packed_pixels)
|
||||
.map_err(|_| "Buffer too small for TILEBANK".to_string())?;
|
||||
.map_err(|_| "Buffer too small for GLYPHBANK".to_string())?;
|
||||
|
||||
let pixel_indices = Self::unpack_tile_bank_pixels(&packed_pixels, logical_pixels);
|
||||
let pixel_indices = Self::unpack_glyph_bank_pixels(&packed_pixels, logical_pixels);
|
||||
|
||||
let mut palette_data = [0_u8; TILE_BANK_PALETTE_BYTES_V1];
|
||||
let mut palette_data = [0_u8; GLYPH_BANK_PALETTE_BYTES_V1];
|
||||
reader
|
||||
.read_exact(&mut palette_data)
|
||||
.map_err(|_| "Buffer too small for TILEBANK".to_string())?;
|
||||
.map_err(|_| "Buffer too small for GLYPHBANK".to_string())?;
|
||||
|
||||
let mut palettes =
|
||||
[[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1];
|
||||
[[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1];
|
||||
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||
for (c, slot) in pal.iter_mut().enumerate() {
|
||||
let offset = (p * 16 + c) * 2;
|
||||
@ -588,7 +591,7 @@ impl AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TileBank { tile_size, width, height, pixel_indices, palettes })
|
||||
Ok(GlyphBank { tile_size, width, height, pixel_indices, palettes })
|
||||
}
|
||||
|
||||
fn perform_load_sound_bank(
|
||||
@ -695,9 +698,9 @@ impl AssetManager {
|
||||
if let Some(h) = handles.get_mut(&handle_id) {
|
||||
if h.status == LoadStatus::READY {
|
||||
match h.slot.asset_type {
|
||||
BankType::TILES => {
|
||||
BankType::GLYPH => {
|
||||
if let Some(bank) = self.gfx_policy.take_staging(handle_id) {
|
||||
self.gfx_installer.install_tile_bank(h.slot.index, bank);
|
||||
self.gfx_installer.install_glyph_bank(h.slot.index, bank);
|
||||
let mut slots = self.gfx_slots.write().unwrap();
|
||||
if h.slot.index < slots.len() {
|
||||
slots[h.slot.index] = Some(h._asset_id);
|
||||
@ -723,7 +726,7 @@ impl AssetManager {
|
||||
|
||||
pub fn bank_info(&self, kind: BankType) -> BankStats {
|
||||
match kind {
|
||||
BankType::TILES => {
|
||||
BankType::GLYPH => {
|
||||
let mut used_bytes = 0;
|
||||
{
|
||||
let resident = self.gfx_policy.resident.read().unwrap();
|
||||
@ -814,7 +817,7 @@ impl AssetManager {
|
||||
|
||||
pub fn slot_info(&self, slot: SlotRef) -> SlotStats {
|
||||
match slot.asset_type {
|
||||
BankType::TILES => {
|
||||
BankType::GLYPH => {
|
||||
let slots = self.gfx_slots.read().unwrap();
|
||||
let asset_id = slots.get(slot.index).and_then(|s| *s);
|
||||
|
||||
@ -872,77 +875,78 @@ impl AssetManager {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::memory_banks::{MemoryBanks, SoundBankPoolAccess, TileBankPoolAccess};
|
||||
use crate::memory_banks::{GlyphBankPoolAccess, MemoryBanks, SoundBankPoolAccess};
|
||||
use prometeu_hal::asset::AssetCodec;
|
||||
|
||||
fn expected_tile_payload_size(width: usize, height: usize) -> usize {
|
||||
(width * height).div_ceil(2) + TILE_BANK_PALETTE_BYTES_V1
|
||||
fn expected_glyph_payload_size(width: usize, height: usize) -> usize {
|
||||
(width * height).div_ceil(2) + GLYPH_BANK_PALETTE_BYTES_V1
|
||||
}
|
||||
|
||||
fn expected_tile_decoded_size(width: usize, height: usize) -> usize {
|
||||
width * height + TILE_BANK_PALETTE_BYTES_V1
|
||||
fn expected_glyph_decoded_size(width: usize, height: usize) -> usize {
|
||||
width * height + GLYPH_BANK_PALETTE_BYTES_V1
|
||||
}
|
||||
|
||||
fn test_tile_asset_data() -> Vec<u8> {
|
||||
fn test_glyph_asset_data() -> Vec<u8> {
|
||||
let mut data = vec![0x11u8; 128];
|
||||
data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_BYTES_V1]);
|
||||
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]);
|
||||
data
|
||||
}
|
||||
|
||||
fn test_tile_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry {
|
||||
fn test_glyph_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry {
|
||||
AssetEntry {
|
||||
asset_id: 0,
|
||||
asset_name: asset_name.to_string(),
|
||||
bank_type: BankType::TILES,
|
||||
bank_type: BankType::GLYPH,
|
||||
offset: 0,
|
||||
size: expected_tile_payload_size(width, height) as u64,
|
||||
decoded_size: expected_tile_decoded_size(width, height) as u64,
|
||||
size: expected_glyph_payload_size(width, height) as u64,
|
||||
decoded_size: expected_glyph_decoded_size(width, height) as u64,
|
||||
codec: AssetCodec::None,
|
||||
metadata: serde_json::json!({
|
||||
"tile_size": 16,
|
||||
"width": width,
|
||||
"height": height,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_tile_bank_unpacks_packed_pixels_and_reads_palette_colors() {
|
||||
let entry = test_tile_asset_entry("tiles", 2, 2);
|
||||
fn test_decode_glyph_bank_unpacks_packed_pixels_and_reads_palette_colors() {
|
||||
let entry = test_glyph_asset_entry("tiles", 2, 2);
|
||||
let mut data = vec![0x10, 0x23];
|
||||
data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_BYTES_V1]);
|
||||
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]);
|
||||
data[2] = 0x34;
|
||||
data[3] = 0x12;
|
||||
|
||||
let bank = AssetManager::decode_tile_bank_from_buffer(&entry, &data).expect("tile decode");
|
||||
let bank =
|
||||
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
||||
|
||||
assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]);
|
||||
assert_eq!(bank.palettes[0][0], Color(0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_tile_bank_rejects_short_packed_buffer() {
|
||||
let entry = test_tile_asset_entry("tiles", 16, 16);
|
||||
let data = vec![0u8; expected_tile_payload_size(16, 16) - 1];
|
||||
fn test_decode_glyph_bank_rejects_short_packed_buffer() {
|
||||
let entry = test_glyph_asset_entry("tiles", 16, 16);
|
||||
let data = vec![0u8; expected_glyph_payload_size(16, 16) - 1];
|
||||
|
||||
let err = match AssetManager::decode_tile_bank_from_buffer(&entry, &data) {
|
||||
Ok(_) => panic!("tile decode should reject short buffer"),
|
||||
let err = match AssetManager::decode_glyph_bank_from_buffer(&entry, &data) {
|
||||
Ok(_) => panic!("glyph decode should reject short buffer"),
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
assert_eq!(err, "Buffer too small for TILEBANK");
|
||||
assert_eq!(err, "Buffer too small for GLYPHBANK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_tile_bank_requires_palette_count_64() {
|
||||
let mut entry = test_tile_asset_entry("tiles", 16, 16);
|
||||
fn test_decode_glyph_bank_requires_palette_count_64() {
|
||||
let mut entry = test_glyph_asset_entry("tiles", 16, 16);
|
||||
entry.metadata["palette_count"] = serde_json::json!(32);
|
||||
|
||||
let err = match AssetManager::decode_tile_bank_from_buffer(&entry, &test_tile_asset_data())
|
||||
{
|
||||
Ok(_) => panic!("tile decode should reject invalid palette_count"),
|
||||
let err =
|
||||
match AssetManager::decode_glyph_bank_from_buffer(&entry, &test_glyph_asset_data()) {
|
||||
Ok(_) => panic!("glyph decode should reject invalid palette_count"),
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
@ -950,15 +954,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_op_mode_for_tiles_none_stages_in_memory() {
|
||||
let entry = test_tile_asset_entry("tiles", 16, 16);
|
||||
fn test_op_mode_for_glyphs_none_stages_in_memory() {
|
||||
let entry = test_glyph_asset_entry("tiles", 16, 16);
|
||||
|
||||
assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_op_mode_for_tiles_none_uses_typed_codec() {
|
||||
let entry = test_tile_asset_entry("tiles", 16, 16);
|
||||
fn test_op_mode_for_glyphs_none_uses_typed_codec() {
|
||||
let entry = test_glyph_asset_entry("tiles", 16, 16);
|
||||
assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory));
|
||||
}
|
||||
|
||||
@ -984,11 +988,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_asset_loading_flow() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
|
||||
let data = test_tile_asset_data();
|
||||
let asset_entry = test_tile_asset_entry("test_tiles", 16, 16);
|
||||
let data = test_glyph_asset_data();
|
||||
let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16);
|
||||
|
||||
let am = AssetManager::new(
|
||||
vec![asset_entry],
|
||||
@ -1016,17 +1020,17 @@ mod tests {
|
||||
am.apply_commits();
|
||||
|
||||
assert_eq!(am.status(handle), LoadStatus::COMMITTED);
|
||||
assert!(banks.tile_bank_slot(0).is_some());
|
||||
assert!(banks.glyph_bank_slot(0).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_asset_dedup() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
|
||||
let data = test_tile_asset_data();
|
||||
let asset_entry = test_tile_asset_entry("test_tiles", 16, 16);
|
||||
let data = test_glyph_asset_data();
|
||||
let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16);
|
||||
|
||||
let am = AssetManager::new(
|
||||
vec![asset_entry],
|
||||
@ -1053,7 +1057,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_sound_asset_loading() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
|
||||
// 100 samples of 16-bit PCM (zeros)
|
||||
@ -1098,7 +1102,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_preload_on_init() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
|
||||
let data = vec![0u8; 200];
|
||||
@ -1139,7 +1143,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_load_returns_asset_not_found() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let am =
|
||||
AssetManager::new(vec![], AssetsPayloadSource::empty(), gfx_installer, sound_installer);
|
||||
@ -1152,11 +1156,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_load_returns_slot_index_invalid() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let data = test_tile_asset_data();
|
||||
let data = test_glyph_asset_data();
|
||||
let am = AssetManager::new(
|
||||
vec![test_tile_asset_entry("test_tiles", 16, 16)],
|
||||
vec![test_glyph_asset_entry("test_tiles", 16, 16)],
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
@ -1170,7 +1174,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_status_returns_unknown_handle() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let am =
|
||||
AssetManager::new(vec![], AssetsPayloadSource::empty(), gfx_installer, sound_installer);
|
||||
@ -1181,11 +1185,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_commit_and_cancel_return_explicit_statuses() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn GlyphBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let data = test_tile_asset_data();
|
||||
let data = test_glyph_asset_data();
|
||||
let am = AssetManager::new(
|
||||
vec![test_tile_asset_entry("test_tiles", 16, 16)],
|
||||
vec![test_glyph_asset_entry("test_tiles", 16, 16)],
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::memory_banks::TileBankPoolAccess;
|
||||
use crate::memory_banks::GlyphBankPoolAccess;
|
||||
use prometeu_hal::GfxBridge;
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::glyph::Glyph;
|
||||
use prometeu_hal::glyph_bank::{GlyphBank, TileSize};
|
||||
use prometeu_hal::sprite::Sprite;
|
||||
use prometeu_hal::tile::Tile;
|
||||
use prometeu_hal::tile_bank::{TileBank, TileSize};
|
||||
use prometeu_hal::tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -57,12 +57,12 @@ pub struct Gfx {
|
||||
/// Back buffer: the "Work RAM" where new frames are composed.
|
||||
back: Vec<u16>,
|
||||
|
||||
/// 4 scrollable background layers. Each can have its own scroll (X, Y) and TileBank.
|
||||
/// 4 scrollable background layers. Each can have its own scroll (X, Y) and GlyphBank.
|
||||
pub layers: [ScrollableTileLayer; 4],
|
||||
/// 1 fixed layer for User Interface (HUD). It doesn't scroll.
|
||||
pub hud: HudTileLayer,
|
||||
/// Shared access to graphical memory banks (tiles and palettes).
|
||||
pub tile_banks: Arc<dyn TileBankPoolAccess>,
|
||||
pub glyph_banks: Arc<dyn GlyphBankPoolAccess>,
|
||||
/// Hardware sprite list (512 slots). Equivalent to OAM (Object Attribute Memory).
|
||||
pub sprites: [Sprite; 512],
|
||||
|
||||
@ -274,7 +274,7 @@ impl GfxBridge for Gfx {
|
||||
|
||||
impl Gfx {
|
||||
/// Initializes the graphics system with a specific resolution and shared memory banks.
|
||||
pub fn new(w: usize, h: usize, tile_banks: Arc<dyn TileBankPoolAccess>) -> Self {
|
||||
pub fn new(w: usize, h: usize, glyph_banks: Arc<dyn GlyphBankPoolAccess>) -> Self {
|
||||
const EMPTY_GLYPH: Glyph = Glyph { glyph_id: 0, palette_id: 0 };
|
||||
|
||||
const EMPTY_SPRITE: Sprite = Sprite {
|
||||
@ -303,7 +303,7 @@ impl Gfx {
|
||||
back: vec![0; len],
|
||||
layers,
|
||||
hud: HudTileLayer::new(64, 32),
|
||||
tile_banks,
|
||||
glyph_banks,
|
||||
sprites: [EMPTY_SPRITE; 512],
|
||||
scene_fade_level: 31,
|
||||
scene_fade_color: Color::BLACK,
|
||||
@ -583,14 +583,14 @@ impl Gfx {
|
||||
self.h,
|
||||
&self.priority_buckets[0],
|
||||
&self.sprites,
|
||||
&*self.tile_banks,
|
||||
&*self.glyph_banks,
|
||||
);
|
||||
|
||||
// 2. Main layers and prioritized sprites.
|
||||
// Order: Layer 0 -> Sprites 1 -> Layer 1 -> Sprites 2 ...
|
||||
for i in 0..self.layers.len() {
|
||||
let bank_id = self.layers[i].bank_id as usize;
|
||||
if let Some(bank) = self.tile_banks.tile_bank_slot(bank_id) {
|
||||
if let Some(bank) = self.glyph_banks.glyph_bank_slot(bank_id) {
|
||||
Self::draw_tile_map(
|
||||
&mut self.back,
|
||||
self.w,
|
||||
@ -609,7 +609,7 @@ impl Gfx {
|
||||
self.h,
|
||||
&self.priority_buckets[i + 1],
|
||||
&self.sprites,
|
||||
&*self.tile_banks,
|
||||
&*self.glyph_banks,
|
||||
);
|
||||
}
|
||||
|
||||
@ -617,7 +617,7 @@ impl Gfx {
|
||||
Self::apply_fade_to_buffer(&mut self.back, self.scene_fade_level, self.scene_fade_color);
|
||||
|
||||
// 5. HUD: The fixed interface layer, always drawn on top of the world.
|
||||
Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.tile_banks);
|
||||
Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.glyph_banks);
|
||||
|
||||
// 6. HUD Fade: Independent fade effect for the UI.
|
||||
Self::apply_fade_to_buffer(&mut self.back, self.hud_fade_level, self.hud_fade_color);
|
||||
@ -633,7 +633,7 @@ impl Gfx {
|
||||
let scroll_x = self.layers[layer_idx].scroll_x;
|
||||
let scroll_y = self.layers[layer_idx].scroll_y;
|
||||
|
||||
let bank = match self.tile_banks.tile_bank_slot(bank_id) {
|
||||
let bank = match self.glyph_banks.glyph_bank_slot(bank_id) {
|
||||
Some(b) => b,
|
||||
_ => return,
|
||||
};
|
||||
@ -651,7 +651,7 @@ impl Gfx {
|
||||
|
||||
/// Renders the HUD (fixed position, no scroll).
|
||||
pub fn render_hud(&mut self) {
|
||||
Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.tile_banks);
|
||||
Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.glyph_banks);
|
||||
}
|
||||
|
||||
fn render_hud_with_pool(
|
||||
@ -659,10 +659,10 @@ impl Gfx {
|
||||
w: usize,
|
||||
h: usize,
|
||||
hud: &HudTileLayer,
|
||||
tile_banks: &dyn TileBankPoolAccess,
|
||||
glyph_banks: &dyn GlyphBankPoolAccess,
|
||||
) {
|
||||
let bank_id = hud.bank_id as usize;
|
||||
let bank = match tile_banks.tile_bank_slot(bank_id) {
|
||||
let bank = match glyph_banks.glyph_bank_slot(bank_id) {
|
||||
Some(b) => b,
|
||||
_ => return,
|
||||
};
|
||||
@ -676,7 +676,7 @@ impl Gfx {
|
||||
screen_w: usize,
|
||||
screen_h: usize,
|
||||
map: &TileMap,
|
||||
bank: &TileBank,
|
||||
bank: &GlyphBank,
|
||||
scroll_x: i32,
|
||||
scroll_y: i32,
|
||||
) {
|
||||
@ -739,7 +739,7 @@ impl Gfx {
|
||||
x: i32,
|
||||
y: i32,
|
||||
tile: Tile,
|
||||
bank: &TileBank,
|
||||
bank: &GlyphBank,
|
||||
) {
|
||||
let size = bank.tile_size as usize;
|
||||
|
||||
@ -781,12 +781,12 @@ impl Gfx {
|
||||
screen_h: usize,
|
||||
bucket: &[usize],
|
||||
sprites: &[Sprite],
|
||||
tile_banks: &dyn TileBankPoolAccess,
|
||||
glyph_banks: &dyn GlyphBankPoolAccess,
|
||||
) {
|
||||
for &idx in bucket {
|
||||
let s = &sprites[idx];
|
||||
let bank_id = s.bank_id as usize;
|
||||
if let Some(bank) = tile_banks.tile_bank_slot(bank_id) {
|
||||
if let Some(bank) = glyph_banks.glyph_bank_slot(bank_id) {
|
||||
Self::draw_sprite_pixel_by_pixel(back, screen_w, screen_h, s, &bank);
|
||||
}
|
||||
}
|
||||
@ -797,7 +797,7 @@ impl Gfx {
|
||||
screen_w: usize,
|
||||
screen_h: usize,
|
||||
sprite: &Sprite,
|
||||
bank: &TileBank,
|
||||
bank: &GlyphBank,
|
||||
) {
|
||||
// ... (same bounds/clipping calculation we already had) ...
|
||||
let size = bank.tile_size as usize;
|
||||
|
||||
@ -2,8 +2,8 @@ use crate::asset::AssetManager;
|
||||
use crate::audio::Audio;
|
||||
use crate::gfx::Gfx;
|
||||
use crate::memory_banks::{
|
||||
MemoryBanks, SoundBankPoolAccess, SoundBankPoolInstaller, TileBankPoolAccess,
|
||||
TileBankPoolInstaller,
|
||||
GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SoundBankPoolAccess,
|
||||
SoundBankPoolInstaller,
|
||||
};
|
||||
use crate::pad::Pad;
|
||||
use crate::touch::Touch;
|
||||
@ -32,7 +32,7 @@ pub struct Hardware {
|
||||
pub pad: Pad,
|
||||
/// The absolute pointer input device (Mouse/Touchscreen).
|
||||
pub touch: Touch,
|
||||
/// The Asset Management system (DMA). Handles loading data into VRAM (TileBanks) and ARAM (SoundBanks).
|
||||
/// The Asset Management system (DMA). Handles loading data into VRAM (GlyphBanks) and ARAM (SoundBanks).
|
||||
pub assets: AssetManager,
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ impl Hardware {
|
||||
gfx: Gfx::new(
|
||||
Self::W,
|
||||
Self::H,
|
||||
Arc::clone(&memory_banks) as Arc<dyn TileBankPoolAccess>,
|
||||
Arc::clone(&memory_banks) as Arc<dyn GlyphBankPoolAccess>,
|
||||
),
|
||||
audio: Audio::new(Arc::clone(&memory_banks) as Arc<dyn SoundBankPoolAccess>),
|
||||
pad: Pad::default(),
|
||||
@ -100,7 +100,7 @@ impl Hardware {
|
||||
assets: AssetManager::new(
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
Arc::clone(&memory_banks) as Arc<dyn TileBankPoolInstaller>,
|
||||
Arc::clone(&memory_banks) as Arc<dyn GlyphBankPoolInstaller>,
|
||||
Arc::clone(&memory_banks) as Arc<dyn SoundBankPoolInstaller>,
|
||||
),
|
||||
}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
use prometeu_hal::glyph_bank::GlyphBank;
|
||||
use prometeu_hal::sound_bank::SoundBank;
|
||||
use prometeu_hal::tile_bank::TileBank;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Non-generic interface for peripherals to access graphical tile banks.
|
||||
pub trait TileBankPoolAccess: Send + Sync {
|
||||
/// Returns a reference to the resident TileBank in the specified slot, if any.
|
||||
fn tile_bank_slot(&self, slot: usize) -> Option<Arc<TileBank>>;
|
||||
/// Non-generic interface for peripherals to access graphical glyph banks.
|
||||
pub trait GlyphBankPoolAccess: Send + Sync {
|
||||
/// Returns a reference to the resident GlyphBank in the specified slot, if any.
|
||||
fn glyph_bank_slot(&self, slot: usize) -> Option<Arc<GlyphBank>>;
|
||||
/// Returns the total number of slots available in this bank.
|
||||
fn tile_bank_slot_count(&self) -> usize;
|
||||
fn glyph_bank_slot_count(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Non-generic interface for the AssetManager to install graphical tile banks.
|
||||
pub trait TileBankPoolInstaller: Send + Sync {
|
||||
/// Atomically swaps the resident TileBank in the specified slot.
|
||||
fn install_tile_bank(&self, slot: usize, bank: Arc<TileBank>);
|
||||
/// Non-generic interface for the AssetManager to install graphical glyph banks.
|
||||
pub trait GlyphBankPoolInstaller: Send + Sync {
|
||||
/// Atomically swaps the resident GlyphBank in the specified slot.
|
||||
fn install_glyph_bank(&self, slot: usize, bank: Arc<GlyphBank>);
|
||||
}
|
||||
|
||||
/// Non-generic interface for peripherals to access sound banks.
|
||||
@ -36,7 +36,7 @@ pub trait SoundBankPoolInstaller: Send + Sync {
|
||||
/// Peripherals consume this state via narrow, non-generic traits.
|
||||
/// AssetManager coordinates residency and installs assets into these slots.
|
||||
pub struct MemoryBanks {
|
||||
tile_bank_pool: Arc<RwLock<[Option<Arc<TileBank>>; 16]>>,
|
||||
glyph_bank_pool: Arc<RwLock<[Option<Arc<GlyphBank>>; 16]>>,
|
||||
sound_bank_pool: Arc<RwLock<[Option<Arc<SoundBank>>; 16]>>,
|
||||
}
|
||||
|
||||
@ -50,26 +50,26 @@ impl MemoryBanks {
|
||||
/// Creates a new set of memory banks with empty slots.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tile_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
|
||||
glyph_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
|
||||
sound_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TileBankPoolAccess for MemoryBanks {
|
||||
fn tile_bank_slot(&self, slot: usize) -> Option<Arc<TileBank>> {
|
||||
let pool = self.tile_bank_pool.read().unwrap();
|
||||
impl GlyphBankPoolAccess for MemoryBanks {
|
||||
fn glyph_bank_slot(&self, slot: usize) -> Option<Arc<GlyphBank>> {
|
||||
let pool = self.glyph_bank_pool.read().unwrap();
|
||||
pool.get(slot).and_then(|s| s.as_ref().map(Arc::clone))
|
||||
}
|
||||
|
||||
fn tile_bank_slot_count(&self) -> usize {
|
||||
fn glyph_bank_slot_count(&self) -> usize {
|
||||
16
|
||||
}
|
||||
}
|
||||
|
||||
impl TileBankPoolInstaller for MemoryBanks {
|
||||
fn install_tile_bank(&self, slot: usize, bank: Arc<TileBank>) {
|
||||
let mut pool = self.tile_bank_pool.write().unwrap();
|
||||
impl GlyphBankPoolInstaller for MemoryBanks {
|
||||
fn install_glyph_bank(&self, slot: usize, bank: Arc<GlyphBank>) {
|
||||
let mut pool = self.glyph_bank_pool.write().unwrap();
|
||||
if slot < 16 {
|
||||
pool[slot] = Some(bank);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ pub type AssetId = i32;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum BankType {
|
||||
TILES,
|
||||
GLYPH,
|
||||
SOUNDS,
|
||||
// TILEMAPS,
|
||||
// BLOBS,
|
||||
@ -31,7 +31,7 @@ pub struct AssetEntry {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct TilesMetadata {
|
||||
pub struct GlyphsMetadata {
|
||||
pub tile_size: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
@ -46,15 +46,15 @@ pub struct SoundsMetadata {
|
||||
}
|
||||
|
||||
impl AssetEntry {
|
||||
pub fn metadata_as_tiles(&self) -> Result<TilesMetadata, String> {
|
||||
if self.bank_type != BankType::TILES {
|
||||
pub fn metadata_as_glyphs(&self) -> Result<GlyphsMetadata, String> {
|
||||
if self.bank_type != BankType::GLYPH {
|
||||
return Err(format!(
|
||||
"Asset {} is not a TILES bank (type: {:?})",
|
||||
"Asset {} is not a GLYPH bank (type: {:?})",
|
||||
self.asset_id, self.bank_type
|
||||
));
|
||||
}
|
||||
serde_json::from_value(self.metadata.clone())
|
||||
.map_err(|e| format!("Invalid TILES metadata for asset {}: {}", self.asset_id, e))
|
||||
.map_err(|e| format!("Invalid GLYPH metadata for asset {}: {}", self.asset_id, e))
|
||||
}
|
||||
|
||||
pub fn metadata_as_sounds(&self) -> Result<SoundsMetadata, String> {
|
||||
@ -130,7 +130,7 @@ pub struct SlotRef {
|
||||
|
||||
impl SlotRef {
|
||||
pub fn gfx(index: usize) -> Self {
|
||||
Self { asset_type: BankType::TILES, index }
|
||||
Self { asset_type: BankType::GLYPH, index }
|
||||
}
|
||||
|
||||
pub fn audio(index: usize) -> Self {
|
||||
|
||||
@ -200,7 +200,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::asset::{AssetCodec, AssetEntry, BankType, PreloadEntry};
|
||||
use crate::cartridge::{ASSETS_PA_MAGIC, ASSETS_PA_SCHEMA_VERSION, AssetsPackPrelude};
|
||||
use crate::tile_bank::TILE_BANK_PALETTE_COUNT_V1;
|
||||
use crate::glyph_bank::GLYPH_BANK_PALETTE_COUNT_V1;
|
||||
use serde_json::json;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
@ -363,17 +363,17 @@ mod tests {
|
||||
AssetEntry {
|
||||
asset_id: 7,
|
||||
asset_name: "tiles".to_string(),
|
||||
bank_type: BankType::TILES,
|
||||
bank_type: BankType::GLYPH,
|
||||
offset,
|
||||
size,
|
||||
decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2),
|
||||
decoded_size: 16 * 16 + (GLYPH_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2),
|
||||
codec: AssetCodec::None,
|
||||
metadata: json!({
|
||||
"tile_size": 16,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -446,17 +446,17 @@ mod tests {
|
||||
AssetEntry {
|
||||
asset_id: 8,
|
||||
asset_name: "other_tiles".to_string(),
|
||||
bank_type: BankType::TILES,
|
||||
bank_type: BankType::GLYPH,
|
||||
offset: 4,
|
||||
size: 4,
|
||||
decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2),
|
||||
decoded_size: 16 * 16 + (GLYPH_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2),
|
||||
codec: AssetCodec::None,
|
||||
metadata: json!({
|
||||
"tile_size": 16,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}),
|
||||
},
|
||||
];
|
||||
@ -507,7 +507,7 @@ mod tests {
|
||||
"asset_table": [{
|
||||
"asset_id": 7,
|
||||
"asset_name": "tiles",
|
||||
"bank_type": "TILES",
|
||||
"bank_type": "GLYPH",
|
||||
"offset": 0,
|
||||
"size": 4,
|
||||
"decoded_size": 768,
|
||||
@ -516,8 +516,8 @@ mod tests {
|
||||
"tile_size": 16,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}
|
||||
}],
|
||||
"preload": []
|
||||
@ -549,7 +549,7 @@ mod tests {
|
||||
"asset_table": [{
|
||||
"asset_id": 7,
|
||||
"asset_name": "tiles",
|
||||
"bank_type": "TILES",
|
||||
"bank_type": "GLYPH",
|
||||
"offset": 0,
|
||||
"size": 4,
|
||||
"decoded_size": 768,
|
||||
@ -558,8 +558,8 @@ mod tests {
|
||||
"tile_size": 16,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}
|
||||
}],
|
||||
"preload": []
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::color::Color;
|
||||
|
||||
pub const TILE_BANK_PALETTE_COUNT_V1: usize = 64;
|
||||
pub const TILE_BANK_COLORS_PER_PALETTE: usize = 16;
|
||||
pub const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64;
|
||||
pub const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16;
|
||||
|
||||
/// Standard sizes for square tiles.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@ -16,12 +16,12 @@ pub enum TileSize {
|
||||
|
||||
/// A container for graphical assets.
|
||||
///
|
||||
/// A TileBank stores the decoded runtime representation of a tile-bank asset.
|
||||
/// A GlyphBank stores the decoded runtime representation of a glyph-bank asset.
|
||||
///
|
||||
/// Serialized `assets.pa` payloads keep pixel indices packed as `4bpp` nibbles.
|
||||
/// After decode, the runtime expands them to one `u8` palette index per pixel
|
||||
/// while preserving the same `0..15` logical range.
|
||||
pub struct TileBank {
|
||||
pub struct GlyphBank {
|
||||
/// Dimension of each individual tile in the bank.
|
||||
pub tile_size: TileSize,
|
||||
/// Width of the full bank sheet in pixels.
|
||||
@ -34,18 +34,18 @@ pub struct TileBank {
|
||||
/// Index 0 is always reserved for transparency.
|
||||
pub pixel_indices: Vec<u8>,
|
||||
/// Runtime-facing v1 palette table: 64 palettes of 16 RGB565 colors each.
|
||||
pub palettes: [[Color; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1],
|
||||
pub palettes: [[Color; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
|
||||
}
|
||||
|
||||
impl TileBank {
|
||||
/// Creates an empty tile bank with the specified dimensions.
|
||||
impl GlyphBank {
|
||||
/// Creates an empty glyph bank with the specified dimensions.
|
||||
pub fn new(tile_size: TileSize, width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
tile_size,
|
||||
width,
|
||||
height,
|
||||
pixel_indices: vec![0; width * height], // Index 0 = Transparent
|
||||
palettes: [[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1],
|
||||
palettes: [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ pub mod color;
|
||||
pub mod debugger_protocol;
|
||||
pub mod gfx_bridge;
|
||||
pub mod glyph;
|
||||
pub mod glyph_bank;
|
||||
pub mod hardware_bridge;
|
||||
pub mod host_context;
|
||||
pub mod host_return;
|
||||
@ -22,7 +23,6 @@ pub mod sprite;
|
||||
pub mod syscalls;
|
||||
pub mod telemetry;
|
||||
pub mod tile;
|
||||
pub mod tile_bank;
|
||||
pub mod tile_layer;
|
||||
pub mod touch_bridge;
|
||||
pub mod vm_fault;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::glyph_bank::TileSize;
|
||||
use crate::glyph_bank::TileSize::Size8;
|
||||
use crate::tile::Tile;
|
||||
use crate::tile_bank::TileSize;
|
||||
use crate::tile_bank::TileSize::Size8;
|
||||
|
||||
pub struct TileMap {
|
||||
pub width: usize,
|
||||
|
||||
@ -481,7 +481,7 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
}
|
||||
Syscall::BankInfo => {
|
||||
let asset_type = match expect_int(args, 0)? as u32 {
|
||||
0 => BankType::TILES,
|
||||
0 => BankType::GLYPH,
|
||||
1 => BankType::SOUNDS,
|
||||
_ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())),
|
||||
};
|
||||
@ -492,7 +492,7 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
}
|
||||
Syscall::BankSlotInfo => {
|
||||
let asset_type = match expect_int(args, 0)? as u32 {
|
||||
0 => BankType::TILES,
|
||||
0 => BankType::GLYPH,
|
||||
1 => BankType::SOUNDS,
|
||||
_ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())),
|
||||
};
|
||||
|
||||
@ -12,8 +12,8 @@ use prometeu_hal::asset::{
|
||||
AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus,
|
||||
};
|
||||
use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge};
|
||||
use prometeu_hal::glyph_bank::GLYPH_BANK_PALETTE_COUNT_V1;
|
||||
use prometeu_hal::syscalls::caps;
|
||||
use prometeu_hal::tile_bank::TILE_BANK_PALETTE_COUNT_V1;
|
||||
use prometeu_vm::VmInitError;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -94,37 +94,37 @@ fn serialized_single_function_module_with_consts(
|
||||
.serialize()
|
||||
}
|
||||
|
||||
fn test_tile_payload_size(width: usize, height: usize) -> usize {
|
||||
(width * height).div_ceil(2) + (TILE_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::<u16>())
|
||||
fn test_glyph_payload_size(width: usize, height: usize) -> usize {
|
||||
(width * height).div_ceil(2) + (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::<u16>())
|
||||
}
|
||||
|
||||
fn test_tile_decoded_size(width: usize, height: usize) -> usize {
|
||||
width * height + (TILE_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::<u16>())
|
||||
fn test_glyph_decoded_size(width: usize, height: usize) -> usize {
|
||||
width * height + (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::<u16>())
|
||||
}
|
||||
|
||||
fn test_tile_asset_entry(asset_name: &str, data_len: usize) -> AssetEntry {
|
||||
fn test_glyph_asset_entry(asset_name: &str, data_len: usize) -> AssetEntry {
|
||||
AssetEntry {
|
||||
asset_id: 7,
|
||||
asset_name: asset_name.to_string(),
|
||||
bank_type: BankType::TILES,
|
||||
bank_type: BankType::GLYPH,
|
||||
offset: 0,
|
||||
size: data_len as u64,
|
||||
decoded_size: test_tile_decoded_size(16, 16) as u64,
|
||||
decoded_size: test_glyph_decoded_size(16, 16) as u64,
|
||||
codec: AssetCodec::None,
|
||||
metadata: serde_json::json!({
|
||||
"tile_size": 16,
|
||||
"width": 16,
|
||||
"height": 16,
|
||||
"palette_count": TILE_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": TILE_BANK_PALETTE_COUNT_V1
|
||||
"palette_count": GLYPH_BANK_PALETTE_COUNT_V1,
|
||||
"palette_authored": GLYPH_BANK_PALETTE_COUNT_V1
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn test_tile_asset_data() -> Vec<u8> {
|
||||
fn test_glyph_asset_data() -> Vec<u8> {
|
||||
let mut data =
|
||||
vec![0x11u8; test_tile_payload_size(16, 16) - (TILE_BANK_PALETTE_COUNT_V1 * 16 * 2)];
|
||||
data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_COUNT_V1 * 16 * 2]);
|
||||
vec![0x11u8; test_glyph_payload_size(16, 16) - (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * 2)];
|
||||
data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_COUNT_V1 * 16 * 2]);
|
||||
data
|
||||
}
|
||||
|
||||
@ -439,10 +439,10 @@ fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() {
|
||||
}],
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
let asset_data = test_tile_asset_data();
|
||||
let asset_data = test_glyph_asset_data();
|
||||
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![test_glyph_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![prometeu_hal::asset::PreloadEntry { asset_id: 7, slot: 0 }],
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
@ -637,9 +637,9 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
let mut vm = VirtualMachine::default();
|
||||
let mut hardware = Hardware::new();
|
||||
let signals = InputSignals::default();
|
||||
let asset_data = test_tile_asset_data();
|
||||
let asset_data = test_glyph_asset_data();
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![test_glyph_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
@ -721,9 +721,9 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
let asset_data = test_tile_asset_data();
|
||||
let asset_data = test_glyph_asset_data();
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![test_glyph_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
@ -782,9 +782,9 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::ASSET);
|
||||
|
||||
let asset_data = test_tile_asset_data();
|
||||
let asset_data = test_glyph_asset_data();
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![test_glyph_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
|
||||
@ -166,7 +166,7 @@ impl VirtualMachineRuntime {
|
||||
|
||||
self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64;
|
||||
|
||||
let gfx_stats = hw.assets().bank_info(BankType::TILES);
|
||||
let gfx_stats = hw.assets().bank_info(BankType::GLYPH);
|
||||
self.telemetry_current.gfx_used_bytes = gfx_stats.used_bytes;
|
||||
self.telemetry_current.gfx_inflight_bytes = gfx_stats.inflight_bytes;
|
||||
self.telemetry_current.gfx_slots_occupied = gfx_stats.slots_occupied as u32;
|
||||
|
||||
@ -67,7 +67,7 @@ pub fn generate() -> Result<()> {
|
||||
const_pool: vec![
|
||||
ConstantPoolEntry::String("stress".into()),
|
||||
ConstantPoolEntry::String("frame".into()),
|
||||
ConstantPoolEntry::String("missing_tile_bank".into()),
|
||||
ConstantPoolEntry::String("missing_glyph_bank".into()),
|
||||
],
|
||||
functions,
|
||||
code: rom,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{"type":"meta","next_id":{"DSC":22,"AGD":20,"DEC":6,"PLN":5,"LSN":25,"CLSN":1}}
|
||||
{"type":"meta","next_id":{"DSC":23,"AGD":21,"DEC":7,"PLN":6,"LSN":25,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||
{"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}
|
||||
{"type":"discussion","id":"DSC-0022","status":"open","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[{"id":"AGD-0020","file":"AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0006","file":"DEC-0006-glyph-bank-domain-naming-contract.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_agenda":"AGD-0020"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-glyph-bank-domain-naming-execution.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_decisions":["DEC-0006"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||
{"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
|
||||
@ -123,7 +123,7 @@ The current asset model works better if you do not treat every bank as equally g
|
||||
|
||||
Some banks are specialized:
|
||||
|
||||
- `TILES`
|
||||
- `GLYPH`
|
||||
- `SOUNDS`
|
||||
|
||||
For these, the bank contract already carries most of the important format rules. That means:
|
||||
@ -163,7 +163,7 @@ It does not say:
|
||||
|
||||
One of the most important asset-management lessons in the current runtime is that `codec = NONE` does not mean "bitwise identical to in-memory layout".
|
||||
|
||||
The tile-bank path is the concrete example:
|
||||
The glyph-bank path is the concrete example:
|
||||
|
||||
- there is no additional generic codec layer for the asset;
|
||||
- the serialized payload stores indexed pixels as packed `4bpp`;
|
||||
@ -182,13 +182,13 @@ That is why `AssetEntry.size` and `AssetEntry.decoded_size` must be thought of a
|
||||
|
||||
If those two numbers are treated as interchangeable, telemetry, budgets, and validation all become misleading.
|
||||
|
||||
## Tile Banks Teach The Real Boundary
|
||||
## Glyph Banks Teach The Real Boundary
|
||||
|
||||
Tile banks are useful because they show the real separation of concerns:
|
||||
Glyph banks are useful because they show the real separation of concerns:
|
||||
|
||||
- `assets.pa` defines the serialized envelope and metadata needed to reconstruct the bank;
|
||||
- the runtime validates that metadata against the expected v1 contract;
|
||||
- the resident `TileBank` is a runtime object, not a direct view over the cold bytes.
|
||||
- the resident `GlyphBank` is a runtime object, not a direct view over the cold bytes.
|
||||
|
||||
This is the right PROMETEU mental model:
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ PROMETEU treats graphics as an explicit peripheral, not as a modern GPU.
|
||||
The right mental model is a retro 2D machine with:
|
||||
|
||||
- framebuffer;
|
||||
- tile banks;
|
||||
- glyph banks;
|
||||
- tile layers;
|
||||
- sprites ordered by draw order;
|
||||
- deterministic composition per frame.
|
||||
@ -67,7 +67,7 @@ That enables:
|
||||
- HUD themes;
|
||||
- day and night cycles.
|
||||
|
||||
In the current tile-bank v1 baseline, this palette model is intentionally bounded:
|
||||
In the current glyph-bank v1 baseline, this palette model is intentionally bounded:
|
||||
|
||||
- each bank carries `64` palettes;
|
||||
- each palette carries `16` colors;
|
||||
@ -75,7 +75,7 @@ In the current tile-bank v1 baseline, this palette model is intentionally bounde
|
||||
|
||||
That limit is not incidental bookkeeping. It is part of how art packaging, runtime validation, and rendering stay aligned.
|
||||
|
||||
## Tile Banks Are Decoded Runtime Objects
|
||||
## Glyph Banks Are Decoded Runtime Objects
|
||||
|
||||
The most useful intuition is to separate three layers:
|
||||
|
||||
@ -83,13 +83,13 @@ The most useful intuition is to separate three layers:
|
||||
- serialized cart payload;
|
||||
- resident runtime bank.
|
||||
|
||||
For tile banks in v1:
|
||||
For glyph banks in v1:
|
||||
|
||||
- authored pixels are logical indices `0..15`;
|
||||
- serialized payload stores those indices as packed `4bpp`;
|
||||
- runtime memory expands them to one `u8` index per pixel after decode.
|
||||
|
||||
So when reading the graphics model, do not imagine the renderer reading packed nibbles directly from cartridge storage. The renderer consumes a decoded `TileBank` object whose memory shape is optimized for runtime lookup, not for transport density.
|
||||
So when reading the graphics model, do not imagine the renderer reading packed nibbles directly from cartridge storage. The renderer consumes a decoded `GlyphBank` object whose memory shape is optimized for runtime lookup, not for transport density.
|
||||
|
||||
## Use Cases
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# LSN-0022: Tilemap Empty Cell Semantics and Glyph Convergence
|
||||
|
||||
## Context
|
||||
During the initial runtime design, `tile_id = 0` was used as a sentinel value for "empty" or "missing" tiles in tilemaps. However, as the asset banking and packer systems evolved, it became clear that `0` should be a valid index for any asset bank, including tile banks. This conflict was formally tracked in `AGD-0015`.
|
||||
During the initial runtime design, `tile_id = 0` was used as a sentinel value for "empty" or "missing" tiles in tilemaps. However, as the asset banking and packer systems evolved, it became clear that `0` should be a valid index for any asset bank, including glyph banks. This conflict was formally tracked in `AGD-0015`.
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ O uso de `serde_json::Value` diretamente nos loaders do runtime introduz riscos
|
||||
|
||||
Para mitigar isso, implementamos o padrão de **Typed Metadata Helpers**:
|
||||
|
||||
1. **Structs Dedicadas**: Criamos structs Rust (ex: `TilesMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco.
|
||||
2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_tiles()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada.
|
||||
1. **Structs Dedicadas**: Criamos structs Rust (ex: `GlyphsMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco.
|
||||
2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_glyphs()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada.
|
||||
3. **Fail-Fast**: A falha no parsing dos metadados deve ser tratada como erro de carregamento do asset, garantindo que o motor não opere com metadados corrompidos ou incompletos.
|
||||
|
||||
### Benefícios
|
||||
|
||||
@ -0,0 +1,221 @@
|
||||
---
|
||||
id: AGD-0020
|
||||
ticket: tile-bank-vs-glyph-bank-domain-naming
|
||||
title: Agenda - Tile Bank vs Glyph Bank Domain Naming
|
||||
status: accepted
|
||||
created: 2026-04-09
|
||||
resolved: 2026-04-10
|
||||
decision: DEC-0006
|
||||
tags: [gfx, runtime, naming, domain-model]
|
||||
---
|
||||
|
||||
# Agenda - Tile Bank vs Glyph Bank Domain Naming
|
||||
|
||||
## Contexto
|
||||
|
||||
Hoje o runtime usa `TileBank`, `tile_bank.rs`, `TileBankPool*` e termos derivados para nomear o banco grafico consumido pelo renderer e pelo pipeline de assets.
|
||||
|
||||
Ao mesmo tempo, existe a vontade de elevar `Tile` para um conceito mais amplo do dominio, nao restrito ao sheet grafico concreto. Nessa leitura:
|
||||
|
||||
- `Tile` passa a ser uma ideia mais geral da grade/elemento logico;
|
||||
- o que hoje e o artefato grafico concreto chamado `TileBank` deveria passar a se chamar `GlyphBank`;
|
||||
- a intencao inicial nao e mudar formato, memoria, payload ou algoritmo, e sim alinhar a linguagem do projeto.
|
||||
|
||||
Esse tema ja encosta em documentacao e lessons existentes, porque o vocabulario atual mistura:
|
||||
|
||||
- tile como unidade de composicao visual;
|
||||
- tile bank como sheet grafico concreto;
|
||||
- referencias esparsas a glyph em lições e agendas.
|
||||
|
||||
## Problema
|
||||
|
||||
Se o projeto mudar o centro semantico de `tile` e `glyph` sem uma decisao explicita, o repositorio tende a ficar com vocabulário hibrido:
|
||||
|
||||
- tipos antigos com nome legado;
|
||||
- docs novas com nome novo;
|
||||
- renderer e assets falando uma lingua;
|
||||
- lessons e discussoes falando outra.
|
||||
|
||||
O problema principal nao e tecnico-algoritmico. E semantico e operacional: qual vocabulario o projeto quer tornar canonico para o banco grafico concreto, e qual parte dessa mudanca e apenas rename mecanico versus mudanca real de modelo.
|
||||
|
||||
## Pontos Criticos
|
||||
|
||||
1. Escopo da mudanca.
|
||||
Precisamos separar rename de dominio de qualquer mudanca de formato ou comportamento.
|
||||
|
||||
2. Contrato externo.
|
||||
Precisamos fechar quais contratos externos tambem entram no rename. Ja existe direcao para migrar `BankType::TILES` para `BankType::GLYPH`.
|
||||
|
||||
3. Consistencia de linguagem.
|
||||
A mudanca so vale a pena se atingir codigo, tests, docs e lessons de forma coordenada.
|
||||
|
||||
4. Custo de churn.
|
||||
Mesmo sem mudar comportamento, o rename atravessa muitos modulos (`hal`, `drivers`, renderer, pools, mensagens de erro, tests e docs).
|
||||
|
||||
5. Fronteira conceitual.
|
||||
Precisamos definir o que `Tile` passa a significar depois do rename, para evitar trocar um overload semantico por outro.
|
||||
|
||||
## Opcoes
|
||||
|
||||
### Opcao A - Manter `TileBank` como esta
|
||||
|
||||
- **Abordagem:** preservar `TileBank` como nome canonico do banco grafico concreto e aceitar que `tile` continue carregando tanto o lado logico quanto o lado visual.
|
||||
- **Pro:** zero churn nominal imediato e nenhuma migracao de docs/codigo.
|
||||
- **Con:** o overload conceitual de `tile` permanece e pode continuar poluindo a linguagem de dominio.
|
||||
- **Tradeoff:** economiza trabalho agora ao custo de clareza futura.
|
||||
|
||||
### Opcao B - Renomear `TileBank` para `GlyphBank` como refactor semantico
|
||||
|
||||
- **Abordagem:** tratar a mudanca como rename consistente de tipos, modulos, docs, testes e mensagens, sem alterar formato de payload, layout em memoria ou renderer.
|
||||
- **Pro:** melhora a linguagem do projeto sem reabrir a arquitetura grafica.
|
||||
- **Con:** exige disciplina para manter a promessa de “rename only” e nao misturar isso com redesign.
|
||||
- **Tradeoff:** churn mecanico relativamente alto para ganhar clareza conceitual.
|
||||
|
||||
### Opcao C - Fazer rename parcial
|
||||
|
||||
- **Abordagem:** adotar `glyph` apenas em docs novas ou em algumas camadas, preservando nomes antigos em APIs e modulos centrais.
|
||||
- **Pro:** menor custo inicial.
|
||||
- **Con:** cria o pior estado intermediario: dois vocabulários concorrentes para o mesmo conceito.
|
||||
- **Tradeoff:** parece barato, mas deixa a linguagem do projeto menos confiavel.
|
||||
|
||||
## Sugestao / Recomendacao
|
||||
|
||||
Seguir inicialmente com a **Opcao B**, desde que a discussao confirme que a mudanca e de nomenclatura e nao de semantica operacional.
|
||||
|
||||
A recomendacao provisoria e:
|
||||
|
||||
- `GlyphBank` se torna o nome do artefato grafico concreto que hoje chamamos de `TileBank`;
|
||||
- `Tile` fica livre para representar um conceito mais geral do dominio;
|
||||
- `BankType::TILES` passa a `BankType::GLYPH`;
|
||||
- a migracao deve ser consistente em codigo, tests, docs e lessons;
|
||||
- `TileLayer` e derivados nao entram automaticamente no rename, porque ja pertencem a outra fronteira de dominio e precisam de triagem separada;
|
||||
- a discussao deve explicitar quais superfícies mudam juntas para impedir vocabulario hibrido sem reabrir a arquitetura grafica.
|
||||
|
||||
## Perguntas em Aberto
|
||||
|
||||
- Confirmar a superficie exata do rename para evitar misturar banco grafico concreto com conceitos de layer/mapa.
|
||||
|
||||
## Discussao
|
||||
|
||||
### Direcao fechada ate aqui
|
||||
|
||||
1. **Natureza da mudanca**
|
||||
A mudanca e `rename-only`. Nao ha intencao de alterar formato, algoritmo, layout em memoria ou comportamento.
|
||||
|
||||
2. **Contrato externo**
|
||||
`BankType::TILES` nao deve permanecer. O contrato deve migrar para `BankType::GLYPH`.
|
||||
|
||||
3. **Escopo documental**
|
||||
A migracao deve ser completa, inclusive em documentacao e lessons historicas.
|
||||
|
||||
4. **Colisao semantica**
|
||||
`glyph` nao colide com outro artefato canonico relevante nesta etapa.
|
||||
|
||||
### Fronteira importante
|
||||
|
||||
Nem todo `Tile*` deve migrar automaticamente para `Glyph*`.
|
||||
|
||||
Existe uma fronteira entre:
|
||||
|
||||
- nomes que descrevem o banco grafico concreto e seu circuito de carga/uso; e
|
||||
- nomes que ja descrevem dominio de layer, mapa, grade ou composicao.
|
||||
|
||||
Por isso, a proxima resposta que falta precisa ser dada por superficie concreta, e nao por regra global simplista.
|
||||
|
||||
### Lista para voto `sim` / `nao`
|
||||
|
||||
Responda `sim` ou `nao` para cada grupo abaixo.
|
||||
|
||||
1. **Banco concreto e modulo base**
|
||||
Inclui `TileBank`, `tile_bank.rs`, comentarios e docs especificos desse tipo.
|
||||
|
||||
2. **Enum e contrato de asset**
|
||||
Inclui `BankType::TILES -> BankType::GLYPH`, mensagens de erro, metadata helpers e referencias equivalentes no path de assets.
|
||||
|
||||
3. **Pools e interfaces de memoria**
|
||||
Inclui `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, campos internos como `tile_bank_pool`.
|
||||
|
||||
4. **Asset manager e loader path**
|
||||
Inclui funcoes como `decode_tile_bank_*`, `perform_load_tile_bank`, variaveis locais, testes e mensagens associadas ao load do banco grafico.
|
||||
|
||||
5. **Renderer e hardware references ao banco**
|
||||
Inclui usos em `gfx.rs` e `hardware.rs` que referenciam o banco concreto, por exemplo campos `tile_banks`, docs sobre VRAM `TileBanks`, parametros `bank: &TileBank`.
|
||||
|
||||
6. **Modulo e tipos de layer/mapa**
|
||||
Inclui `tile_layer.rs`, `TileMap`, `TileLayer`, `ScrollableTileLayer`, `HudTileLayer`.
|
||||
Este grupo e o mais sensivel porque pode nao ser rename de banco concreto, e sim outro dominio.
|
||||
|
||||
7. **TileSize**
|
||||
Inclui `TileSize` e referencias ao tamanho de tile como unidade geometrica.
|
||||
Este grupo pode continuar como `TileSize` mesmo se o banco virar `GlyphBank`.
|
||||
|
||||
8. **Strings, fixtures e test names**
|
||||
Inclui nomes de testes, helper names, snapshots, mensagens e dados de teste que ainda falam em tile bank.
|
||||
|
||||
9. **Lessons e docs historicas**
|
||||
Inclui lessons ja publicadas e material de discussao/mental model que hoje falam em `TileBank`.
|
||||
|
||||
### Votos registrados
|
||||
|
||||
1. **Banco concreto e modulo base**: `sim`
|
||||
`TileBank`, `tile_bank.rs`, modulo base e documentacao associada entram na migracao.
|
||||
|
||||
2. **Enum e contrato de asset**: `sim`
|
||||
`BankType::TILES` e o contrato textual equivalente entram na migracao para `GLYPH`.
|
||||
|
||||
3. **Pools e interfaces de memoria**: `sim`
|
||||
`TileBankPool*`, `tile_bank_slot` e derivados entram na migracao.
|
||||
|
||||
4. **Asset manager e loader path**: `sim`
|
||||
Helpers, decode path, mensagens e testes associados ao banco grafico concreto entram na migracao.
|
||||
|
||||
5. **Renderer e hardware references ao banco**: `sim`
|
||||
Referencias ao banco concreto em renderer e hardware entram na migracao.
|
||||
|
||||
6. **Modulo e tipos de layer/mapa**: `nao`
|
||||
`TileLayer`, `TileMap`, `ScrollableTileLayer` e `HudTileLayer` ficam fora desta rodada.
|
||||
|
||||
7. **TileSize**: `nao`
|
||||
`TileSize` permanece como conceito geometrico e nao entra automaticamente no rename.
|
||||
|
||||
8. **Strings, fixtures e test names**: `sim`
|
||||
Helpers, fixtures, mensagens e nomes de testes entram na migracao para evitar residuos do vocabulario antigo.
|
||||
|
||||
9. **Lessons e docs historicas**: `sim`
|
||||
A migracao documental e historica entra no escopo, seguindo a mesma fronteira desta agenda: renomear o banco grafico concreto sem arrastar automaticamente o dominio de layer/mapa.
|
||||
|
||||
## Resolucao Provisoria
|
||||
|
||||
Ha consenso provisoriamente estabelecido sobre os seguintes pontos:
|
||||
|
||||
- a mudanca e `rename-only`;
|
||||
- o vocabulario canonico do banco grafico concreto passa de `TileBank` para `GlyphBank`;
|
||||
- `BankType::TILES` deve migrar para `BankType::GLYPH`;
|
||||
- pools, contrato de asset, loader path, renderer, hardware references, fixtures e nomes de teste entram na migracao;
|
||||
- `TileLayer`/`TileMap` e derivados ficam fora desta rodada;
|
||||
- `TileSize` fica fora desta rodada;
|
||||
- docs e lessons historicas entram no escopo, respeitando essa mesma fronteira.
|
||||
|
||||
O principal ponto restante para encerrar a agenda e confirmar se essa resolucao ja e suficiente para virar decisao normativa, ou se ainda falta detalhar como a migracao documental deve tratar referencias historicas em texto corrido quando `tile` continuar correto para layer/mapa.
|
||||
|
||||
## Resolucao
|
||||
|
||||
A agenda fica encerrada com a seguinte orientacao:
|
||||
|
||||
- a mudanca e `rename-only`;
|
||||
- o banco grafico concreto passa a se chamar `GlyphBank`;
|
||||
- `BankType::TILES` passa a `BankType::GLYPH`;
|
||||
- pools, contrato de asset, loader path, renderer, hardware references, fixtures, nomes de teste, docs e lessons entram na migracao;
|
||||
- `TileLayer`, `TileMap` e derivados ficam fora;
|
||||
- `TileSize` fica fora;
|
||||
- docs e lessons antigas devem migrar referencias ao banco grafico concreto para `GlyphBank`, mantendo `tile` quando o assunto for layer, mapa, grade ou geometria.
|
||||
|
||||
## Criterio para Encerrar
|
||||
|
||||
Esta agenda pode ser encerrada quando houver consenso escrito sobre:
|
||||
|
||||
- se a mudanca e rename-only ou nao;
|
||||
- qual vocabulario passa a ser canonico;
|
||||
- quais contratos externos entram no rename;
|
||||
- quais grupos concretos entram ou nao na migracao;
|
||||
- como evitar que `TileLayer`/`TileMap` sejam arrastados automaticamente sem decisao propria.
|
||||
@ -0,0 +1,93 @@
|
||||
---
|
||||
id: DEC-0006
|
||||
ticket: tile-bank-vs-glyph-bank-domain-naming
|
||||
title: Glyph Bank Domain Naming Contract
|
||||
status: accepted
|
||||
created: 2026-04-10
|
||||
accepted: 2026-04-10
|
||||
agenda: AGD-0020
|
||||
plans: [PLN-0005]
|
||||
tags: [gfx, runtime, naming, domain-model]
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Accepted on 2026-04-10.
|
||||
|
||||
## Contexto
|
||||
|
||||
The runtime currently uses `TileBank` and related names for the concrete graphical bank consumed by the asset pipeline and renderer. At the same time, the project wants `Tile` to remain available as a broader domain concept for grid, layer, map, and geometric tile semantics.
|
||||
|
||||
This decision closes the naming contract by separating:
|
||||
|
||||
- the concrete graphical bank artifact;
|
||||
- the logical tile/layer/map domain;
|
||||
- the migration boundary for code, tests, docs, and historical lessons.
|
||||
|
||||
The change is explicitly rename-only. It does not alter format, runtime behavior, memory layout, payload structure, or renderer algorithms.
|
||||
|
||||
## Decisao
|
||||
|
||||
1. The concrete graphical bank currently named `TileBank` MUST be renamed to `GlyphBank`.
|
||||
2. This rename MUST be treated as nomenclature-only. It MUST NOT change payload format, runtime behavior, memory layout, codec behavior, metadata structure, or rendering semantics.
|
||||
3. `BankType::TILES` MUST be renamed to `BankType::GLYPH`.
|
||||
4. The asset contract and runtime code that refer to the concrete graphical bank MUST migrate to `Glyph*` naming consistently.
|
||||
5. The following groups MUST be included in the rename:
|
||||
- concrete bank type and module;
|
||||
- asset contract and asset-facing terminology;
|
||||
- memory pools and bank installation/access interfaces;
|
||||
- asset manager decode/load path;
|
||||
- renderer and hardware references to the concrete bank;
|
||||
- fixtures, test names, helper names, and user-facing/runtime-facing strings for the concrete bank;
|
||||
- documentation and historical lessons, subject to the editorial boundary below.
|
||||
6. The following groups MUST NOT be renamed as part of this decision:
|
||||
- `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and related layer/map structures;
|
||||
- `TileSize` and geometric tile-size concepts.
|
||||
7. Documentation and historical lessons MUST be updated with the following editorial rule:
|
||||
- references to the concrete graphical bank artifact MUST migrate to `GlyphBank` and equivalent `Glyph*` naming;
|
||||
- references to layer, map, grid, or geometric tile concepts MUST remain `tile` when that is the correct domain meaning.
|
||||
8. The project MUST NOT adopt a partial mixed vocabulary where the same concrete bank artifact is simultaneously described as both `TileBank` and `GlyphBank` in active code or maintained documentation.
|
||||
9. `glyph` is treated as a new canonical artifact name for this stage and does not conflict with an existing canonical artifact that would block adoption.
|
||||
|
||||
## Rationale
|
||||
|
||||
- The current name overloads `tile` across both logical map/layer concepts and the concrete graphical bank artifact.
|
||||
- Renaming only the concrete bank artifact improves domain clarity without reopening graphics architecture.
|
||||
- Excluding `TileLayer`, `TileMap`, and `TileSize` preserves established semantics where `tile` still means the logical or geometric concept.
|
||||
- A full migration across code, tests, docs, and lessons avoids the unstable mixed-language state that tends to follow partial renames.
|
||||
|
||||
## Invariantes / Contrato
|
||||
|
||||
- `GlyphBank` is the canonical name for the concrete graphical bank artifact.
|
||||
- `tile` remains canonical for layer/map/geometric concepts unless a later decision explicitly changes that.
|
||||
- `BankType::GLYPH` is the canonical asset-bank enum variant for the concrete graphical bank.
|
||||
- This decision is a rename boundary, not a behavior-change boundary.
|
||||
- Documentation must follow artifact meaning, not mechanical string replacement.
|
||||
|
||||
## Impactos
|
||||
|
||||
- `prometeu-hal` will need type/module/enum renames for the concrete graphical bank path.
|
||||
- `prometeu-drivers` will need renderer, memory-pool, hardware, and asset-manager naming migration for bank-specific references.
|
||||
- Tests, fixtures, and helper names need coordinated updates to avoid mixed terminology.
|
||||
- Docs and lessons need targeted rewriting rather than blind search-and-replace, because `tile` remains correct in map/layer contexts.
|
||||
|
||||
## Referencias
|
||||
|
||||
- AGD-0020: Tile Bank vs Glyph Bank Domain Naming
|
||||
- LSN-0022: Tilemap Empty Cell Convergence
|
||||
- `crates/console/prometeu-hal/src/tile_bank.rs`
|
||||
- `crates/console/prometeu-hal/src/tile_layer.rs`
|
||||
- `crates/console/prometeu-drivers/src/gfx.rs`
|
||||
- `crates/console/prometeu-drivers/src/memory_banks.rs`
|
||||
- `crates/console/prometeu-drivers/src/asset.rs`
|
||||
|
||||
## Propagacao Necessaria
|
||||
|
||||
- Write an execution plan before code and documentation migration.
|
||||
- Rename the concrete bank surface consistently across runtime crates.
|
||||
- Preserve `tile` naming in layer/map/geometric surfaces excluded by this decision.
|
||||
- Update docs and lessons according to artifact meaning, not blanket replacement.
|
||||
|
||||
## Revision Log
|
||||
|
||||
- 2026-04-10: Initial accepted decision from AGD-0020.
|
||||
@ -0,0 +1,161 @@
|
||||
---
|
||||
id: PLN-0005
|
||||
ticket: tile-bank-vs-glyph-bank-domain-naming
|
||||
title: Glyph Bank Domain Naming Execution
|
||||
status: accepted
|
||||
created: 2026-04-10
|
||||
completed:
|
||||
tags: [gfx, runtime, naming, domain-model]
|
||||
---
|
||||
|
||||
## Briefing
|
||||
|
||||
Implement DEC-0006 by renaming the concrete graphical bank artifact from `TileBank` to `GlyphBank` across runtime code, tests, documentation, and historical lessons, while preserving behavior and keeping the excluded tile-domain surfaces untouched. This is a rename-only migration. It must not alter payload format, rendering semantics, codec behavior, metadata structure, or memory layout.
|
||||
|
||||
## Decisions de Origem
|
||||
|
||||
- DEC-0006: Glyph Bank Domain Naming Contract
|
||||
|
||||
## Alvo
|
||||
|
||||
Land a consistent runtime-and-docs migration that:
|
||||
|
||||
- renames the concrete bank artifact and its module/type surface to `GlyphBank`;
|
||||
- renames `BankType::TILES` to `BankType::GLYPH`;
|
||||
- renames pool and loader/runtime bank-specific APIs accordingly;
|
||||
- preserves `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize`;
|
||||
- rewrites maintained docs and lessons according to artifact meaning rather than blanket token replacement.
|
||||
|
||||
## Escopo
|
||||
|
||||
- `prometeu-hal` concrete bank type/module/enum renames.
|
||||
- `prometeu-drivers` renderer, memory-bank, hardware, and asset-manager bank-specific renames.
|
||||
- `prometeu-system` callsites and dispatch paths that refer to the asset-bank enum variant.
|
||||
- Tests, helpers, fixtures, and strings that still describe the concrete bank as `TileBank`.
|
||||
- Discussion lessons and maintained discussion artifacts that refer to the concrete graphical bank artifact.
|
||||
|
||||
## Fora de Escopo
|
||||
|
||||
- Any format, algorithm, codec, metadata, or layout change.
|
||||
- Renaming `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, or related layer/map structures.
|
||||
- Renaming `TileSize` or geometric tile-size concepts.
|
||||
- Redesigning tile/glyph semantics beyond the naming boundary established by DEC-0006.
|
||||
|
||||
## Plano de Execucao
|
||||
|
||||
### Step 1 - Rename the HAL concrete bank surface
|
||||
|
||||
**What:**
|
||||
Rename the concrete bank module and type surface in `prometeu-hal` from `TileBank` to `GlyphBank`.
|
||||
|
||||
**How:**
|
||||
Rename `tile_bank.rs` to `glyph_bank.rs`, update `lib.rs` exports/import paths, rename `TileBank` to `GlyphBank`, and rename concrete-bank-specific constants if they are artifact-specific rather than geometric. Keep `TileSize` unchanged even if it continues to live near the bank implementation or must be re-exported from the renamed module in a compatibility-preserving layout inside this refactor.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-hal/src/tile_bank.rs`
|
||||
- `crates/console/prometeu-hal/src/lib.rs`
|
||||
- any direct imports of `prometeu_hal::tile_bank::*` in runtime crates
|
||||
|
||||
### Step 2 - Rename the asset-bank contract
|
||||
|
||||
**What:**
|
||||
Migrate the asset-bank enum and related asset-facing terminology from `TILES` to `GLYPH`.
|
||||
|
||||
**How:**
|
||||
Rename `BankType::TILES` to `BankType::GLYPH`, update any JSON/serde expectations and validation paths, and rename asset-facing metadata helpers only where they describe the concrete bank artifact rather than the geometric tile model. Preserve behavior and fail-fast semantics unchanged.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-hal/src/asset.rs`
|
||||
- `crates/console/prometeu-hal/src/cartridge_loader.rs`
|
||||
- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs`
|
||||
- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs`
|
||||
- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs`
|
||||
|
||||
### Step 3 - Rename memory pools and concrete-bank runtime APIs
|
||||
|
||||
**What:**
|
||||
Rename bank-specific pool interfaces and storage paths that refer to the concrete graphical bank.
|
||||
|
||||
**How:**
|
||||
Rename `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, `tile_bank_pool`, and related fields/traits/vars to `Glyph*` equivalents. Keep semantics and slot behavior unchanged.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/memory_banks.rs`
|
||||
- `crates/console/prometeu-drivers/src/hardware.rs`
|
||||
- `crates/console/prometeu-drivers/src/gfx.rs`
|
||||
- `crates/console/prometeu-drivers/src/asset.rs`
|
||||
|
||||
### Step 4 - Rename concrete-bank decode/load path and tests
|
||||
|
||||
**What:**
|
||||
Update the asset manager and concrete-bank decode path to the new artifact name.
|
||||
|
||||
**How:**
|
||||
Rename functions such as `decode_tile_bank_*`, `perform_load_tile_bank`, and related test helpers/messages to `GlyphBank` naming. Keep payload interpretation, palette handling, and runtime materialization identical to current behavior. Do not rename `TileSize`.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/asset.rs`
|
||||
- affected tests in `crates/console/prometeu-drivers/src/asset.rs`
|
||||
- any helper or stress/test utility strings such as `missing_tile_bank`
|
||||
|
||||
### Step 5 - Apply renderer and hardware editorial cleanup
|
||||
|
||||
**What:**
|
||||
Rename only the references that describe the concrete bank artifact inside renderer and hardware documentation/comments.
|
||||
|
||||
**How:**
|
||||
Update comments, docs, field names, and user-facing/runtime-facing strings so that references to the bank artifact become `GlyphBank`/`glyph bank`, while preserving `tile` terminology for layers, maps, and geometric concepts. Review `gfx.rs` carefully because it contains both domains.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/gfx.rs`
|
||||
- `crates/console/prometeu-drivers/src/hardware.rs`
|
||||
- any crate-level docs/comments on the bank artifact path
|
||||
|
||||
### Step 6 - Rewrite maintained lessons and discussion docs by meaning
|
||||
|
||||
**What:**
|
||||
Migrate maintained lessons and relevant discussion text to the new canonical artifact name.
|
||||
|
||||
**How:**
|
||||
Update published lessons and maintained discussion artifacts so that references to the concrete graphical bank become `GlyphBank`, but references to tile layers, tilemaps, grid semantics, or tile geometry remain `tile`. Use manual review, not blanket replacement.
|
||||
|
||||
**File(s):**
|
||||
- `discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md`
|
||||
- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md`
|
||||
- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md`
|
||||
- any maintained agenda/decision/plan artifacts still intentionally retained and referencing the concrete bank artifact
|
||||
|
||||
## Criterios de Aceite
|
||||
|
||||
- The canonical concrete bank type/module name in runtime code is `GlyphBank`.
|
||||
- The canonical asset-bank enum variant is `BankType::GLYPH`.
|
||||
- Pool interfaces and concrete-bank runtime helpers use `Glyph*` naming consistently.
|
||||
- `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize` remain unchanged.
|
||||
- No behavior, payload format, or renderer semantics change as part of this migration.
|
||||
- Tests, helper names, and runtime-facing strings do not keep active mixed `TileBank`/`GlyphBank` naming for the same artifact.
|
||||
- Maintained lessons and docs use `GlyphBank` for the concrete bank artifact while preserving `tile` where it refers to layer/map/geometric concepts.
|
||||
|
||||
## Tests / Validacao
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Update and run unit tests that directly construct or decode the concrete graphical bank path.
|
||||
- Confirm renamed bank/pool/helper symbols compile without compatibility shims.
|
||||
|
||||
### Integration Tests
|
||||
|
||||
- Run `prometeu-hal`, `prometeu-drivers`, and `prometeu-system` test targets affected by the rename.
|
||||
- Verify asset loading still succeeds for the renamed `BankType::GLYPH` path.
|
||||
|
||||
### Manual Verification
|
||||
|
||||
- Review representative docs and lessons to confirm `GlyphBank` only replaces the concrete bank artifact meaning.
|
||||
- Review `gfx.rs` and `tile_layer.rs` boundaries to confirm tile-domain structures were not accidentally renamed.
|
||||
- Search the repository for residual `TileBank` references and classify any remaining occurrence as either acceptable historical residue outside scope or a migration miss.
|
||||
|
||||
## Riscos
|
||||
|
||||
- The repository uses `tile` in two different domains, so blind search-and-replace is likely to over-rename excluded surfaces.
|
||||
- Renaming the enum variant from `TILES` to `GLYPH` touches serialization contracts and can break test fixtures or external packer assumptions if not coordinated.
|
||||
- Moving `tile_bank.rs` to `glyph_bank.rs` may cause broad import churn across crates and tests.
|
||||
- Historical lessons can easily drift into editorial inconsistency if the rewrite is done mechanically instead of semantically.
|
||||
Loading…
x
Reference in New Issue
Block a user