PR004: implement asset status-first lifecycle surface
This commit is contained in:
parent
998252aa25
commit
c8b3b6afcc
@ -2,7 +2,8 @@
|
||||
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
||||
use prometeu_hal::AssetBridge;
|
||||
use prometeu_hal::asset::{
|
||||
AssetEntry, BankStats, BankType, HandleId, LoadStatus, PreloadEntry, SlotRef, SlotStats,
|
||||
AssetEntry, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
||||
PreloadEntry, SlotRef, SlotStats,
|
||||
};
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::sample::Sample;
|
||||
@ -139,16 +140,16 @@ impl AssetBridge for AssetManager {
|
||||
) {
|
||||
self.initialize_for_cartridge(assets, preload, assets_data)
|
||||
}
|
||||
fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, String> {
|
||||
fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, AssetLoadError> {
|
||||
self.load(asset_name, slot)
|
||||
}
|
||||
fn status(&self, handle: HandleId) -> LoadStatus {
|
||||
self.status(handle)
|
||||
}
|
||||
fn commit(&self, handle: HandleId) {
|
||||
fn commit(&self, handle: HandleId) -> AssetOpStatus {
|
||||
self.commit(handle)
|
||||
}
|
||||
fn cancel(&self, handle: HandleId) {
|
||||
fn cancel(&self, handle: HandleId) -> AssetOpStatus {
|
||||
self.cancel(handle)
|
||||
}
|
||||
fn apply_commits(&self) {
|
||||
@ -293,19 +294,22 @@ impl AssetManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, String> {
|
||||
pub fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, AssetLoadError> {
|
||||
if slot.index >= 16 {
|
||||
return Err(AssetLoadError::SlotIndexInvalid);
|
||||
}
|
||||
let entry = {
|
||||
let assets = self.assets.read().unwrap();
|
||||
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()
|
||||
.ok_or(AssetLoadError::AssetNotFound)?;
|
||||
assets.get(id).ok_or(AssetLoadError::BackendError)?.clone()
|
||||
};
|
||||
let asset_id = entry.asset_id;
|
||||
|
||||
if slot.asset_type != entry.bank_type {
|
||||
return Err("INCOMPATIBLE_SLOT_KIND".to_string());
|
||||
return Err(AssetLoadError::SlotKindMismatch);
|
||||
}
|
||||
|
||||
let mut next_id = self.next_handle_id.lock().unwrap();
|
||||
@ -533,21 +537,38 @@ impl AssetManager {
|
||||
}
|
||||
|
||||
pub fn status(&self, handle: HandleId) -> LoadStatus {
|
||||
self.handles.read().unwrap().get(&handle).map(|h| h.status).unwrap_or(LoadStatus::ERROR)
|
||||
self.handles
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&handle)
|
||||
.map(|h| h.status)
|
||||
.unwrap_or(LoadStatus::UnknownHandle)
|
||||
}
|
||||
|
||||
pub fn commit(&self, handle: HandleId) {
|
||||
pub fn commit(&self, handle: HandleId) -> AssetOpStatus {
|
||||
let mut handles_map = self.handles.write().unwrap();
|
||||
if let Some(h) = handles_map.get_mut(&handle) {
|
||||
let Some(h) = handles_map.get_mut(&handle) else {
|
||||
return AssetOpStatus::UnknownHandle;
|
||||
};
|
||||
if h.status == LoadStatus::READY {
|
||||
self.pending_commits.lock().unwrap().push(handle);
|
||||
}
|
||||
AssetOpStatus::Ok
|
||||
} else {
|
||||
AssetOpStatus::InvalidState
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel(&self, handle: HandleId) {
|
||||
pub fn cancel(&self, handle: HandleId) -> AssetOpStatus {
|
||||
let mut final_status = AssetOpStatus::UnknownHandle;
|
||||
let mut handles_map = self.handles.write().unwrap();
|
||||
if let Some(h) = handles_map.get_mut(&handle) {
|
||||
final_status = match h.status {
|
||||
LoadStatus::PENDING | LoadStatus::LOADING | LoadStatus::READY => {
|
||||
AssetOpStatus::Ok
|
||||
}
|
||||
LoadStatus::CANCELED => AssetOpStatus::Ok,
|
||||
_ => AssetOpStatus::InvalidState,
|
||||
};
|
||||
match h.status {
|
||||
LoadStatus::PENDING | LoadStatus::LOADING | LoadStatus::READY => {
|
||||
h.status = LoadStatus::CANCELED;
|
||||
@ -557,6 +578,7 @@ impl AssetManager {
|
||||
}
|
||||
self.gfx_policy.take_staging(handle);
|
||||
self.sound_policy.take_staging(handle);
|
||||
final_status
|
||||
}
|
||||
|
||||
pub fn apply_commits(&self) {
|
||||
|
||||
@ -37,6 +37,24 @@ pub enum LoadStatus {
|
||||
COMMITTED,
|
||||
CANCELED,
|
||||
ERROR,
|
||||
UnknownHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum AssetLoadError {
|
||||
AssetNotFound = 1,
|
||||
SlotKindMismatch = 2,
|
||||
SlotIndexInvalid = 3,
|
||||
BackendError = 4,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum AssetOpStatus {
|
||||
Ok = 0,
|
||||
UnknownHandle = 1,
|
||||
InvalidState = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::asset::{
|
||||
AssetEntry, BankStats, BankType, HandleId, LoadStatus, PreloadEntry, SlotRef, SlotStats,
|
||||
AssetEntry, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
||||
PreloadEntry, SlotRef, SlotStats,
|
||||
};
|
||||
|
||||
pub trait AssetBridge {
|
||||
@ -9,10 +10,10 @@ pub trait AssetBridge {
|
||||
preload: Vec<PreloadEntry>,
|
||||
assets_data: Vec<u8>,
|
||||
);
|
||||
fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, String>;
|
||||
fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, AssetLoadError>;
|
||||
fn status(&self, handle: HandleId) -> LoadStatus;
|
||||
fn commit(&self, handle: HandleId);
|
||||
fn cancel(&self, handle: HandleId);
|
||||
fn commit(&self, handle: HandleId) -> AssetOpStatus;
|
||||
fn cancel(&self, handle: HandleId) -> AssetOpStatus;
|
||||
fn apply_commits(&self);
|
||||
fn bank_info(&self, kind: BankType) -> BankStats;
|
||||
fn slot_info(&self, slot: SlotRef) -> SlotStats;
|
||||
|
||||
@ -8,7 +8,7 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
||||
"load",
|
||||
1,
|
||||
3,
|
||||
1,
|
||||
2,
|
||||
caps::ASSET,
|
||||
Determinism::NonDeterministic,
|
||||
false,
|
||||
@ -32,7 +32,7 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
||||
"commit",
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
caps::ASSET,
|
||||
Determinism::NonDeterministic,
|
||||
false,
|
||||
@ -44,7 +44,7 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
||||
"cancel",
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
caps::ASSET,
|
||||
Determinism::NonDeterministic,
|
||||
false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::*;
|
||||
use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
||||
use prometeu_hal::asset::{BankType, LoadStatus, SlotRef};
|
||||
use prometeu_hal::asset::{AssetLoadError, AssetOpStatus, BankType, LoadStatus, SlotRef};
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::log::{LogLevel, LogSource};
|
||||
use prometeu_hal::sprite::Sprite;
|
||||
@ -373,10 +373,21 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
|
||||
match hw.assets().load(&asset_id, slot) {
|
||||
Ok(handle) => {
|
||||
ret.push_int(AssetOpStatus::Ok as i64);
|
||||
ret.push_int(handle as i64);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(VmFault::Panic(e)),
|
||||
Err(status) => {
|
||||
let status_val = match status {
|
||||
AssetLoadError::AssetNotFound => 3,
|
||||
AssetLoadError::SlotKindMismatch => 4,
|
||||
AssetLoadError::SlotIndexInvalid => 5,
|
||||
AssetLoadError::BackendError => 6,
|
||||
};
|
||||
ret.push_int(status_val);
|
||||
ret.push_int(0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
Syscall::AssetStatus => {
|
||||
@ -387,16 +398,19 @@ impl NativeInterface for VirtualMachineRuntime {
|
||||
LoadStatus::COMMITTED => 3,
|
||||
LoadStatus::CANCELED => 4,
|
||||
LoadStatus::ERROR => 5,
|
||||
LoadStatus::UnknownHandle => 6,
|
||||
};
|
||||
ret.push_int(status_val);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::AssetCommit => {
|
||||
hw.assets().commit(expect_int(args, 0)? as u32);
|
||||
let status = hw.assets().commit(expect_int(args, 0)? as u32);
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::AssetCancel => {
|
||||
hw.assets().cancel(expect_int(args, 0)? as u32);
|
||||
let status = hw.assets().cancel(expect_int(args, 0)? as u32);
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::BankInfo => {
|
||||
|
||||
@ -126,3 +126,52 @@ These preload entries are consumed during cartridge initialization so the asset
|
||||
- [`13-cartridge.md`](13-cartridge.md) defines cartridge fields that carry `asset_table`, `preload`, and `assets.pa`.
|
||||
- [`16-host-abi-and-syscalls.md`](16-host-abi-and-syscalls.md) defines the syscall boundary used to manipulate assets.
|
||||
- [`03-memory-stack-heap-and-allocation.md`](03-memory-stack-heap-and-allocation.md) defines the distinction between VM heap memory and host-owned memory.
|
||||
|
||||
## 11 Syscall Surface and Status Policy
|
||||
|
||||
`asset` follows status-first policy.
|
||||
|
||||
Fault boundary:
|
||||
|
||||
- `Trap`: structural ABI misuse (type/arity/capability/shape mismatch);
|
||||
- `status`: operational failure;
|
||||
- `Panic`: internal invariant break only.
|
||||
|
||||
### 11.1 MVP syscall shape
|
||||
|
||||
- `asset.load(name, kind, slot) -> (status:int, handle:int)`
|
||||
- `asset.status(handle) -> status:int`
|
||||
- `asset.commit(handle) -> status:int`
|
||||
- `asset.cancel(handle) -> status:int`
|
||||
|
||||
Rules:
|
||||
|
||||
- `handle` is valid only when `load` status is `OK`;
|
||||
- failed `load` returns `handle = 0`;
|
||||
- `commit` and `cancel` must not be silent no-op for unknown/invalid handle state.
|
||||
|
||||
### 11.2 Minimum status tables
|
||||
|
||||
`asset.load` request statuses:
|
||||
|
||||
- `0` = `OK`
|
||||
- `3` = `ASSET_NOT_FOUND`
|
||||
- `4` = `SLOT_KIND_MISMATCH`
|
||||
- `5` = `SLOT_INDEX_INVALID`
|
||||
- `6` = `BACKEND_ERROR`
|
||||
|
||||
`asset.status` lifecycle statuses:
|
||||
|
||||
- `0` = `PENDING`
|
||||
- `1` = `LOADING`
|
||||
- `2` = `READY`
|
||||
- `3` = `COMMITTED`
|
||||
- `4` = `CANCELED`
|
||||
- `5` = `ERROR`
|
||||
- `6` = `UNKNOWN_HANDLE`
|
||||
|
||||
`asset.commit` and `asset.cancel` operation statuses:
|
||||
|
||||
- `0` = `OK`
|
||||
- `1` = `UNKNOWN_HANDLE`
|
||||
- `2` = `INVALID_STATE`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user