From e34eb6212d269a42614ff828cb8a68701cbe4a81 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 27 Apr 2020 17:48:28 +0900 Subject: [PATCH] using coroutines/graphics: lcd mode just for the heck of it --- src/net/torvald/tsvm/VMGUI.kt | 17 ++- src/net/torvald/tsvm/VMRunnerFactory.kt | 19 +-- .../tsvm/peripheral/GraphicsAdapter.kt | 133 +++++++++++++++++- 3 files changed, 149 insertions(+), 20 deletions(-) diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index d1c795c..dcb3d30 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -5,7 +5,10 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration import com.badlogic.gdx.graphics.OrthographicCamera import com.badlogic.gdx.graphics.g2d.SpriteBatch +import kotlinx.coroutines.* import net.torvald.tsvm.peripheral.GraphicsAdapter +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.suspendCoroutine class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { @@ -17,10 +20,12 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() lateinit var vmRunner: VMRunner + lateinit var coroutineJob: Job + override fun create() { super.create() - gpu = GraphicsAdapter() + gpu = GraphicsAdapter(lcdMode = true) vm.peripheralTable[1] = PeripheralEntry( VM.PERITYPE_GRAPHICS, @@ -43,7 +48,11 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() //vmRunner.executeCommand(gpuTestPalette) // TEST KTS PRG vmRunner = VMRunnerFactory(vm, "kts") - vmRunner.executeCommand(gpuTestPaletteKt) + //vmRunner.executeCommand(gpuTestPaletteKt) + //launch { vmRunner.executeCommand(gpuTestPaletteKt) } } + coroutineJob = GlobalScope.launch { + vmRunner.executeCommand(gpuTestPaletteKt) + } } private var updateAkku = 0.0 @@ -70,6 +79,8 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() private var latch = true private fun updateGame(delta: Float) { + + } fun poke(addr: Long, value: Byte) = vm.poke(addr, value) @@ -357,6 +368,8 @@ while True: override fun dispose() { super.dispose() + batch.dispose() + coroutineJob.cancel() } } diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt index ee5bffb..0dab64c 100644 --- a/src/net/torvald/tsvm/VMRunnerFactory.kt +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -8,8 +8,7 @@ import kotlin.test.assertNotNull abstract class VMRunner(val extension: String) { - var thread = Thread() - abstract fun executeCommand(command: String) + abstract suspend fun executeCommand(command: String) } @@ -32,11 +31,8 @@ object VMRunnerFactory { private val vmLua = VMLuaAdapter(vm) - override fun executeCommand(command: String) { - thread = Thread { - vmLua.lua.load(command).call() - } - thread.start() + override suspend fun executeCommand(command: String) { + vmLua.lua.load(command).call() } } } @@ -58,12 +54,9 @@ object VMRunnerFactory { //bind.put("nanotime", { System.nanoTime() }) } - override fun executeCommand(command: String) { - thread = Thread { - //(engine as Compilable).compile(command).eval(context) // compiling does not work with bindings in kts - engine.eval(command, context) - } - thread.start() + override suspend fun executeCommand(command: String) { + //(engine as Compilable).compile(command).eval(context) // compiling does not work with bindings in kts + engine.eval(command, context) } } } diff --git a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 0e700a0..11dac9d 100644 --- a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -11,7 +11,7 @@ import net.torvald.tsvm.kB import sun.nio.ch.DirectBuffer import kotlin.experimental.and -class GraphicsAdapter : PeriBase { +class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase { internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888) private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888) @@ -26,8 +26,8 @@ class GraphicsAdapter : PeriBase { private val spriteAndTextArea = UnsafeHelper.allocate(10660L) private val unusedArea = ByteArray(92) - private val paletteShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, DRAW_SHADER_FRAG) - private val textShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, TEXT_TILING_SHADER) + private val paletteShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode) DRAW_SHADER_FRAG_LCD else DRAW_SHADER_FRAG) + private val textShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode) TEXT_TILING_SHADER_LCD else TEXT_TILING_SHADER) private var textmodeBlinkCursor = true private var graphicsUseSprites = false @@ -232,7 +232,7 @@ class GraphicsAdapter : PeriBase { batch.begin() // clear screen - batch.color = Color.BLACK + batch.color = if (lcdMode) LCD_BASE_COL else Color.BLACK batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) @@ -244,6 +244,7 @@ class GraphicsAdapter : PeriBase { // must be done every time the shader is "actually loaded" // try this: if above line precedes 'batch.shader = paletteShader', it won't work batch.shader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) + if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) // draw framebuffer batch.draw(rendertex, x, y) @@ -296,6 +297,7 @@ class GraphicsAdapter : PeriBase { textShader.setUniformf("screenDimension", WIDTH.toFloat(), HEIGHT.toFloat()) textShader.setUniformf("tilesInAtlas", 16f, 16f) textShader.setUniformf("atlasTexSize", chrrom0.width.toFloat(), chrrom0.height.toFloat()) + if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) @@ -366,6 +368,8 @@ class GraphicsAdapter : PeriBase { const val TEXT_ROWS = 32 val VRAM_SIZE = 256.kB() + private val LCD_BASE_COL = Color(0xa1a99cff.toInt()) + val DRAW_SHADER_FRAG = """ #version 120 @@ -380,7 +384,31 @@ float rand(vec2 co){ void main(void) { gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).r * 255.0)]; - //gl_FragColor = vec4(texture2D(u_texture, v_texCoords).rrr, 1.0); +} + """.trimIndent() + + val DRAW_SHADER_FRAG_LCD = """ +#version 120 + +varying vec4 v_color; +varying vec2 v_texCoords; +uniform sampler2D u_texture; +uniform vec4 pal[256]; + +float intensitySteps = 4.0; +uniform vec4 lcdBaseCol; + +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +void main(void) { + vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).r * 255.0)]; + float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps; + vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a); + + // LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD. + gl_FragColor = lcdBaseCol * outIntensity; } """.trimIndent() @@ -488,8 +516,103 @@ void main() { gl_FragColor = backColFromMap; } } +""".trimIndent() + + val TEXT_TILING_SHADER_LCD = """ +#version 120 +#ifdef GL_ES +precision mediump float; +#endif +#extension GL_EXT_gpu_shader4 : enable + +//layout(origin_upper_left) in vec4 gl_FragCoord; // commented; requires #version 150 or later +// gl_FragCoord is origin to bottom-left + +varying vec4 v_color; +varying vec2 v_texCoords; +uniform sampler2D u_texture; +uniform vec2 screenDimension; +uniform vec2 tilesInAxes; // size of the tilemap texture; vec2(tiles_in_horizontal, tiles_in_vertical) + +uniform sampler2D tilesAtlas; +uniform sampler2D foreColours; +uniform sampler2D backColours; +uniform sampler2D tilemap; + +uniform vec2 tilesInAtlas = ivec2(16.0, 16.0); +uniform vec2 atlasTexSize = ivec2(128.0, 224.0); +vec2 tileSizeInPx = atlasTexSize / tilesInAtlas; // should be like ivec2(16, 16) + +ivec2 getTileXY(int tileNumber) { + return ivec2(tileNumber % int(tilesInAtlas.x), tileNumber / int(tilesInAtlas.x)); +} + +// return: int=0xaarrggbb +int _colToInt(vec4 color) { + return int(color.b * 255) | (int(color.g * 255) << 8) | (int(color.r * 255) << 16) | (int(color.a * 255) << 24); +} + +// 0x0rggbb where int=0xaarrggbb +// return: [0..1048575] +int getTileFromColor(vec4 color) { + return _colToInt(color) & 0xFFFFF; +} + +float intensitySteps = 4.0; +uniform vec4 lcdBaseCol; + +void main() { + + // READ THE FUCKING MANUAL, YOU DONKEY !! // + // This code purposedly uses flipped fragcoord. // + // Make sure you don't use gl_FragCoord unknowingly! // + // Remember, if there's a compile error, shader SILENTLY won't do anything // + + + // default gl_FragCoord takes half-integer (represeting centre of the pixel) -- could be useful for phys solver? + // This one, however, takes exact integer by rounding down. // + vec2 flippedFragCoord = vec2(gl_FragCoord.x, screenDimension.y - gl_FragCoord.y); // NO IVEC2!!; this flips Y + + // get required tile numbers // + + vec4 tileFromMap = texture2D(tilemap, flippedFragCoord / screenDimension); // raw tile number + vec4 foreColFromMap = texture2D(foreColours, flippedFragCoord / screenDimension); + vec4 backColFromMap = texture2D(backColours, flippedFragCoord / screenDimension); + + int tile = getTileFromColor(tileFromMap); + ivec2 tileXY = getTileXY(tile); + + // calculate the UV coord value for texture sampling // + + // don't really need highp here; read the GLES spec + vec2 uvCoordForTile = (mod(flippedFragCoord, tileSizeInPx) / tileSizeInPx) / tilesInAtlas; // 0..0.00390625 regardless of tile position in atlas + vec2 uvCoordOffsetTile = tileXY / tilesInAtlas; // where the tile starts in the atlas, using uv coord (0..1) + + // get final UV coord for the actual sampling // + + vec2 finalUVCoordForTile = uvCoordForTile + uvCoordOffsetTile;// where we should be actually looking for in atlas, using UV coord (0..1) + + // blending a breakage tex with main tex // + + vec4 tileCol = texture2D(tilesAtlas, finalUVCoordForTile); + + vec4 palCol = vec4(1.0); + // apply colour + if (tileCol.r > 0) { + palCol = foreColFromMap; + } + else { + palCol = backColFromMap; + } + + float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps; + vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a); + + // LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD. + gl_FragColor = lcdBaseCol * outIntensity; +} """.trimIndent() val DEFAULT_PALETTE = intArrayOf( // 0b rrrrrrrr gggggggg bbbbbbbb aaaaaaaa