asset name and id
This commit is contained in:
parent
8d97f6225b
commit
81f7813c7c
@ -38,7 +38,7 @@ impl<T> ResidentEntry<T> {
|
|||||||
/// This is internal to the AssetManager and not visible to peripherals.
|
/// This is internal to the AssetManager and not visible to peripherals.
|
||||||
pub struct BankPolicy<T> {
|
pub struct BankPolicy<T> {
|
||||||
/// Dedup table: asset_id -> resident entry (value + telemetry).
|
/// Dedup table: asset_id -> resident entry (value + telemetry).
|
||||||
resident: Arc<RwLock<HashMap<String, ResidentEntry<T>>>>,
|
resident: Arc<RwLock<HashMap<u32, ResidentEntry<T>>>>,
|
||||||
|
|
||||||
/// Staging area: handle -> value ready to commit.
|
/// Staging area: handle -> value ready to commit.
|
||||||
staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>,
|
staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>,
|
||||||
@ -53,15 +53,15 @@ impl<T> BankPolicy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try get a resident value by asset_id (dedupe path).
|
/// Try get a resident value by asset_id (dedupe path).
|
||||||
pub fn get_resident(&self, asset_id: &str) -> Option<Arc<T>> {
|
pub fn get_resident(&self, asset_id: u32) -> Option<Arc<T>> {
|
||||||
let mut map = self.resident.write().unwrap();
|
let mut map = self.resident.write().unwrap();
|
||||||
let entry = map.get_mut(asset_id)?;
|
let entry = map.get_mut(&asset_id)?;
|
||||||
entry.last_used = Instant::now();
|
entry.last_used = Instant::now();
|
||||||
Some(Arc::clone(&entry.value))
|
Some(Arc::clone(&entry.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert or reuse a resident entry. Returns the resident Arc<T>.
|
/// Insert or reuse a resident entry. Returns the resident Arc<T>.
|
||||||
pub fn put_resident(&self, asset_id: String, value: Arc<T>, bytes: usize) -> Arc<T> {
|
pub fn put_resident(&self, asset_id: u32, value: Arc<T>, bytes: usize) -> Arc<T> {
|
||||||
let mut map = self.resident.write().unwrap();
|
let mut map = self.resident.write().unwrap();
|
||||||
match map.get_mut(&asset_id) {
|
match map.get_mut(&asset_id) {
|
||||||
Some(existing) => {
|
Some(existing) => {
|
||||||
@ -94,7 +94,8 @@ impl<T> BankPolicy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AssetManager {
|
pub struct AssetManager {
|
||||||
assets: Arc<RwLock<HashMap<String, AssetEntry>>>,
|
assets: Arc<RwLock<HashMap<u32, AssetEntry>>>,
|
||||||
|
name_to_id: Arc<RwLock<HashMap<String, u32>>>,
|
||||||
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
|
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
|
||||||
next_handle_id: Mutex<HandleId>,
|
next_handle_id: Mutex<HandleId>,
|
||||||
assets_data: Arc<RwLock<Vec<u8>>>,
|
assets_data: Arc<RwLock<Vec<u8>>>,
|
||||||
@ -104,8 +105,8 @@ pub struct AssetManager {
|
|||||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||||
|
|
||||||
/// Track what is installed in each hardware slot (for stats/info).
|
/// Track what is installed in each hardware slot (for stats/info).
|
||||||
gfx_slots: Arc<RwLock<[Option<String>; 16]>>,
|
gfx_slots: Arc<RwLock<[Option<u32>; 16]>>,
|
||||||
sound_slots: Arc<RwLock<[Option<String>; 16]>>,
|
sound_slots: Arc<RwLock<[Option<u32>; 16]>>,
|
||||||
|
|
||||||
/// Residency policy for GFX tile banks.
|
/// Residency policy for GFX tile banks.
|
||||||
gfx_policy: BankPolicy<TileBank>,
|
gfx_policy: BankPolicy<TileBank>,
|
||||||
@ -117,7 +118,7 @@ pub struct AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LoadHandleInfo {
|
struct LoadHandleInfo {
|
||||||
_asset_id: String,
|
_asset_id: u32,
|
||||||
slot: SlotRef,
|
slot: SlotRef,
|
||||||
status: LoadStatus,
|
status: LoadStatus,
|
||||||
}
|
}
|
||||||
@ -130,12 +131,15 @@ impl AssetManager {
|
|||||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut asset_map = HashMap::new();
|
let mut asset_map = HashMap::new();
|
||||||
|
let mut name_to_id = HashMap::new();
|
||||||
for entry in assets {
|
for entry in assets {
|
||||||
asset_map.insert(entry.asset_id.clone(), entry);
|
name_to_id.insert(entry.asset_name.clone(), entry.asset_id);
|
||||||
|
asset_map.insert(entry.asset_id, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
assets: Arc::new(RwLock::new(asset_map)),
|
assets: Arc::new(RwLock::new(asset_map)),
|
||||||
|
name_to_id: Arc::new(RwLock::new(name_to_id)),
|
||||||
gfx_installer,
|
gfx_installer,
|
||||||
sound_installer,
|
sound_installer,
|
||||||
gfx_slots: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
|
gfx_slots: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
|
||||||
@ -153,9 +157,12 @@ impl AssetManager {
|
|||||||
self.shutdown();
|
self.shutdown();
|
||||||
{
|
{
|
||||||
let mut asset_map = self.assets.write().unwrap();
|
let mut asset_map = self.assets.write().unwrap();
|
||||||
|
let mut name_to_id = self.name_to_id.write().unwrap();
|
||||||
asset_map.clear();
|
asset_map.clear();
|
||||||
|
name_to_id.clear();
|
||||||
for entry in assets.iter() {
|
for entry in assets.iter() {
|
||||||
asset_map.insert(entry.asset_id.clone(), entry.clone());
|
name_to_id.insert(entry.asset_name.clone(), entry.asset_id);
|
||||||
|
asset_map.insert(entry.asset_id, entry.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self.assets_data.write().unwrap() = assets_data;
|
*self.assets_data.write().unwrap() = assets_data;
|
||||||
@ -164,7 +171,10 @@ impl AssetManager {
|
|||||||
for item in preload {
|
for item in preload {
|
||||||
let entry_opt = {
|
let entry_opt = {
|
||||||
let assets = self.assets.read().unwrap();
|
let assets = self.assets.read().unwrap();
|
||||||
assets.get(&item.asset_id).cloned()
|
let name_to_id = self.name_to_id.read().unwrap();
|
||||||
|
name_to_id.get(&item.asset_name)
|
||||||
|
.and_then(|id| assets.get(id))
|
||||||
|
.cloned()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(entry) = entry_opt {
|
if let Some(entry) = entry_opt {
|
||||||
@ -173,43 +183,46 @@ impl AssetManager {
|
|||||||
BankType::TILES => {
|
BankType::TILES => {
|
||||||
if let Ok(bank) = Self::perform_load_tile_bank(&entry, self.assets_data.clone()) {
|
if let Ok(bank) = Self::perform_load_tile_bank(&entry, self.assets_data.clone()) {
|
||||||
let bank_arc = Arc::new(bank);
|
let bank_arc = Arc::new(bank);
|
||||||
self.gfx_policy.put_resident(entry.asset_id.clone(), Arc::clone(&bank_arc), entry.decoded_size as usize);
|
self.gfx_policy.put_resident(entry.asset_id, Arc::clone(&bank_arc), entry.decoded_size as usize);
|
||||||
self.gfx_installer.install_tile_bank(slot_index, bank_arc);
|
self.gfx_installer.install_tile_bank(slot_index, bank_arc);
|
||||||
let mut slots = self.gfx_slots.write().unwrap();
|
let mut slots = self.gfx_slots.write().unwrap();
|
||||||
if slot_index < slots.len() {
|
if slot_index < slots.len() {
|
||||||
slots[slot_index] = Some(entry.asset_id.clone());
|
slots[slot_index] = Some(entry.asset_id);
|
||||||
}
|
}
|
||||||
println!("[AssetManager] Preloaded tile asset '{}' into slot {}", entry.asset_id, slot_index);
|
println!("[AssetManager] Preloaded tile asset '{}' (id: {}) into slot {}", entry.asset_name, entry.asset_id, slot_index);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[AssetManager] Failed to preload tile asset '{}'", entry.asset_id);
|
eprintln!("[AssetManager] Failed to preload tile asset '{}'", entry.asset_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BankType::SOUNDS => {
|
BankType::SOUNDS => {
|
||||||
if let Ok(bank) = Self::perform_load_sound_bank(&entry, self.assets_data.clone()) {
|
if let Ok(bank) = Self::perform_load_sound_bank(&entry, self.assets_data.clone()) {
|
||||||
let bank_arc = Arc::new(bank);
|
let bank_arc = Arc::new(bank);
|
||||||
self.sound_policy.put_resident(entry.asset_id.clone(), Arc::clone(&bank_arc), entry.decoded_size as usize);
|
self.sound_policy.put_resident(entry.asset_id, Arc::clone(&bank_arc), entry.decoded_size as usize);
|
||||||
self.sound_installer.install_sound_bank(slot_index, bank_arc);
|
self.sound_installer.install_sound_bank(slot_index, bank_arc);
|
||||||
let mut slots = self.sound_slots.write().unwrap();
|
let mut slots = self.sound_slots.write().unwrap();
|
||||||
if slot_index < slots.len() {
|
if slot_index < slots.len() {
|
||||||
slots[slot_index] = Some(entry.asset_id.clone());
|
slots[slot_index] = Some(entry.asset_id);
|
||||||
}
|
}
|
||||||
println!("[AssetManager] Preloaded sound asset '{}' into slot {}", entry.asset_id, slot_index);
|
println!("[AssetManager] Preloaded sound asset '{}' (id: {}) into slot {}", entry.asset_name, entry.asset_id, slot_index);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[AssetManager] Failed to preload sound asset '{}'", entry.asset_id);
|
eprintln!("[AssetManager] Failed to preload sound asset '{}'", entry.asset_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[AssetManager] Preload failed: asset '{}' not found in table", item.asset_id);
|
eprintln!("[AssetManager] Preload failed: asset '{}' not found in table", item.asset_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&self, asset_id: &str, slot: SlotRef) -> Result<HandleId, String> {
|
pub fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, String> {
|
||||||
let entry = {
|
let entry = {
|
||||||
let assets = self.assets.read().unwrap();
|
let assets = self.assets.read().unwrap();
|
||||||
assets.get(asset_id).ok_or_else(|| format!("Asset not found: {}", asset_id))?.clone()
|
let name_to_id = self.name_to_id.read().unwrap();
|
||||||
|
let id = name_to_id.get(asset_name).ok_or_else(|| format!("Asset not found: {}", asset_name))?;
|
||||||
|
assets.get(id).ok_or_else(|| format!("Asset ID {} not found in table", id))?.clone()
|
||||||
};
|
};
|
||||||
|
let asset_id = entry.asset_id;
|
||||||
|
|
||||||
if slot.asset_type != entry.bank_type {
|
if slot.asset_type != entry.bank_type {
|
||||||
return Err("INCOMPATIBLE_SLOT_KIND".to_string());
|
return Err("INCOMPATIBLE_SLOT_KIND".to_string());
|
||||||
@ -237,7 +250,7 @@ impl AssetManager {
|
|||||||
|
|
||||||
if already_resident {
|
if already_resident {
|
||||||
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
|
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
|
||||||
_asset_id: asset_id.to_string(),
|
_asset_id: asset_id,
|
||||||
slot,
|
slot,
|
||||||
status: LoadStatus::READY,
|
status: LoadStatus::READY,
|
||||||
});
|
});
|
||||||
@ -246,7 +259,7 @@ impl AssetManager {
|
|||||||
|
|
||||||
// Not resident, start loading
|
// Not resident, start loading
|
||||||
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
|
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
|
||||||
_asset_id: asset_id.to_string(),
|
_asset_id: asset_id,
|
||||||
slot,
|
slot,
|
||||||
status: LoadStatus::PENDING,
|
status: LoadStatus::PENDING,
|
||||||
});
|
});
|
||||||
@ -254,7 +267,6 @@ impl AssetManager {
|
|||||||
let handles = self.handles.clone();
|
let handles = self.handles.clone();
|
||||||
let assets_data = self.assets_data.clone();
|
let assets_data = self.assets_data.clone();
|
||||||
let entry_clone = entry.clone();
|
let entry_clone = entry.clone();
|
||||||
let asset_id_clone = asset_id.to_string();
|
|
||||||
|
|
||||||
// Capture policies for the worker thread
|
// Capture policies for the worker thread
|
||||||
let gfx_policy_resident = Arc::clone(&self.gfx_policy.resident);
|
let gfx_policy_resident = Arc::clone(&self.gfx_policy.resident);
|
||||||
@ -284,13 +296,13 @@ impl AssetManager {
|
|||||||
let bank_arc = Arc::new(tilebank);
|
let bank_arc = Arc::new(tilebank);
|
||||||
let resident_arc = {
|
let resident_arc = {
|
||||||
let mut map = gfx_policy_resident.write().unwrap();
|
let mut map = gfx_policy_resident.write().unwrap();
|
||||||
if let Some(existing) = map.get_mut(&asset_id_clone) {
|
if let Some(existing) = map.get_mut(&asset_id) {
|
||||||
existing.last_used = Instant::now();
|
existing.last_used = Instant::now();
|
||||||
existing.loads += 1;
|
existing.loads += 1;
|
||||||
Arc::clone(&existing.value)
|
Arc::clone(&existing.value)
|
||||||
} else {
|
} else {
|
||||||
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
|
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
|
||||||
map.insert(asset_id_clone, entry);
|
map.insert(asset_id, entry);
|
||||||
bank_arc
|
bank_arc
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -310,13 +322,13 @@ impl AssetManager {
|
|||||||
let bank_arc = Arc::new(soundbank);
|
let bank_arc = Arc::new(soundbank);
|
||||||
let resident_arc = {
|
let resident_arc = {
|
||||||
let mut map = sound_policy_resident.write().unwrap();
|
let mut map = sound_policy_resident.write().unwrap();
|
||||||
if let Some(existing) = map.get_mut(&asset_id_clone) {
|
if let Some(existing) = map.get_mut(&asset_id) {
|
||||||
existing.last_used = Instant::now();
|
existing.last_used = Instant::now();
|
||||||
existing.loads += 1;
|
existing.loads += 1;
|
||||||
Arc::clone(&existing.value)
|
Arc::clone(&existing.value)
|
||||||
} else {
|
} else {
|
||||||
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
|
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
|
||||||
map.insert(asset_id_clone, entry);
|
map.insert(asset_id, entry);
|
||||||
bank_arc
|
bank_arc
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -459,7 +471,7 @@ impl AssetManager {
|
|||||||
self.gfx_installer.install_tile_bank(h.slot.index, bank);
|
self.gfx_installer.install_tile_bank(h.slot.index, bank);
|
||||||
let mut slots = self.gfx_slots.write().unwrap();
|
let mut slots = self.gfx_slots.write().unwrap();
|
||||||
if h.slot.index < slots.len() {
|
if h.slot.index < slots.len() {
|
||||||
slots[h.slot.index] = Some(h._asset_id.clone());
|
slots[h.slot.index] = Some(h._asset_id);
|
||||||
}
|
}
|
||||||
h.status = LoadStatus::COMMITTED;
|
h.status = LoadStatus::COMMITTED;
|
||||||
}
|
}
|
||||||
@ -469,7 +481,7 @@ impl AssetManager {
|
|||||||
self.sound_installer.install_sound_bank(h.slot.index, bank);
|
self.sound_installer.install_sound_bank(h.slot.index, bank);
|
||||||
let mut slots = self.sound_slots.write().unwrap();
|
let mut slots = self.sound_slots.write().unwrap();
|
||||||
if h.slot.index < slots.len() {
|
if h.slot.index < slots.len() {
|
||||||
slots[h.slot.index] = Some(h._asset_id.clone());
|
slots[h.slot.index] = Some(h._asset_id);
|
||||||
}
|
}
|
||||||
h.status = LoadStatus::COMMITTED;
|
h.status = LoadStatus::COMMITTED;
|
||||||
}
|
}
|
||||||
@ -573,17 +585,20 @@ impl AssetManager {
|
|||||||
let slots = self.gfx_slots.read().unwrap();
|
let slots = self.gfx_slots.read().unwrap();
|
||||||
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
||||||
|
|
||||||
let bytes = if let Some(id) = &asset_id {
|
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
||||||
self.gfx_policy.resident.read().unwrap()
|
let bytes = self.gfx_policy.resident.read().unwrap()
|
||||||
.get(id)
|
.get(id)
|
||||||
.map(|entry| entry.bytes)
|
.map(|entry| entry.bytes)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0);
|
||||||
|
let name = self.assets.read().unwrap().get(id).map(|e| e.asset_name.clone());
|
||||||
|
(bytes, name)
|
||||||
} else {
|
} else {
|
||||||
0
|
(0, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
SlotStats {
|
SlotStats {
|
||||||
asset_id,
|
asset_id,
|
||||||
|
asset_name,
|
||||||
generation: 0,
|
generation: 0,
|
||||||
resident_bytes: bytes,
|
resident_bytes: bytes,
|
||||||
}
|
}
|
||||||
@ -592,17 +607,20 @@ impl AssetManager {
|
|||||||
let slots = self.sound_slots.read().unwrap();
|
let slots = self.sound_slots.read().unwrap();
|
||||||
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
||||||
|
|
||||||
let bytes = if let Some(id) = &asset_id {
|
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
||||||
self.sound_policy.resident.read().unwrap()
|
let bytes = self.sound_policy.resident.read().unwrap()
|
||||||
.get(id)
|
.get(id)
|
||||||
.map(|entry| entry.bytes)
|
.map(|entry| entry.bytes)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0);
|
||||||
|
let name = self.assets.read().unwrap().get(id).map(|e| e.asset_name.clone());
|
||||||
|
(bytes, name)
|
||||||
} else {
|
} else {
|
||||||
0
|
(0, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
SlotStats {
|
SlotStats {
|
||||||
asset_id,
|
asset_id,
|
||||||
|
asset_name,
|
||||||
generation: 0,
|
generation: 0,
|
||||||
resident_bytes: bytes,
|
resident_bytes: bytes,
|
||||||
}
|
}
|
||||||
@ -635,7 +653,8 @@ mod tests {
|
|||||||
data.extend_from_slice(&[0u8; 2048]);
|
data.extend_from_slice(&[0u8; 2048]);
|
||||||
|
|
||||||
let asset_entry = AssetEntry {
|
let asset_entry = AssetEntry {
|
||||||
asset_id: "test_tiles".to_string(),
|
asset_id: 0,
|
||||||
|
asset_name: "test_tiles".to_string(),
|
||||||
bank_type: BankType::TILES,
|
bank_type: BankType::TILES,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: data.len() as u64,
|
size: data.len() as u64,
|
||||||
@ -684,7 +703,8 @@ mod tests {
|
|||||||
data.extend_from_slice(&[0u8; 2048]);
|
data.extend_from_slice(&[0u8; 2048]);
|
||||||
|
|
||||||
let asset_entry = AssetEntry {
|
let asset_entry = AssetEntry {
|
||||||
asset_id: "test_tiles".to_string(),
|
asset_id: 0,
|
||||||
|
asset_name: "test_tiles".to_string(),
|
||||||
bank_type: BankType::TILES,
|
bank_type: BankType::TILES,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: data.len() as u64,
|
size: data.len() as u64,
|
||||||
@ -724,7 +744,8 @@ mod tests {
|
|||||||
let data = vec![0u8; 200];
|
let data = vec![0u8; 200];
|
||||||
|
|
||||||
let asset_entry = AssetEntry {
|
let asset_entry = AssetEntry {
|
||||||
asset_id: "test_sound".to_string(),
|
asset_id: 1,
|
||||||
|
asset_name: "test_sound".to_string(),
|
||||||
bank_type: BankType::SOUNDS,
|
bank_type: BankType::SOUNDS,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: data.len() as u64,
|
size: data.len() as u64,
|
||||||
@ -763,7 +784,8 @@ mod tests {
|
|||||||
let data = vec![0u8; 200];
|
let data = vec![0u8; 200];
|
||||||
|
|
||||||
let asset_entry = AssetEntry {
|
let asset_entry = AssetEntry {
|
||||||
asset_id: "preload_sound".to_string(),
|
asset_id: 2,
|
||||||
|
asset_name: "preload_sound".to_string(),
|
||||||
bank_type: BankType::SOUNDS,
|
bank_type: BankType::SOUNDS,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: data.len() as u64,
|
size: data.len() as u64,
|
||||||
@ -775,7 +797,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let preload = vec![
|
let preload = vec![
|
||||||
PreloadEntry { asset_id: "preload_sound".to_string(), slot: 5 }
|
PreloadEntry { asset_name: "preload_sound".to_string(), slot: 5 }
|
||||||
];
|
];
|
||||||
|
|
||||||
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
||||||
@ -787,6 +809,6 @@ mod tests {
|
|||||||
|
|
||||||
// After init, slot 5 should be occupied because of preload
|
// After init, slot 5 should be occupied because of preload
|
||||||
assert!(banks.sound_bank_slot(5).is_some());
|
assert!(banks.sound_bank_slot(5).is_some());
|
||||||
assert_eq!(am.slot_info(SlotRef::audio(5)).asset_id, Some("preload_sound".to_string()));
|
assert_eq!(am.slot_info(SlotRef::audio(5)).asset_id, Some(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,8 @@ pub enum BankType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct AssetEntry {
|
pub struct AssetEntry {
|
||||||
pub asset_id: String,
|
pub asset_id: u32,
|
||||||
|
pub asset_name: String,
|
||||||
pub bank_type: BankType,
|
pub bank_type: BankType,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
@ -24,7 +25,7 @@ pub struct AssetEntry {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct PreloadEntry {
|
pub struct PreloadEntry {
|
||||||
pub asset_id: String,
|
pub asset_name: String,
|
||||||
pub slot: usize,
|
pub slot: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +51,8 @@ pub struct BankStats {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SlotStats {
|
pub struct SlotStats {
|
||||||
pub asset_id: Option<String>,
|
pub asset_id: Option<u32>,
|
||||||
|
pub asset_name: Option<String>,
|
||||||
pub generation: u32,
|
pub generation: u32,
|
||||||
pub resident_bytes: usize,
|
pub resident_bytes: usize,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -900,6 +900,7 @@ impl NativeInterface for PrometeuOS {
|
|||||||
|
|
||||||
let asset_type = match asset_type_val {
|
let asset_type = match asset_type_val {
|
||||||
0 => crate::model::BankType::TILES,
|
0 => crate::model::BankType::TILES,
|
||||||
|
1 => crate::model::BankType::SOUNDS,
|
||||||
_ => return Err("Invalid asset type".to_string()),
|
_ => return Err("Invalid asset type".to_string()),
|
||||||
};
|
};
|
||||||
let slot = crate::model::SlotRef { asset_type, index: slot_index };
|
let slot = crate::model::SlotRef { asset_type, index: slot_index };
|
||||||
@ -942,6 +943,7 @@ impl NativeInterface for PrometeuOS {
|
|||||||
let asset_type_val = vm.pop_integer()? as u32;
|
let asset_type_val = vm.pop_integer()? as u32;
|
||||||
let asset_type = match asset_type_val {
|
let asset_type = match asset_type_val {
|
||||||
0 => crate::model::BankType::TILES,
|
0 => crate::model::BankType::TILES,
|
||||||
|
1 => crate::model::BankType::SOUNDS,
|
||||||
_ => return Err("Invalid asset type".to_string()),
|
_ => return Err("Invalid asset type".to_string()),
|
||||||
};
|
};
|
||||||
let info = hw.assets().bank_info(asset_type);
|
let info = hw.assets().bank_info(asset_type);
|
||||||
@ -954,6 +956,7 @@ impl NativeInterface for PrometeuOS {
|
|||||||
let asset_type_val = vm.pop_integer()? as u32;
|
let asset_type_val = vm.pop_integer()? as u32;
|
||||||
let asset_type = match asset_type_val {
|
let asset_type = match asset_type_val {
|
||||||
0 => crate::model::BankType::TILES,
|
0 => crate::model::BankType::TILES,
|
||||||
|
1 => crate::model::BankType::SOUNDS,
|
||||||
_ => return Err("Invalid asset type".to_string()),
|
_ => return Err("Invalid asset type".to_string()),
|
||||||
};
|
};
|
||||||
let slot = crate::model::SlotRef { asset_type, index: slot_index };
|
let slot = crate::model::SlotRef { asset_type, index: slot_index };
|
||||||
|
|||||||
@ -44,7 +44,8 @@ It describes **content**, not residency.
|
|||||||
|
|
||||||
### Required Fields (conceptual)
|
### Required Fields (conceptual)
|
||||||
|
|
||||||
* `asset_id` (string or hash)
|
* `asset_id` (integer, internal identifier)
|
||||||
|
* `asset_name` (string, user-facing identifier)
|
||||||
* `asset_type` (TILEBANK, SOUNDBANK, BLOB, TILEMAP, ...)
|
* `asset_type` (TILEBANK, SOUNDBANK, BLOB, TILEMAP, ...)
|
||||||
* `bank_kind` (mandatory, single)
|
* `bank_kind` (mandatory, single)
|
||||||
* `offset` (byte offset in cartridge)
|
* `offset` (byte offset in cartridge)
|
||||||
@ -139,12 +140,12 @@ Conceptually, each Bank is a **specialized allocator**.
|
|||||||
### Conceptual API
|
### Conceptual API
|
||||||
|
|
||||||
```text
|
```text
|
||||||
handle = asset.load(asset_id, slotRef, flags)
|
handle = asset.load(asset_name, slotRef, flags)
|
||||||
```
|
```
|
||||||
|
|
||||||
Load flow:
|
Load flow:
|
||||||
|
|
||||||
1. Resolve `asset_id` via Asset Table
|
1. Resolve `asset_name` via Asset Table to get its `asset_id`
|
||||||
2. Read `bank_kind` from asset entry
|
2. Read `bank_kind` from asset entry
|
||||||
3. Validate compatibility with `slotRef`
|
3. Validate compatibility with `slotRef`
|
||||||
4. Enqueue load request
|
4. Enqueue load request
|
||||||
@ -264,6 +265,7 @@ Each Bank must expose:
|
|||||||
* inflight memory
|
* inflight memory
|
||||||
* occupied slots
|
* occupied slots
|
||||||
* `asset_id` per slot
|
* `asset_id` per slot
|
||||||
|
* `asset_name` per slot
|
||||||
* generation per slot
|
* generation per slot
|
||||||
|
|
||||||
This enables debuggers to visualize:
|
This enables debuggers to visualize:
|
||||||
@ -279,7 +281,7 @@ This enables debuggers to visualize:
|
|||||||
The following syscalls form the minimal hardware contract for asset management:
|
The following syscalls form the minimal hardware contract for asset management:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
asset.load(asset_id, slotRef, flags) -> handle
|
asset.load(asset_name, slotRef, flags) -> handle
|
||||||
asset.status(handle) -> LoadStatus
|
asset.status(handle) -> LoadStatus
|
||||||
asset.commit(handle)
|
asset.commit(handle)
|
||||||
asset.cancel(handle)
|
asset.cancel(handle)
|
||||||
@ -292,7 +294,7 @@ Where:
|
|||||||
|
|
||||||
* `LoadStatus` ∈ { PENDING, LOADING, READY, COMMITTED, CANCELED, ERROR }
|
* `LoadStatus` ∈ { PENDING, LOADING, READY, COMMITTED, CANCELED, ERROR }
|
||||||
* `BankStats` exposes memory usage and limits
|
* `BankStats` exposes memory usage and limits
|
||||||
* `SlotStats` exposes current asset_id and generation
|
* `SlotStats` exposes current asset_id, asset_name and generation
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
"entrypoint": "0",
|
"entrypoint": "0",
|
||||||
"asset_table": [
|
"asset_table": [
|
||||||
{
|
{
|
||||||
"asset_id": "bgm_music",
|
"asset_id": 0,
|
||||||
|
"asset_name": "bgm_music",
|
||||||
"bank_type": "SOUNDS",
|
"bank_type": "SOUNDS",
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
"size": 88200,
|
"size": 88200,
|
||||||
@ -19,7 +20,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"asset_id": "mouse_cursor",
|
"asset_id": 1,
|
||||||
|
"asset_name": "mouse_cursor",
|
||||||
"bank_type": "TILES",
|
"bank_type": "TILES",
|
||||||
"offset": 88200,
|
"offset": 88200,
|
||||||
"size": 2304,
|
"size": 2304,
|
||||||
@ -33,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"preload": [
|
"preload": [
|
||||||
{ "asset_id": "bgm_music", "slot": 0 },
|
{ "asset_name": "bgm_music", "slot": 0 },
|
||||||
{ "asset_id": "mouse_cursor", "slot": 1 }
|
{ "asset_name": "mouse_cursor", "slot": 1 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user