From 2c76371578e042b846274595c764ed892bcbb415 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Mon, 26 Sep 2022 13:52:09 +0200 Subject: [PATCH] make render system safer, detect gpu memory leaks --- .../minosoft/gui/rendering/RenderWindow.kt | 1 + .../gui/rendering/system/base/RenderSystem.kt | 3 +++ .../system/base/buffer/render/Renderbuffer.kt | 1 + .../base/buffer/render/RenderbufferStates.kt | 21 +++++++++++++++++ .../system/opengl/MemoryLeakException.kt | 16 +++++++++++++ .../system/opengl/OpenGLRenderSystem.kt | 10 +++++++- .../opengl/buffer/OpenGLRenderableBuffer.kt | 6 +++++ .../opengl/buffer/frame/OpenGLFramebuffer.kt | 12 ++++++++-- .../buffer/render/OpenGLRenderbuffer.kt | 23 ++++++++++++++++++- .../buffer/vertex/FloatOpenGLVertexBuffer.kt | 4 ++-- 10 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/RenderbufferStates.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/MemoryLeakException.kt diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index 962bc3f83..15c7b7054 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -301,6 +301,7 @@ class RenderWindow( Log.log(LogMessageType.RENDERING_LOADING) { "Destroying render window..." } renderingState = RenderingStates.STOPPED + renderSystem.destroy() window.destroy() Log.log(LogMessageType.RENDERING_LOADING) { "Render window destroyed!" } // disconnect diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt index dbb96939a..ae71bf7e1 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/RenderSystem.kt @@ -37,7 +37,10 @@ interface RenderSystem { var shader: Shader? var framebuffer: Framebuffer? + val active: Boolean + fun init() + fun destroy() fun reset( depthTest: Boolean = true, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/Renderbuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/Renderbuffer.kt index 1d9e79613..73af18701 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/Renderbuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/Renderbuffer.kt @@ -18,6 +18,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i interface Renderbuffer { val mode: RenderbufferModes val size: Vec2i + val state: RenderbufferStates fun init() fun unload() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/RenderbufferStates.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/RenderbufferStates.kt new file mode 100644 index 000000000..f67755a54 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/buffer/render/RenderbufferStates.kt @@ -0,0 +1,21 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.system.base.buffer.render + +enum class RenderbufferStates { + PREPARING, + GENERATED, + UNLOADED, + ; +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/MemoryLeakException.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/MemoryLeakException.kt new file mode 100644 index 000000000..cda6f26a8 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/MemoryLeakException.kt @@ -0,0 +1,16 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.system.opengl + +class MemoryLeakException(message: String? = null) : Exception(message) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLRenderSystem.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLRenderSystem.kt index 08d3ede1e..fce36126b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLRenderSystem.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLRenderSystem.kt @@ -55,6 +55,8 @@ class OpenGLRenderSystem( private val capabilities: MutableSet = synchronizedSetOf() override lateinit var vendor: OpenGLVendor private set + override var active: Boolean = false + private set var blendingSource = BlendingFunctions.ONE private set @@ -140,6 +142,11 @@ class OpenGLRenderSystem( Log.log(LogMessageType.RENDERING_GENERAL, LogLevels.VERBOSE) { "OpenGL error: source=$source, type=$type, id=$id, severity=$severity, length=$length, message=$message, userParameter=$userParameter" } }, 0) } + active = true + } + + override fun destroy() { + active = false } override fun enable(capability: RenderingCapabilities) { @@ -184,6 +191,7 @@ class OpenGLRenderSystem( private var destinationRGB: BlendingFunctions = BlendingFunctions.ONE private var sourceAlpha: BlendingFunctions = BlendingFunctions.ONE private var destinationAlpha: BlendingFunctions = BlendingFunctions.ONE + override fun setBlendFunction(sourceRGB: BlendingFunctions, destinationRGB: BlendingFunctions, sourceAlpha: BlendingFunctions, destinationAlpha: BlendingFunctions) { if (this.sourceRGB == sourceRGB && this.destinationRGB == destinationRGB && this.sourceAlpha == sourceAlpha && this.destinationAlpha == destinationAlpha) { return @@ -261,7 +269,7 @@ class OpenGLRenderSystem( } override fun createFramebuffer(): OpenGLFramebuffer { - return OpenGLFramebuffer(renderWindow.window.size) + return OpenGLFramebuffer(this, renderWindow.window.size) } override fun createTextureManager(): OpenGLTextureManager { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/OpenGLRenderableBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/OpenGLRenderableBuffer.kt index cb73d6b1d..28c8ec944 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/OpenGLRenderableBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/OpenGLRenderableBuffer.kt @@ -17,6 +17,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBuffer import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferDrawTypes import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferStates import de.bixilon.minosoft.gui.rendering.system.base.buffer.RenderableBufferTypes +import de.bixilon.minosoft.gui.rendering.system.opengl.MemoryLeakException import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import org.lwjgl.opengl.GL15.* import org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER @@ -59,6 +60,11 @@ abstract class OpenGLRenderableBuffer( state = RenderableBufferStates.UNLOADED } + protected fun finalize() { + if (state == RenderableBufferStates.UPLOADED && renderSystem.active) { + throw MemoryLeakException("Buffer has not been unloaded!") + } + } protected companion object { val RenderableBufferTypes.gl: Int diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/frame/OpenGLFramebuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/frame/OpenGLFramebuffer.kt index f136e64a9..e0712534b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/frame/OpenGLFramebuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/frame/OpenGLFramebuffer.kt @@ -19,12 +19,14 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.frame.FramebufferSta import de.bixilon.minosoft.gui.rendering.system.base.buffer.frame.texture.FramebufferTexture import de.bixilon.minosoft.gui.rendering.system.base.buffer.render.Renderbuffer import de.bixilon.minosoft.gui.rendering.system.base.buffer.render.RenderbufferModes +import de.bixilon.minosoft.gui.rendering.system.opengl.MemoryLeakException +import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import de.bixilon.minosoft.gui.rendering.system.opengl.buffer.frame.texture.OpenGLFramebufferColorTexture import de.bixilon.minosoft.gui.rendering.system.opengl.buffer.frame.texture.OpenGLFramebufferDepthTexture import de.bixilon.minosoft.gui.rendering.system.opengl.buffer.render.OpenGLRenderbuffer import org.lwjgl.opengl.GL30.* -class OpenGLFramebuffer(var size: Vec2i) : Framebuffer { +class OpenGLFramebuffer(val renderSystem: OpenGLRenderSystem, var size: Vec2i) : Framebuffer { override var state: FramebufferState = FramebufferState.PREPARING private set @@ -45,7 +47,7 @@ class OpenGLFramebuffer(var size: Vec2i) : Framebuffer { colorTexture.init() attach(colorTexture) - renderbuffer = OpenGLRenderbuffer(RenderbufferModes.DEPTH_COMPONENT24, size) + renderbuffer = OpenGLRenderbuffer(renderSystem, RenderbufferModes.DEPTH_COMPONENT24, size) renderbuffer.init() attach(renderbuffer) @@ -109,4 +111,10 @@ class OpenGLFramebuffer(var size: Vec2i) : Framebuffer { delete() init() } + + protected fun finalize() { + if (state == FramebufferState.COMPLETE && renderSystem.active) { + throw MemoryLeakException("Buffer has not been unloaded!") + } + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/render/OpenGLRenderbuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/render/OpenGLRenderbuffer.kt index 22154503f..b59ce7db9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/render/OpenGLRenderbuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/render/OpenGLRenderbuffer.kt @@ -16,28 +16,49 @@ package de.bixilon.minosoft.gui.rendering.system.opengl.buffer.render import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.minosoft.gui.rendering.system.base.buffer.render.Renderbuffer import de.bixilon.minosoft.gui.rendering.system.base.buffer.render.RenderbufferModes +import de.bixilon.minosoft.gui.rendering.system.base.buffer.render.RenderbufferStates +import de.bixilon.minosoft.gui.rendering.system.opengl.MemoryLeakException +import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import org.lwjgl.opengl.GL30.* class OpenGLRenderbuffer( + private val renderSystem: OpenGLRenderSystem, override val mode: RenderbufferModes, override val size: Vec2i, ) : Renderbuffer { + override var state: RenderbufferStates = RenderbufferStates.PREPARING + private set var id = -1 private set override fun init() { + check(state == RenderbufferStates.PREPARING) { "Can not init renderbuffer in $state" } id = glGenRenderbuffers() - bind() + unsafeBind() glRenderbufferStorage(GL_RENDERBUFFER, mode.gl, size.x, size.y) + state = RenderbufferStates.GENERATED } fun bind() { + check(state == RenderbufferStates.GENERATED) { "Can not bind renderbuffer in $state" } + unsafeBind() + } + + fun unsafeBind() { glBindRenderbuffer(GL_RENDERBUFFER, id) } override fun unload() { + check(state == RenderbufferStates.GENERATED) { "Can not unload renderbuffer in $state" } glDeleteRenderbuffers(id) id = -1 + state = RenderbufferStates.UNLOADED + } + + protected fun finalize() { + if (state == RenderbufferStates.GENERATED && renderSystem.active) { + throw MemoryLeakException("Renderbuffer has not been unloaded!") + } } companion object { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/vertex/FloatOpenGLVertexBuffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/vertex/FloatOpenGLVertexBuffer.kt index 613dde552..554e9d580 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/vertex/FloatOpenGLVertexBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/buffer/vertex/FloatOpenGLVertexBuffer.kt @@ -27,7 +27,8 @@ import java.nio.FloatBuffer class FloatOpenGLVertexBuffer( renderSystem: OpenGLRenderSystem, - override val structure: MeshStruct, data: FloatBuffer, + override val structure: MeshStruct, + data: FloatBuffer, override val primitiveType: PrimitiveTypes, ) : FloatOpenGLBuffer(renderSystem, data), FloatVertexBuffer { override var vertices = -1 @@ -89,7 +90,6 @@ class FloatOpenGLVertexBuffer( super.unload() } - private companion object { val PrimitiveTypes.gl: Int get() {