graphics mode 5

This commit is contained in:
minjaesong
2025-10-09 16:01:26 +09:00
parent 78a7cdc08f
commit 912e35a122
3 changed files with 160 additions and 36 deletions

View File

@@ -112,7 +112,7 @@ if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAP
con.clear()
con.curs_set(0)
graphics.setGraphicsMode(4) // 4096-colour mode
graphics.setGraphicsMode(5) // 32-bit colour mode
graphics.clearPixels(0)
graphics.clearPixels2(0)

View File

@@ -1454,6 +1454,7 @@ class GraphicsJSR223Delegate(private val vm: VM) {
*/
fun uploadRGBToFramebuffer(rgbAddr: Long, width: Int, height: Int, frameCount: Int, resizeToFull: Boolean) {
val gpu = (vm.peripheralTable[1].peripheral as GraphicsAdapter)
val graphicsMode = gpu.graphicsMode
val rgbAddrIncVec = if (rgbAddr >= 0) 1 else -1
@@ -1471,6 +1472,10 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val rgbBulkBuffer = ByteArray(chunkSize * 3)
val rgChunk = ByteArray(chunkSize)
val baChunk = ByteArray(chunkSize)
val rChunk = ByteArray(chunkSize)
val gChunk = ByteArray(chunkSize)
val bChunk = ByteArray(chunkSize)
val aChunk = ByteArray(chunkSize); aChunk.fill(-1)
while (pixelsProcessed < totalNativePixels) {
val pixelsInChunk = kotlin.math.min(chunkSize, totalNativePixels - pixelsProcessed)
@@ -1490,24 +1495,53 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val g = rgbBulkBuffer[i*3 + 1].toUint()
val b = rgbBulkBuffer[i*3 + 2].toUint()
// 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)
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)
// 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 15).toByte()
// 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 15).toByte()
// Write directly to framebuffer position
val nativePos = videoY * nativeWidth + videoX
}
else if (graphicsMode == 5) {
rChunk[i] = r.toByte()
gChunk[i] = g.toByte()
bChunk[i] = b.toByte()
}
}
val pixelIndex = pixelsProcessed
val videoY = pixelIndex / width
val videoX = pixelIndex % width
val nativePos = videoY * nativeWidth + videoX
if (graphicsMode == 4) {
UnsafeHelper.memcpyRaw(
rgChunk, UnsafeHelper.getArrayOffset(rgChunk) + i,
null, gpu.framebuffer.ptr + nativePos, 1L
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
baChunk, UnsafeHelper.getArrayOffset(baChunk) + i,
null, gpu.framebuffer2!!.ptr + nativePos, 1L
baChunk, UnsafeHelper.getArrayOffset(baChunk),
null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong()
)
}
else if (graphicsMode == 5) {
UnsafeHelper.memcpyRaw(
rChunk, UnsafeHelper.getArrayOffset(rChunk),
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
gChunk, UnsafeHelper.getArrayOffset(gChunk),
null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
bChunk, UnsafeHelper.getArrayOffset(bChunk),
null, gpu.framebuffer3!!.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
aChunk, UnsafeHelper.getArrayOffset(aChunk),
null, gpu.framebuffer4!!.ptr + nativePos, pixelsInChunk.toLong()
)
}
@@ -1520,9 +1554,13 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val scaleY = height.toFloat() / nativeHeight.toFloat()
// Process native pixels in 8KB chunks
val chunkSize = 8192
val chunkSize = 32768
val rgChunk = ByteArray(chunkSize)
val baChunk = ByteArray(chunkSize)
val rChunk = ByteArray(chunkSize)
val gChunk = ByteArray(chunkSize)
val bChunk = ByteArray(chunkSize)
val aChunk = ByteArray(chunkSize); aChunk.fill(-1)
var pixelsProcessed = 0
@@ -1545,23 +1583,54 @@ class GraphicsJSR223Delegate(private val vm: VM) {
val g = rgb[1]
val b = rgb[2]
// Apply Bayer dithering and convert to 4-bit using native coordinates
val r4 = ditherValue(r, nativeX, nativeY, frameCount)
val g4 = ditherValue(g, nativeX, nativeY, frameCount)
val b4 = ditherValue(b, nativeX, nativeY, frameCount)
if (graphicsMode == 4) {
// Apply Bayer dithering and convert to 4-bit using native coordinates
val r4 = ditherValue(r, nativeX, nativeY, frameCount)
val g4 = ditherValue(g, nativeX, nativeY, frameCount)
val b4 = ditherValue(b, nativeX, nativeY, frameCount)
// Pack and store in chunk buffers
rgChunk[i] = ((r4 shl 4) or g4).toByte()
baChunk[i] = ((b4 shl 4) or 15).toByte()
// Pack and store in chunk buffers
rgChunk[i] = ((r4 shl 4) or g4).toByte()
baChunk[i] = ((b4 shl 4) or 15).toByte()
}
else if (graphicsMode == 5) {
rChunk[i] = r.toByte()
gChunk[i] = g.toByte()
bChunk[i] = b.toByte()
}
}
val pixelIndex = pixelsProcessed
val videoY = pixelIndex / width
val videoX = pixelIndex % width
val nativePos = videoY * nativeWidth + videoX
if (graphicsMode == 4) {
UnsafeHelper.memcpyRaw(
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
baChunk, UnsafeHelper.getArrayOffset(baChunk),
null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong()
)
}
else if (graphicsMode == 5) {
UnsafeHelper.memcpyRaw(
rChunk, UnsafeHelper.getArrayOffset(rChunk),
null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
gChunk, UnsafeHelper.getArrayOffset(gChunk),
null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
bChunk, UnsafeHelper.getArrayOffset(bChunk),
null, gpu.framebuffer3!!.ptr + nativePos, pixelsInChunk.toLong()
)
UnsafeHelper.memcpyRaw(
aChunk, UnsafeHelper.getArrayOffset(aChunk),
null, gpu.framebuffer4!!.ptr + nativePos, pixelsInChunk.toLong()
)
}
// Write pixels to their sequential positions in framebuffer
UnsafeHelper.memcpyRaw(
rgChunk, UnsafeHelper.getArrayOffset(rgChunk),
null, gpu.framebuffer.ptr + pixelsProcessed, pixelsInChunk.toLong())
UnsafeHelper.memcpyRaw(
baChunk, UnsafeHelper.getArrayOffset(baChunk),
null, gpu.framebuffer2!!.ptr + pixelsProcessed, pixelsInChunk.toLong())
pixelsProcessed += pixelsInChunk
}

View File

@@ -50,7 +50,7 @@ data class SuperGraphicsAddonConfig(
val bankCount: Int = 1
)
class ReferenceGraphicsAdapter(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(2))
class ReferenceGraphicsAdapter(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(4))
class ReferenceGraphicsAdapter2(assetsRoot: String, vm: VM) : RemoteGraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(2))
class ReferenceLikeLCD(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_PMLCD)
@@ -76,6 +76,9 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
internal val framebuffer = UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this)//Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
internal val framebuffer2 = if (sgr.bankCount >= 2) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null
internal val framebuffer3 = if (sgr.bankCount >= 3) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null
internal val framebuffer4 = if (sgr.bankCount >= 4) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null
internal val framebufferOut = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
protected var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
internal val paletteOfFloats = FloatArray(1024) {
@@ -130,7 +133,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
private val outFBOregion = Array(2) { TextureRegion(outFBOs[it].colorBufferTexture) }
private val outFBObatch = SpriteBatch(1000, DefaultGL32Shaders.createSpriteBatchShader())
private var graphicsMode = 0
var graphicsMode = 0
private var layerArrangement = 0
@@ -227,7 +230,19 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
override fun peek(addr: Long): Byte? {
val adi = addr.toInt()
if (framebuffer2 != null && addr >= 262144) {
if (framebuffer4 != null && addr >= 524288) {
return when (addr - 524288) {
in 0 until 250880 -> framebuffer4[addr - 524288]
else -> null
}
}
else if (framebuffer3 != null && addr >= 393216) {
return when (addr - 393216) {
in 0 until 250880 -> framebuffer3[addr - 393216]
else -> null
}
}
else if (framebuffer2 != null && addr >= 262144) {
return when (addr - 262144) {
in 0 until 250880 -> framebuffer2[addr - 262144]
else -> null
@@ -275,6 +290,24 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
override fun poke(addr: Long, byte: Byte) {
val adi = addr.toInt()
val bi = byte.toInt().and(255)
if (framebuffer4 != null) {
when (addr - 524288) {
in 0 until 250880 -> {
lastUsedColour = byte
framebuffer4[addr - 524288] = byte
return
}
}
}
if (framebuffer3 != null) {
when (addr - 393216) {
in 0 until 250880 -> {
lastUsedColour = byte
framebuffer3[addr - 393216] = byte
return
}
}
}
if (framebuffer2 != null) {
when (addr - 262144) {
in 0 until 250880 -> {
@@ -899,6 +932,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
// textShader.tryDispose()
framebuffer.destroy()
framebuffer2?.destroy()
framebuffer3?.destroy()
framebuffer4?.destroy()
framebufferOut.tryDispose()
rendertex.tryDispose()
textArea.destroy()
@@ -942,7 +977,27 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
chrrom.pixels.position(0)
framebufferOut.setColor(-1);framebufferOut.fill()
if (graphicsMode == 4 && framebuffer2 != null) {
if (graphicsMode == 5 && 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()
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 r = framebuffer[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
val g = framebuffer2[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
val b = framebuffer3[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
val a = framebuffer4[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559
framebufferOut.setColor(
r.shl(24) or g.shl(16) or b.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)
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
@@ -968,7 +1023,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
}
}
else if (graphicsMode == 3 && framebuffer2 != null) {
val layerOrder = (if (graphicsMode == 1) LAYERORDERS4 else LAYERORDERS2)[layerArrangement]
val layerOrder = LAYERORDERS2[layerArrangement]
val fb1 = if (layerOrder[0] == 0) framebuffer else framebuffer2
val fb2 = if (layerOrder[0] == 0) framebuffer2 else framebuffer