optimize sprites in compressed chunks

This commit is contained in:
Maxicarlos08 2024-01-26 16:26:26 +01:00
parent b4ac1f7036
commit c5ddf73dc2
No known key found for this signature in database
3 changed files with 141 additions and 43 deletions

View File

@ -184,9 +184,11 @@ impl<'a, VIE: VoxelImageDecoding> VoxelImageDecoding for &'a VIE {
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct QuadPngEncoding<const RESOLUTION_DIVIDER: u32>();
pub struct QuadPngEncoding<const RESOLUTION_DIVIDER: u32, const HASH_CONS_SPRITES: bool>();
impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
impl<const N: u32, const HASH_CONS_SPRITES: bool> VoxelImageEncoding
for QuadPngEncoding<N, HASH_CONS_SPRITES>
{
type Output = CompressedData<(Vec<u8>, [usize; 3], Vec<[u8; 3]>)>;
type Workspace = (
ImageBuffer<image::Luma<u8>, Vec<u8>>,
@ -194,6 +196,7 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
ImageBuffer<image::Luma<u8>, Vec<u8>>,
ImageBuffer<image::Rgb<u8>, Vec<u8>>,
Vec<[u8; 3]>,
HashMap<[u8; 3], u16>,
);
fn create(width: u32, height: u32) -> Self::Workspace {
@ -203,6 +206,7 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
ImageBuffer::new(width, height),
ImageBuffer::new(width / N, height / N),
Vec::new(),
HashMap::new(),
)
}
@ -221,12 +225,24 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
kind: BlockKind,
sprite_data: [u8; 3],
) {
let index = if HASH_CONS_SPRITES {
let index = ws.5.entry(sprite_data).or_insert_with(|| {
let index =
ws.4.len()
.try_into()
.expect("Cannot have more than 2^16 sprites in one chunk");
ws.4.push(sprite_data);
index
});
index.to_be_bytes()
} else {
let index: u16 =
ws.4.len()
.try_into()
.expect("Cannot have more than 2^16 sprites in one chunk");
let index = index.to_be_bytes();
ws.4.push(sprite_data);
index.to_be_bytes()
};
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
ws.1.put_pixel(x, y, image::Luma([index[0]]));
@ -323,7 +339,9 @@ const fn gen_lanczos_lookup<const N: u32, const R: u32>(
array
}
impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<N> {
impl<const N: u32, const HASH_CONS_SPRITES: bool> VoxelImageDecoding
for QuadPngEncoding<N, HASH_CONS_SPRITES>
{
fn start(data: &Self::Output) -> Option<Self::Workspace> {
use image::codecs::png::PngDecoder;
let (quad, indices, sprite_data) = data.decompress()?;
@ -337,7 +355,7 @@ impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<N> {
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()]).ok()?)?;
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()]).ok()?)?;
let d = image_from_bytes(PngDecoder::new(&quad[ranges[3].clone()]).ok()?)?;
Some((a, b, c, d, sprite_data))
Some((a, b, c, d, sprite_data, HashMap::new()))
}
fn get_block(ws: &Self::Workspace, x: u32, y: u32, is_border: bool) -> Block {
@ -461,9 +479,11 @@ impl<const N: u32> VoxelImageDecoding for QuadPngEncoding<N> {
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct TriPngEncoding<const AVERAGE_PALETTE: bool>();
pub struct TriPngEncoding<const AVERAGE_PALETTE: bool, const HASH_CONS_SPRITES: bool>();
impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_PALETTE> {
impl<const AVERAGE_PALETTE: bool, const HASH_CONS_SPRITES: bool> VoxelImageEncoding
for TriPngEncoding<AVERAGE_PALETTE, HASH_CONS_SPRITES>
{
type Output = CompressedData<(Vec<u8>, Vec<Rgb<u8>>, [usize; 3], Vec<[u8; 3]>)>;
type Workspace = (
ImageBuffer<image::Luma<u8>, Vec<u8>>,
@ -471,6 +491,7 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_
ImageBuffer<image::Luma<u8>, Vec<u8>>,
HashMap<BlockKind, HashMap<Rgb<u8>, usize>>,
Vec<[u8; 3]>,
HashMap<[u8; 3], u16>,
);
fn create(width: u32, height: u32) -> Self::Workspace {
@ -480,6 +501,7 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_
ImageBuffer::new(width, height),
HashMap::new(),
Vec::new(),
HashMap::new(),
)
}
@ -500,12 +522,24 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_
kind: BlockKind,
sprite_data: [u8; 3],
) {
let index = if HASH_CONS_SPRITES {
let index = ws.5.entry(sprite_data).or_insert_with(|| {
let index =
ws.4.len()
.try_into()
.expect("Cannot have more than 2^16 sprites in one chunk");
ws.4.push(sprite_data);
index
});
index.to_be_bytes()
} else {
let index: u16 =
ws.4.len()
.try_into()
.expect("Cannot have more than 2^16 sprites in one chunk");
let index = index.to_be_bytes();
ws.4.push(sprite_data);
index.to_be_bytes()
};
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
ws.1.put_pixel(x, y, image::Luma([index[0]]));
@ -561,7 +595,9 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageEncoding for TriPngEncoding<AVERAGE_
}
}
impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_PALETTE> {
impl<const AVERAGE_PALETTE: bool, const HASH_CONS_SPRITE: bool> VoxelImageDecoding
for TriPngEncoding<AVERAGE_PALETTE, HASH_CONS_SPRITE>
{
fn start(data: &Self::Output) -> Option<Self::Workspace> {
use image::codecs::png::PngDecoder;
let (quad, palette, indices, sprite_data) = data.decompress()?;
@ -585,7 +621,7 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
}
}
Some((a, b, c, d, sprite_data))
Some((a, b, c, d, sprite_data, HashMap::new()))
}
fn get_block(ws: &Self::Workspace, x: u32, y: u32, _: bool) -> Block {

View File

@ -83,8 +83,17 @@ pub type ServerRegisterAnswer = Result<(), RegisterError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SerializedTerrainChunk {
DeflatedChonk(CompressedData<TerrainChunk>),
QuadPng(WireChonk<QuadPngEncoding<4>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
TriPng(WireChonk<TriPngEncoding<false>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
QuadPng(
WireChonk<QuadPngEncoding<4, true>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>,
),
TriPng(
WireChonk<
TriPngEncoding<false, true>,
WidePacking<true>,
TerrainChunkMeta,
TerrainChunkSize,
>,
),
}
impl SerializedTerrainChunk {

View File

@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
io::{Read, Write},
mem,
sync::Arc,
time::Instant,
};
@ -980,10 +981,33 @@ fn main() {
let pngchonk_post = Instant::now();
sizes.extend_from_slice(&[
("jpegchonkgrid", jpegchonkgrid.0.len() as f32 / n as f32),
("jpegchonktall", jpegchonktall.0.len() as f32 / n as f32),
("jpegchonkflip", jpegchonkflip.0.len() as f32 / n as f32),
("pngchonk", pngchonk.0.len() as f32 / n as f32),
(
"jpegchonkgrid",
(jpegchonkgrid.0.len()
+ jpegchonkgrid.1.len() * mem::size_of::<[u8; 3]>())
as f32
/ n as f32,
),
(
"jpegchonktall",
(jpegchonktall.0.len()
+ jpegchonktall.1.len() * mem::size_of::<[u8; 3]>())
as f32
/ n as f32,
),
(
"jpegchonkflip",
(jpegchonkflip.0.len()
+ jpegchonkflip.1.len() * mem::size_of::<[u8; 3]>())
as f32
/ n as f32,
),
(
"pngchonk",
(pngchonk.0.len() + pngchonk.1.len() * mem::size_of::<[u8; 3]>())
as f32
/ n as f32,
),
]);
#[rustfmt::skip]
timings.extend_from_slice(&[
@ -1026,7 +1050,7 @@ fn main() {
let quadpngfull_pre = Instant::now();
let quadpngfull = image_terrain_chonk(
&QuadPngEncoding::<1>(),
&QuadPngEncoding::<1, true>(),
TallPacking { flip_y: true },
&chunk,
)
@ -1035,7 +1059,7 @@ fn main() {
let quadpnghalf_pre = Instant::now();
let quadpnghalf = image_terrain_chonk(
&QuadPngEncoding::<2>(),
&QuadPngEncoding::<2, true>(),
TallPacking { flip_y: true },
&chunk,
)
@ -1044,30 +1068,55 @@ fn main() {
let quadpngquarttall_pre = Instant::now();
let quadpngquarttall = image_terrain_chonk(
&QuadPngEncoding::<4>(),
&QuadPngEncoding::<4, false>(),
TallPacking { flip_y: true },
&chunk,
)
.unwrap();
let quadpngquarttall_post = Instant::now();
let quadpngquarttallhash_pre = Instant::now();
let quadpngquarttallhash = image_terrain_chonk(
&QuadPngEncoding::<4, true>(),
TallPacking { flip_y: true },
&chunk,
)
.unwrap();
let quadpngquarttallhash_post = Instant::now();
let quadpngquartwide_pre = Instant::now();
let quadpngquartwide =
image_terrain_chonk(&QuadPngEncoding::<4>(), WidePacking::<true>(), &chunk)
let quadpngquartwide = image_terrain_chonk(
&QuadPngEncoding::<4, true>(),
WidePacking::<true>(),
&chunk,
)
.unwrap();
let quadpngquartwide_post = Instant::now();
let tripngaverage_pre = Instant::now();
let tripngaverage =
image_terrain_chonk(&TriPngEncoding::<true>(), WidePacking::<true>(), &chunk)
let tripngaverage = image_terrain_chonk(
&TriPngEncoding::<true, true>(),
WidePacking::<true>(),
&chunk,
)
.unwrap();
let tripngaverage_post = Instant::now();
let tripngconst_pre = Instant::now();
let tripngconst =
image_terrain_chonk(&TriPngEncoding::<false>(), WidePacking::<true>(), &chunk)
let tripngconstbump_pre = Instant::now();
let tripngconstbump = image_terrain_chonk(
&TriPngEncoding::<false, false>(),
WidePacking::<true>(),
&chunk,
)
.unwrap();
let tripngconst_post = Instant::now();
let tripngconstbump_post = Instant::now();
let tripngconsthash_pre = Instant::now();
let tripngconsthash = image_terrain_chonk(
&TriPngEncoding::<false, true>(),
WidePacking::<true>(),
&chunk,
)
.unwrap();
let tripngconsthash_post = Instant::now();
let palette_kdtree_pre = Instant::now();
let palette_kdtree = image_terrain_chonk(
@ -1092,9 +1141,11 @@ fn main() {
("quadpngfull", quadpngfull.data.len() as f32 / n as f32),
("quadpnghalf", quadpnghalf.data.len() as f32 / n as f32),
("quadpngquarttall", quadpngquarttall.data.len() as f32 / n as f32),
("quadpngquarttallhash", quadpngquarttallhash.data.len() as f32 / n as f32),
("quadpngquartwide", quadpngquartwide.data.len() as f32 / n as f32),
("tripngaverage", tripngaverage.data.len() as f32 / n as f32),
("tripngconst", tripngconst.data.len() as f32 / n as f32),
("tripngconstbump", tripngconstbump.data.len() as f32 / n as f32),
("tripngconsthash", tripngconsthash.data.len() as f32 / n as f32),
("palette_kdtree", palette_kdtree.data.len() as f32 / n as f32),
("palette_rtree", palette_rtree.data.len() as f32 / n as f32),
]);
@ -1114,9 +1165,11 @@ fn main() {
("quadpngfull", (quadpngfull_post - quadpngfull_pre).subsec_nanos()),
("quadpnghalf", (quadpnghalf_post - quadpnghalf_pre).subsec_nanos()),
("quadpngquarttall", (quadpngquarttall_post - quadpngquarttall_pre).subsec_nanos()),
("quadpngquarttallhash", (quadpngquarttallhash_post - quadpngquarttallhash_pre).subsec_nanos()),
("quadpngquartwide", (quadpngquartwide_post - quadpngquartwide_pre).subsec_nanos()),
("tripngaverage", (tripngaverage_post - tripngaverage_pre).subsec_nanos()),
("tripngconst", (tripngconst_post - tripngconst_pre).subsec_nanos()),
("tripngconstbump", (tripngconstbump_post - tripngconstbump_pre).subsec_nanos()),
("tripngconsthash", (tripngconsthash_post - tripngconsthash_pre).subsec_nanos()),
("palette_kdtree", (palette_kdtree_post - palette_kdtree_pre).subsec_nanos()),
("palette_rtree", (palette_rtree_post - palette_rtree_pre).subsec_nanos()),
]);
@ -1156,7 +1209,7 @@ fn main() {
.entry(chunk.get_max_z() - chunk.get_min_z())
.or_insert((0, 0.0));
bucket.0 += 1;
bucket.1 += (tripngconst_post - tripngconst_pre).subsec_nanos() as f32;
bucket.1 += (tripngconstbump_post - tripngconstbump_pre).subsec_nanos() as f32;
}
if true {
let bucket = z_buckets