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 {
|
impl PackingFormula for TallPacking {
|
||||||
|
#[inline(always)]
|
||||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x, dims.y * dims.z) }
|
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x, dims.y * dims.z) }
|
||||||
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
|
#[inline(always)]
|
||||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||||
let i = x;
|
let i = x;
|
||||||
let j0 = if self.flip_y {
|
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.
|
/// 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
|
/// Convenient for visualizing terrain, but wastes space if the number of z
|
||||||
/// levels isn't a perfect square.
|
/// levels isn't a perfect square.
|
||||||
@ -111,12 +134,14 @@ impl PackingFormula for TallPacking {
|
|||||||
pub struct GridLtrPacking;
|
pub struct GridLtrPacking;
|
||||||
|
|
||||||
impl PackingFormula for GridLtrPacking {
|
impl PackingFormula for GridLtrPacking {
|
||||||
|
#[inline(always)]
|
||||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) {
|
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) {
|
||||||
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
||||||
(dims.x * rootz, dims.y * rootz)
|
(dims.x * rootz, dims.y * rootz)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
|
#[inline(always)]
|
||||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
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 rootz = (dims.z as f64).sqrt().ceil() as u32;
|
||||||
let i = x + (z % rootz) * dims.x;
|
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>) {
|
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.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
ws.1.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.2.put_pixel(x, y, image::Luma([0]));
|
||||||
ws.3.put_pixel(x / N, y / N, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
ws.3.put_pixel(x / N, y / N, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn put_sprite(
|
fn put_sprite(
|
||||||
ws: &mut Self::Workspace,
|
ws: &mut Self::Workspace,
|
||||||
x: u32,
|
x: u32,
|
||||||
@ -414,7 +441,7 @@ impl<const N: u32> VoxelImageEncoding for QuadPngEncoding<N> {
|
|||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Rle,
|
CompressionType::Rle,
|
||||||
FilterType::Paeth,
|
FilterType::Sub,
|
||||||
);
|
);
|
||||||
png.encode(
|
png.encode(
|
||||||
&*ws.3.as_raw(),
|
&*ws.3.as_raw(),
|
||||||
|
@ -9,7 +9,7 @@ pub use self::{
|
|||||||
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
||||||
compression::{
|
compression::{
|
||||||
CompressedData, GridLtrPacking, JpegEncoding, MixedEncoding, PackingFormula, PngEncoding,
|
CompressedData, GridLtrPacking, JpegEncoding, MixedEncoding, PackingFormula, PngEncoding,
|
||||||
QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding, WireChonk,
|
QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding, WidePacking, WireChonk,
|
||||||
},
|
},
|
||||||
ecs_packet::EcsCompPacket,
|
ecs_packet::EcsCompPacket,
|
||||||
server::{
|
server::{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, MixedEncoding, PingMsg,
|
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, PingMsg, QuadPngEncoding,
|
||||||
QuadPngEncoding, TallPacking, TriPngEncoding, WireChonk,
|
TriPngEncoding, WidePacking, WireChonk,
|
||||||
};
|
};
|
||||||
use crate::sync;
|
use crate::sync;
|
||||||
use common::{
|
use common::{
|
||||||
@ -69,39 +69,25 @@ pub type ServerRegisterAnswer = Result<(), RegisterError>;
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum SerializedTerrainChunk {
|
pub enum SerializedTerrainChunk {
|
||||||
DeflatedChonk(CompressedData<TerrainChunk>),
|
DeflatedChonk(CompressedData<TerrainChunk>),
|
||||||
PngPngPngJpeg(WireChonk<MixedEncoding, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
QuadPng(WireChonk<QuadPngEncoding<4>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
|
||||||
QuadPng(WireChonk<QuadPngEncoding<4>, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
TriPng(WireChonk<TriPngEncoding, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
|
||||||
TriPng(WireChonk<TriPngEncoding, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerializedTerrainChunk {
|
impl SerializedTerrainChunk {
|
||||||
pub fn image(chunk: &TerrainChunk) -> Self {
|
pub fn via_heuristic(chunk: &TerrainChunk) -> Self {
|
||||||
match 2 {
|
if chunk.get_max_z() - chunk.get_min_z() < 128 {
|
||||||
0 => Self::deflate(chunk),
|
Self::quadpng(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)
|
|
||||||
} else {
|
} else {
|
||||||
warn!("Image encoding failure occurred, falling back to deflate");
|
|
||||||
Self::deflate(chunk)
|
Self::deflate(chunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deflate(chunk: &TerrainChunk) -> Self {
|
||||||
|
Self::DeflatedChonk(CompressedData::compress(chunk, 1))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn quadpng(chunk: &TerrainChunk) -> Self {
|
pub fn quadpng(chunk: &TerrainChunk) -> Self {
|
||||||
if let Some(wc) =
|
if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(), WidePacking(), chunk) {
|
||||||
WireChonk::from_chonk(QuadPngEncoding(), TallPacking { flip_y: true }, chunk)
|
|
||||||
{
|
|
||||||
Self::QuadPng(wc)
|
Self::QuadPng(wc)
|
||||||
} else {
|
} else {
|
||||||
warn!("Image encoding failure occurred, falling back to deflate");
|
warn!("Image encoding failure occurred, falling back to deflate");
|
||||||
@ -110,8 +96,7 @@ impl SerializedTerrainChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tripng(chunk: &TerrainChunk) -> Self {
|
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)
|
Self::TriPng(wc)
|
||||||
} else {
|
} else {
|
||||||
warn!("Image encoding failure occurred, falling back to deflate");
|
warn!("Image encoding failure occurred, falling back to deflate");
|
||||||
@ -122,7 +107,6 @@ impl SerializedTerrainChunk {
|
|||||||
pub fn to_chunk(&self) -> Option<TerrainChunk> {
|
pub fn to_chunk(&self) -> Option<TerrainChunk> {
|
||||||
match self {
|
match self {
|
||||||
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
||||||
Self::PngPngPngJpeg(wc) => wc.to_chonk(),
|
|
||||||
Self::QuadPng(wc) => wc.to_chonk(),
|
Self::QuadPng(wc) => wc.to_chonk(),
|
||||||
Self::TriPng(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();
|
network_metrics.chunks_served_from_memory.inc();
|
||||||
client.send(ServerGeneral::TerrainChunkUpdate {
|
client.send(ServerGeneral::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(SerializedTerrainChunk::image(&chunk)),
|
chunk: Ok(SerializedTerrainChunk::via_heuristic(
|
||||||
|
&chunk,
|
||||||
|
)),
|
||||||
})?
|
})?
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
@ -224,7 +224,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
||||||
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
|
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(SerializedTerrainChunk::image(&*chunk)),
|
chunk: Ok(SerializedTerrainChunk::via_heuristic(&*chunk)),
|
||||||
});
|
});
|
||||||
let mut lazy_msg = None;
|
let mut lazy_msg = None;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
|
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
|
||||||
key: *chunk_key,
|
key: *chunk_key,
|
||||||
chunk: Ok(match terrain.get_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,
|
None => break 'chunk,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
@ -10,6 +10,7 @@ use common::{
|
|||||||
use common_net::msg::compression::{
|
use common_net::msg::compression::{
|
||||||
image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking, JpegEncoding,
|
image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking, JpegEncoding,
|
||||||
MixedEncoding, PngEncoding, QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding,
|
MixedEncoding, PngEncoding, QuadPngEncoding, TallPacking, TriPngEncoding, VoxelImageEncoding,
|
||||||
|
WidePacking,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
@ -417,7 +418,7 @@ fn main() {
|
|||||||
bucket.0 += 1;
|
bucket.0 += 1;
|
||||||
bucket.1 += (lz4chonk_post - lz4chonk_pre).subsec_nanos() as f32;
|
bucket.1 += (lz4chonk_post - lz4chonk_pre).subsec_nanos() as f32;
|
||||||
}
|
}
|
||||||
{
|
if false {
|
||||||
let bucket = z_buckets
|
let bucket = z_buckets
|
||||||
.entry("rle")
|
.entry("rle")
|
||||||
.or_default()
|
.or_default()
|
||||||
@ -426,7 +427,7 @@ fn main() {
|
|||||||
bucket.0 += 1;
|
bucket.0 += 1;
|
||||||
bucket.1 += (rlechonk_post - rlechonk_pre).subsec_nanos() as f32;
|
bucket.1 += (rlechonk_post - rlechonk_pre).subsec_nanos() as f32;
|
||||||
}
|
}
|
||||||
{
|
if false {
|
||||||
let bucket = z_buckets
|
let bucket = z_buckets
|
||||||
.entry("deflate0")
|
.entry("deflate0")
|
||||||
.or_default()
|
.or_default()
|
||||||
@ -604,24 +605,32 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let quadpnghalf_post = Instant::now();
|
let quadpnghalf_post = Instant::now();
|
||||||
|
|
||||||
let quadpngquart_pre = Instant::now();
|
let quadpngquarttall_pre = Instant::now();
|
||||||
let quadpngquart = image_terrain_chonk(
|
let quadpngquarttall = image_terrain_chonk(
|
||||||
QuadPngEncoding::<4>(),
|
QuadPngEncoding::<4>(),
|
||||||
TallPacking { flip_y: true },
|
TallPacking { flip_y: true },
|
||||||
&chunk,
|
&chunk,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.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_pre = Instant::now();
|
||||||
let tripng =
|
let tripng =
|
||||||
image_terrain_chonk(TriPngEncoding, TallPacking { flip_y: true }, &chunk)
|
image_terrain_chonk(TriPngEncoding, TallPacking { flip_y: true }, &chunk)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tripng_post = Instant::now();
|
let tripng_post = Instant::now();
|
||||||
|
#[rustfmt::skip]
|
||||||
sizes.extend_from_slice(&[
|
sizes.extend_from_slice(&[
|
||||||
("quadpngfull", quadpngfull.data.len() as f32 / n as f32),
|
("quadpngfull", quadpngfull.data.len() as f32 / n as f32),
|
||||||
("quadpnghalf", quadpnghalf.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),
|
("tripng", tripng.data.len() as f32 / n as f32),
|
||||||
]);
|
]);
|
||||||
let best_idx = sizes
|
let best_idx = sizes
|
||||||
@ -639,19 +648,31 @@ fn main() {
|
|||||||
timings.extend_from_slice(&[
|
timings.extend_from_slice(&[
|
||||||
("quadpngfull", (quadpngfull_post - quadpngfull_pre).subsec_nanos()),
|
("quadpngfull", (quadpngfull_post - quadpngfull_pre).subsec_nanos()),
|
||||||
("quadpnghalf", (quadpnghalf_post - quadpnghalf_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()),
|
("tripng", (tripng_post - tripng_pre).subsec_nanos()),
|
||||||
]);
|
]);
|
||||||
{
|
{
|
||||||
let bucket = z_buckets
|
let bucket = z_buckets
|
||||||
.entry("quadpngquart")
|
.entry("quadpngquarttall")
|
||||||
.or_default()
|
.or_default()
|
||||||
.entry(chunk.get_max_z() - chunk.get_min_z())
|
.entry(chunk.get_max_z() - chunk.get_min_z())
|
||||||
.or_insert((0, 0.0));
|
.or_insert((0, 0.0));
|
||||||
bucket.0 += 1;
|
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
|
let bucket = z_buckets
|
||||||
.entry("tripng")
|
.entry("tripng")
|
||||||
.or_default()
|
.or_default()
|
||||||
|
Loading…
Reference in New Issue
Block a user