2026-06-16 05:44:08 +01:00

163 lines
5.4 KiB
Rust

use crate::asset::GlyphAssetSlotIndex;
use crate::frame_composer::FrameComposer;
use crate::gfx::Gfx;
use crate::memory_banks::{GlyphBankPoolAccess, MemoryBanks, SceneBankPoolAccess};
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::{
OwnedRgba8888Frame, RenderSubmission, RenderSubmissionPacket, RenderWorkerBackend,
RenderWorkerError,
};
use std::sync::{Arc, Mutex};
pub struct LocalFramebufferRenderBackend {
gfx: Mutex<Gfx>,
frame_composer: Mutex<FrameComposer>,
}
impl LocalFramebufferRenderBackend {
pub fn new(width: usize, height: usize, glyph_banks: Arc<dyn GlyphBankPoolAccess>) -> Self {
Self::new_with_parts(
width,
height,
glyph_banks,
Arc::new(MemoryBanks::new()),
GlyphAssetSlotIndex::new(),
)
}
pub fn new_with_memory_banks(
width: usize,
height: usize,
memory_banks: Arc<MemoryBanks>,
glyph_slot_index: GlyphAssetSlotIndex,
) -> Self {
Self::new_with_parts(
width,
height,
Arc::clone(&memory_banks) as Arc<dyn GlyphBankPoolAccess>,
memory_banks,
glyph_slot_index,
)
}
fn new_with_parts(
width: usize,
height: usize,
glyph_banks: Arc<dyn GlyphBankPoolAccess>,
scene_banks: Arc<dyn SceneBankPoolAccess>,
glyph_slot_index: GlyphAssetSlotIndex,
) -> Self {
Self {
gfx: Mutex::new(Gfx::new(width, height, glyph_banks)),
frame_composer: Mutex::new(FrameComposer::new(
width,
height,
scene_banks,
glyph_slot_index,
)),
}
}
pub fn render_submission(
&self,
submission: &RenderSubmission,
) -> Result<OwnedRgba8888Frame, RenderWorkerError> {
let mut gfx = self.gfx.lock().map_err(|_| RenderWorkerError::InternalFailure)?;
match &submission.packet {
RenderSubmissionPacket::Game2D(packet) if submission.app_mode == AppMode::Game => {
if packet.composer.bound_scene.is_none() {
gfx.render_game2d_frame_packet(packet);
} else {
let mut frame_composer = self
.frame_composer
.lock()
.map_err(|_| RenderWorkerError::InternalFailure)?;
frame_composer.render_game2d_frame_packet(packet, &mut *gfx);
gfx.apply_gfx2d_commands(&packet.gfx2d);
}
}
RenderSubmissionPacket::ShellUi(packet) if submission.app_mode == AppMode::Shell => {
gfx.render_shell_ui_frame_packet(packet);
}
_ => return Err(RenderWorkerError::RenderFailed),
}
gfx.present();
let (width, height) = gfx.size();
OwnedRgba8888Frame::packed(
submission.frame_id,
submission.ownership,
width,
height,
gfx.front_buffer().to_vec(),
)
.map_err(|_| RenderWorkerError::InternalFailure)
}
}
impl RenderWorkerBackend for LocalFramebufferRenderBackend {
fn render(
&self,
submission: &RenderSubmission,
) -> Result<OwnedRgba8888Frame, RenderWorkerError> {
self.render_submission(submission)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory_banks::MemoryBanks;
use prometeu_hal::app_mode::AppMode;
use prometeu_hal::color::Color;
use prometeu_hal::primitives::Rect;
use prometeu_hal::{
FrameId, Game2DFramePacket, Gfx2dCommand, GfxUiCommand, RenderOwnership, ShellUiFramePacket,
};
fn backend() -> LocalFramebufferRenderBackend {
let banks = Arc::new(MemoryBanks::new());
LocalFramebufferRenderBackend::new(4, 4, banks)
}
#[test]
fn local_backend_renders_game2d_submission_to_owned_frame() {
let backend = backend();
let mut packet = Game2DFramePacket::default();
packet.gfx2d = vec![Gfx2dCommand::FillRect {
rect: Rect { x: 1, y: 1, w: 2, h: 2 },
color: Color::RED,
}];
let ownership = RenderOwnership::new(3, AppMode::Game, 9);
let submission =
RenderSubmission::game2d(FrameId::new(5), packet).with_ownership(ownership);
let frame = backend.render(&submission).expect("game frame should render");
assert_eq!(frame.frame_id, FrameId::new(5));
assert_eq!(frame.ownership, ownership);
assert_eq!(frame.width, 4);
assert_eq!(frame.height, 4);
assert_eq!(frame.pixels[1 + 4], Color::RED.raw());
assert_eq!(frame.pixels[2 + 2 * 4], Color::RED.raw());
}
#[test]
fn local_backend_renders_shell_ui_submission_to_owned_frame() {
let backend = backend();
let packet = ShellUiFramePacket::new(vec![GfxUiCommand::FillRect {
rect: Rect { x: 0, y: 0, w: 1, h: 1 },
color: Color::GREEN,
}]);
let ownership = RenderOwnership::new(4, AppMode::Shell, 10);
let submission =
RenderSubmission::shell_ui(FrameId::new(6), packet).with_ownership(ownership);
let frame = backend.render(&submission).expect("shell frame should render");
assert_eq!(frame.frame_id, FrameId::new(6));
assert_eq!(frame.ownership, ownership);
assert_eq!(frame.pixels[0], Color::GREEN.raw());
}
}