From fed45d9fe6e5931a122ef2ab7823b15b01c38672 Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Sat, 25 Feb 2017 02:39:02 +0900 Subject: [PATCH] no memory leak on ImageBuffer rendering Turns out, .image creates new Image instance which will NEVER be reclaimed (which causes OutOfMemoryError) unless manually destroy()ed. Former-commit-id: 34840cf63e52e5635ec8acd5fb1bb78923c61063 Former-commit-id: aca4388320fae022a4744f75c1b0f66b544bdafb --- src/net/torvald/terrarum/StateBlurTest.kt | 15 ++++--- .../terrarum/StateGraphicComputerTest.kt | 16 +++++--- src/net/torvald/terrarum/StateNoiseTexGen.kt | 5 ++- src/net/torvald/terrarum/Terrarum.kt | 6 ++- .../terrarum/gameactors/TapestryObject.kt | 3 +- .../peripheral/PeripheralVideoCard.kt | 40 ++++++++++++++----- 6 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/net/torvald/terrarum/StateBlurTest.kt b/src/net/torvald/terrarum/StateBlurTest.kt index f1072e743..d2c0a9a30 100644 --- a/src/net/torvald/terrarum/StateBlurTest.kt +++ b/src/net/torvald/terrarum/StateBlurTest.kt @@ -23,21 +23,21 @@ class StateBlurTest : BasicGameState() { override fun init(gc: GameContainer, sbg: StateBasedGame) { testImage.flushPixelData() - System.arraycopy( + /*System.arraycopy( testImage.texture.textureData, 0, bluredImage.rgba, 0, testImage.texture.textureData.size ) - kotlin.repeat(3, { fastBoxBlur(bluredImage, 3) }) + kotlin.repeat(3, { fastBoxBlur(bluredImage, 3) })*/ } override fun update(gc: GameContainer, sbg: StateBasedGame, delta: Int) { Terrarum.appgc.setTitle("${Terrarum.NAME} — F: ${Terrarum.appgc.fps}") - /*System.arraycopy( + System.arraycopy( testImage.texture.textureData, 0, bluredImage.rgba, 0, testImage.texture.textureData.size - )*/ - //fastBoxBlur(testImage, bluredImage, 3) + ) + fastBoxBlur(testImage, bluredImage, 3) //fastBoxBlur(bluredImage, 3) //fastBoxBlur(bluredImage, 3) } @@ -46,11 +46,14 @@ class StateBlurTest : BasicGameState() { override fun render(gc: GameContainer, sbg: StateBasedGame, g: Graphics) { g.background = Color(0x404040) - g.drawImage(bluredImage.image, + val image = bluredImage.image + g.drawImage(image, Terrarum.WIDTH.minus(testImage.width).div(2f).floor(), Terrarum.HEIGHT.minus(testImage.height).div(2f).floor() ) g.flush() + + image.destroy() } private val isLE: Boolean diff --git a/src/net/torvald/terrarum/StateGraphicComputerTest.kt b/src/net/torvald/terrarum/StateGraphicComputerTest.kt index c71925cae..cda447c04 100644 --- a/src/net/torvald/terrarum/StateGraphicComputerTest.kt +++ b/src/net/torvald/terrarum/StateGraphicComputerTest.kt @@ -49,20 +49,26 @@ class StateGraphicComputerTest : BasicGameState() { monitor.update(container, delta) computer.update(container, delta) - val sprite = (computer.getPeripheral("ppu") as PeripheralVideoCard).vram.sprites[0] + val vcard = (computer.getPeripheral("ppu") as PeripheralVideoCard) + val sprite = vcard.vram.sprites[0] angle += delta / 500.0 sprite.posX = (Math.cos(angle) * 80 + 100).roundInt() sprite.posY = (Math.sin(angle) * 80 + 100).roundInt() - sprite.pal0 = (sprite.pal0 + 1) % 65 - sprite.pal1 = (sprite.pal1 + 1) % 65 - sprite.pal2 = (sprite.pal2 + 1) % 65 - sprite.pal3 = (sprite.pal3 + 1) % 65 + //sprite.pal0 = (sprite.pal0 + 1) % 65 + //sprite.pal1 = (sprite.pal1 + 1) % 65 + //sprite.pal2 = (sprite.pal2 + 1) % 65 + //sprite.pal3 = (sprite.pal3 + 1) % 65 sprite.rotation = (angle * 2 / Math.PI).roundInt() % 4 + + //vcard.vram.setBackgroundPixel(Random().nextInt(320), Random().nextInt(200), Random().nextInt(64)) + //kotlin.repeat(64) { + // vcard.vram.setBackgroundPixel(Random().nextInt(320), Random().nextInt(200), 0) + //} } override fun getID() = Terrarum.STATE_ID_TEST_TTY diff --git a/src/net/torvald/terrarum/StateNoiseTexGen.kt b/src/net/torvald/terrarum/StateNoiseTexGen.kt index fa2aaea93..b6738f919 100644 --- a/src/net/torvald/terrarum/StateNoiseTexGen.kt +++ b/src/net/torvald/terrarum/StateNoiseTexGen.kt @@ -155,10 +155,13 @@ class StateNoiseTexGen : BasicGameState() { g.drawString("CPUs: ${Terrarum.THREADS}", Terrarum.WIDTH - 90f, 8f) g.background = Color.cyan - g.drawImage(noiseImageBuffer.image,//noiseImage, + val img = noiseImageBuffer.image + g.drawImage(img,//noiseImage, Terrarum.WIDTH.minus(imagesize).div(2).toFloat(), Terrarum.HEIGHT.minus(imagesize).div(2).toFloat() ) + + img.destroy() } override fun keyPressed(key: Int, c: Char) { diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 5cb39cf60..67b5ab655 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -44,6 +44,10 @@ constructor(gamename: String) : StateBasedGame(gamename) { // just in case println("[Terrarum] os.arch = $systemArch") + if (is32Bit) { + println("Java is running in 32 Bit") + } + gameConfig = GameConfig() joypadLabelStart = when (getConfigString("joypadlabelstyle")) { @@ -131,7 +135,7 @@ constructor(gamename: String) : StateBasedGame(gamename) { gc.graphics.clear() // clean up any 'dust' in the buffer //addState(StateVTTest()) - addState(StateGraphicComputerTest()) + //addState(StateGraphicComputerTest()) //addState(StateTestingLightning()) //addState(StateSplash()) //addState(StateMonitorCheck()) diff --git a/src/net/torvald/terrarum/gameactors/TapestryObject.kt b/src/net/torvald/terrarum/gameactors/TapestryObject.kt index 83ceb270a..476cc8fbb 100644 --- a/src/net/torvald/terrarum/gameactors/TapestryObject.kt +++ b/src/net/torvald/terrarum/gameactors/TapestryObject.kt @@ -10,7 +10,7 @@ import org.newdawn.slick.Image /** * Created by minjaesong on 2017-01-07. */ -class TapestryObject(val image: Image, val artName: String, val artAuthor: String) : FixtureBase(physics = false) { +class TapestryObject(image: Image, val artName: String, val artAuthor: String) : FixtureBase(physics = false) { // physics = false only speeds up for ~2 frames with 50 tapestries @@ -19,6 +19,7 @@ class TapestryObject(val image: Image, val artName: String, val artAuthor: Strin makeNewSprite(image.width, image.height, image) setHitboxDimension(image.width, image.height, 0, 0) setPosition(Terrarum.appgc.mouseX, Terrarum.appgc.mouseY) + // you CAN'T destroy the image } override fun update(gc: GameContainer, delta: Int) { diff --git a/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt index 5c5fd43bf..6ac07ace5 100644 --- a/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt +++ b/src/net/torvald/terrarum/virtualcomputer/peripheral/PeripheralVideoCard.kt @@ -45,8 +45,8 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : val height = termH * blockH val vram = VRAM(width, height, 64) - val frameBuffer = Image(width, height) - val frameBufferG = frameBuffer.graphics + val frameBuffer = ImageBuffer(width, height) + val frameBufferImage = frameBuffer.image // hard-coded 8x8 var fontRom = Array(256, { Array(blockH, { 0 }).toIntArray() }) @@ -54,6 +54,8 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : init { // build it for first time resetTextRom() + + frameBufferImage.filter = Image.FILTER_NEAREST } val CLUT = VRAM.CLUT @@ -119,27 +121,45 @@ class PeripheralVideoCard(val termW: Int = 40, val termH: Int = 25) : } } - frameBuffer.filter = Image.FILTER_NEAREST - frameBufferG.clear() - frameBufferG.drawImage(vram.background.image, 0f, 0f) + System.arraycopy(vram.background.rgba, 0, frameBuffer.rgba, 0, vram.background.rgba.size) vram.sprites.forEach { if (it.isBackground) { it.render() - frameBufferG.drawImage(spriteBuffer.image, it.posX.toFloat(), it.posY.toFloat()) + frameBuffer.softwareRender(spriteBuffer, it.posX, it.posY) } } - frameBufferG.drawImage(vram.foreground.image, 0f, 0f) + frameBuffer.softwareRender(vram.foreground, 0, 0) vram.sprites.forEach { if (!it.isBackground) { it.render() - frameBufferG.drawImage(spriteBuffer.image, it.posX.toFloat(), it.posY.toFloat()) + frameBuffer.softwareRender(spriteBuffer, it.posX, it.posY) } } - frameBufferG.flush() - g.drawImage(frameBuffer.getScaledCopy(2f), 0f, 0f) + val img = frameBuffer.image + img.filter = Image.FILTER_NEAREST + g.drawImage(img.getScaledCopy(2f), 0f, 0f) + + img.destroy() + } + + fun ImageBuffer.softwareRender(other: ImageBuffer, posX: Int, posY: Int) { + for (y in 0..other.height - 1) { + for (x in 0..other.width - 1) { + val ix = posX + x + val iy = posY + y + if (ix >= 0 && iy >= 0 && ix < this.width && iy < this.height) { + if (other.rgba[4 * (y * other.width + x) + 3] != 0.toByte()) { // if not transparent + this.rgba[4 * (iy * this.texWidth + ix) + 0] = other.rgba[4 * (y * other.texWidth + x) + 0] + this.rgba[4 * (iy * this.texWidth + ix) + 1] = other.rgba[4 * (y * other.texWidth + x) + 1] + this.rgba[4 * (iy * this.texWidth + ix) + 2] = other.rgba[4 * (y * other.texWidth + x) + 2] + this.rgba[4 * (iy * this.texWidth + ix) + 3] = other.rgba[4 * (y * other.texWidth + x) + 3] + } + } + } + } } private var foreColor = 49 // white