mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Improve quadpng
by adding WidePacking
, which makes a wider image, which is faster due to PNG compressing by row. Heuristically switch between quadpng and deflate based on chunk height to reduce variance.
This commit is contained in:
parent
dffc7db8f5
commit
cdc2eccda8
@ -89,9 +89,11 @@ pub struct TallPacking {
|
||||
}
|
||||
|
||||
impl PackingFormula for TallPacking {
|
||||
#[inline(always)]
|
||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x, dims.y * dims.z) }
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[inline(always)]
|
||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||
let i = x;
|
||||
let j0 = if self.flip_y {
|
||||
@ -104,6 +106,27 @@ impl PackingFormula for TallPacking {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct WidePacking<const FLIP_X: bool>();
|
||||
|
||||
impl<const FLIP_X: bool> PackingFormula for WidePacking<FLIP_X> {
|
||||
#[inline(always)]
|
||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x * dims.z, dims.y) }
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[inline(always)]
|
||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||
let i0 = if FLIP_X {
|
||||
if z % 2 == 0 { x } else { dims.x - x - 1 }
|
||||
} else {
|
||||
x
|
||||
};
|
||||
let i = z * dims.x + i0;
|
||||
let j = y;
|
||||
(i, j)
|
||||
}
|
||||
}
|
||||
|
||||
/// A grid of the z levels, left to right, top to bottom, like English prose.
|
||||
/// Convenient for visualizing terrain, but wastes space if the number of z
|
||||
/// levels isn't a perfect square.
|
||||
@ -111,12 +134,14 @@ impl PackingFormula for TallPacking {
|
||||
pub struct GridLtrPacking;
|
||||
|
||||
impl PackingFormula for GridLtrPacking {
|
||||
#[inline(always)]
|
||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) {
|
||||
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
||||
(dims.x * rootz, dims.y * rootz)
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[inline(always)]
|
||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
||||
let i = x + (z % rootz) * dims.x;
|
||||
@ -371,13 +396,15 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
|
||||
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||
ws.1.put_pixel(x, y, image::Luma([0]));
|
||||
ws.2.put_pixel(x, y, image::Luma([0]));
|
||||
//ws.1.put_pixel(x, y, image::Luma([0]));
|
||||
//ws.2.put_pixel(x, y, image::Luma([0]));
|
||||
ws.3.put_pixel(x / N, y / N, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn put_sprite(
|
||||
ws: &mut Self::Workspace,
|
||||
x: u32,
|
||||
@ -414,7 +441,7 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
|
||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||
&mut buf,
|
||||
CompressionType::Rle,
|
||||
FilterType::Paeth,
|
||||
FilterType::Sub,
|
||||
);
|
||||
png.encode(
|
||||
&*ws.3.as_raw(),
|
||||
|
@ -9,7 +9,7 @@ pub use self::{
|
||||
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
||||
compression::{
|
||||
CompressedData, GridLtrPacking, JpegEncoding, MixedEncoding, PackingFormula, PngEncoding,
|
||||
QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding, WireChonk,
|
||||
QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding, WidePacking, WireChonk,
|
||||
},
|
||||
ecs_packet::EcsCompPacket,
|
||||
server::{
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, MixedEncoding, PingMsg,
|
||||
QuadPngEncoding, TallPacking, TriPngEncoding, WireChonk,
|
||||
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, PingMsg, QuadPngEncoding,
|
||||
TriPngEncoding, WidePacking, WireChonk,
|
||||
};
|
||||
use crate::sync;
|
||||
use common::{
|
||||
@ -69,39 +69,25 @@ pub type ServerRegisterAnswer = Result<(), RegisterError>;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum SerializedTerrainChunk {
|
||||
DeflatedChonk(CompressedData<TerrainChunk>),
|
||||
PngPngPngJpeg(WireChonk<MixedEncoding, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
||||
QuadPng(WireChonk<QuadPngEncoding<4>, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
||||
TriPng(WireChonk<TriPngEncoding, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
||||
QuadPng(WireChonk<QuadPngEncoding<4>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
|
||||
TriPng(WireChonk<TriPngEncoding, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
|
||||
}
|
||||
|
||||
impl SerializedTerrainChunk {
|
||||
pub fn image(chunk: &TerrainChunk) -> Self {
|
||||
match 2 {
|
||||
0 => Self::deflate(chunk),
|
||||
1 => Self::jpeg(chunk),
|
||||
2 => Self::quadpng(chunk),
|
||||
_ => Self::tripng(chunk),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deflate(chunk: &TerrainChunk) -> Self {
|
||||
Self::DeflatedChonk(CompressedData::compress(chunk, 5))
|
||||
}
|
||||
|
||||
pub fn jpeg(chunk: &TerrainChunk) -> Self {
|
||||
if let Some(wc) = WireChonk::from_chonk(MixedEncoding, TallPacking { flip_y: true }, chunk)
|
||||
{
|
||||
Self::PngPngPngJpeg(wc)
|
||||
pub fn via_heuristic(chunk: &TerrainChunk) -> Self {
|
||||
if chunk.get_max_z() - chunk.get_min_z() < 128 {
|
||||
Self::quadpng(chunk)
|
||||
} else {
|
||||
warn!("Image encoding failure occurred, falling back to deflate");
|
||||
Self::deflate(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deflate(chunk: &TerrainChunk) -> Self {
|
||||
Self::DeflatedChonk(CompressedData::compress(chunk, 1))
|
||||
}
|
||||
|
||||
pub fn quadpng(chunk: &TerrainChunk) -> Self {
|
||||
if let Some(wc) =
|
||||
WireChonk::from_chonk(QuadPngEncoding(), TallPacking { flip_y: true }, chunk)
|
||||
{
|
||||
if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(), WidePacking(), chunk) {
|
||||
Self::QuadPng(wc)
|
||||
} else {
|
||||
warn!("Image encoding failure occurred, falling back to deflate");
|
||||
@ -110,8 +96,7 @@ impl SerializedTerrainChunk {
|
||||
}
|
||||
|
||||
pub fn tripng(chunk: &TerrainChunk) -> Self {
|
||||
if let Some(wc) = WireChonk::from_chonk(TriPngEncoding, TallPacking { flip_y: true }, chunk)
|
||||
{
|
||||
if let Some(wc) = WireChonk::from_chonk(TriPngEncoding, WidePacking(), chunk) {
|
||||
Self::TriPng(wc)
|
||||
} else {
|
||||
warn!("Image encoding failure occurred, falling back to deflate");
|
||||
@ -122,7 +107,6 @@ impl SerializedTerrainChunk {
|
||||
pub fn to_chunk(&self) -> Option<TerrainChunk> {
|
||||
match self {
|
||||
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
||||
Self::PngPngPngJpeg(wc) => wc.to_chonk(),
|
||||
Self::QuadPng(wc) => wc.to_chonk(),
|
||||
Self::TriPng(wc) => wc.to_chonk(),
|
||||
}
|
||||
|
@ -79,7 +79,9 @@ impl<'a> System<'a> for Sys {
|
||||
network_metrics.chunks_served_from_memory.inc();
|
||||
client.send(ServerGeneral::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(SerializedTerrainChunk::image(&chunk)),
|
||||
chunk: Ok(SerializedTerrainChunk::via_heuristic(
|
||||
&chunk,
|
||||
)),
|
||||
})?
|
||||
},
|
||||
None => {
|
||||
|
@ -224,7 +224,7 @@ impl<'a> System<'a> for Sys {
|
||||
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
||||
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Ok(SerializedTerrainChunk::image(&*chunk)),
|
||||
chunk: Ok(SerializedTerrainChunk::via_heuristic(&*chunk)),
|
||||
});
|
||||
let mut lazy_msg = None;
|
||||
|
||||
|
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
|
||||
key: *chunk_key,
|
||||
chunk: Ok(match terrain.get_key(*chunk_key) {
|
||||
Some(chunk) => SerializedTerrainChunk::image(&chunk),
|
||||
Some(chunk) => SerializedTerrainChunk::via_heuristic(&chunk),
|
||||
None => break 'chunk,
|
||||
}),
|
||||
}));
|
||||
|
@ -10,6 +10,7 @@ use common::{
|
||||
use common_net::msg::compression::{
|
||||
image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking, JpegEncoding,
|
||||
MixedEncoding, PngEncoding, QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding,
|
||||
WidePacking,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use image::ImageBuffer;
|
||||
@ -417,7 +418,7 @@ fn main() {
|
||||
bucket.0 += 1;
|
||||
bucket.1 += (lz4chonk_post - lz4chonk_pre).subsec_nanos() as f32;
|
||||
}
|
||||
{
|
||||
if false {
|
||||
let bucket = z_buckets
|
||||
.entry("rle")
|
||||
.or_default()
|
||||
@ -426,7 +427,7 @@ fn main() {
|
||||
bucket.0 += 1;
|
||||
bucket.1 += (rlechonk_post - rlechonk_pre).subsec_nanos() as f32;
|
||||
}
|
||||
{
|
||||
if false {
|
||||
let bucket = z_buckets
|
||||
.entry("deflate0")
|
||||
.or_default()
|
||||
@ -604,24 +605,32 @@ fn main() {
|
||||
.unwrap();
|
||||
let quadpnghalf_post = Instant::now();
|
||||
|
||||
let quadpngquart_pre = Instant::now();
|
||||
let quadpngquart = image_terrain_chonk(
|
||||
let quadpngquarttall_pre = Instant::now();
|
||||
let quadpngquarttall = image_terrain_chonk(
|
||||
QuadPngEncoding::<4>(),
|
||||
TallPacking { flip_y: true },
|
||||
&chunk,
|
||||
)
|
||||
.unwrap();
|
||||
let quadpngquart_post = Instant::now();
|
||||
let quadpngquarttall_post = Instant::now();
|
||||
|
||||
let quadpngquartwide_pre = Instant::now();
|
||||
let quadpngquartwide =
|
||||
image_terrain_chonk(QuadPngEncoding::<4>(), WidePacking::<true>(), &chunk)
|
||||
.unwrap();
|
||||
let quadpngquartwide_post = Instant::now();
|
||||
|
||||
let tripng_pre = Instant::now();
|
||||
let tripng =
|
||||
image_terrain_chonk(TriPngEncoding, TallPacking { flip_y: true }, &chunk)
|
||||
.unwrap();
|
||||
let tripng_post = Instant::now();
|
||||
#[rustfmt::skip]
|
||||
sizes.extend_from_slice(&[
|
||||
("quadpngfull", quadpngfull.data.len() as f32 / n as f32),
|
||||
("quadpnghalf", quadpnghalf.data.len() as f32 / n as f32),
|
||||
("quadpngquart", quadpngquart.data.len() as f32 / n as f32),
|
||||
("quadpngquarttall", quadpngquarttall.data.len() as f32 / n as f32),
|
||||
("quadpngquartwide", quadpngquartwide.data.len() as f32 / n as f32),
|
||||
("tripng", tripng.data.len() as f32 / n as f32),
|
||||
]);
|
||||
let best_idx = sizes
|
||||
@ -639,19 +648,31 @@ fn main() {
|
||||
timings.extend_from_slice(&[
|
||||
("quadpngfull", (quadpngfull_post - quadpngfull_pre).subsec_nanos()),
|
||||
("quadpnghalf", (quadpnghalf_post - quadpnghalf_pre).subsec_nanos()),
|
||||
("quadpngquart", (quadpngquart_post - quadpngquart_pre).subsec_nanos()),
|
||||
("quadpngquarttall", (quadpngquarttall_post - quadpngquarttall_pre).subsec_nanos()),
|
||||
("quadpngquartwide", (quadpngquartwide_post - quadpngquartwide_pre).subsec_nanos()),
|
||||
("tripng", (tripng_post - tripng_pre).subsec_nanos()),
|
||||
]);
|
||||
{
|
||||
let bucket = z_buckets
|
||||
.entry("quadpngquart")
|
||||
.entry("quadpngquarttall")
|
||||
.or_default()
|
||||
.entry(chunk.get_max_z() - chunk.get_min_z())
|
||||
.or_insert((0, 0.0));
|
||||
bucket.0 += 1;
|
||||
bucket.1 += (quadpngquart_post - quadpngquart_pre).subsec_nanos() as f32;
|
||||
bucket.1 +=
|
||||
(quadpngquarttall_post - quadpngquarttall_pre).subsec_nanos() as f32;
|
||||
}
|
||||
{
|
||||
let bucket = z_buckets
|
||||
.entry("quadpngquartwide")
|
||||
.or_default()
|
||||
.entry(chunk.get_max_z() - chunk.get_min_z())
|
||||
.or_insert((0, 0.0));
|
||||
bucket.0 += 1;
|
||||
bucket.1 +=
|
||||
(quadpngquartwide_post - quadpngquartwide_pre).subsec_nanos() as f32;
|
||||
}
|
||||
if false {
|
||||
let bucket = z_buckets
|
||||
.entry("tripng")
|
||||
.or_default()
|
||||
|
Loading…
Reference in New Issue
Block a user