mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-10 06:54:04 +09:00
TAV: decompression done on GPU
This commit is contained in:
@@ -120,6 +120,10 @@ Peripheral memories can be accessed using `vm.peek()` and `vm.poke()` functions,
|
|||||||
- JavaScript test programs available in `assets/disk0/`
|
- JavaScript test programs available in `assets/disk0/`
|
||||||
- Videotron2K assembly examples in documentation
|
- Videotron2K assembly examples in documentation
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The 'gzip' namespace in TSVM's JS programs is a misnomer: the actual 'gzip' functions (defined in CompressorDelegate.kt) call Zstd functions.
|
||||||
|
|
||||||
## TVDOS
|
## TVDOS
|
||||||
|
|
||||||
### TVDOS Movie Formats
|
### TVDOS Movie Formats
|
||||||
|
|||||||
@@ -69,18 +69,17 @@ let notifHideTimer = 0
|
|||||||
const NOTIF_SHOWUPTIME = 3000000000
|
const NOTIF_SHOWUPTIME = 3000000000
|
||||||
let [cy, cx] = con.getyx()
|
let [cy, cx] = con.getyx()
|
||||||
|
|
||||||
let seqreadserial = require("seqread")
|
//let playgui = require("playgui")
|
||||||
let seqreadtape = require("seqreadtape")
|
|
||||||
let seqread = undefined
|
let seqread = undefined
|
||||||
let fullFilePathStr = fullFilePath.full
|
let fullFilePathStr = fullFilePath.full
|
||||||
|
|
||||||
// Select seqread driver to use
|
// Select seqread driver to use
|
||||||
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\\\TAPE')) {
|
if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAPE')) {
|
||||||
seqread = seqreadtape
|
seqread = require("seqreadtape")
|
||||||
seqread.prepare(fullFilePathStr)
|
seqread.prepare(fullFilePathStr)
|
||||||
seqread.seek(0)
|
seqread.seek(0)
|
||||||
} else {
|
} else {
|
||||||
seqread = seqreadserial
|
seqread = require("seqread")
|
||||||
seqread.prepare(fullFilePathStr)
|
seqread.prepare(fullFilePathStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,33 +685,20 @@ try {
|
|||||||
else if (packetType === TAV_PACKET_IFRAME || packetType === TAV_PACKET_PFRAME) {
|
else if (packetType === TAV_PACKET_IFRAME || packetType === TAV_PACKET_PFRAME) {
|
||||||
// Video packet
|
// Video packet
|
||||||
const compressedSize = seqread.readInt()
|
const compressedSize = seqread.readInt()
|
||||||
const isKeyframe = (packetType === TAV_PACKET_IFRAME)
|
|
||||||
|
|
||||||
// Read compressed tile data
|
// Read compressed tile data
|
||||||
let compressedPtr = seqread.readBytes(compressedSize)
|
let compressedPtr = seqread.readBytes(compressedSize)
|
||||||
updateDataRateBin(compressedSize)
|
updateDataRateBin(compressedSize)
|
||||||
|
|
||||||
let actualSize
|
|
||||||
let decompressStart = sys.nanoTime()
|
|
||||||
try {
|
try {
|
||||||
// Use gzip decompression (only compression format supported in TSVM JS)
|
|
||||||
actualSize = gzip.decompFromTo(compressedPtr, compressedSize, blockDataPtr)
|
|
||||||
decompressTime = (sys.nanoTime() - decompressStart) / 1000000.0
|
|
||||||
} catch (e) {
|
|
||||||
decompressTime = (sys.nanoTime() - decompressStart) / 1000000.0
|
|
||||||
console.log(`Frame ${frameCount}: Gzip decompression failed, skipping (compressed size: ${compressedSize}, error: ${e})`)
|
|
||||||
sys.free(compressedPtr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// serial.println(actualSize)
|
|
||||||
let decodeStart = sys.nanoTime()
|
let decodeStart = sys.nanoTime()
|
||||||
|
|
||||||
// Call TAV hardware decoder (like TEV's tevDecode but with RGB buffer outputs)
|
// Call new TAV hardware decoder that handles Zstd decompression internally
|
||||||
graphics.tavDecode(
|
// Note: No longer using JS gzip.decompFromTo - Kotlin handles Zstd natively
|
||||||
blockDataPtr,
|
graphics.tavDecodeCompressed(
|
||||||
CURRENT_RGB_ADDR, PREV_RGB_ADDR, // RGB buffer pointers (not float arrays!)
|
compressedPtr, // Pass compressed data directly
|
||||||
|
compressedSize, // Size of compressed data
|
||||||
|
CURRENT_RGB_ADDR, PREV_RGB_ADDR, // RGB buffer pointers
|
||||||
header.width, header.height,
|
header.width, header.height,
|
||||||
header.qualityLevel, header.qualityY, header.qualityCo, header.qualityCg,
|
header.qualityLevel, header.qualityY, header.qualityCo, header.qualityCg,
|
||||||
frameCount,
|
frameCount,
|
||||||
@@ -723,6 +709,7 @@ try {
|
|||||||
)
|
)
|
||||||
|
|
||||||
decodeTime = (sys.nanoTime() - decodeStart) / 1000000.0
|
decodeTime = (sys.nanoTime() - decodeStart) / 1000000.0
|
||||||
|
decompressTime = 0 // Decompression time now included in decode time
|
||||||
|
|
||||||
// Upload RGB buffer to display framebuffer (like TEV)
|
// Upload RGB buffer to display framebuffer (like TEV)
|
||||||
let uploadStart = sys.nanoTime()
|
let uploadStart = sys.nanoTime()
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ import kotlin.text.format
|
|||||||
import kotlin.text.lowercase
|
import kotlin.text.lowercase
|
||||||
import kotlin.text.toString
|
import kotlin.text.toString
|
||||||
import kotlin.times
|
import kotlin.times
|
||||||
|
import io.airlift.compress.zstd.ZstdInputStream
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
class GraphicsJSR223Delegate(private val vm: VM) {
|
class GraphicsJSR223Delegate(private val vm: VM) {
|
||||||
|
|
||||||
@@ -4137,6 +4139,53 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
|||||||
private val tavDebugFrameTarget = -1 // use negative number to disable the debug print
|
private val tavDebugFrameTarget = -1 // use negative number to disable the debug print
|
||||||
private var tavDebugCurrentFrameNumber = 0
|
private var tavDebugCurrentFrameNumber = 0
|
||||||
|
|
||||||
|
// New tavDecode function that accepts compressed data and decompresses internally
|
||||||
|
fun tavDecodeCompressed(compressedDataPtr: Long, compressedSize: Int, currentRGBAddr: Long, prevRGBAddr: Long,
|
||||||
|
width: Int, height: Int, qIndex: Int, qYGlobal: Int, qCoGlobal: Int, qCgGlobal: Int, frameCount: Int,
|
||||||
|
waveletFilter: Int = 1, decompLevels: Int = 6, isLossless: Boolean = false, tavVersion: Int = 1) {
|
||||||
|
|
||||||
|
// Read compressed data from VM memory into byte array
|
||||||
|
val compressedData = ByteArray(compressedSize)
|
||||||
|
for (i in 0 until compressedSize) {
|
||||||
|
compressedData[i] = vm.peek(compressedDataPtr + i)!!.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Decompress using Zstd
|
||||||
|
val bais = ByteArrayInputStream(compressedData)
|
||||||
|
val zis = ZstdInputStream(bais)
|
||||||
|
val decompressedData = zis.readBytes()
|
||||||
|
zis.close()
|
||||||
|
bais.close()
|
||||||
|
|
||||||
|
// Allocate buffer for decompressed data
|
||||||
|
val decompressedBuffer = vm.malloc(decompressedData.size)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Copy decompressed data to unsafe buffer
|
||||||
|
UnsafeHelper.memcpyRaw(
|
||||||
|
decompressedData, UnsafeHelper.getArrayOffset(decompressedData),
|
||||||
|
null, vm.usermem.ptr + decompressedBuffer.toLong(),
|
||||||
|
decompressedData.size.toLong()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call the existing tavDecode function with decompressed data
|
||||||
|
tavDecode(decompressedBuffer.toLong(), currentRGBAddr, prevRGBAddr,
|
||||||
|
width, height, qIndex, qYGlobal, qCoGlobal, qCgGlobal, frameCount,
|
||||||
|
waveletFilter, decompLevels, isLossless, tavVersion)
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// Clean up allocated buffer
|
||||||
|
vm.free(decompressedBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("TAV Zstd decompression error: ${e.message}")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original tavDecode function for backward compatibility (now handles decompressed data)
|
||||||
fun tavDecode(blockDataPtr: Long, currentRGBAddr: Long, prevRGBAddr: Long,
|
fun tavDecode(blockDataPtr: Long, currentRGBAddr: Long, prevRGBAddr: Long,
|
||||||
width: Int, height: Int, qIndex: Int, qYGlobal: Int, qCoGlobal: Int, qCgGlobal: Int, frameCount: Int,
|
width: Int, height: Int, qIndex: Int, qYGlobal: Int, qCoGlobal: Int, qCgGlobal: Int, frameCount: Int,
|
||||||
waveletFilter: Int = 1, decompLevels: Int = 6, isLossless: Boolean = false, tavVersion: Int = 1) {
|
waveletFilter: Int = 1, decompLevels: Int = 6, isLossless: Boolean = false, tavVersion: Int = 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user