diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 487b4c5f..928019a1 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -75,7 +75,7 @@ fun main() { for (chunkX in 0 .. 100) { //for (chunkX in 0 .. 17) { // for (chunkY in 21 .. 21) { - for (chunkY in 0 .. 24) { + for (chunkY in 18 .. 24) { val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte())) val data2 = db.read(byteArrayOf(2, 0, chunkX.toByte(), 0, chunkY.toByte())) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 19b6f43a..ac21dad0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -88,7 +88,7 @@ class StarboundClient : Closeable { val thread: Thread = Thread.currentThread() // client specific executor which will accept tasks which involve probable // callback to foreground executor to initialize thread-unsafe data - // In above case multiple threads will introduce big congestion for resources + // In above case too many threads will introduce big congestion for resources, stalling entire workload; wasting cpu resources val backgroundExecutor = ForkJoinPool(Runtime.getRuntime().availableProcessors().coerceAtMost(4)) val foregroundExecutor = ManualExecutorService(thread) val capabilities: GLCapabilities diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/LightCalculator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/LightCalculator.kt index 1267d584..5228934f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/LightCalculator.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/LightCalculator.kt @@ -8,8 +8,13 @@ import ru.dbotthepony.kvector.arrays.Object2DArray import ru.dbotthepony.kvector.util.linearInterpolation import ru.dbotthepony.kvector.vector.RGBAColor import java.nio.ByteBuffer +import java.util.concurrent.Callable +import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.Future import java.util.concurrent.locks.LockSupport +import java.util.function.Supplier import kotlin.math.roundToInt import kotlin.random.Random @@ -380,10 +385,9 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) // perform multithreaded calculation only when it makes sense if (multithreaded && pointLights.size > 1) { val thread = Thread.currentThread() - // calculate k-means clusters of point lights // to effectively utilize CPU cores - val clusterCount = threads.size.coerceAtMost(pointLights.size) + val clusterCount = ForkJoinPool.commonPool().parallelism.coerceAtMost(pointLights.size) val clusters = ArrayList(clusterCount) val startingPoints = IntArraySet() // val rand = Random(System.nanoTime()) @@ -394,7 +398,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) } for (index in startingPoints.intIterator()) { - clusters.add(TaskCluster(pointLights[index].x.toDouble(), pointLights[index].y.toDouble(), this, thread)) + clusters.add(TaskCluster(pointLights[index].x.toDouble(), pointLights[index].y.toDouble(), this)) } var converged = false @@ -459,25 +463,14 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) } } - /*for ((i, cluster) in clusters.withIndex()) { - val (r, g, b) = clusterColors[i] + val tasks = ArrayList>() + clusters.forEach { tasks.add(CompletableFuture.supplyAsync(it, ForkJoinPool.commonPool()).also { it.thenApply { LockSupport.unpark(thread); it } }) } - for (light in cluster.lights) { - light.red = r - light.green = g - light.blue = b - } - }*/ + while (tasks.isNotEmpty()) { + tasks.removeIf { + if (it.isDone) { + val grid = it.get() - tasks.addAll(clusters) - wakeup() - - while (clusters.isNotEmpty()) { - clusters.removeIf { - val grid = it.grid - - // wait until thread local caches are refreshed - if (grid != null) { if (targetMem != null) { for (x in grid.minX - 1 .. grid.maxX) { for (y in grid.minY - 1 .. grid.maxY) { @@ -522,10 +515,10 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) } } - grid != null + it.isDone } - LockSupport.parkNanos(500_000) + LockSupport.parkNanos(500_000L) } } else { val grid = Grid() @@ -589,8 +582,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) var x: Double, var y: Double, val parent: LightCalculator, - val parentThread: Thread - ) { + ) : Supplier { val lights = ArrayList() fun updateCenter() { @@ -608,13 +600,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) y /= lights.size } - @Volatile - var grid: Grid? = null - private set - - fun execute() { - if (grid != null) return - + override fun get(): Grid { val grid = parent.Grid() for (light in lights) { @@ -635,46 +621,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int) } grid.calculateSpread() - this.grid = grid - LockSupport.unpark(parentThread) - } - } - - companion object { - private val tasks = ConcurrentLinkedQueue() - private val threads = ArrayList() - - private val clusterColors = ArrayList() - - private fun wakeup() { - for (thread in threads) { - LockSupport.unpark(thread) - } - } - - private fun thread() { - while (true) { - val next = tasks.poll() - - if (next == null) { - LockSupport.park() - continue - } - - next.execute() - } - } - - init { - for (i in 0 until Runtime.getRuntime().availableProcessors()) { - Thread(::thread, "Starbound Lighting Thread $i").also { threads.add(it); it.isDaemon = true }.start() - } - - val rand = Random(System.nanoTime()) - - for (i in threads.indices) { - clusterColors.add(RGBAColor(rand.nextFloat() * 0.5f + 0.5f, rand.nextFloat() * 0.5f + 0.5f, rand.nextFloat() * 0.5f + 0.5f)) - } + return grid } } }