gpu to actually use 8bpp texture format to make memcpying easier

This commit is contained in:
minjaesong
2020-05-09 03:13:52 +09:00
parent e34eb6212d
commit cbcd1ac7af
9 changed files with 280 additions and 78 deletions

View File

@@ -1,6 +1,8 @@
package net.torvald.tsvm
import net.torvald.UnsafeHelper
import net.torvald.tsvm.peripheral.GraphicsAdapter
import sun.nio.ch.DirectBuffer
class GraphicsJSR223Delegate(val vm: VM) {
@@ -25,4 +27,23 @@ class GraphicsJSR223Delegate(val vm: VM) {
}
}
fun loadBulk(fromAddr: Int, toAddr: Int, length: Int) {
getFirstGPU()?.let {
it._loadbulk(fromAddr, toAddr, length)
}
}
private fun GraphicsAdapter._loadbulk(fromAddr: Int, toAddr: Int, length: Int) {
if (toAddr < 250880) {
UnsafeHelper.memcpy(
vm.usermem.ptr + fromAddr,
(this.framebuffer.pixels as DirectBuffer).address() + toAddr,
length.toLong()
)
}
else if (toAddr < 250972) {
}
}
}

View File

@@ -7,6 +7,8 @@ import net.torvald.tsvm.firmware.Firmware.Companion.toLuaValue
import net.torvald.tsvm.peripheral.IOSpace
import net.torvald.tsvm.peripheral.PeriBase
import org.luaj.vm2.LuaValue
import java.util.*
import kotlin.math.ceil
import kotlin.random.Random
/**
@@ -55,6 +57,8 @@ class VM(
val id = java.util.Random().nextInt()
val memsize = minOf(USER_SPACE_SIZE, _memsize.toLong())
private val MALLOC_UNIT = 64
private val mallocBlockSize = (memsize / MALLOC_UNIT).toInt()
internal val usermem = UnsafeHelper.allocate(memsize)
@@ -135,8 +139,55 @@ class VM(
(memspace as PeriBase).peek(offset)
}
fun Byte.toLuaValue() = LuaValue.valueOf(this.toInt())
private val mallocMap = BitSet(mallocBlockSize)
private val mallocSizes = HashMap<Int, Int>() // HashMap<Block Index, Block Count>
private fun findEmptySpace(blockSize: Int): Int? {
var cursorHead = 0
var cursorTail: Int
val cursorHeadMaxInclusive = mallocBlockSize - blockSize
while (cursorHead <= cursorHeadMaxInclusive) {
cursorHead = mallocMap.nextClearBit(cursorHead)
cursorTail = cursorHead + blockSize - 1
if (cursorTail > mallocBlockSize) return null
if (mallocMap.get(cursorTail) == false) {
var isNotEmpty = false
for (k in cursorHead..cursorTail) {
isNotEmpty = isNotEmpty or mallocMap[k]
}
if (!isNotEmpty) {
mallocMap.set(cursorHead, cursorTail + 1)
return cursorHead
}
}
cursorHead = cursorTail + 1
}
return null
}
internal fun malloc(size: Int): Int {
val allocBlocks = ceil(size.toDouble() / MALLOC_UNIT).toInt()
val blockStart = findEmptySpace(allocBlocks)
if (blockStart == null) throw OutOfMemoryError()
mallocSizes[blockStart] = allocBlocks
return blockStart * MALLOC_UNIT
}
internal fun free(ptr: Int) {
val index = ptr / MALLOC_UNIT
val count = mallocSizes[index]
if (count == null) throw OutOfMemoryError()
mallocMap.set(index, index + count, false)
mallocSizes.remove(index)
}
//fun Byte.toLuaValue() = LuaValue.valueOf(this.toInt())
internal data class VMNativePtr(val address: Int, val size: Int)
}
data class PeripheralEntry(

View File

@@ -25,7 +25,7 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
override fun create() {
super.create()
gpu = GraphicsAdapter(lcdMode = true)
gpu = GraphicsAdapter(lcdMode = false)
vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_GRAPHICS,
@@ -43,15 +43,10 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height)
// TEST LUA PRG
//vmRunner = VMRunnerFactory(vm, "lua")
//vmRunner.executeCommand(gpuTestPalette)
// TEST KTS PRG
vmRunner = VMRunnerFactory(vm, "kts")
//vmRunner.executeCommand(gpuTestPaletteKt)
//launch { vmRunner.executeCommand(gpuTestPaletteKt) } }
// TEST PRG
vmRunner = VMRunnerFactory(vm, "js")
coroutineJob = GlobalScope.launch {
vmRunner.executeCommand(gpuTestPaletteKt)
vmRunner.executeCommand(gpuTestPaletteJs)
}
}
@@ -166,6 +161,7 @@ while (true) {
}
""".trimIndent()
private val gpuTestPalette = """
local vm = require("rawmem")
local bit = require("bit32")
@@ -213,55 +209,54 @@ while true do
end
""".trimIndent()
private val gpuTestPaletteJs = """
var w = 560
var h = 448
var hwoff = 1048576
print(typeof print) //function
print(typeof vm.poke) //function
private val jscode = """
var w = 560;
var h = 448;
var hwoff = 1048576;
function inthash(x) {
x = ((x >> 16) ^ x) * 0x45d9f3b
x = ((x >> 16) ^ x) * 0x45d9f3b
x = (x >> 16) ^ x
return x
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
var rng = Math.floor(Math.random() * 2147483647) + 1
var rng = Math.floor(Math.random() * 2147483647) + 1;
while (true) {
var tstart = vm.nanoTime()
var tstart = vm.nanoTime();
for (var y = 0; y < 360; y++) {
for (var x = 0; x < w; x++) {
var palnum = 20 * Math.floor(y / 30) + Math.floor(x / 28)
vm.poke(-(y * w + x + 1) - hwoff, inthash(palnum + rng))
var palnum = 20 * Math.floor(y / 30) + Math.floor(x / 28);
vm.poke(-(y * w + x + 1) - hwoff, inthash(palnum + rng));
}
}
for (var y = 360; y < h; y++) {
for (var x = 0; x < w; x++) {
var palnum = 240 + Math.floor(x / 35)
vm.poke(-(y * w + x + 1) - hwoff, palnum)
var palnum = 240 + Math.floor(x / 35);
vm.poke(-(y * w + x + 1) - hwoff, palnum);
}
}
for (var k = 0; k < 2560; k++) {
vm.poke(-(253952 + k + 1) - hwoff, -2) // white
vm.poke(-(253952 + 2560 + k + 1) - hwoff, -1) // transparent
vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255))
vm.poke(-(253952 + k + 1) - hwoff, -2); // transparent
vm.poke(-(253952 + 2560 + k + 1) - hwoff, -1); // white
vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255));
}
rng = inthash(rng)
rng = inthash(rng);
var tend = vm.nanoTime()
var tend = vm.nanoTime();
print("Apparent FPS: " + (1000000000 / (tend - tstart)))
print("Apparent FPS: " + (1000000000 / (tend - tstart)));
}
""".trimIndent()
private val gpuTestPaletteJs = "eval('${jscode.replace(Regex("//[^\\n]*"), "").replace('\n', ' ')}')"
""".trimIndent()
private val gpuTestPaletteJava = """
int w = 560;
@@ -370,6 +365,7 @@ while True:
super.dispose()
batch.dispose()
coroutineJob.cancel()
vm.dispose()
}
}

View File

@@ -10,13 +10,7 @@ class VMJSR223Delegate(val vm: VM) {
fun poke(addr: Int, value: Int) = vm.poke(addr.toLong(), value.toByte())
fun peek(addr: Int) = vm.peek(addr.toLong())
fun nanoTime() = System.nanoTime()
fun dmagload(from: Int, to: Int, length: Int) {
val periid = vm.findPeribyType("gpu")
if (periid == null)
throw IllegalStateException("GPU not found")
else {
(vm.peripheralTable[periid].peripheral as GraphicsAdapter).bulkLoad(vm, from.toLong(), to.toLong(), length.toLong())
}
}
fun malloc(size: Int) = vm.malloc(size)
fun free(ptr: Int) = vm.free(ptr)
}

View File

@@ -53,7 +53,7 @@ internal class Firmware(val vm: VM) : TwoArgFunction() {
val t = LuaTable()
t["poke"] = Poke(vm)
t["peek"] = Peek(vm)
t["nanotime"] = object : ZeroArgFunction() {
t["nanoTime"] = object : ZeroArgFunction() {
override fun call(): LuaValue {
return LuaValue.valueOf(System.nanoTime().toDouble())
}

View File

@@ -0,0 +1,137 @@
package net.torvald.tsvm.peripheral
import com.badlogic.gdx.utils.Queue
/**
* Implements standard TTY that can interpret some of the ANSI escape sequences
*/
abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
abstract fun getCursorPos(): Pair<Int, Int>
abstract fun setCursorPos(x: Int, y: Int)
abstract var rawCursorPos: Int
abstract var blinkCursor: Boolean
abstract var ttyFore: Int
abstract var ttyBack: Int
abstract var ttyRawMode: Boolean
abstract fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte = ttyFore.toByte(), backColour: Byte = ttyBack.toByte())
private var ttyMode = Queue<Byte>() // stores escape sequences like: <ESC> [
/**
* ONLY accepts a character to either process the escape sequence, or say the input character is allowed to print.
* This function will alter the internal state of the TTY intepreter (aka this very class)
*
* Any unrecognisable escape sequence will result the internal state to be reset but the character WILL NOT be marked
* as printable.
*
* @return true if character should be printed as-is
*/
private fun acceptChar(char: Byte): Boolean {
TODO()
if (ESC == char) {
// beginning of the escape sequence
if (ttyMode.isEmpty) {
ttyMode.addLast(char)
}
else {
return true
}
}
// Any escape sequences
else if (ttyMode.size >= 1) {
// make a state machine; if the machine should move into accepting state: accept a char, and return false;
// for a rejecting state (sequence not in the transition table): clear the ttyMode, and return false;
// for a terminating state (escape sequence is terminated successfully): run interpretCSI(), and return false.
return false
}
}
private fun interpretEscapeSequence() {
TODO()
}
private val ESC = 0x1B.toByte()
private val LBRACKET = 0x5B.toByte()
private val FORE_DEFAULT = 254
private val BACK_DEFAULT = 255
}
/*
Note 1. State machine for Escape sequence
digraph G {
ESC -> Reset [label="c"]
ESC -> CSI [label="["]
CSI -> numeral [label="0..9"]
CSI -> CursorUp [label="A"]
CSI -> CursorDown [label="B"]
CSI -> CursorFwd [label="C"]
CSI -> CursorBack [label="D"]
CSI -> CursorNextLine [label="E"]
CSI -> CursorPrevLine [label="F"]
CSI -> CursorY [label="G"]
CSI -> EraseDisp [label="J"]
CSI -> EraseLine [label="K"]
CSI -> ScrollUp [label="S"]
CSI -> ScrollDown [label="T"]
numeral -> numeral [label="0..9"]
numeral -> CursorUp [label="A"]
numeral -> CursorDown [label="B"]
numeral -> CursorFwd [label="C"]
numeral -> CursorBack [label="D"]
numeral -> CursorNextLine [label="E"]
numeral -> CursorPrevLine [label="F"]
numeral -> CursorY [label="G"]
numeral -> EraseDisp [label="J"]
numeral -> EraseLine [label="K"]
numeral -> ScrollUp [label="S"]
numeral -> ScrollDown [label="T"]
numeral -> SGR [label="(any unacceptable char)"]
numeral -> separator1 [label=";"]
separator1 -> numeral2 [label="0..9"]
numeral2 -> numeral2 [label="0..9"]
numeral2 -> CursorPos [label="H"]
numeral2 -> separator2 [label=";"]
separator2 -> numeral3 [label="0..9"]
numeral3 -> numeral3 [label="0..9"]
numeral3 -> "SGR-Colour" [label="(any unacceptable char)"]
ESC [shape=Mdiamond]
Reset -> end
CursorUp -> end
CursorDown -> end
CursorFwd -> end
CursorBack -> end
CursorNextLine -> end
CursorPrevLine -> end
CursorY -> end
EraseDisp -> end
EraseLine -> end
ScrollUp -> end
ScrollDown -> end
CursorPos -> end
SGR -> end
"SGR-Colour" -> end
end [shape=Msquare]
}
*/

View File

@@ -11,9 +11,9 @@ import net.torvald.tsvm.kB
import sun.nio.ch.DirectBuffer
import kotlin.experimental.and
class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_ROWS, Companion.TEXT_COLS), PeriBase {
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
internal val paletteOfFloats = FloatArray(1024) {
val rgba = DEFAULT_PALETTE[it / 4]
@@ -29,15 +29,16 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
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
override var blinkCursor = true
override var ttyRawMode = false
private var graphicsUseSprites = false
private var lastUsedColour = (-1).toByte()
private var currentChrRom = 0
private var chrWidth = 7f
private var chrHeight = 14f
private var ttyFore: Int = 254 // cannot be Byte
private var ttyBack: Int = 255 // cannot be Byte
override var ttyFore: Int = 254 // cannot be Byte
override var ttyBack: Int = 255 // cannot be Byte
private val textForePixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888)
private val textBackPixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888)
@@ -52,7 +53,14 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
private val memTextBackOffset = 2980L + 2560
private val memTextOffset = 2980L + 2560 + 2560
private fun getTtyCursorPos() = spriteAndTextArea.getShort(memTextCursorPosOffset) % TEXT_COLS to spriteAndTextArea.getShort(3938L) / TEXT_COLS
override var rawCursorPos: Int
get() = spriteAndTextArea.getShort(memTextCursorPosOffset).toInt()
set(value) { spriteAndTextArea.setShort(memTextCursorPosOffset, value.toShort()) }
override fun getCursorPos() = rawCursorPos % TEXT_COLS to rawCursorPos / TEXT_COLS
override fun setCursorPos(x: Int, y: Int) {
rawCursorPos = toTtyTextOffset(x, y)
}
private fun toTtyTextOffset(x: Int, y: Int) = y * TEXT_COLS + x
init {
@@ -71,12 +79,15 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
// when in text mode, and that's undesired behaviour
// -1 is preferred because it points to the colour CLEAR, and it's constant.
spriteAndTextArea.fillWith(-1)
println(framebuffer.pixels.limit())
}
override fun peek(addr: Long): Byte? {
val adi = addr.toInt()
return when (addr) {
in 0 until 250880 -> framebuffer.getPixel(adi % WIDTH, adi / WIDTH).toByte()
in 0 until 250880 -> framebuffer.pixels.get(adi)//framebuffer.getPixel(adi % WIDTH, adi / WIDTH).toByte()
in 250880 until 250972 -> unusedArea[adi - 250880]
in 250972 until 261632 -> spriteAndTextArea[addr - 250972]
in 261632 until 262144 -> peekPalette(adi - 261632)
@@ -94,7 +105,7 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
when (addr) {
in 0 until 250880 -> {
lastUsedColour = byte
framebuffer.drawPixel(adi % WIDTH, adi / WIDTH, bi.shl(24))
framebuffer.pixels.put(adi, byte)
}
250883L -> {
unusedArea[adi - 250880] = byte
@@ -110,13 +121,16 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
}
}
private fun getTextmodeAttirbutes(): Byte = (currentChrRom.and(15).shl(4) or textmodeBlinkCursor.toInt()).toByte()
private fun getTextmodeAttirbutes(): Byte = (currentChrRom.and(15).shl(4) or
ttyRawMode.toInt().shl(1) or
blinkCursor.toInt()).toByte()
private fun getGraphicsAttributes(): Byte = graphicsUseSprites.toInt().toByte()
private fun setTextmodeAttributes(rawbyte: Byte) {
currentChrRom = rawbyte.toInt().and(0b11110000).ushr(4)
textmodeBlinkCursor = rawbyte.and(1) == 1.toByte()
blinkCursor = rawbyte.and(0b0001) != 0.toByte()
ttyRawMode = rawbyte.and(0b0010) != 0.toByte()
}
private fun setGraphicsAttributes(rawbyte: Byte) {
@@ -173,35 +187,17 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
* @param to memory "offset" in Graphics Adapter's memory space, starts from zero.
* @param length how many bytes should be moved
*/
fun bulkLoad(vm: VM, from: Long, to: Long, length: Long) {
/*fun bulkLoad(vm: VM, from: Long, to: Long, length: Long) {
UnsafeHelper.unsafe.copyMemory(null, vm.usermem.ptr + from, (framebuffer.pixels as DirectBuffer).address(), to, length)
}
}*/
private fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte = ttyFore.toByte(), backColour: Byte = ttyBack.toByte()) {
override fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte, backColour: Byte) {
val textOff = toTtyTextOffset(x, y)
spriteAndTextArea[memTextForeOffset + textOff] = foreColour
spriteAndTextArea[memTextBackOffset + textOff] = backColour
spriteAndTextArea[memTextOffset + textOff] = text
}
private fun advanceCursor() {
spriteAndTextArea.setShort(
memTextCursorPosOffset,
((spriteAndTextArea.getShort(memTextCursorPosOffset) + 1) % (TEXT_COLS * TEXT_ROWS)).toShort()
)
}
// how TTY should work with all those ASCII control characters
fun print(char: Byte) {
val (cx, cy) = getTtyCursorPos()
when (char) {
in 0x20..0x7E, in 0x80..0xFF -> {
putChar(cx, cy, char)
advanceCursor()
}
}
}
override fun dispose() {
framebuffer.dispose()
rendertex.dispose()
@@ -225,7 +221,7 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : PeriBase {
fun render(delta: Float, batch: SpriteBatch, x: Float, y: Float) {
rendertex.dispose()
rendertex = Texture(framebuffer)
rendertex = Texture(framebuffer, Pixmap.Format.RGBA8888, false)
batch.shader = null
@@ -383,7 +379,7 @@ float rand(vec2 co){
}
void main(void) {
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).r * 255.0)];
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
}
""".trimIndent()
@@ -403,7 +399,7 @@ float rand(vec2 co){
}
void main(void) {
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).r * 255.0)];
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 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);

View File

@@ -0,0 +1,7 @@
package net.torvald.tsvm.peripheral
internal class TermSim {
}