adjustments over frame composer contract - agnostic tile size
This commit is contained in:
parent
a1bd60671b
commit
f4260d0cf4
@ -79,6 +79,21 @@ pub struct Gfx {
|
|||||||
|
|
||||||
const GLYPH_UNKNOWN: [u8; 5] = [0x7, 0x7, 0x7, 0x7, 0x7];
|
const GLYPH_UNKNOWN: [u8; 5] = [0x7, 0x7, 0x7, 0x7, 0x7];
|
||||||
|
|
||||||
|
struct RenderTarget<'a> {
|
||||||
|
back: &'a mut [u16],
|
||||||
|
screen_w: usize,
|
||||||
|
screen_h: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct CachedTileDraw<'a> {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
entry: CachedTileEntry,
|
||||||
|
bank: &'a GlyphBank,
|
||||||
|
tile_size: prometeu_hal::glyph_bank::TileSize,
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn glyph_for_char(c: char) -> &'static [u8; 5] {
|
fn glyph_for_char(c: char) -> &'static [u8; 5] {
|
||||||
match c.to_ascii_uppercase() {
|
match c.to_ascii_uppercase() {
|
||||||
@ -629,6 +644,7 @@ impl Gfx {
|
|||||||
request: &LayerCopyRequest,
|
request: &LayerCopyRequest,
|
||||||
glyph_banks: &dyn GlyphBankPoolAccess,
|
glyph_banks: &dyn GlyphBankPoolAccess,
|
||||||
) {
|
) {
|
||||||
|
let mut target = RenderTarget { back, screen_w, screen_h };
|
||||||
let layer_cache = &cache.layers[request.layer_index];
|
let layer_cache = &cache.layers[request.layer_index];
|
||||||
if !layer_cache.valid {
|
if !layer_cache.valid {
|
||||||
return;
|
return;
|
||||||
@ -657,52 +673,43 @@ impl Gfx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self::draw_cached_tile_pixels(
|
Self::draw_cached_tile_pixels(
|
||||||
back,
|
&mut target,
|
||||||
screen_w,
|
CachedTileDraw {
|
||||||
screen_h,
|
x: screen_tile_x,
|
||||||
screen_tile_x,
|
y: screen_tile_y,
|
||||||
screen_tile_y,
|
entry,
|
||||||
entry,
|
bank: &bank,
|
||||||
&bank,
|
tile_size: request.tile_size,
|
||||||
request.tile_size,
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_cached_tile_pixels(
|
fn draw_cached_tile_pixels(target: &mut RenderTarget<'_>, tile: CachedTileDraw<'_>) {
|
||||||
back: &mut [u16],
|
let size = tile.tile_size as usize;
|
||||||
screen_w: usize,
|
|
||||||
screen_h: usize,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
entry: CachedTileEntry,
|
|
||||||
bank: &GlyphBank,
|
|
||||||
tile_size: prometeu_hal::glyph_bank::TileSize,
|
|
||||||
) {
|
|
||||||
let size = tile_size as usize;
|
|
||||||
|
|
||||||
for local_y in 0..size {
|
for local_y in 0..size {
|
||||||
let world_y = y + local_y as i32;
|
let world_y = tile.y + local_y as i32;
|
||||||
if world_y < 0 || world_y >= screen_h as i32 {
|
if world_y < 0 || world_y >= target.screen_h as i32 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for local_x in 0..size {
|
for local_x in 0..size {
|
||||||
let world_x = x + local_x as i32;
|
let world_x = tile.x + local_x as i32;
|
||||||
if world_x < 0 || world_x >= screen_w as i32 {
|
if world_x < 0 || world_x >= target.screen_w as i32 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fetch_x = if entry.flip_x() { size - 1 - local_x } else { local_x };
|
let fetch_x = if tile.entry.flip_x() { size - 1 - local_x } else { local_x };
|
||||||
let fetch_y = if entry.flip_y() { size - 1 - local_y } else { local_y };
|
let fetch_y = if tile.entry.flip_y() { size - 1 - local_y } else { local_y };
|
||||||
let px_index = bank.get_pixel_index(entry.glyph_id, fetch_x, fetch_y);
|
let px_index = tile.bank.get_pixel_index(tile.entry.glyph_id, fetch_x, fetch_y);
|
||||||
if px_index == 0 {
|
if px_index == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = bank.resolve_color(entry.palette_id, px_index);
|
let color = tile.bank.resolve_color(tile.entry.palette_id, px_index);
|
||||||
back[world_y as usize * screen_w + world_x as usize] = color.raw();
|
target.back[world_y as usize * target.screen_w + world_x as usize] = color.raw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -325,6 +325,34 @@ mod tests {
|
|||||||
assert_eq!(cache.layers[0].ring_origin(), (1, 1));
|
assert_eq!(cache.layers[0].ring_origin(), (1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_cache_wraps_ring_origin_for_negative_and_large_movements() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut cache = SceneViewportCache::new(&scene, 3, 3);
|
||||||
|
|
||||||
|
cache.move_layer_window_by(0, -1, -4);
|
||||||
|
assert_eq!(cache.layers[0].logical_origin(), (-1, -4));
|
||||||
|
assert_eq!(cache.layers[0].ring_origin(), (2, 2));
|
||||||
|
|
||||||
|
cache.move_layer_window_by(0, 7, 8);
|
||||||
|
assert_eq!(cache.layers[0].logical_origin(), (6, 4));
|
||||||
|
assert_eq!(cache.layers[0].ring_origin(), (0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_window_to_matches_incremental_ring_movement() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut direct = SceneViewportCache::new(&scene, 4, 4);
|
||||||
|
let mut incremental = SceneViewportCache::new(&scene, 4, 4);
|
||||||
|
|
||||||
|
direct.move_layer_window_to(0, 9, -6);
|
||||||
|
incremental.move_layer_window_by(0, 5, -2);
|
||||||
|
incremental.move_layer_window_by(0, 4, -4);
|
||||||
|
|
||||||
|
assert_eq!(direct.layers[0].logical_origin(), incremental.layers[0].logical_origin());
|
||||||
|
assert_eq!(direct.layers[0].ring_origin(), incremental.layers[0].ring_origin());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cache_entry_fields_are_derived_from_scene_tiles() {
|
fn cache_entry_fields_are_derived_from_scene_tiles() {
|
||||||
let scene = make_scene();
|
let scene = make_scene();
|
||||||
@ -415,6 +443,113 @@ mod tests {
|
|||||||
assert_eq!(cache.layers[3].entry(3, 3).glyph_id, 415);
|
assert_eq!(cache.layers[3].entry(3, 3).glyph_id, 415);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn refresh_after_wrapped_window_move_materializes_new_logical_tiles() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut cache = SceneViewportCache::new(&scene, 3, 3);
|
||||||
|
|
||||||
|
cache.refresh_layer_all(&scene, 0);
|
||||||
|
cache.move_layer_window_to(0, 1, 2);
|
||||||
|
cache.refresh_layer_all(&scene, 0);
|
||||||
|
|
||||||
|
assert_eq!(cache.layers[0].logical_origin(), (1, 2));
|
||||||
|
assert_eq!(cache.layers[0].ring_origin(), (1, 2));
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 0).glyph_id, 109);
|
||||||
|
assert_eq!(cache.layers[0].entry(1, 0).glyph_id, 110);
|
||||||
|
assert_eq!(cache.layers[0].entry(2, 0).glyph_id, 111);
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 1).glyph_id, 113);
|
||||||
|
assert_eq!(cache.layers[0].entry(2, 1).glyph_id, 115);
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 2), CachedTileEntry::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_refresh_uses_wrapped_physical_slots_after_window_move() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut cache = SceneViewportCache::new(&scene, 3, 3);
|
||||||
|
|
||||||
|
cache.move_layer_window_to(0, 1, 0);
|
||||||
|
cache.refresh_layer_column(&scene, 0, 0);
|
||||||
|
|
||||||
|
assert_eq!(cache.layers[0].ring_origin(), (1, 0));
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 0).glyph_id, 101);
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 1).glyph_id, 105);
|
||||||
|
assert_eq!(cache.layers[0].entry(0, 2).glyph_id, 109);
|
||||||
|
assert_eq!(cache.layers[0].entry(1, 0), CachedTileEntry::default());
|
||||||
|
assert_eq!(cache.layers[0].entry(2, 0), CachedTileEntry::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn out_of_bounds_logical_origins_materialize_default_entries_after_wrap() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut cache = SceneViewportCache::new(&scene, 2, 2);
|
||||||
|
|
||||||
|
cache.move_layer_window_to(0, -2, 3);
|
||||||
|
cache.refresh_layer_all(&scene, 0);
|
||||||
|
|
||||||
|
for y in 0..2 {
|
||||||
|
for x in 0..2 {
|
||||||
|
assert_eq!(cache.layers[0].entry(x, y), CachedTileEntry::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ringbuffer_preserves_logical_tile_mapping_across_long_mixed_movements() {
|
||||||
|
let scene = make_scene();
|
||||||
|
let mut cache = SceneViewportCache::new(&scene, 3, 3);
|
||||||
|
let motions = [
|
||||||
|
(1, 0),
|
||||||
|
(0, 1),
|
||||||
|
(2, 2),
|
||||||
|
(-1, 0),
|
||||||
|
(0, -2),
|
||||||
|
(4, 1),
|
||||||
|
(-3, 3),
|
||||||
|
(5, -4),
|
||||||
|
(-6, 2),
|
||||||
|
(3, -3),
|
||||||
|
(7, 7),
|
||||||
|
(-8, -5),
|
||||||
|
];
|
||||||
|
|
||||||
|
for &(dx, dy) in &motions {
|
||||||
|
cache.move_layer_window_by(0, dx, dy);
|
||||||
|
cache.refresh_layer_all(&scene, 0);
|
||||||
|
|
||||||
|
let (origin_x, origin_y) = cache.layers[0].logical_origin();
|
||||||
|
for cache_y in 0..cache.height() {
|
||||||
|
for cache_x in 0..cache.width() {
|
||||||
|
let expected_scene_x = origin_x + cache_x as i32;
|
||||||
|
let expected_scene_y = origin_y + cache_y as i32;
|
||||||
|
|
||||||
|
let expected = if expected_scene_x < 0
|
||||||
|
|| expected_scene_y < 0
|
||||||
|
|| expected_scene_x as usize >= scene.layers[0].tilemap.width
|
||||||
|
|| expected_scene_y as usize >= scene.layers[0].tilemap.height
|
||||||
|
{
|
||||||
|
CachedTileEntry::default()
|
||||||
|
} else {
|
||||||
|
let tile_x = expected_scene_x as usize;
|
||||||
|
let tile_y = expected_scene_y as usize;
|
||||||
|
let tile = scene.layers[0].tilemap.tiles
|
||||||
|
[tile_y * scene.layers[0].tilemap.width + tile_x];
|
||||||
|
CachedTileEntry::from_tile(&scene.layers[0], tile)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cache.layers[0].entry(cache_x, cache_y),
|
||||||
|
expected,
|
||||||
|
"mismatch at logical origin ({}, {}), cache ({}, {})",
|
||||||
|
origin_x,
|
||||||
|
origin_y,
|
||||||
|
cache_x,
|
||||||
|
cache_y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn materialization_populates_all_four_layers() {
|
fn materialization_populates_all_four_layers() {
|
||||||
let scene = make_scene();
|
let scene = make_scene();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user