implements PLN-0021
This commit is contained in:
parent
5ef43045bc
commit
a1bd60671b
@ -251,7 +251,7 @@ impl FrameComposer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx.render_all();
|
gfx.render_no_scene_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_scene_runtime(
|
fn build_scene_runtime(
|
||||||
|
|||||||
@ -209,8 +209,8 @@ impl GfxBridge for Gfx {
|
|||||||
fn present(&mut self) {
|
fn present(&mut self) {
|
||||||
self.present()
|
self.present()
|
||||||
}
|
}
|
||||||
fn render_all(&mut self) {
|
fn render_no_scene_frame(&mut self) {
|
||||||
self.render_all()
|
self.render_no_scene_frame()
|
||||||
}
|
}
|
||||||
fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate) {
|
fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate) {
|
||||||
self.render_scene_from_cache(cache, update)
|
self.render_scene_from_cache(cache, update)
|
||||||
@ -552,7 +552,7 @@ impl Gfx {
|
|||||||
/// This method composes the final frame by rasterizing layers and sprites in the
|
/// This method composes the final frame by rasterizing layers and sprites in the
|
||||||
/// correct priority order into the back buffer.
|
/// correct priority order into the back buffer.
|
||||||
/// Follows the hardware model where layers and sprites are composed every frame.
|
/// Follows the hardware model where layers and sprites are composed every frame.
|
||||||
pub fn render_all(&mut self) {
|
pub fn render_no_scene_frame(&mut self) {
|
||||||
self.populate_layer_buckets();
|
self.populate_layer_buckets();
|
||||||
for bucket in &self.layer_buckets {
|
for bucket in &self.layer_buckets {
|
||||||
Self::draw_bucket_on_buffer(
|
Self::draw_bucket_on_buffer(
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use crate::memory_banks::{
|
|||||||
use crate::pad::Pad;
|
use crate::pad::Pad;
|
||||||
use crate::touch::Touch;
|
use crate::touch::Touch;
|
||||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||||
|
use prometeu_hal::sprite::Sprite;
|
||||||
use prometeu_hal::{AssetBridge, AudioBridge, GfxBridge, HardwareBridge, PadBridge, TouchBridge};
|
use prometeu_hal::{AssetBridge, AudioBridge, GfxBridge, HardwareBridge, PadBridge, TouchBridge};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -46,6 +47,18 @@ impl Default for Hardware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HardwareBridge for Hardware {
|
impl HardwareBridge for Hardware {
|
||||||
|
fn begin_frame(&mut self) {
|
||||||
|
self.frame_composer.begin_frame();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_sprite(&mut self, sprite: Sprite) {
|
||||||
|
let _ = self.frame_composer.emit_sprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_frame(&mut self) {
|
||||||
|
self.frame_composer.render_frame(&mut self.gfx);
|
||||||
|
}
|
||||||
|
|
||||||
fn gfx(&self) -> &dyn GfxBridge {
|
fn gfx(&self) -> &dyn GfxBridge {
|
||||||
&self.gfx
|
&self.gfx
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ pub trait GfxBridge {
|
|||||||
fn draw_horizontal_line(&mut self, x0: i32, x1: i32, y: i32, color: Color);
|
fn draw_horizontal_line(&mut self, x0: i32, x1: i32, y: i32, color: Color);
|
||||||
fn draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color);
|
fn draw_vertical_line(&mut self, x: i32, y0: i32, y1: i32, color: Color);
|
||||||
fn present(&mut self);
|
fn present(&mut self);
|
||||||
fn render_all(&mut self);
|
fn render_no_scene_frame(&mut self);
|
||||||
fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate);
|
fn render_scene_from_cache(&mut self, cache: &SceneViewportCache, update: &ResolverUpdate);
|
||||||
fn load_frame_sprites(&mut self, sprites: &[Sprite]);
|
fn load_frame_sprites(&mut self, sprites: &[Sprite]);
|
||||||
fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color);
|
fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color);
|
||||||
|
|||||||
@ -2,9 +2,14 @@ use crate::asset_bridge::AssetBridge;
|
|||||||
use crate::audio_bridge::AudioBridge;
|
use crate::audio_bridge::AudioBridge;
|
||||||
use crate::gfx_bridge::GfxBridge;
|
use crate::gfx_bridge::GfxBridge;
|
||||||
use crate::pad_bridge::PadBridge;
|
use crate::pad_bridge::PadBridge;
|
||||||
|
use crate::sprite::Sprite;
|
||||||
use crate::touch_bridge::TouchBridge;
|
use crate::touch_bridge::TouchBridge;
|
||||||
|
|
||||||
pub trait HardwareBridge {
|
pub trait HardwareBridge {
|
||||||
|
fn begin_frame(&mut self);
|
||||||
|
fn emit_sprite(&mut self, sprite: Sprite);
|
||||||
|
fn render_frame(&mut self);
|
||||||
|
|
||||||
fn gfx(&self) -> &dyn GfxBridge;
|
fn gfx(&self) -> &dyn GfxBridge;
|
||||||
fn gfx_mut(&mut self) -> &mut dyn GfxBridge;
|
fn gfx_mut(&mut self) -> &mut dyn GfxBridge;
|
||||||
|
|
||||||
|
|||||||
@ -162,17 +162,19 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
*hw.gfx_mut().sprite_mut(index) = Sprite {
|
if active {
|
||||||
glyph: Glyph { glyph_id, palette_id },
|
hw.emit_sprite(Sprite {
|
||||||
x,
|
glyph: Glyph { glyph_id, palette_id },
|
||||||
y,
|
x,
|
||||||
layer: 0,
|
y,
|
||||||
bank_id,
|
layer: 0,
|
||||||
active,
|
bank_id,
|
||||||
flip_x,
|
active: false,
|
||||||
flip_y,
|
flip_x,
|
||||||
priority,
|
flip_y,
|
||||||
};
|
priority,
|
||||||
|
});
|
||||||
|
}
|
||||||
ret.push_int(GfxOpStatus::Ok as i64);
|
ret.push_int(GfxOpStatus::Ok as i64);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use prometeu_bytecode::Value;
|
|||||||
use prometeu_bytecode::assembler::assemble;
|
use prometeu_bytecode::assembler::assemble;
|
||||||
use prometeu_bytecode::model::{BytecodeModule, ConstantPoolEntry, FunctionMeta, SyscallDecl};
|
use prometeu_bytecode::model::{BytecodeModule, ConstantPoolEntry, FunctionMeta, SyscallDecl};
|
||||||
use prometeu_drivers::hardware::Hardware;
|
use prometeu_drivers::hardware::Hardware;
|
||||||
|
use prometeu_drivers::{GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolInstaller};
|
||||||
use prometeu_hal::AudioOpStatus;
|
use prometeu_hal::AudioOpStatus;
|
||||||
use prometeu_hal::GfxOpStatus;
|
use prometeu_hal::GfxOpStatus;
|
||||||
use prometeu_hal::InputSignals;
|
use prometeu_hal::InputSignals;
|
||||||
@ -12,10 +13,17 @@ use prometeu_hal::asset::{
|
|||||||
AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus,
|
AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus,
|
||||||
};
|
};
|
||||||
use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge};
|
use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge};
|
||||||
use prometeu_hal::glyph_bank::GLYPH_BANK_PALETTE_COUNT_V1;
|
use prometeu_hal::color::Color;
|
||||||
|
use prometeu_hal::glyph::Glyph;
|
||||||
|
use prometeu_hal::glyph_bank::{GLYPH_BANK_PALETTE_COUNT_V1, GlyphBank, TileSize};
|
||||||
|
use prometeu_hal::scene_bank::SceneBank;
|
||||||
|
use prometeu_hal::scene_layer::{ParallaxFactor, SceneLayer};
|
||||||
use prometeu_hal::syscalls::caps;
|
use prometeu_hal::syscalls::caps;
|
||||||
|
use prometeu_hal::tile::Tile;
|
||||||
|
use prometeu_hal::tilemap::TileMap;
|
||||||
use prometeu_vm::VmInitError;
|
use prometeu_vm::VmInitError;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -129,6 +137,40 @@ fn test_glyph_asset_data() -> Vec<u8> {
|
|||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn runtime_test_glyph_bank(tile_size: TileSize, palette_id: u8, color: Color) -> GlyphBank {
|
||||||
|
let size = tile_size as usize;
|
||||||
|
let mut bank = GlyphBank::new(tile_size, size, size);
|
||||||
|
bank.palettes[palette_id as usize][1] = color;
|
||||||
|
for pixel in &mut bank.pixel_indices {
|
||||||
|
*pixel = 1;
|
||||||
|
}
|
||||||
|
bank
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_test_scene(glyph_bank_id: u8, palette_id: u8, tile_size: TileSize) -> SceneBank {
|
||||||
|
let layer = SceneLayer {
|
||||||
|
active: true,
|
||||||
|
glyph_bank_id,
|
||||||
|
tile_size,
|
||||||
|
parallax_factor: ParallaxFactor { x: 1.0, y: 1.0 },
|
||||||
|
tilemap: TileMap {
|
||||||
|
width: 2,
|
||||||
|
height: 2,
|
||||||
|
tiles: vec![
|
||||||
|
Tile {
|
||||||
|
active: true,
|
||||||
|
glyph: Glyph { glyph_id: 0, palette_id },
|
||||||
|
flip_x: false,
|
||||||
|
flip_y: false,
|
||||||
|
};
|
||||||
|
4
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
SceneBank { layers: std::array::from_fn(|_| layer.clone()) }
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
fn initialize_vm_applies_cartridge_capabilities_before_loader_resolution() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
@ -233,6 +275,29 @@ fn tick_returns_panic_report_distinct_from_trap() {
|
|||||||
assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmPanic { .. })));
|
assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmPanic { .. })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_renders_bound_eight_pixel_scene_through_frame_composer_path() {
|
||||||
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
let program =
|
||||||
|
serialized_single_function_module(assemble("FRAME_SYNC\nHALT").expect("assemble"), vec![]);
|
||||||
|
let cartridge = cartridge_with_program(program, caps::NONE);
|
||||||
|
|
||||||
|
let banks = Arc::new(MemoryBanks::new());
|
||||||
|
banks.install_glyph_bank(0, Arc::new(runtime_test_glyph_bank(TileSize::Size8, 2, Color::BLUE)));
|
||||||
|
banks.install_scene_bank(0, Arc::new(runtime_test_scene(0, 2, TileSize::Size8)));
|
||||||
|
let mut hardware = Hardware::new_with_memory_banks(Arc::clone(&banks));
|
||||||
|
assert!(hardware.frame_composer.bind_scene(0));
|
||||||
|
|
||||||
|
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
|
||||||
|
let report = runtime.tick(&mut vm, &signals, &mut hardware);
|
||||||
|
|
||||||
|
assert!(report.is_none(), "frame render path must not crash");
|
||||||
|
hardware.gfx.present();
|
||||||
|
assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn initialize_vm_success_clears_previous_crash_report() {
|
fn initialize_vm_success_clears_previous_crash_report() {
|
||||||
let mut runtime = VirtualMachineRuntime::new(None);
|
let mut runtime = VirtualMachineRuntime::new(None);
|
||||||
|
|||||||
@ -145,7 +145,7 @@ impl VirtualMachineRuntime {
|
|||||||
if run.reason == LogicalFrameEndingReason::FrameSync
|
if run.reason == LogicalFrameEndingReason::FrameSync
|
||||||
|| run.reason == LogicalFrameEndingReason::EndOfRom
|
|| run.reason == LogicalFrameEndingReason::EndOfRom
|
||||||
{
|
{
|
||||||
hw.gfx_mut().render_all();
|
hw.render_frame();
|
||||||
|
|
||||||
// 1. Snapshot full telemetry at logical frame end
|
// 1. Snapshot full telemetry at logical frame end
|
||||||
let (glyph_bank, sound_bank) = Self::bank_telemetry_summary(hw);
|
let (glyph_bank, sound_bank) = Self::bank_telemetry_summary(hw);
|
||||||
@ -250,6 +250,7 @@ impl VirtualMachineRuntime {
|
|||||||
_signals: &InputSignals,
|
_signals: &InputSignals,
|
||||||
hw: &mut dyn HardwareBridge,
|
hw: &mut dyn HardwareBridge,
|
||||||
) {
|
) {
|
||||||
|
hw.begin_frame();
|
||||||
hw.audio_mut().clear_commands();
|
hw.audio_mut().clear_commands();
|
||||||
self.logs_written_this_frame.clear();
|
self.logs_written_this_frame.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user