mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 03:24:06 +09:00
new thread pooling strategy and test program WIP
This commit is contained in:
@@ -204,7 +204,7 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
|
|||||||
|
|
||||||
|
|
||||||
fun insertionSortLastElem(arr: ArrayList<Actor>) {
|
fun insertionSortLastElem(arr: ArrayList<Actor>) {
|
||||||
lock(ReentrantLock()) {
|
ReentrantLock().lock {
|
||||||
var j = arr.lastIndex - 1
|
var j = arr.lastIndex - 1
|
||||||
val x = arr.last()
|
val x = arr.last()
|
||||||
while (j >= 0 && arr[j] > x) {
|
while (j >= 0 && arr[j] > x) {
|
||||||
@@ -215,13 +215,14 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun lock(lock: Lock, body: () -> Unit) {
|
}
|
||||||
lock.lock()
|
|
||||||
try {
|
inline fun Lock.lock(body: () -> Unit) {
|
||||||
body()
|
this.lock()
|
||||||
}
|
try {
|
||||||
finally {
|
body()
|
||||||
lock.unlock()
|
}
|
||||||
}
|
finally {
|
||||||
|
this.unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
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
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
typealias RunnableFun = () -> Unit
|
typealias RunnableFun = () -> Unit
|
||||||
@@ -57,7 +61,7 @@ object ThreadParallel {
|
|||||||
* Primitive locking
|
* Primitive locking
|
||||||
*/
|
*/
|
||||||
fun allFinished(): Boolean {
|
fun allFinished(): Boolean {
|
||||||
pool.forEach { if (it?.state != Thread.State.TERMINATED) return false }
|
pool.forEach { if (it != null && it.state != Thread.State.TERMINATED) return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,24 +75,30 @@ object BlockingThreadPool {
|
|||||||
val threadCount = Terrarum.THREADS // modify this to your taste
|
val threadCount = Terrarum.THREADS // modify this to your taste
|
||||||
private val pool: Array<Thread?> = Array(threadCount, { null })
|
private val pool: Array<Thread?> = Array(threadCount, { null })
|
||||||
private var tasks: List<RunnableFun> = ArrayList<RunnableFun>()
|
private var tasks: List<RunnableFun> = ArrayList<RunnableFun>()
|
||||||
private var allTasksDone = false
|
@Volatile private var dispatchedTasks = 0
|
||||||
private var dispatchedTasks = 0
|
|
||||||
private var threadPrefix = ""
|
private var threadPrefix = ""
|
||||||
|
|
||||||
|
/** @return false on failure (likely the previous jobs not finished), true on success */
|
||||||
fun map(prefix: String, tasks: List<RunnableFun>) = setTasks(tasks, prefix)
|
fun map(prefix: String, tasks: List<RunnableFun>) = setTasks(tasks, prefix)
|
||||||
fun setTasks(tasks: List<RunnableFun>, prefix: String) {
|
/** @return false on failure (likely the previous jobs not finished), true on success */
|
||||||
|
fun setTasks(tasks: List<RunnableFun>, prefix: String): Boolean {
|
||||||
|
if (!allFinished())
|
||||||
|
return false
|
||||||
|
|
||||||
this.tasks = tasks
|
this.tasks = tasks
|
||||||
dispatchedTasks = 0
|
dispatchedTasks = 0
|
||||||
threadPrefix = prefix
|
threadPrefix = prefix
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dequeueTask(): RunnableFun {
|
private fun dequeueTask(): RunnableFun {
|
||||||
dispatchedTasks += 1
|
dispatchedTasks += 1
|
||||||
return tasks[dispatchedTasks - 1]
|
return tasks[dispatchedTasks - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun startAllWaitForDie() {
|
fun startAllWaitForDie() {
|
||||||
while (!allTasksDone) {
|
while (dispatchedTasks <= tasks.lastIndex) {
|
||||||
// marble rolling down the slanted channel-track of threads, if a channel is empty (a task assigned
|
// marble rolling down the slanted channel-track of threads, if a channel is empty (a task assigned
|
||||||
// to the thread is dead) the marble will roll into the channel, and the marble is a task #MarbleMachineX
|
// to the thread is dead) the marble will roll into the channel, and the marble is a task #MarbleMachineX
|
||||||
for (i in 0 until threadCount) {
|
for (i in 0 until threadCount) {
|
||||||
@@ -96,7 +106,8 @@ object BlockingThreadPool {
|
|||||||
// of marbles and put it into an empty channel whenever we encounter one
|
// of marbles and put it into an empty channel whenever we encounter one
|
||||||
|
|
||||||
// SO WHAT WE DO is first fill any empty channels:
|
// SO WHAT WE DO is first fill any empty channels:
|
||||||
if (pool[i] == null || pool[i]!!.state == Thread.State.TERMINATED) {
|
if (dispatchedTasks <= tasks.lastIndex && // because cache invalidation damnit
|
||||||
|
(pool[i] == null || pool[i]!!.state == Thread.State.TERMINATED)) {
|
||||||
pool[i] = Thread(dequeueTask().makeRunnable(), "$threadPrefix-$dispatchedTasks") // thread name index is one-based
|
pool[i] = Thread(dequeueTask().makeRunnable(), "$threadPrefix-$dispatchedTasks") // thread name index is one-based
|
||||||
pool[i]!!.start()
|
pool[i]!!.start()
|
||||||
}
|
}
|
||||||
@@ -109,6 +120,11 @@ object BlockingThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allFinished(): Boolean {
|
||||||
|
pool.forEach { if (it != null && it.state != Thread.State.TERMINATED) return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun RunnableFun.makeRunnable() = Runnable { this.invoke() }
|
private fun RunnableFun.makeRunnable() = Runnable { this.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -869,7 +869,7 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun insertionSortLastElemAV(arr: ArrayList<ActorWithBody>) { // out-projection doesn't work, duh
|
private fun insertionSortLastElemAV(arr: ArrayList<ActorWithBody>) { // out-projection doesn't work, duh
|
||||||
lock(ReentrantLock()) {
|
ReentrantLock().lock {
|
||||||
var j = arr.lastIndex - 1
|
var j = arr.lastIndex - 1
|
||||||
val x = arr.last()
|
val x = arr.last()
|
||||||
while (j >= 0 && arr[j] > x) {
|
while (j >= 0 && arr[j] > x) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.torvald.terrarum.itemproperties.ItemCodex
|
|||||||
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_DYNAMIC
|
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_DYNAMIC
|
||||||
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_WALLS
|
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_WALLS
|
||||||
import net.torvald.terrarum.itemproperties.ItemID
|
import net.torvald.terrarum.itemproperties.ItemID
|
||||||
|
import net.torvald.terrarum.lock
|
||||||
import net.torvald.terrarum.modulebasegame.Ingame
|
import net.torvald.terrarum.modulebasegame.Ingame
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.locks.Lock
|
import java.util.concurrent.locks.Lock
|
||||||
@@ -253,7 +254,7 @@ class ActorInventory(val actor: Pocketed, var maxCapacity: Int, var capacityMode
|
|||||||
return itemList[index]
|
return itemList[index]
|
||||||
}
|
}
|
||||||
private fun insertionSortLastElem(arr: ArrayList<InventoryPair>) {
|
private fun insertionSortLastElem(arr: ArrayList<InventoryPair>) {
|
||||||
lock(ReentrantLock()) {
|
ReentrantLock().lock {
|
||||||
var j = arr.lastIndex - 1
|
var j = arr.lastIndex - 1
|
||||||
val x = arr.last()
|
val x = arr.last()
|
||||||
while (j >= 0 && arr[j].item > x.item) {
|
while (j >= 0 && arr[j].item > x.item) {
|
||||||
@@ -284,15 +285,6 @@ class ActorInventory(val actor: Pocketed, var maxCapacity: Int, var capacityMode
|
|||||||
}
|
}
|
||||||
return -(low + 1) // key not found
|
return -(low + 1) // key not found
|
||||||
}
|
}
|
||||||
inline fun lock(lock: Lock, body: () -> Unit) {
|
|
||||||
lock.lock()
|
|
||||||
try {
|
|
||||||
body()
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class InventoryPair(val item: GameItem, var amount: Int)
|
data class InventoryPair(val item: GameItem, var amount: Int)
|
||||||
@@ -76,8 +76,8 @@ object WorldSimulator {
|
|||||||
fun moveFluids(delta: Float) {
|
fun moveFluids(delta: Float) {
|
||||||
makeFluidMapFromWorld()
|
makeFluidMapFromWorld()
|
||||||
|
|
||||||
//simCompression()
|
simCompression()
|
||||||
for (y in 1 until fluidMap.size - 1) {
|
/*for (y in 1 until fluidMap.size - 1) {
|
||||||
for (x in 1 until fluidMap[0].size - 1) {
|
for (x in 1 until fluidMap[0].size - 1) {
|
||||||
val worldX = x + updateXFrom
|
val worldX = x + updateXFrom
|
||||||
val worldY = y + updateYFrom
|
val worldY = y + updateYFrom
|
||||||
@@ -98,7 +98,7 @@ object WorldSimulator {
|
|||||||
fluidNewMap[y + 1][x] += remainingMass
|
fluidNewMap[y + 1][x] += remainingMass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fluidmapToWorld()
|
fluidmapToWorld()
|
||||||
}
|
}
|
||||||
|
|||||||
180
src/net/torvald/terrarum/tests/NoiseGenerator.kt
Normal file
180
src/net/torvald/terrarum/tests/NoiseGenerator.kt
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package net.torvald.terrarum.tests
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
|
import com.badlogic.gdx.InputAdapter
|
||||||
|
import com.badlogic.gdx.ScreenAdapter
|
||||||
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplication
|
||||||
|
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||||
|
import com.badlogic.gdx.graphics.Pixmap
|
||||||
|
import com.badlogic.gdx.graphics.Texture
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
||||||
|
import com.sudoplay.joise.Joise
|
||||||
|
import com.sudoplay.joise.module.ModuleBasisFunction
|
||||||
|
import com.sudoplay.joise.module.ModuleFractal
|
||||||
|
import com.sudoplay.joise.module.ModuleScaleDomain
|
||||||
|
import com.sudoplay.joise.module.ModuleScaleOffset
|
||||||
|
import net.torvald.random.HQRNG
|
||||||
|
import net.torvald.terrarum.AppLoader
|
||||||
|
import net.torvald.terrarum.AppLoader.printdbg
|
||||||
|
import net.torvald.terrarum.concurrent.BlockingThreadPool
|
||||||
|
import net.torvald.terrarum.concurrent.RunnableFun
|
||||||
|
import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
|
||||||
|
import net.torvald.terrarum.concurrent.ThreadParallel
|
||||||
|
import net.torvald.terrarum.inUse
|
||||||
|
import net.torvald.terrarum.modulebasegame.Ingame
|
||||||
|
import net.torvald.terrarum.roundInt
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.system.measureNanoTime
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2018-12-14.
|
||||||
|
*/
|
||||||
|
class NoiseGenerator : ScreenAdapter() {
|
||||||
|
|
||||||
|
lateinit var batch: SpriteBatch
|
||||||
|
lateinit var camera: OrthographicCamera
|
||||||
|
lateinit var pixmap: Pixmap
|
||||||
|
lateinit var texture: Texture
|
||||||
|
private val IMAGE_SIZE = 1024
|
||||||
|
private val IMAGE_SIZEF = IMAGE_SIZE.toFloat()
|
||||||
|
private val IMAGE_SIZED = IMAGE_SIZE.toDouble()
|
||||||
|
private val RNG = HQRNG()
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
Gdx.input.inputProcessor = NoiseGeneratorController(this)
|
||||||
|
|
||||||
|
batch = SpriteBatch()
|
||||||
|
camera = OrthographicCamera(AppLoader.appConfig.width.toFloat(), AppLoader.appConfig.height.toFloat())
|
||||||
|
|
||||||
|
camera.setToOrtho(true, AppLoader.appConfig.width.toFloat(), AppLoader.appConfig.height.toFloat())
|
||||||
|
camera.update()
|
||||||
|
Gdx.gl20.glViewport(0, 0, AppLoader.appConfig.width, AppLoader.appConfig.height)
|
||||||
|
|
||||||
|
pixmap = Pixmap(IMAGE_SIZE, IMAGE_SIZE, Pixmap.Format.RGBA8888)
|
||||||
|
texture = Texture(1, 1, Pixmap.Format.RGBA8888)
|
||||||
|
}
|
||||||
|
|
||||||
|
var regenerate = true
|
||||||
|
|
||||||
|
private var pixelsInSingleJob = (IMAGE_SIZE * IMAGE_SIZE) / 16 // CHANGE THIS VALUE HERE
|
||||||
|
|
||||||
|
private val jobsCount: Int
|
||||||
|
get() = (IMAGE_SIZE * IMAGE_SIZE) / pixelsInSingleJob
|
||||||
|
private val rawPixelsList: List<IntRange>
|
||||||
|
get() = (0 until IMAGE_SIZE * IMAGE_SIZE).sliceEvenly(jobsCount)
|
||||||
|
private fun makeGenFun(seed: Long, index: Int) = { //i: Int ->
|
||||||
|
val module = ModuleFractal()
|
||||||
|
module.setType(ModuleFractal.FractalType.BILLOW)
|
||||||
|
module.setAllSourceBasisTypes(ModuleBasisFunction.BasisType.GRADIENT)
|
||||||
|
module.setAllSourceInterpolationTypes(ModuleBasisFunction.InterpolationType.QUINTIC)
|
||||||
|
module.setNumOctaves(10)
|
||||||
|
module.setFrequency(8.0)
|
||||||
|
module.seed = seed
|
||||||
|
|
||||||
|
val moduleScale = ModuleScaleOffset()
|
||||||
|
moduleScale.setSource(module)
|
||||||
|
moduleScale.setScale(0.5)
|
||||||
|
moduleScale.setOffset(0.0)
|
||||||
|
|
||||||
|
val noiseModule = Joise(moduleScale)
|
||||||
|
|
||||||
|
for (c in rawPixelsList[index]) {
|
||||||
|
val x = c % IMAGE_SIZE
|
||||||
|
val y = c / IMAGE_SIZE
|
||||||
|
val uvX = x / IMAGE_SIZED
|
||||||
|
val uvY = y / IMAGE_SIZED
|
||||||
|
|
||||||
|
val noiseValue = noiseModule.get(uvX, uvY).absoluteValue
|
||||||
|
val rgb = (noiseValue * 255.0).roundInt()
|
||||||
|
|
||||||
|
pixmap.drawPixel(x, y, (rgb shl 24) or (rgb shl 16) or (rgb shl 8) or 0xFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var timerStart = 0L
|
||||||
|
private var timerFired = false
|
||||||
|
|
||||||
|
override fun render(delta: Float) {
|
||||||
|
Gdx.graphics.setTitle(Ingame.getCanonicalTitle())
|
||||||
|
|
||||||
|
// regen
|
||||||
|
if (timerFired && BlockingThreadPool.allFinished()) {
|
||||||
|
val timeTook = System.currentTimeMillis() - timerStart
|
||||||
|
timerFired = false
|
||||||
|
|
||||||
|
printdbg(this, "> $timeTook ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regenerate && BlockingThreadPool.allFinished()) {
|
||||||
|
printdbg(this, "Reticulating splines...")
|
||||||
|
|
||||||
|
regenerate = false
|
||||||
|
// don't join while rendering noise
|
||||||
|
|
||||||
|
timerStart = System.currentTimeMillis()
|
||||||
|
timerFired = true
|
||||||
|
|
||||||
|
val seed = RNG.nextLong()
|
||||||
|
val jobs = List(jobsCount) { makeGenFun(seed, it) }
|
||||||
|
BlockingThreadPool.setTasks(jobs, "")
|
||||||
|
BlockingThreadPool.startAllWaitForDie()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// render
|
||||||
|
texture.dispose()
|
||||||
|
texture = Texture(pixmap)
|
||||||
|
|
||||||
|
batch.inUse {
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
batch.draw(texture, 0f, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pause() {
|
||||||
|
super.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resume() {
|
||||||
|
super.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resize(width: Int, height: Int) {
|
||||||
|
super.resize(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
pixmap.dispose()
|
||||||
|
texture.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoiseGeneratorController(val host: NoiseGenerator) : InputAdapter() {
|
||||||
|
override fun keyDown(keycode: Int): Boolean {
|
||||||
|
if (keycode == Input.Keys.SPACE) {
|
||||||
|
host.regenerate = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
ShaderProgram.pedantic = false
|
||||||
|
|
||||||
|
val appConfig = LwjglApplicationConfiguration()
|
||||||
|
appConfig.vSyncEnabled = false
|
||||||
|
appConfig.resizable = false//true;
|
||||||
|
appConfig.width = 1024
|
||||||
|
appConfig.height = 1024
|
||||||
|
appConfig.backgroundFPS = 9999
|
||||||
|
appConfig.foregroundFPS = 9999
|
||||||
|
appConfig.forceExit = false
|
||||||
|
|
||||||
|
LwjglApplication(AppLoader(appConfig, NoiseGenerator()), appConfig)
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import com.badlogic.gdx.graphics.Texture
|
|||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
|
import net.torvald.terrarum.modulebasegame.Ingame
|
||||||
import net.torvald.terrarum.ui.UINSMenu
|
import net.torvald.terrarum.ui.UINSMenu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,6 +84,9 @@ class UITestPad1 : ScreenAdapter() {
|
|||||||
var _dct = 0f
|
var _dct = 0f
|
||||||
|
|
||||||
override fun render(delta: Float) {
|
override fun render(delta: Float) {
|
||||||
|
Gdx.graphics.setTitle(Ingame.getCanonicalTitle())
|
||||||
|
|
||||||
|
|
||||||
// UPDATE
|
// UPDATE
|
||||||
nsMenu.update(delta)
|
nsMenu.update(delta)
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,13 @@ class UINSMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun popSubMenu() {
|
private fun popSubMenu() {
|
||||||
if (listStack.size == 1) throw Error("Tried to pop root menu")
|
if (listStack.size == 1) {
|
||||||
|
System.err.println("[UINSMenu] Tried to pop root menu")
|
||||||
|
Thread.currentThread().getStackTrace().forEach {
|
||||||
|
System.err.println(it)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val poppedUIItem = listStack.pop()
|
val poppedUIItem = listStack.pop()
|
||||||
width -= poppedUIItem.ui.width
|
width -= poppedUIItem.ui.width
|
||||||
|
|||||||
Reference in New Issue
Block a user