parallel light always does this random noise

This commit is contained in:
minjaesong
2019-01-18 04:24:14 +09:00
parent 784a6a13e3
commit 68df2a223e
3 changed files with 77 additions and 85 deletions

View File

@@ -1,11 +1,6 @@
package net.torvald.terrarum.concurrent package net.torvald.terrarum.concurrent
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.lock
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.collections.ArrayList
typealias RunnableFun = () -> Unit typealias RunnableFun = () -> Unit
/** Int: index of the processing core */ /** Int: index of the processing core */
@@ -174,9 +169,9 @@ object ParallelUtils {
return al return al
} }
fun Iterable<*>.sliceEvenly(slices: Int): List<List<*>> = this.toList().sliceEvenly(slices) fun <T> Iterable<T>.sliceEvenly(slices: Int): List<List<T>> = this.toList().sliceEvenly(slices)
fun List<*>.sliceEvenly(slices: Int): List<List<*>> { fun <T> List<T>.sliceEvenly(slices: Int): List<List<T>> {
return (0 until slices).map { return (0 until slices).map {
this.subList( this.subList(
this.size.toFloat().div(slices).times(it).roundInt(), this.size.toFloat().div(slices).times(it).roundInt(),
@@ -185,7 +180,7 @@ object ParallelUtils {
} }
} }
fun Array<*>.sliceEvenly(slices: Int): List<Array<*>> { fun <T> Array<T>.sliceEvenly(slices: Int): List<Array<T>> {
return (0 until slices).map { return (0 until slices).map {
this.sliceArray( this.sliceArray(
this.size.toFloat().div(slices).times(it).roundInt() until this.size.toFloat().div(slices).times(it).roundInt() until

View File

@@ -492,7 +492,7 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
//renderGame(batch) //renderGame(batch)
AppLoader.debugTimers["Ingame.render"] = measureNanoTime { renderGame() } AppLoader.debugTimers["Ingame.render"] = measureNanoTime { renderGame() }
AppLoader.debugTimers["Ingame.render-Light"] = AppLoader.debugTimers["Ingame.render-Light"] =
(AppLoader.debugTimers["Ingame.render"] as Long) - ((AppLoader.debugTimers["Renderer.LightSequential"] as? Long) ?: 0) (AppLoader.debugTimers["Ingame.render"] as Long) - ((AppLoader.debugTimers["Renderer.LightTotal"] as? Long) ?: 0)
} }
protected fun updateGame(delta: Float) { protected fun updateGame(delta: Float) {

View File

@@ -17,6 +17,7 @@ import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameactors.Luminous import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.IngameRenderer
import java.util.concurrent.atomic.AtomicReferenceArray
import kotlin.system.measureNanoTime import kotlin.system.measureNanoTime
/** /**
@@ -51,6 +52,8 @@ object LightmapRenderer {
for (i in 0 until lightmap.size) { for (i in 0 until lightmap.size) {
lightmap[i] = colourNull lightmap[i] = colourNull
} }
makeUpdateTaskList()
} }
} }
catch (e: UninitializedPropertyAccessException) { catch (e: UninitializedPropertyAccessException) {
@@ -278,102 +281,96 @@ object LightmapRenderer {
} }
} }
AppLoader.debugTimers["Renderer.LightSequential"] = AppLoader.debugTimers["Renderer.LightTotal"] =
(AppLoader.debugTimers["Renderer.Light1"]!! as Long) + (AppLoader.debugTimers["Renderer.Light1"]!! as Long) +
(AppLoader.debugTimers["Renderer.Light2"]!! as Long) + (AppLoader.debugTimers["Renderer.Light2"]!! as Long) +
(AppLoader.debugTimers["Renderer.Light3"]!! as Long) + (AppLoader.debugTimers["Renderer.Light3"]!! as Long) +
(AppLoader.debugTimers["Renderer.Light4"]!! as Long) + (AppLoader.debugTimers["Renderer.Light4"]!! as Long) +
(AppLoader.debugTimers["Renderer.Light0"]!! as Long) (AppLoader.debugTimers["Renderer.Light0"]!! as Long)
} }
else { else if (world.worldIndex != -1) { // to avoid updating on the null world
AppLoader.debugTimers["Renderer.LightPre"] = measureNanoTime { val buf = AtomicReferenceArray<Color>(lightmap.size)
val bufferForPasses = arrayOf( AppLoader.debugTimers["Renderer.LightPrlPre"] = measureNanoTime {
lightmap.copyOf(), lightmap.copyOf(), lightmap.copyOf(), lightmap.copyOf() // update the content of buf using maxBlend -- it's not meant for overwrite
)
//val combiningBuffer = Array(lightmap.size) { colourNull }
// this is kind of inefficient... updateMessages.forEachIndexed { index, msg ->
val calcTask = ArrayList<ThreadedLightmapUpdateMessage>() ThreadParallel.map(index, "Light") {
// for the message slices...
msg.forEach { m ->
// update the content of buf using maxBlend -- it's not meant for overwrite
buf.getAndUpdate(m.y * LIGHTMAP_WIDTH + m.x) { oldCol ->
val ux = m.x + for_x_start - overscan_open
val uy = m.y + for_y_start - overscan_open
// Round 1 preload (oldCol ?: colourNull) maxBlend calculate(ux, uy)
for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_start - overscan_open..for_x_end) {
calcTask.add(ThreadedLightmapUpdateMessage(x, y, 1))
}
}
// Round 2 preload
for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_start - overscan_open..for_x_end) {
calcTask.add(ThreadedLightmapUpdateMessage(x, y, 2))
}
}
// Round 3 preload
for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_end + overscan_open downTo for_x_start) {
calcTask.add(ThreadedLightmapUpdateMessage(x, y, 3))
}
}
// Round 4 preload
for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_end + overscan_open downTo for_x_start) {
calcTask.add(ThreadedLightmapUpdateMessage(x, y, 4))
}
}
val calcTasks = calcTask.sliceEvenly(Terrarum.THREADS)
val combineTasks = (0 until lightmap.size).sliceEvenly(Terrarum.THREADS)
// couldn't help but do this nested timer
AppLoader.debugTimers["Renderer.LightParallel${Terrarum.THREADS}x"] = measureNanoTime {
calcTasks.forEachIndexed { index, list ->
ThreadParallel.map(index, "LightCalculate") { index -> // this index is that index
list.forEach {
val it = it as ThreadedLightmapUpdateMessage
setLightOf(bufferForPasses[it.pass - 1], it.x, it.y, calculate(it.x, it.y))
} }
} }
} }
ThreadParallel.startAllWaitForDie()
}
AppLoader.debugTimers["Runderer.LightPost"] = measureNanoTime {
combineTasks.forEachIndexed { index, intRange ->
ThreadParallel.map(index, "LightCombine") { index -> // this index is that index
for (i in intRange) {
val max1 = bufferForPasses[0][i] maxBlend bufferForPasses[1][i]
val max2 = bufferForPasses[2][i] maxBlend bufferForPasses[3][i]
val max = max1 maxBlend max2
lightmap[i] = max
}
}
}
ThreadParallel.startAllWaitForDie()
} }
} }
AppLoader.debugTimers["Renderer.LightPrlRun"] = measureNanoTime {
ThreadParallel.startAllWaitForDie()
}
// get correct Renderer.LightPre by subtracting some shits AppLoader.debugTimers["Renderer.LightPrlPost"] = measureNanoTime {
AppLoader.debugTimers["Renderer.LightParaTotal"] = AppLoader.debugTimers["Renderer.LightPre"]!! // copy to lightmap
AppLoader.debugTimers["Renderer.LightPre"] = for (k in 0 until lightmap.size) {
(AppLoader.debugTimers["Renderer.LightPre"]!! as Long) - lightmap[k] = buf.getPlain(k) ?: colourNull
(AppLoader.debugTimers["Renderer.LightParallel${Terrarum.THREADS}x"]!! as Long) - }
(AppLoader.debugTimers["Runderer.LightPost"]!! as Long) }
// accuracy may suffer (overheads maybe?) but it doesn't matter (i think...) AppLoader.debugTimers["Renderer.LightTotal"] =
(AppLoader.debugTimers["Renderer.LightPrlPre"]!! as Long) +
(AppLoader.debugTimers["Renderer.LightPrlRun"]!! as Long) +
(AppLoader.debugTimers["Renderer.LightPrlPost"]!! as Long)
} }
} }
internal data class ThreadedLightmapUpdateMessage(val x: Int, val y: Int, val pass: Int) // TODO re-init at every resize
private lateinit var updateMessages: List<Array<ThreadedLightmapUpdateMessage>>
private fun makeUpdateTaskList() {
val lightTaskArr = ArrayList<ThreadedLightmapUpdateMessage>()
val for_x_start = overscan_open
val for_y_start = overscan_open
val for_x_end = for_x_start + WorldCamera.width / TILE_SIZE + 3
val for_y_end = for_y_start + WorldCamera.height / TILE_SIZE + 3 // same fix as above
// Round 2
for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_start - overscan_open..for_x_end) {
lightTaskArr.add(ThreadedLightmapUpdateMessage(x, y))
}
}
// Round 3
for (y in for_y_end + overscan_open downTo for_y_start) {
for (x in for_x_end + overscan_open downTo for_x_start) {
lightTaskArr.add(ThreadedLightmapUpdateMessage(x, y))
}
}
// Round 4
for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_end + overscan_open downTo for_x_start) {
lightTaskArr.add(ThreadedLightmapUpdateMessage(x, y))
}
}
// Round 1
for (y in for_y_start - overscan_open..for_y_end) {
for (x in for_x_start - overscan_open..for_x_end) {
lightTaskArr.add(ThreadedLightmapUpdateMessage(x, y))
}
}
updateMessages = lightTaskArr.toTypedArray().sliceEvenly(Terrarum.THREADS)
}
internal data class ThreadedLightmapUpdateMessage(val x: Int, val y: Int)
private fun buildLanternmap() { private fun buildLanternmap() {