mirror of
https://github.com/neocturne/MinedMap.git
synced 2025-03-05 01:24:53 +01:00
minedmap: skip generation steps when the inputs are unchanged
This commit is contained in:
parent
6077138292
commit
80781c9c20
4 changed files with 97 additions and 50 deletions
|
@ -66,43 +66,46 @@ impl<'a> RegionProcessor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_region(
|
fn save_region(
|
||||||
&self,
|
path: &Path,
|
||||||
coords: TileCoords,
|
|
||||||
processed_region: &ProcessedRegion,
|
processed_region: &ProcessedRegion,
|
||||||
timestamp: SystemTime,
|
timestamp: SystemTime,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let output_path = self.config.processed_path(coords);
|
storage::write(path, processed_region, FILE_META_VERSION, timestamp)
|
||||||
storage::write(&output_path, processed_region, FILE_META_VERSION, timestamp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_lightmap(
|
fn save_lightmap(
|
||||||
&self,
|
path: &Path,
|
||||||
coords: TileCoords,
|
|
||||||
lightmap: &image::GrayAlphaImage,
|
lightmap: &image::GrayAlphaImage,
|
||||||
timestamp: SystemTime,
|
timestamp: SystemTime,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
fs::create_with_timestamp(
|
fs::create_with_timestamp(path, FILE_META_VERSION, timestamp, |file| {
|
||||||
&self.config.tile_path(TileKind::Lightmap, 0, coords),
|
lightmap
|
||||||
FILE_META_VERSION,
|
.write_to(file, image::ImageFormat::Png)
|
||||||
timestamp,
|
.context("Failed to save image")
|
||||||
|file| {
|
})
|
||||||
lightmap
|
|
||||||
.write_to(file, image::ImageFormat::Png)
|
|
||||||
.context("Failed to save image")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a single region file
|
/// Processes a single region file
|
||||||
fn process_region(&self, path: &Path, coords: TileCoords) -> Result<()> {
|
fn process_region(&self, path: &Path, coords: TileCoords) -> Result<()> {
|
||||||
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
||||||
|
|
||||||
println!("Processing region r.{}.{}.mca", coords.x, coords.z);
|
|
||||||
|
|
||||||
let mut processed_region = ProcessedRegion::default();
|
let mut processed_region = ProcessedRegion::default();
|
||||||
let mut lightmap = image::GrayAlphaImage::new(N, N);
|
let mut lightmap = image::GrayAlphaImage::new(N, N);
|
||||||
|
|
||||||
let timestamp = fs::modified_timestamp(path)?;
|
let input_timestamp = fs::modified_timestamp(path)?;
|
||||||
|
|
||||||
|
let output_path = self.config.processed_path(coords);
|
||||||
|
let output_timestamp = fs::read_timestamp(&output_path, FILE_META_VERSION);
|
||||||
|
let lightmap_path = self.config.tile_path(TileKind::Lightmap, 0, coords);
|
||||||
|
let lightmap_timestamp = fs::read_timestamp(&lightmap_path, FILE_META_VERSION);
|
||||||
|
|
||||||
|
if Some(input_timestamp) <= output_timestamp && Some(input_timestamp) <= lightmap_timestamp
|
||||||
|
{
|
||||||
|
println!("Skipping unchanged region r.{}.{}.mca", coords.x, coords.z);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Processing region r.{}.{}.mca", coords.x, coords.z);
|
||||||
|
|
||||||
minedmap::io::region::from_file(path)?.foreach_chunk(
|
minedmap::io::region::from_file(path)?.foreach_chunk(
|
||||||
|chunk_coords, data: world::de::Chunk| {
|
|chunk_coords, data: world::de::Chunk| {
|
||||||
|
@ -125,8 +128,12 @@ impl<'a> RegionProcessor<'a> {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.save_region(coords, &processed_region, timestamp)?;
|
if Some(input_timestamp) > output_timestamp {
|
||||||
self.save_lightmap(coords, &lightmap, timestamp)?;
|
Self::save_region(&output_path, &processed_region, input_timestamp)?;
|
||||||
|
}
|
||||||
|
if Some(input_timestamp) > lightmap_timestamp {
|
||||||
|
Self::save_lightmap(&lightmap_path, &lightmap, input_timestamp)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,17 +49,7 @@ impl<'a> TileMipmapper<'a> {
|
||||||
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
||||||
|
|
||||||
let output_path = self.config.tile_path(kind, level, coords);
|
let output_path = self.config.tile_path(kind, level, coords);
|
||||||
|
let output_timestamp = fs::read_timestamp(&output_path, FILE_META_VERSION);
|
||||||
println!(
|
|
||||||
"Rendering mipmap tile {}",
|
|
||||||
output_path
|
|
||||||
.strip_prefix(&self.config.output_dir)
|
|
||||||
.expect("tile path must be in output directory")
|
|
||||||
.display(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut image: image::DynamicImage =
|
|
||||||
image::ImageBuffer::<P, Vec<P::Subpixel>>::new(N, N).into();
|
|
||||||
|
|
||||||
let sources: Vec<_> = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
let sources: Vec<_> = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -84,10 +74,32 @@ impl<'a> TileMipmapper<'a> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let Some(timestamp) = sources.iter().map(|(_, _, ts)| *ts).max() else {
|
let Some(input_timestamp) = sources.iter().map(|(_, _, ts)| *ts).max() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if Some(input_timestamp) <= output_timestamp {
|
||||||
|
println!(
|
||||||
|
"Skipping unchanged mipmap tile {}",
|
||||||
|
output_path
|
||||||
|
.strip_prefix(&self.config.output_dir)
|
||||||
|
.expect("tile path must be in output directory")
|
||||||
|
.display(),
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Rendering mipmap tile {}",
|
||||||
|
output_path
|
||||||
|
.strip_prefix(&self.config.output_dir)
|
||||||
|
.expect("tile path must be in output directory")
|
||||||
|
.display(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut image: image::DynamicImage =
|
||||||
|
image::ImageBuffer::<P, Vec<P::Subpixel>>::new(N, N).into();
|
||||||
|
|
||||||
for ((dx, dz), source_path, _) in sources {
|
for ((dx, dz), source_path, _) in sources {
|
||||||
let source = match image::open(&source_path) {
|
let source = match image::open(&source_path) {
|
||||||
Ok(source) => source,
|
Ok(source) => source,
|
||||||
|
@ -109,7 +121,7 @@ impl<'a> TileMipmapper<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::create_with_timestamp(&output_path, FILE_META_VERSION, timestamp, |file| {
|
fs::create_with_timestamp(&output_path, FILE_META_VERSION, input_timestamp, |file| {
|
||||||
image
|
image
|
||||||
.write_to(file, image::ImageFormat::Png)
|
.write_to(file, image::ImageFormat::Png)
|
||||||
.context("Failed to save image")
|
.context("Failed to save image")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::time::SystemTime;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
@ -19,12 +19,8 @@ impl<'a> TileRenderer<'a> {
|
||||||
TileRenderer { config }
|
TileRenderer { config }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_region(&self, coords: TileCoords) -> Result<(ProcessedRegion, SystemTime)> {
|
fn load_region(processed_path: &Path) -> Result<ProcessedRegion> {
|
||||||
let processed_path = self.config.processed_path(coords);
|
storage::read(processed_path).context("Failed to load processed region data")
|
||||||
let timestamp = fs::modified_timestamp(&processed_path)?;
|
|
||||||
let region =
|
|
||||||
storage::read(&processed_path).context("Failed to load processed region data")?;
|
|
||||||
Ok((region, timestamp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(image: &mut image::RgbaImage, coords: ChunkCoords, chunk: &ProcessedChunk) {
|
fn render_chunk(image: &mut image::RgbaImage, coords: ChunkCoords, chunk: &ProcessedChunk) {
|
||||||
|
@ -64,7 +60,22 @@ impl<'a> TileRenderer<'a> {
|
||||||
fn render_tile(&self, coords: TileCoords) -> Result<()> {
|
fn render_tile(&self, coords: TileCoords) -> Result<()> {
|
||||||
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
const N: u32 = (BLOCKS_PER_CHUNK * CHUNKS_PER_REGION) as u32;
|
||||||
|
|
||||||
|
let processed_path = self.config.processed_path(coords);
|
||||||
|
let processed_timestamp = fs::modified_timestamp(&processed_path)?;
|
||||||
|
|
||||||
let output_path = self.config.tile_path(TileKind::Map, 0, coords);
|
let output_path = self.config.tile_path(TileKind::Map, 0, coords);
|
||||||
|
let output_timestamp = fs::read_timestamp(&output_path, FILE_META_VERSION);
|
||||||
|
|
||||||
|
if Some(processed_timestamp) <= output_timestamp {
|
||||||
|
println!(
|
||||||
|
"Skipping unchanged tile {}",
|
||||||
|
output_path
|
||||||
|
.strip_prefix(&self.config.output_dir)
|
||||||
|
.expect("tile path must be in output directory")
|
||||||
|
.display(),
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Rendering tile {}",
|
"Rendering tile {}",
|
||||||
|
@ -74,15 +85,20 @@ impl<'a> TileRenderer<'a> {
|
||||||
.display(),
|
.display(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (region, timestamp) = self.load_region(coords)?;
|
let region = Self::load_region(&processed_path)?;
|
||||||
let mut image = image::RgbaImage::new(N, N);
|
let mut image = image::RgbaImage::new(N, N);
|
||||||
Self::render_region(&mut image, ®ion);
|
Self::render_region(&mut image, ®ion);
|
||||||
|
|
||||||
fs::create_with_timestamp(&output_path, FILE_META_VERSION, timestamp, |file| {
|
fs::create_with_timestamp(
|
||||||
image
|
&output_path,
|
||||||
.write_to(file, image::ImageFormat::Png)
|
FILE_META_VERSION,
|
||||||
.context("Failed to save image")
|
processed_timestamp,
|
||||||
})
|
|file| {
|
||||||
|
image
|
||||||
|
.write_to(file, image::ImageFormat::Png)
|
||||||
|
.context("Failed to save image")
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self, regions: &[TileCoords]) -> Result<()> {
|
pub fn run(self, regions: &[TileCoords]) -> Result<()> {
|
||||||
|
|
18
src/io/fs.rs
18
src/io/fs.rs
|
@ -6,12 +6,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Ok, Result};
|
use anyhow::{Context, Ok, Result};
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct FileMetaVersion(pub u32);
|
pub struct FileMetaVersion(pub u32);
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct FileMeta {
|
struct FileMeta {
|
||||||
version: FileMetaVersion,
|
version: FileMetaVersion,
|
||||||
timestamp: SystemTime,
|
timestamp: SystemTime,
|
||||||
|
@ -115,6 +115,18 @@ pub fn modified_timestamp(path: &Path) -> Result<SystemTime> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_timestamp(path: &Path, version: FileMetaVersion) -> Option<SystemTime> {
|
||||||
|
let meta_path = metafile_name(path);
|
||||||
|
let mut file = BufReader::new(fs::File::open(meta_path).ok()?);
|
||||||
|
|
||||||
|
let meta: FileMeta = serde_json::from_reader(&mut file).ok()?;
|
||||||
|
if meta.version != version {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(meta.timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_with_timestamp<T, F>(
|
pub fn create_with_timestamp<T, F>(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
version: FileMetaVersion,
|
version: FileMetaVersion,
|
||||||
|
|
Loading…
Add table
Reference in a new issue