graphics adapter: render framebuffer is back to 32bpp to support direct colour mode

This commit is contained in:
minjaesong
2022-04-18 01:16:13 +09:00
parent 59ce7e5fac
commit 9e990ccdf4
2 changed files with 193 additions and 16 deletions

View File

@@ -253,6 +253,69 @@ MMIO
current TTY background colour (useful for print() function)
11 RO
Number of Banks, or VRAM size (1 = 256 kB, max 4)
12 RW
Graphics Mode
0: 560x448, 256 Colours, 1 layer
1: 280x224, 256 Colours, 4 layers
2: 280x224, 4096 Colours, 2 layers
3: 560x448, 256 Colours, 2 layers (if bank 2 is not installed, will fall back to mode 0)
4: 560x448, 4096 Colours, 1 layer (if bank 2 is not installed, will fall back to mode 0)
4096 is also known as "direct colour mode" (4096 colours * 16 transparency -> 65536 colours)
13 RW
Layer Arrangement
If 4 layers are used:
Num LO<->HI
0 1234
1 1243
2 1324
3 1342
4 1423
5 1432
6 2134
7 2143
8 2314
9 2341
10 2413
11 2431
12 3124
13 3142
14 3214
15 3241
16 3412
17 3421
18 4123
19 4132
20 4213
21 4231
22 4312
23 4321
If 2 layers are used:
Num LO<->HI
0 12
1 12
2 12
3 12
4 12
5 12
6 12
7 21
8 21
9 21
10 21
11 21
12 12
13 12
14 21
15 21
16 12
17 21
18 12
19 12
20 21
21 21
22 12
23 21
If 1 layer is used, this field will do nothing and always fall back to 0
Text-mode-font-ROM is immutable and does not belong to VRAM
Even in the text mode framebuffer is still being drawn onto the screen, and the texts are drawn on top of it

View File

@@ -63,7 +63,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
protected val TAB_SIZE = 8
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
internal val framebuffer2 = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
internal val framebuffer2 = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
protected var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
internal val paletteOfFloats = FloatArray(1024) {
val rgba = DEFAULT_PALETTE[it / 4]
@@ -108,6 +108,9 @@ 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()
private var graphicsMode = 0
private var layerArrangement = 0
private val memTextCursorPosOffset = 0L
private val memTextForeOffset = 2L
@@ -269,6 +272,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
9L -> ttyFore.toByte()
10L -> ttyBack.toByte()
11L -> sgr.bankCount.toByte()
12L -> graphicsMode.toByte()
13L -> layerArrangement.toByte()
in 0 until VM.MMIO_SIZE -> -1
else -> null
@@ -276,7 +281,16 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
}
override fun mmio_write(addr: Long, byte: Byte) {
TODO("Not yet implemented")
val bi = byte.toUint()
when (addr) {
6L -> setTextmodeAttributes(byte)
7L -> setGraphicsAttributes(byte)
9L -> { ttyFore = bi }
10L -> { ttyBack = bi }
12L -> { graphicsMode = bi }
13L -> { layerArrangement = bi }
else -> null
}
}
private fun runCommand(opcode: Byte) {
@@ -713,6 +727,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
unusedArea[1].toInt().and(15).toFloat() / 15f,
unusedArea[2].toInt().and(15).toFloat() / 15f, 1f)
private val isRefSize = (config.width == 560 && config.height == 448)
open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, uiFBO: FrameBuffer? = null) {
uiFBO?.end()
@@ -722,21 +738,64 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
chrrom.pixels.position(0)
framebuffer2.setColor(-1);framebuffer2.fill()
for (y in 0 until config.height) {
var xoff = unusedArea[20L + 2*y].toUint().shl(8) or unusedArea[20L + 2*y + 1].toUint()
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
val xs = (0+xoff).coerceIn(0,config.width-1) .. (config.width-1+xoff).coerceIn(0,config.width-1)
if (isRefSize && graphicsMode == 1) {
val layerOrder = LAYERORDERS4[layerArrangement]
for (y in 0..223) {
var xoff = unusedArea[20L + 2 * y].toUint().shl(8) or unusedArea[20L + 2 * y + 1].toUint()
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
val xs =
(0 + xoff).coerceIn(0, 279)..(279 + xoff).coerceIn(0, 279)
if (xoff in -(config.width-1)..config.width-1) {
for (x in xs) {
// this only works because framebuffer is guaranteed to be 8bpp
framebuffer2.pixels.put(y*config.width+x,
framebuffer.pixels.get(y*config.width + (x - xoff)) // coerceIn not required as (x - xoff) never escapes 0..559
)
if (xoff in -(280 - 1) until 280) {
for (x in xs) {
val colour = layerOrder.map {
val colourIndex = framebuffer.pixels.get(280 * 224 * it + (y * 224 + x)).toUint()
Color(paletteOfFloats[4*colourIndex], paletteOfFloats[4*colourIndex+1], paletteOfFloats[4*colourIndex+2], paletteOfFloats[4*colourIndex+3])
}.fold(Color(0)) { dest, src ->
// manually alpha compositing
// out_color = {src_color * src_alpha + dest_color * dest_alpha * (1-src_alpha)} / out_alpha
// see https://gamedev.stackexchange.com/a/115786
val outAlpha = (1f - (1f - dest.a) * (1f - src.a)).coerceIn(0.0001f, 1f)
// src.a + dest.a - src.a*dest.a)
Color(
(src.r * src.a + dest.r * dest.a * (1f - src.a)) / outAlpha,
(src.g * src.a + dest.g * dest.a * (1f - src.a)) / outAlpha,
(src.b * src.a + dest.b* dest.a * (1f - src.a)) / outAlpha,
outAlpha
)
}
framebuffer2.setColor(colour)
framebuffer2.drawPixel(x*2, y*2)
framebuffer2.drawPixel(x*2+1, y*2)
framebuffer2.drawPixel(x*2, y*2+1)
framebuffer2.drawPixel(x*2+1, y*2+1)
}
}
}
}
else {
for (y in 0 until config.height) {
var xoff = unusedArea[20L + 2 * y].toUint().shl(8) or unusedArea[20L + 2 * y + 1].toUint()
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
val xs =
(0 + xoff).coerceIn(0, config.width - 1)..(config.width - 1 + xoff).coerceIn(0, config.width - 1)
if (xoff in -(config.width - 1) until config.width) {
for (x in xs) {
// this only works because framebuffer is guaranteed to be 8bpp
/*framebuffer2.pixels.put(
y * config.width + x,
framebuffer.pixels.get(y * config.width + (x - xoff)) // coerceIn not required as (x - xoff) never escapes 0..559
)*/
val colourIndex = framebuffer.pixels.get(y * config.width + (x - xoff)).toUint() // coerceIn not required as (x - xoff) never escapes 0..559
framebuffer2.setColor(paletteOfFloats[4*colourIndex], paletteOfFloats[4*colourIndex+1], paletteOfFloats[4*colourIndex+2], paletteOfFloats[4*colourIndex+3])
framebuffer2.drawPixel(x, y)
}
}
}
}
chrrom0.dispose()
chrrom0 = Texture(chrrom)
@@ -973,7 +1032,7 @@ uniform sampler2D u_texture;
uniform vec4 pal[256];
void main(void) {
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
gl_FragColor = texture2D(u_texture, v_texCoords);
}
""".trimIndent()
@@ -989,7 +1048,8 @@ float intensitySteps = 4.0;
uniform vec4 lcdBaseCol;
void main(void) {
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
// vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
vec4 palCol = texture2D(u_texture, v_texCoords);
float lum = ceil((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a);
@@ -1010,8 +1070,8 @@ float intensitySteps = 4.0;
uniform vec4 lcdBaseCol;
void main(void) {
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 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
vec4 palCol = texture2D(u_texture, v_texCoords); float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
vec4 outIntensity = vec4(vec3(lum), palCol.a);
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
@@ -1765,6 +1825,60 @@ void main() {
it.and(255).div(255f)
)
}
val LAYERORDERS4 = listOf( // [drawn first, second, third, fourth], zero-indexed
"1234",
"1243",
"1324",
"1342",
"1423",
"1432",
"2134",
"2143",
"2314",
"2341",
"2413",
"2431",
"3124",
"3142",
"3214",
"3241",
"3412",
"3421",
"4123",
"4132",
"4213",
"4231",
"4312",
"4321",
).map { s -> (0..3).map { s[it].toInt() - 61 } }
val LAYERORDERS2 = listOf( // [drawn first, second], zero-indexed
"12",
"12",
"12",
"12",
"12",
"12",
"12",
"21",
"21",
"21",
"21",
"21",
"12",
"12",
"21",
"21",
"12",
"21",
"12",
"12",
"21",
"21",
"12",
"21",
).map { s -> (0..1).map { s[it].toInt() - 61 } }
}
}