Gjallarhorn: Block Color Key and Render Pool Enhancements

This commit is contained in:
Kenneth Endfinger
2022-01-09 04:03:07 -05:00
parent 94d644916b
commit 2d429ae04d
6 changed files with 48 additions and 33 deletions

View File

@ -2,13 +2,14 @@ package cloud.kubelet.foundation.gjallarhorn.render
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
import cloud.kubelet.foundation.gjallarhorn.state.BlockMap
import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey
import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey
import cloud.kubelet.foundation.gjallarhorn.util.defaultBlockColorMap
import java.awt.Color
import java.awt.image.BufferedImage
class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) :
BlockGridRenderer(quadPixelSize) {
private val randomColorKey = RandomColorKey()
private val blockColorKey = BlockColorKey(defaultBlockColorMap)
override fun render(map: BlockMap): BufferedImage = buildPixelQuadImage(expanse) { graphics, x, z ->
val maybeYBlocks = map.blocks[x]?.get(z)
@ -22,7 +23,7 @@ class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = def
return@buildPixelQuadImage
}
val color = randomColorKey.map(maxBlockState.type)
val color = blockColorKey.map(maxBlockState.type)
setPixelQuad(graphics, x, z, color.rgb)
}
}

View File

@ -6,10 +6,10 @@ import java.time.Instant
data class BlockChangelogSlice(val from: Instant, val to: Instant, val relative: Duration) {
constructor(from: Instant, to: Instant) : this(from, to, Duration.ofMillis(to.toEpochMilli() - from.toEpochMilli()))
fun changeResolutionTime(): Instant = to.minus(relative)
val changeResolutionTime: Instant = to.minus(relative)
fun isTimeWithin(time: Instant) = time in from..to
fun isRelativeWithin(time: Instant) = time in changeResolutionTime()..to
fun isRelativeWithin(time: Instant) = time in changeResolutionTime..to
fun split(): List<BlockChangelogSlice> {
val half = relative.dividedBy(2)

View File

@ -16,12 +16,12 @@ class BlockMapRenderPool<T>(
private val playbackJobFutures = ConcurrentHashMap<BlockChangelogSlice, Future<*>>()
private val renderJobFutures = ConcurrentHashMap<BlockChangelogSlice, Future<*>>()
fun submitPlaybackJob(slice: BlockChangelogSlice) {
fun submitPlaybackJob(id: String, slice: BlockChangelogSlice) {
val future = threadPoolExecutor.submit {
try {
runPlaybackSlice(slice)
runPlaybackSlice(id, slice)
} catch (e: Exception) {
logger.error("Failed to run playback job for slice $slice", e)
logger.error("Failed to run playback job for slice $id", e)
}
}
playbackJobFutures[slice] = future
@ -41,7 +41,7 @@ class BlockMapRenderPool<T>(
fun render(slices: List<BlockChangelogSlice>) {
for (slice in slices) {
submitPlaybackJob(slice)
submitPlaybackJob((slices.indexOf(slice) + 1).toString(), slice)
}
for (future in playbackJobFutures.values) {
@ -55,16 +55,22 @@ class BlockMapRenderPool<T>(
}
}
private fun runPlaybackSlice(slice: BlockChangelogSlice) {
private fun runPlaybackSlice(id: String, slice: BlockChangelogSlice) {
val start = System.currentTimeMillis()
val sliced = changelog.slice(slice)
val tracker = BlockLogTracker(blockTrackMode)
tracker.replay(sliced)
delegate.postProcessTracker(tracker)
if (tracker.isNotEmpty()) {
trackers[slice] = tracker
}
val end = System.currentTimeMillis()
val timeInMilliseconds = end - start
logger.info("Playback Completed for Slice $id in ${timeInMilliseconds}ms")
}
interface RenderPoolDelegate<T> {
fun postProcessTracker(tracker: BlockLogTracker)
fun buildRenderJobs(pool: BlockMapRenderPool<T>, trackers: MutableMap<BlockChangelogSlice, BlockLogTracker>)
}

View File

@ -2,6 +2,7 @@ package cloud.kubelet.foundation.gjallarhorn.state
import java.time.Duration
import java.time.Instant
import java.util.stream.Stream
class BlockMapTimelapse<T>(val trim: Pair<BlockCoordinate, BlockCoordinate>? = null) :
BlockMapRenderPool.RenderPoolDelegate<T> {
@ -28,35 +29,23 @@ class BlockMapTimelapse<T>(val trim: Pair<BlockCoordinate, BlockCoordinate>? = n
minimumTimeInterval: Duration,
slices: List<BlockChangelogSlice>
): List<BlockChangelogSlice> {
return slices.flatMap { slice ->
return slices.parallelStream().flatMap { slice ->
val count = changelog.countRelativeChangesInSlice(slice)
if (count < targetChangeThreshold ||
slice.relative < minimumTimeInterval
) {
return@flatMap listOf(slice)
return@flatMap Stream.of(slice)
}
val split = slice.split()
return@flatMap splitChangelogSlicesWithThreshold(changelog, targetChangeThreshold, minimumTimeInterval, split)
}
return@flatMap splitChangelogSlicesWithThreshold(changelog, targetChangeThreshold, minimumTimeInterval, split).parallelStream()
}.toList()
}
override fun buildRenderJobs(
pool: BlockMapRenderPool<T>,
trackers: MutableMap<BlockChangelogSlice, BlockLogTracker>
) {
if (trim != null) {
trackers.values.forEach { tracker ->
tracker.trimOutsideXAndZRange(trim.first, trim.second)
}
}
for ((slice, tracker) in trackers.entries.toList()) {
if (tracker.isEmpty()) {
trackers.remove(slice)
}
}
val allBlockOffsets = trackers.map { it.value.calculateZeroBlockOffset() }
val globalBlockOffset = BlockCoordinate.maxOf(allBlockOffsets)
val allBlockMaxes = trackers.map { it.value.calculateMaxBlock() }
@ -65,14 +54,16 @@ class BlockMapTimelapse<T>(val trim: Pair<BlockCoordinate, BlockCoordinate>? = n
val renderer = pool.rendererFactory(globalBlockExpanse)
for ((slice, tracker) in trackers) {
if (tracker.isEmpty()) {
continue
}
pool.submitRenderJob(slice) {
val map = tracker.buildBlockMap(globalBlockExpanse.offset)
renderer.render(map)
}
}
}
override fun postProcessTracker(tracker: BlockLogTracker) {
if (trim != null) {
tracker.trimOutsideXAndZRange(trim.first, trim.second)
}
}
}

View File

@ -3,10 +3,10 @@ package cloud.kubelet.foundation.gjallarhorn.util
import java.awt.Color
import java.util.concurrent.ConcurrentHashMap
class RandomColorKey {
private val colors = ConcurrentHashMap<String, Color>()
class BlockColorKey(assigned: Map<String, Color>) {
private val colors = ConcurrentHashMap(assigned)
fun map(key: String) = colors.computeIfAbsent(key) { findUniqueColor() }
fun map(key: String): Color = colors.computeIfAbsent(key) { findUniqueColor() }
private fun findUniqueColor(): Color {
var random = randomColor()

View File

@ -0,0 +1,17 @@
package cloud.kubelet.foundation.gjallarhorn.util
import java.awt.Color
val defaultBlockColorMap = mapOf<String, Color>(
"minecraft:air" to Color.black,
"minecraft:dirt" to Color.decode("#9b7653"),
"minecraft:farmland" to Color.decode("#5d3f2a"),
"minecraft:stone" to Color.decode("#787366"),
"minecraft:cobblestone" to Color.decode("#c4bca7"),
"minecraft:wheat" to Color.decode("#9e884c"),
"minecraft:carrots" to Color.decode("#f89d40"),
"minecraft:stone_brick_stairs" to Color.decode("#b8a18c"),
"minecraft:dirt_path" to Color.decode("#8f743d"),
"minecraft:deepslate_tiles" to Color.decode("#49494b"),
"minecraft:spruce_planks" to Color.decode("#60492d")
)