mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
gpu: 5bpp mode
This commit is contained in:
@@ -142,7 +142,7 @@ if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAP
|
||||
con.clear()
|
||||
con.curs_set(0)
|
||||
graphics.setGraphicsMode(4) // initially set to 4bpp mode
|
||||
//graphics.setGraphicsMode(5) // set to 8bpp mode. If GPU don't support it, mode will remain to 4
|
||||
graphics.setGraphicsMode(5) // then try to set to 5bpp mode
|
||||
graphics.clearPixels(0)
|
||||
graphics.clearPixels2(0)
|
||||
graphics.clearPixels3(0)
|
||||
@@ -542,9 +542,11 @@ function getRGBfromScr(x, y) {
|
||||
let offset = y * WIDTH + x
|
||||
let fb1 = sys.peek(-1048577 - offset)
|
||||
let fb2 = sys.peek(-1310721 - offset)
|
||||
let fb3 = sys.peek(-1310721 - (262144 * (gpuGraphicsMode - 4)) - offset)
|
||||
let fb3 = sys.peek(-1310721 - 262144 - offset)
|
||||
|
||||
if (gpuGraphicsMode == 4)
|
||||
if (gpuGraphicsMode == 5)
|
||||
return [((fb1 >>> 2) & 31) / 31.0, (((fb1 & 3) << 3) | ((fb2 >>> 5) & 7)) / 31.0, (fb2 & 31) / 31.0]
|
||||
else if (gpuGraphicsMode == 4)
|
||||
return [(fb1 >>> 4) / 15.0, (fb1 & 15) / 15.0, (fb2 >>> 4) / 15.0]
|
||||
else
|
||||
return [fb1 / 255.0, fb2 / 255.0, fb3 / 255.0]
|
||||
|
||||
@@ -1495,7 +1495,7 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
val videoY = (pixelIndex / width) + startY
|
||||
val videoX = (pixelIndex % width) + startX
|
||||
val nativePos = videoY * nativeWidth + videoX
|
||||
if (graphicsMode == 4) {
|
||||
if (graphicsMode == 4 || graphicsMode == 5) {
|
||||
UnsafeHelper.memcpyRaw(
|
||||
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
|
||||
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
|
||||
@@ -1530,14 +1530,23 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
fun writeToChunk(r: Int, g: Int, b: Int, videoX: Int, videoY: Int, i: Int, coordInVideoFrame: Boolean = true) {
|
||||
if (graphicsMode == 4) {
|
||||
// Apply Bayer dithering and convert to 4-bit
|
||||
val r4 = ditherValue(r, videoX, videoY, frameCount)
|
||||
val g4 = ditherValue(g, videoX, videoY, frameCount)
|
||||
val b4 = ditherValue(b, videoX, videoY, frameCount)
|
||||
val r4 = ditherValue4(r, videoX, videoY, frameCount)
|
||||
val g4 = ditherValue4(g, videoX, videoY, frameCount)
|
||||
val b4 = ditherValue4(b, videoX, videoY, frameCount)
|
||||
|
||||
// Pack RGB values and store in chunk arrays for batch processing
|
||||
rgChunk[i] = ((r4 shl 4) or g4).toByte()
|
||||
baChunk[i] = ((b4 shl 4) or coordInVideoFrame.toInt().times(15)).toByte()
|
||||
}
|
||||
else if (graphicsMode == 5) {
|
||||
// Apply Bayer dithering and convert to 4-bit
|
||||
val r5 = ditherValue5(r, videoX, videoY, frameCount)
|
||||
val g5 = ditherValue5(g, videoX, videoY, frameCount)
|
||||
val b5 = ditherValue5(b, videoX, videoY, frameCount)
|
||||
|
||||
// Pack RGB values and store in chunk arrays for batch processing
|
||||
rgChunk[i] = (coordInVideoFrame.toInt(8) or r5.shl(2) or g5.ushr(3)).toByte()
|
||||
baChunk[i] = (g5.and(7).shl(5) or b5).toByte()
|
||||
}
|
||||
else {
|
||||
rChunk[i] = r.toByte()
|
||||
@@ -1647,7 +1656,7 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
/**
|
||||
* Apply Bayer dithering to reduce banding when quantising to 4-bit
|
||||
*/
|
||||
private fun ditherValue(value: Int, x: Int, y: Int, f: Int): Int {
|
||||
private fun ditherValue4(value: Int, x: Int, y: Int, f: Int): Int {
|
||||
// Preserve pure values (0 and 255) exactly to maintain colour primaries
|
||||
if (value == 0) return 0
|
||||
if (value == 255) return 15
|
||||
@@ -1657,6 +1666,16 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
return round(15f * q)
|
||||
}
|
||||
|
||||
private fun ditherValue5(value: Int, x: Int, y: Int, f: Int): Int {
|
||||
// Preserve pure values (0 and 255) exactly to maintain colour primaries
|
||||
if (value == 0) return 0
|
||||
if (value == 255) return 31
|
||||
|
||||
val t = bayerKernels[f % 4][4 * (y % 4) + (x % 4)] // use rotating bayerKernel to time-dither the static pattern for even better visuals
|
||||
val q = floor((t / 31f + (value / 255f)) * 31f) / 31f
|
||||
return round(31f * q)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample RGB values using bilinear interpolation
|
||||
* @param rgbAddr Source RGB buffer address
|
||||
@@ -6694,14 +6713,28 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
|
||||
if (graphicsMode == 4) {
|
||||
// 4bpp mode: dithered RGB (RG in fb1, B_ in fb2)
|
||||
val threshold = bayerKernelsInt[frameCount % 4][4 * (y % 4) + (x % 4)]
|
||||
val rDithered = ((r + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
val gDithered = ((g + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
val bDithered = ((b + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
// val threshold = bayerKernelsInt[frameCount % 4][4 * (y % 4) + (x % 4)]
|
||||
// val r4 = ((r + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
// val g4 = ((g + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
// val b4 = ((b + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||
|
||||
gpu.framebuffer[screenPixelIdx] = ((rDithered shl 4) or gDithered).toByte()
|
||||
gpu.framebuffer2?.set(screenPixelIdx, ((bDithered shl 4) or 15).toByte())
|
||||
} else if (graphicsMode == 5) {
|
||||
val r4 = ditherValue4(r, x, y, frameCount)
|
||||
val g4 = ditherValue4(g, x, y, frameCount)
|
||||
val b4 = ditherValue4(b, x, y, frameCount)
|
||||
|
||||
gpu.framebuffer[screenPixelIdx] = ((r4 shl 4) or g4).toByte()
|
||||
gpu.framebuffer2?.set(screenPixelIdx, ((b4 shl 4) or 15).toByte())
|
||||
}
|
||||
else if (graphicsMode == 5) {
|
||||
// 5bpp mode: dithered RGB (ARRRRRGG in fb1, GGGBBBBB in fb2; aka Targa 16bpp pixel format)
|
||||
val r5 = ditherValue5(r, x, y, frameCount)
|
||||
val g5 = ditherValue5(g, x, y, frameCount)
|
||||
val b5 = ditherValue5(b, x, y, frameCount)
|
||||
|
||||
gpu.framebuffer[screenPixelIdx] = (0x80 or r5.shl(2) or g5.ushr(3)).toByte()
|
||||
gpu.framebuffer2?.set(screenPixelIdx, (g5.and(7).shl(5) or b5).toByte())
|
||||
}
|
||||
else if (graphicsMode == 8) {
|
||||
// 8bpp mode: full RGB (R in fb1, G in fb2, B in fb3)
|
||||
gpu.framebuffer[screenPixelIdx] = r.toByte()
|
||||
gpu.framebuffer2?.set(screenPixelIdx, g.toByte())
|
||||
|
||||
@@ -994,7 +994,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
chrrom.pixels.position(0)
|
||||
|
||||
framebufferOut.setColor(-1);framebufferOut.fill()
|
||||
if (graphicsMode == 5 && framebuffer4 != null && framebuffer3 != null && framebuffer2 != null) {
|
||||
if (graphicsMode == 8 && framebuffer4 != null && framebuffer3 != null && framebuffer2 != null) {
|
||||
for (y in 0 until HEIGHT) {
|
||||
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
||||
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
||||
@@ -1014,6 +1014,31 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (graphicsMode == 5 && framebuffer2 != null) {
|
||||
for (y in 0 until HEIGHT) {
|
||||
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
||||
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
||||
val xs = (0 + xoff).coerceIn(0, WIDTH - 1)..(WIDTH - 1 + xoff).coerceIn(0, WIDTH - 1)
|
||||
|
||||
if (xoff in -(WIDTH - 1) until WIDTH) {
|
||||
for (x in xs) {
|
||||
val rg = framebuffer[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
|
||||
val ba = framebuffer2[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
|
||||
val r = rg.ushr(2).and(31)
|
||||
val g = rg.and(3).shl(3) or ba.ushr(5)
|
||||
val b = ba.and(31)
|
||||
val a = rg.ushr(7) * 255
|
||||
framebufferOut.setColor(
|
||||
r.shl(27) or r.ushr(2).shl(24) or
|
||||
g.shl(19) or g.ushr(2).shl(16) or
|
||||
b.shl(11) or b.ushr(2).shl(8) or
|
||||
a
|
||||
)
|
||||
framebufferOut.drawPixel(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (graphicsMode == 4 && framebuffer2 != null) {
|
||||
for (y in 0 until HEIGHT) {
|
||||
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
||||
|
||||
Reference in New Issue
Block a user