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.clear()
|
||||||
con.curs_set(0)
|
con.curs_set(0)
|
||||||
graphics.setGraphicsMode(4) // initially set to 4bpp mode
|
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.clearPixels(0)
|
||||||
graphics.clearPixels2(0)
|
graphics.clearPixels2(0)
|
||||||
graphics.clearPixels3(0)
|
graphics.clearPixels3(0)
|
||||||
@@ -542,9 +542,11 @@ function getRGBfromScr(x, y) {
|
|||||||
let offset = y * WIDTH + x
|
let offset = y * WIDTH + x
|
||||||
let fb1 = sys.peek(-1048577 - offset)
|
let fb1 = sys.peek(-1048577 - offset)
|
||||||
let fb2 = sys.peek(-1310721 - 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]
|
return [(fb1 >>> 4) / 15.0, (fb1 & 15) / 15.0, (fb2 >>> 4) / 15.0]
|
||||||
else
|
else
|
||||||
return [fb1 / 255.0, fb2 / 255.0, fb3 / 255.0]
|
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 videoY = (pixelIndex / width) + startY
|
||||||
val videoX = (pixelIndex % width) + startX
|
val videoX = (pixelIndex % width) + startX
|
||||||
val nativePos = videoY * nativeWidth + videoX
|
val nativePos = videoY * nativeWidth + videoX
|
||||||
if (graphicsMode == 4) {
|
if (graphicsMode == 4 || graphicsMode == 5) {
|
||||||
UnsafeHelper.memcpyRaw(
|
UnsafeHelper.memcpyRaw(
|
||||||
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
|
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
|
||||||
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
|
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) {
|
fun writeToChunk(r: Int, g: Int, b: Int, videoX: Int, videoY: Int, i: Int, coordInVideoFrame: Boolean = true) {
|
||||||
if (graphicsMode == 4) {
|
if (graphicsMode == 4) {
|
||||||
// Apply Bayer dithering and convert to 4-bit
|
// Apply Bayer dithering and convert to 4-bit
|
||||||
val r4 = ditherValue(r, videoX, videoY, frameCount)
|
val r4 = ditherValue4(r, videoX, videoY, frameCount)
|
||||||
val g4 = ditherValue(g, videoX, videoY, frameCount)
|
val g4 = ditherValue4(g, videoX, videoY, frameCount)
|
||||||
val b4 = ditherValue(b, videoX, videoY, frameCount)
|
val b4 = ditherValue4(b, videoX, videoY, frameCount)
|
||||||
|
|
||||||
// Pack RGB values and store in chunk arrays for batch processing
|
// Pack RGB values and store in chunk arrays for batch processing
|
||||||
rgChunk[i] = ((r4 shl 4) or g4).toByte()
|
rgChunk[i] = ((r4 shl 4) or g4).toByte()
|
||||||
baChunk[i] = ((b4 shl 4) or coordInVideoFrame.toInt().times(15)).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 {
|
else {
|
||||||
rChunk[i] = r.toByte()
|
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
|
* 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
|
// Preserve pure values (0 and 255) exactly to maintain colour primaries
|
||||||
if (value == 0) return 0
|
if (value == 0) return 0
|
||||||
if (value == 255) return 15
|
if (value == 255) return 15
|
||||||
@@ -1657,6 +1666,16 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
return round(15f * q)
|
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
|
* Sample RGB values using bilinear interpolation
|
||||||
* @param rgbAddr Source RGB buffer address
|
* @param rgbAddr Source RGB buffer address
|
||||||
@@ -6694,14 +6713,28 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
|
|
||||||
if (graphicsMode == 4) {
|
if (graphicsMode == 4) {
|
||||||
// 4bpp mode: dithered RGB (RG in fb1, B_ in fb2)
|
// 4bpp mode: dithered RGB (RG in fb1, B_ in fb2)
|
||||||
val threshold = bayerKernelsInt[frameCount % 4][4 * (y % 4) + (x % 4)]
|
// val threshold = bayerKernelsInt[frameCount % 4][4 * (y % 4) + (x % 4)]
|
||||||
val rDithered = ((r + (threshold - 8)) shr 4).coerceIn(0, 15)
|
// val r4 = ((r + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||||
val gDithered = ((g + (threshold - 8)) shr 4).coerceIn(0, 15)
|
// val g4 = ((g + (threshold - 8)) shr 4).coerceIn(0, 15)
|
||||||
val bDithered = ((b + (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()
|
val r4 = ditherValue4(r, x, y, frameCount)
|
||||||
gpu.framebuffer2?.set(screenPixelIdx, ((bDithered shl 4) or 15).toByte())
|
val g4 = ditherValue4(g, x, y, frameCount)
|
||||||
} else if (graphicsMode == 5) {
|
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)
|
// 8bpp mode: full RGB (R in fb1, G in fb2, B in fb3)
|
||||||
gpu.framebuffer[screenPixelIdx] = r.toByte()
|
gpu.framebuffer[screenPixelIdx] = r.toByte()
|
||||||
gpu.framebuffer2?.set(screenPixelIdx, g.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)
|
chrrom.pixels.position(0)
|
||||||
|
|
||||||
framebufferOut.setColor(-1);framebufferOut.fill()
|
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) {
|
for (y in 0 until HEIGHT) {
|
||||||
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
||||||
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
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) {
|
else if (graphicsMode == 4 && framebuffer2 != null) {
|
||||||
for (y in 0 until HEIGHT) {
|
for (y in 0 until HEIGHT) {
|
||||||
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8)
|
||||||
|
|||||||
Reference in New Issue
Block a user