it turns out the test-entering is broken; will fix later idk; Threadparallel.startAllWaitForDie seems to work as intended

This commit is contained in:
minjaesong
2019-06-21 16:05:02 +09:00
parent 306f45e7ee
commit b45caebda0
14 changed files with 310 additions and 119 deletions

Binary file not shown.

View File

@@ -1,16 +1,26 @@
module terrarum {
// java
requires java.desktop;
requires java.logging;
requires jdk.unsupported; // sun.misc.Unsafe
// kotlin
requires kotlin.stdlib;
// gdx
requires gdx;
requires gdx.backend.lwjgl;
requires gdx.controllers;
// terrarum
requires TerrarumSansBitmap;
requires TerranVirtualDisk;
requires Terrarum.Joise;
// etc
requires GetCpuName;
requires jxinput;
requires gson;
requires GetCpuName;
requires TerrarumSansBitmap;
requires kotlin.stdlib;
requires java.desktop;
requires java.logging;
requires TerranVirtualDisk;
requires commons.codec;
requires commons.csv;
requires Terrarum.Joise;
}

View File

@@ -125,10 +125,10 @@ public class AppLoader implements ApplicationListener {
public static String GAME_LOCALE = System.getProperty("user.language") + System.getProperty("user.country");
public static final String systemArch = System.getProperty("os.arch");
public static String processor;
public static String processorVendor;
public static String renderer;
public static String rendererVendor;
public static String processor = "(a super-duper virtual processor)";
public static String processorVendor = "(andromeda software development)"; // definitely not taken from "that" demogroup
public static String renderer = "(a super-fancy virtual photoradiator)";
public static String rendererVendor = "(radiosity)";
public static final boolean is32BitJVM = !System.getProperty("sun.arch.data.model").contains("64");
@@ -610,15 +610,14 @@ public class AppLoader implements ApplicationListener {
if (injectScreen != null) {
setScreen(injectScreen);
}
else {
ModMgr.INSTANCE.invoke(); // invoke Module Manager
AppLoader.resourcePool.loadAll();
printdbg(this, "all modules loaded successfully");
ModMgr.INSTANCE.invoke(); // invoke Module Manager
AppLoader.resourcePool.loadAll();
printdbg(this, "all modules loaded successfully");
BlocksDrawer.INSTANCE.getWorld(); // will initialize the BlocksDrawer by calling dummy method
LightmapRenderer.INSTANCE.hdr(0f);
BlocksDrawer.INSTANCE.getWorld(); // will initialize the BlocksDrawer by calling dummy method
LightmapRenderer.INSTANCE.hdr(0f);
}
printdbg(this, "PostInit done");

View File

@@ -15,6 +15,7 @@ import com.badlogic.gdx.utils.GdxRuntimeException
import com.jme3.math.FastMath
import net.torvald.random.HQRNG
import net.torvald.terrarum.AppLoader.*
import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.imagefont.TinyAlphNum
@@ -166,7 +167,7 @@ object Terrarum : Screen, Disposable {
val STATE_ID_TOOL_RUMBLE_DIAGNOSIS = 0x201
/** Available CPU threads */
val THREADS = Runtime.getRuntime().availableProcessors() + 1
val THREADS = ThreadParallel.threadCount //Runtime.getRuntime().availableProcessors() + 1
/**
* If the game is multithreading.
@@ -196,19 +197,22 @@ object Terrarum : Screen, Disposable {
init {
println("$NAME version ${AppLoader.getVERSION_STRING()}")
println("LibGDX version ${com.badlogic.gdx.Version.VERSION}")
println("[Terrarum] init called by:")
Thread.currentThread().stackTrace.forEach { println("... $it") }
println("[Terrarum] $NAME version ${AppLoader.getVERSION_STRING()}")
println("[Terrarum] LibGDX version ${com.badlogic.gdx.Version.VERSION}")
println("os.arch = $systemArch") // debug info
println("[Terrarum] os.arch = $systemArch") // debug info
if (is32BitJVM) {
printdbgerr(this, "32 Bit JVM detected")
}
println("processor = $processor")
println("vendor = $processorVendor")
println("[Terrarum] processor = $processor")
println("[Terrarum] vendor = $processorVendor")
setGamepadButtonLabels()
@@ -216,6 +220,9 @@ object Terrarum : Screen, Disposable {
AppLoader.disposableSingletonsPool.add(this)
println("[Terrarum] init complete")
}
private fun setGamepadButtonLabels() {

View File

@@ -93,7 +93,7 @@ object BlockCodex {
}
}
fun getOrNull(rawIndex: Int?): BlockProp? {
fun getOrNull(rawIndex: Int?): BlockProp? {//<O>
return blockProps[rawIndex]
}

View File

@@ -1,6 +1,5 @@
package net.torvald.terrarum.concurrent
import net.torvald.terrarum.Terrarum
import kotlin.math.absoluteValue
typealias RunnableFun = () -> Unit
@@ -11,7 +10,7 @@ typealias ThreadableFun = (Int) -> Unit
* Created by minjaesong on 2016-05-25.
*/
object ThreadParallel {
val threadCount = Terrarum.THREADS // modify this to your taste
val threadCount = Runtime.getRuntime().availableProcessors() + 1 // modify this to your taste
private val pool: Array<Thread?> = Array(threadCount) { null }
@@ -49,6 +48,7 @@ object ThreadParallel {
* Start all thread in the pool and wait for them to all die. If the thread in the pool is NULL, it will simply ignored.
*/
fun startAllWaitForDie() {
// ThreadParallelTester says this function actually works as intended...
pool.forEach { it?.start() }
pool.forEach { it?.join() }
}
@@ -69,7 +69,7 @@ object ThreadParallel {
*/
@Deprecated("Experimental.", ReplaceWith("ThreadParallel", "net.torvald.terrarum.concurrent.ThreadParallel"))
object BlockingThreadPool {
val threadCount = Terrarum.THREADS // modify this to your taste
val threadCount = Runtime.getRuntime().availableProcessors() + 1 // modify this to your taste
private val pool: Array<Thread?> = Array(threadCount, { null })
private var tasks: List<RunnableFun> = ArrayList<RunnableFun>()
@Volatile private var dispatchedTasks = 0
@@ -127,85 +127,84 @@ object BlockingThreadPool {
}
}
object ParallelUtils {
fun <T, R> Iterable<T>.parallelMap(transform: (T) -> R): List<R> {
val tasks = this.sliceEvenly(ThreadParallel.threadCount)
val destination = Array(ThreadParallel.threadCount) { ArrayList<R>() }
tasks.forEachIndexed { index, list ->
ThreadParallel.map(index, "ParallelUtils.parallelMap@${this.javaClass.canonicalName}") {
for (item in list)
destination[index].add(transform(item as T))
}
}
ThreadParallel.startAllWaitForDie()
return destination.flatten()
}
/**
* Shallow flat of the array
*/
fun <T> Array<out Iterable<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
/**
* Shallow flat of the iterable
*/
fun <T> Iterable<out Iterable<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
/**
* Shallow flat of the array
*/
fun <T> Array<out Array<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
fun <T> Iterable<T>.sliceEvenly(slices: Int): List<List<T>> = this.toList().sliceEvenly(slices)
fun <T> List<T>.sliceEvenly(slices: Int): List<List<T>> {
return (0 until slices).map {
this.subList(
this.size.toFloat().div(slices).times(it).roundInt(),
this.size.toFloat().div(slices).times(it + 1).roundInt()
)
fun <T, R> Iterable<T>.parallelMap(transform: (T) -> R): List<R> {
val tasks = this.sliceEvenly(ThreadParallel.threadCount)
val destination = Array(ThreadParallel.threadCount) { ArrayList<R>() }
tasks.forEachIndexed { index, list ->
ThreadParallel.map(index, "ParallelUtils.parallelMap@${this.javaClass.canonicalName}") {
for (item in list)
destination[index].add(transform(item as T))
}
}
fun <T> Array<T>.sliceEvenly(slices: Int): List<Array<T>> {
return (0 until slices).map {
this.sliceArray(
this.size.toFloat().div(slices).times(it).roundInt() until
this.size.toFloat().div(slices).times(it + 1).roundInt()
)
}
ThreadParallel.startAllWaitForDie()
return destination.flatten()
}
/**
* Shallow flat of the array
*/
fun <T> Array<out Iterable<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
/**
* Shallow flat of the iterable
*/
fun <T> Iterable<out Iterable<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
/**
* Shallow flat of the array
*/
fun <T> Array<out Array<T>>.flatten(): List<T> {
val al = ArrayList<T>()
this.forEach { it.forEach { al.add(it) } }
return al
}
fun <T> Iterable<T>.sliceEvenly(slices: Int): List<List<T>> = this.toList().sliceEvenly(slices)
fun <T> List<T>.sliceEvenly(slices: Int): List<List<T>> {
return (0 until slices).map {
this.subList(
this.size.toFloat().div(slices).times(it).roundInt(),
this.size.toFloat().div(slices).times(it + 1).roundInt()
)
}
}
fun IntProgression.sliceEvenly(slices: Int): List<IntProgression> {
if (this.step.absoluteValue != 1) throw UnsupportedOperationException("Sorry, step != +1/-1")
val size = (this.last - this.first).absoluteValue + (this.step.toFloat()).absoluteValue
// println(size)
return if (this.first < this.last) (0 until slices).map {
this.first + size.div(slices).times(it).roundInt() ..
this.first + size.div(slices).times(it + 1).roundInt() - 1
}
else (0 until slices).map {
this.first - size.div(slices).times(it).roundInt() downTo
this.first - size.div(slices).times(it + 1).roundInt() + 1
}
fun <T> Array<T>.sliceEvenly(slices: Int): List<Array<T>> {
return (0 until slices).map {
this.sliceArray(
this.size.toFloat().div(slices).times(it).roundInt() until
this.size.toFloat().div(slices).times(it + 1).roundInt()
)
}
}
fun IntProgression.sliceEvenly(slices: Int): List<IntProgression> {
if (this.step.absoluteValue != 1) throw UnsupportedOperationException("Sorry, step != +1/-1")
val size = (this.last - this.first).absoluteValue + (this.step.toFloat()).absoluteValue
// println(size)
return if (this.first < this.last) (0 until slices).map {
this.first + size.div(slices).times(it).roundInt() ..
this.first + size.div(slices).times(it + 1).roundInt() - 1
}
else (0 until slices).map {
this.first - size.div(slices).times(it).roundInt() downTo
this.first - size.div(slices).times(it + 1).roundInt() + 1
}
}
private inline fun Float.roundInt(): Int = Math.round(this)
}
private inline fun Float.roundInt(): Int = Math.round(this)

View File

@@ -464,6 +464,7 @@ open class GameWorld : Disposable {
override fun dispose() {
layerWall.dispose()
layerTerrain.dispose()
//nullWorldInstance?.dispose() // must be called ONLY ONCE; preferably when the app exits
}
companion object {

View File

@@ -86,7 +86,7 @@ class EntryPoint : ModuleEntryPoint() {
println("Welcome back!")
println("[Basegame.EntryPoint] Welcome back!")
}
override fun dispose() {

View File

@@ -52,6 +52,7 @@ object WorldSimulator {
private val ingame = Terrarum.ingame!!
private val world = ingame.world
// TODO use R-Tree instead? https://stackoverflow.com/questions/10269179/find-rectangles-that-contain-point-efficient-algorithm#10269695
private var actorsKDTree: KDTree? = null
fun resetForThisFrame() {

View File

@@ -20,7 +20,7 @@ import net.torvald.random.HQRNG
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.concurrent.BlockingThreadPool
import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.inUse
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.roundInt

View File

@@ -0,0 +1,164 @@
package net.torvald.terrarum.tests
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.InputAdapter
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.ModuleScaleOffset
import net.torvald.random.HQRNG
import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.concurrent.ThreadableFun
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.inUse
import net.torvald.terrarum.roundInt
import kotlin.math.absoluteValue
import kotlin.system.measureNanoTime
/**
* Created by minjaesong on 2019-06-17.
*/
class ThreadParallelTester : ApplicationAdapter() {
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 create() {
Gdx.input.inputProcessor = ThreadParallelTesterController(this)
batch = SpriteBatch()
camera = OrthographicCamera(IMAGE_SIZEF, IMAGE_SIZEF)
camera.setToOrtho(true, IMAGE_SIZEF, IMAGE_SIZEF)
camera.update()
Gdx.gl20.glViewport(0, 0, IMAGE_SIZE, IMAGE_SIZE)
pixmap = Pixmap(IMAGE_SIZE, IMAGE_SIZE, Pixmap.Format.RGBA8888)
texture = Texture(1, 1, Pixmap.Format.RGBA8888)
batch.projectionMatrix = camera.combined
println("[ThreadParallelTester] Hello, world!")
}
var regenerate = true
var generateDone = true
override fun render() {
Gdx.graphics.setTitle("F: ${Gdx.graphics.framesPerSecond}")
if (regenerate) {
// fill pixmap with slow-to-calculate noises.
// create parallel job
// run parallel job, wait all of them to die
// render the pixmap
// expected result: app freezes (or nothing is drawn) until all the parallel job is done
println("Noise regenerating...")
regenerate = false
generateDone = false
val time = measureNanoTime {
setupParallelJobs()
//ThreadParallel.startAll()
ThreadParallel.startAllWaitForDie()
}
generateDone = true
println("Noise generation complete, took ${time.toDouble() / 1_000_000} ms\n")
}
// render
texture.dispose()
texture = Texture(pixmap)
batch.inUse {
batch.projectionMatrix = camera.combined
batch.color = Color.WHITE
batch.draw(texture, 0f, 0f)
}
}
private fun setupParallelJobs() {
val seed = RNG.nextLong()
for (i in 0 until ThreadParallel.threadCount) {
ThreadParallel.map(i, "NoiseGen", makeGenFun(seed, i))
}
}
private val scanlineNumbers: List<IntProgression> = (0 until IMAGE_SIZE).sliceEvenly(ThreadParallel.threadCount)
private fun makeGenFun(seed: Long, index: Int): ThreadableFun = { i ->
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 (y in scanlineNumbers[index]) {
for (x in 0 until 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)
}
}
}
override fun dispose() {
pixmap.dispose()
texture.dispose()
}
}
class ThreadParallelTesterController(val host: ThreadParallelTester) : InputAdapter() {
override fun keyDown(keycode: Int): Boolean {
if (keycode == Input.Keys.SPACE && host.generateDone) {
host.regenerate = true
}
return true
}
}
fun main(args: Array<String>) {
ShaderProgram.pedantic = false
val appConfig = LwjglApplicationConfiguration()
appConfig.vSyncEnabled = false
appConfig.resizable = false
appConfig.width = 1024
appConfig.height = 1024
appConfig.backgroundFPS = 9999
appConfig.foregroundFPS = 9999
appConfig.forceExit = true
//LwjglApplication(AppLoader(appConfig, ThreadParallelTester()), appConfig)
LwjglApplication(ThreadParallelTester(), appConfig)
}

View File

@@ -20,33 +20,33 @@ class UITestPad1 : ScreenAdapter() {
val treeStr = """
- File
- New : Ctrl-N
- Open : Ctrl-O
- New
- Open
- Open Recent
- yaml_example.yaml
- Yaml.kt
- Close : Ctrl-W
- Close
- Settings
- Line Separators
- CRLF
- CR
- LF
- Edit
- Undo : Ctrl-Z
- Redo : Shift-Ctrl-Z
- Cut : Ctrl-X
- Copy : Ctrl-C
- Paste : Ctrl-V
- Undo
- Redo
- Cut
- Copy
- Paste
- Find
- Find : Ctrl-F
- Replace : Shift-Ctrl-F
- Find
- Replace
- Convert Indents
- To Spaces
- Set Project Indentation
- To Tabs
- Refactor
- Refactor This
- Rename : Shift-Ctrl-R
- Rename
- Extract
- Variable
- Property

View File

@@ -13,7 +13,6 @@ import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
import net.torvald.terrarum.modulebasegame.gameworld.WorldTime
import net.torvald.terrarum.utils.JsonWriter
import net.torvald.terrarum.worlddrawer.CreateTileAtlas.TILES_IN_X
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.roundToInt
@@ -98,7 +97,7 @@ internal object BlocksDrawer {
printdbg(this, "Making terrain textures...")
CreateTileAtlas()
JsonWriter.writeToFile(CreateTileAtlas.tags, "${AppLoader.defaultDir}/test_rendertags.json")
//JsonWriter.writeToFile(CreateTileAtlas.tags, "${AppLoader.defaultDir}/test_rendertags.json")
// each takes about 60 seconds
//printdbg(this, "Writing pixmap as tga: atlas.tga")
//PixmapIO2.writeTGA(Gdx.files.absolute("${AppLoader.defaultDir}/atlas.tga"), CreateTileAtlas.atlas, false)

View File

@@ -11,7 +11,7 @@ import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
import net.torvald.terrarum.concurrent.sliceEvenly
import net.torvald.terrarum.concurrent.ThreadParallel
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameactors.ActorWithBody
@@ -400,7 +400,15 @@ object LightmapRenderer {
}
private fun buildNoopMask() {
fun isShaded(x: Int, y: Int) = BlockCodex[world.getTileFromTerrain(x, y) ?: Block.STONE].isSolid
fun isShaded(x: Int, y: Int) = try {
BlockCodex[world.getTileFromTerrain(x, y)].isSolid
}
catch (e: NullPointerException) {
System.err.println("Invalid block id ${world.getTileFromTerrain(x, y)} from coord ($x, $y)")
e.printStackTrace()
false
}
/*
update ordering: clockwise snake