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