implements PLN-0102
This commit is contained in:
parent
6f3df74e79
commit
d2bace2612
@ -8,8 +8,8 @@ use prometeu_hal::scene_viewport_resolver::{
|
||||
};
|
||||
use prometeu_hal::sprite::Sprite;
|
||||
use prometeu_hal::{
|
||||
BoundScenePacket, Camera2D, ComposerFramePacket, Game2DFramePacket, GameSpritePacket,
|
||||
GfxBridge, HudPacket,
|
||||
BoundScenePacket, Camera2D, ComposerFramePacket, ComposerOpStatus, Game2DFrameComposer,
|
||||
Game2DFramePacket, GameSpritePacket, GfxBridge, HudPacket,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -452,6 +452,41 @@ impl FrameComposer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Game2DFrameComposer for FrameComposer {
|
||||
fn begin_frame(&mut self) {
|
||||
FrameComposer::begin_frame(self);
|
||||
}
|
||||
|
||||
fn bind_scene(&mut self, scene_bank_id: usize) -> ComposerOpStatus {
|
||||
if FrameComposer::bind_scene(self, scene_bank_id) {
|
||||
ComposerOpStatus::Ok
|
||||
} else {
|
||||
ComposerOpStatus::SceneUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind_scene(&mut self) {
|
||||
FrameComposer::unbind_scene(self);
|
||||
}
|
||||
|
||||
fn set_camera(&mut self, x: i32, y: i32) -> ComposerOpStatus {
|
||||
FrameComposer::set_camera(self, x, y);
|
||||
ComposerOpStatus::Ok
|
||||
}
|
||||
|
||||
fn emit_sprite(&mut self, sprite: Sprite) -> ComposerOpStatus {
|
||||
if FrameComposer::emit_sprite(self, sprite) {
|
||||
ComposerOpStatus::Ok
|
||||
} else {
|
||||
ComposerOpStatus::SpriteOverflow
|
||||
}
|
||||
}
|
||||
|
||||
fn close_game2d_packet(&self) -> Game2DFramePacket {
|
||||
FrameComposer::close_game2d_packet(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -896,6 +931,45 @@ mod tests {
|
||||
assert!(packet.gfx2d.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn game2d_frame_composer_facade_closes_equivalent_packet() {
|
||||
let mut frame_composer = FrameComposer::new(
|
||||
Hardware::W,
|
||||
Hardware::H,
|
||||
Arc::new(MemoryBanks::default()),
|
||||
make_glyph_slot_index(&[]),
|
||||
);
|
||||
|
||||
let composer: &mut dyn Game2DFrameComposer = &mut frame_composer;
|
||||
assert_eq!(composer.set_camera(10, 20), ComposerOpStatus::Ok);
|
||||
composer.begin_frame();
|
||||
assert_eq!(
|
||||
composer.emit_sprite(Sprite {
|
||||
glyph: Glyph { glyph_id: 7, palette_id: 3 },
|
||||
x: 4,
|
||||
y: 5,
|
||||
layer: 2,
|
||||
bank_id: 1,
|
||||
active: false,
|
||||
flip_x: true,
|
||||
flip_y: false,
|
||||
priority: 9,
|
||||
}),
|
||||
ComposerOpStatus::Ok
|
||||
);
|
||||
|
||||
let packet = composer.close_game2d_packet();
|
||||
|
||||
assert_eq!(packet.composer.bound_scene, None);
|
||||
assert_eq!(packet.composer.camera, Camera2D { x: 10, y: 20 });
|
||||
assert_eq!(packet.composer.sprites.len(), 1);
|
||||
assert_eq!(packet.composer.sprites[0].glyph_id, 7);
|
||||
assert_eq!(packet.composer.sprites[0].palette_id, 3);
|
||||
assert_eq!(packet.composer.sprites[0].layer, 2);
|
||||
assert!(packet.composer.sprites[0].flip_x);
|
||||
assert!(packet.gfx2d.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn composer_buffer_preserves_integer_layer_ordering() {
|
||||
let mut frame_composer = FrameComposer::new(
|
||||
|
||||
@ -11,7 +11,9 @@ use crate::touch::Touch;
|
||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||
use prometeu_hal::sprite::Sprite;
|
||||
use prometeu_hal::{AssetBridge, AudioBridge, GfxBridge, HardwareBridge, PadBridge, TouchBridge};
|
||||
use prometeu_hal::{Game2DFramePacket, RenderSubmission, RenderSubmissionPacket};
|
||||
use prometeu_hal::{
|
||||
Game2DFrameComposer, Game2DFramePacket, RenderSubmission, RenderSubmissionPacket,
|
||||
};
|
||||
use prometeu_hal::{RenderSubmissionSink, RenderSubmitError};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -149,6 +151,45 @@ impl RenderSubmissionSink for Hardware {
|
||||
}
|
||||
}
|
||||
|
||||
impl Game2DFrameComposer for Hardware {
|
||||
fn begin_frame(&mut self) {
|
||||
self.frame_composer.begin_frame();
|
||||
}
|
||||
|
||||
fn bind_scene(&mut self, scene_bank_id: usize) -> prometeu_hal::ComposerOpStatus {
|
||||
if self.frame_composer.bind_scene(scene_bank_id) {
|
||||
prometeu_hal::ComposerOpStatus::Ok
|
||||
} else {
|
||||
prometeu_hal::ComposerOpStatus::SceneUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind_scene(&mut self) {
|
||||
self.frame_composer.unbind_scene();
|
||||
}
|
||||
|
||||
fn set_camera(&mut self, x: i32, y: i32) -> prometeu_hal::ComposerOpStatus {
|
||||
self.frame_composer.set_camera(x, y);
|
||||
prometeu_hal::ComposerOpStatus::Ok
|
||||
}
|
||||
|
||||
fn emit_sprite(&mut self, sprite: Sprite) -> prometeu_hal::ComposerOpStatus {
|
||||
if !self.has_glyph_bank(sprite.bank_id as usize) {
|
||||
return prometeu_hal::ComposerOpStatus::BankInvalid;
|
||||
}
|
||||
|
||||
if self.frame_composer.emit_sprite(sprite) {
|
||||
prometeu_hal::ComposerOpStatus::Ok
|
||||
} else {
|
||||
prometeu_hal::ComposerOpStatus::SpriteOverflow
|
||||
}
|
||||
}
|
||||
|
||||
fn close_game2d_packet(&self) -> Game2DFramePacket {
|
||||
self.frame_composer.close_game2d_packet()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hardware {
|
||||
/// Internal hardware width in pixels.
|
||||
pub const W: usize = 480;
|
||||
@ -294,9 +335,9 @@ mod tests {
|
||||
let mut glyph_slots = [None; 16];
|
||||
glyph_slots[0] = Some(0);
|
||||
hardware.assets.glyph_asset_slot_index().rebuild_from_slots(&glyph_slots);
|
||||
assert!(hardware.bind_scene(0));
|
||||
assert!(HardwareBridge::bind_scene(&mut hardware, 0));
|
||||
|
||||
let mut packet = hardware.close_game2d_packet();
|
||||
let mut packet = HardwareBridge::close_game2d_packet(&hardware);
|
||||
packet.gfx2d = vec![Gfx2dCommand::FillRect {
|
||||
rect: Rect { x: 0, y: 0, w: 1, h: 1 },
|
||||
color: Color::BLUE,
|
||||
@ -321,7 +362,7 @@ mod tests {
|
||||
let mut glyph_slots = [None; 16];
|
||||
glyph_slots[0] = Some(0);
|
||||
hardware.assets.glyph_asset_slot_index().rebuild_from_slots(&glyph_slots);
|
||||
assert!(hardware.bind_scene(0));
|
||||
assert!(HardwareBridge::bind_scene(&mut hardware, 0));
|
||||
|
||||
let packet = Game2DFramePacket::new(
|
||||
ComposerFramePacket {
|
||||
@ -343,7 +384,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
);
|
||||
|
||||
hardware.begin_frame();
|
||||
HardwareBridge::begin_frame(&mut hardware);
|
||||
RenderSubmissionSink::submit_render_submission(
|
||||
&mut hardware,
|
||||
RenderSubmission::game2d(FrameId::ZERO, packet),
|
||||
|
||||
@ -49,8 +49,8 @@ pub use native_interface::{NativeInterface, SyscallId};
|
||||
pub use pad_bridge::PadBridge;
|
||||
pub use platform::{
|
||||
AssetStoragePlatform, AudioPlatform, ClockPacingPlatform, Game2DFrameComposer, InputPlatform,
|
||||
LegacyHardwareRenderSubmissionSink, RenderBackend, RenderSubmissionSink, RenderSubmitError,
|
||||
RuntimePlatform, TelemetryPlatform, TestPlatform,
|
||||
LegacyHardwareGame2DFrameComposer, LegacyHardwareRenderSubmissionSink, RenderBackend,
|
||||
RenderSubmissionSink, RenderSubmitError, RuntimePlatform, TelemetryPlatform, TestPlatform,
|
||||
};
|
||||
pub use render_submission::{
|
||||
BoundScenePacket, Camera2D, ComposerFramePacket, FrameId, Game2DFramePacket, GameSpritePacket,
|
||||
|
||||
@ -52,7 +52,55 @@ pub trait Game2DFrameComposer {
|
||||
fn set_camera(&mut self, x: i32, y: i32) -> ComposerOpStatus;
|
||||
fn emit_sprite(&mut self, sprite: Sprite) -> ComposerOpStatus;
|
||||
fn close_game2d_packet(&self) -> Game2DFramePacket;
|
||||
fn has_glyph_bank(&self, bank_id: usize) -> bool;
|
||||
}
|
||||
|
||||
pub struct LegacyHardwareGame2DFrameComposer<'a> {
|
||||
hw: &'a mut dyn HardwareBridge,
|
||||
}
|
||||
|
||||
impl<'a> LegacyHardwareGame2DFrameComposer<'a> {
|
||||
pub fn new(hw: &'a mut dyn HardwareBridge) -> Self {
|
||||
Self { hw }
|
||||
}
|
||||
}
|
||||
|
||||
impl Game2DFrameComposer for LegacyHardwareGame2DFrameComposer<'_> {
|
||||
fn begin_frame(&mut self) {
|
||||
self.hw.begin_frame();
|
||||
}
|
||||
|
||||
fn bind_scene(&mut self, scene_bank_id: usize) -> ComposerOpStatus {
|
||||
if self.hw.bind_scene(scene_bank_id) {
|
||||
ComposerOpStatus::Ok
|
||||
} else {
|
||||
ComposerOpStatus::SceneUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind_scene(&mut self) {
|
||||
self.hw.unbind_scene();
|
||||
}
|
||||
|
||||
fn set_camera(&mut self, x: i32, y: i32) -> ComposerOpStatus {
|
||||
self.hw.set_camera(x, y);
|
||||
ComposerOpStatus::Ok
|
||||
}
|
||||
|
||||
fn emit_sprite(&mut self, sprite: Sprite) -> ComposerOpStatus {
|
||||
if !self.hw.has_glyph_bank(sprite.bank_id as usize) {
|
||||
return ComposerOpStatus::BankInvalid;
|
||||
}
|
||||
|
||||
if self.hw.emit_sprite(sprite) {
|
||||
ComposerOpStatus::Ok
|
||||
} else {
|
||||
ComposerOpStatus::SpriteOverflow
|
||||
}
|
||||
}
|
||||
|
||||
fn close_game2d_packet(&self) -> Game2DFramePacket {
|
||||
self.hw.close_game2d_packet()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AudioPlatform: AudioBridge {}
|
||||
@ -135,10 +183,6 @@ mod tests {
|
||||
fn close_game2d_packet(&self) -> Game2DFramePacket {
|
||||
Game2DFramePacket::default()
|
||||
}
|
||||
|
||||
fn has_glyph_bank(&self, _bank_id: usize) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -12,8 +12,9 @@ use prometeu_hal::sprite::Sprite;
|
||||
use prometeu_hal::syscalls::Syscall;
|
||||
use prometeu_hal::vm_fault::VmFault;
|
||||
use prometeu_hal::{
|
||||
AudioOpStatus, ComposerOpStatus, Gfx2dCommand, GfxUiCommand, HostContext, HostReturn,
|
||||
NativeInterface, SyscallId, expect_bool, expect_int,
|
||||
AudioOpStatus, ComposerOpStatus, Game2DFrameComposer, Gfx2dCommand, GfxUiCommand, HostContext,
|
||||
HostReturn, LegacyHardwareGame2DFrameComposer, NativeInterface, SyscallId, expect_bool,
|
||||
expect_int,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
@ -385,23 +386,24 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
let bound = catch_unwind(AssertUnwindSafe(|| hw.bind_scene(scene_bank_id)))
|
||||
.map_err(|payload| VmFault::Panic(Self::panic_payload_to_string(payload)))?;
|
||||
let status = catch_unwind(AssertUnwindSafe(|| {
|
||||
let mut composer = LegacyHardwareGame2DFrameComposer::new(hw);
|
||||
composer.bind_scene(scene_bank_id)
|
||||
}))
|
||||
.map_err(|payload| VmFault::Panic(Self::panic_payload_to_string(payload)))?;
|
||||
|
||||
let status =
|
||||
if bound { ComposerOpStatus::Ok } else { ComposerOpStatus::SceneUnavailable };
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::ComposerUnbindScene => {
|
||||
hw.unbind_scene();
|
||||
LegacyHardwareGame2DFrameComposer::new(hw).unbind_scene();
|
||||
ret.push_int(ComposerOpStatus::Ok as i64);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::ComposerSetCamera => {
|
||||
let x = Self::int_arg_to_i32_trap(expect_int(args, 0)?, "camera x")?;
|
||||
let y = Self::int_arg_to_i32_trap(expect_int(args, 1)?, "camera y")?;
|
||||
hw.set_camera(x, y);
|
||||
LegacyHardwareGame2DFrameComposer::new(hw).set_camera(x, y);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::ComposerEmitSprite => {
|
||||
@ -449,12 +451,7 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
if !hw.has_glyph_bank(bank_id as usize) {
|
||||
ret.push_int(ComposerOpStatus::BankInvalid as i64);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let emitted = hw.emit_sprite(Sprite {
|
||||
let status = LegacyHardwareGame2DFrameComposer::new(hw).emit_sprite(Sprite {
|
||||
glyph: Glyph { glyph_id, palette_id },
|
||||
x,
|
||||
y,
|
||||
@ -465,8 +462,6 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
flip_y,
|
||||
priority,
|
||||
});
|
||||
let status =
|
||||
if emitted { ComposerOpStatus::Ok } else { ComposerOpStatus::SpriteOverflow };
|
||||
ret.push_int(status as i64);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user