diff --git a/src/main/java/dev/silenium/compose/gl/fbo/FBO.kt b/src/main/java/dev/silenium/compose/gl/fbo/FBO.kt index 374133e..a5dfb94 100644 --- a/src/main/java/dev/silenium/compose/gl/fbo/FBO.kt +++ b/src/main/java/dev/silenium/compose/gl/fbo/FBO.kt @@ -3,17 +3,18 @@ package dev.silenium.compose.gl.fbo import androidx.compose.ui.unit.IntSize import dev.silenium.compose.gl.objects.Renderbuffer import dev.silenium.compose.gl.objects.Texture +import dev.silenium.compose.gl.util.DoubleDestructionProtection import dev.silenium.compose.gl.util.checkGLError import org.lwjgl.opengl.GL30.* import org.slf4j.LoggerFactory -import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicReference data class FBO( - val id: Int, + override val id: Int, val size: IntSize, val colorAttachment: Texture, val depthStencilAttachment: Renderbuffer, -) { +) : DoubleDestructionProtection() { fun bind() { glBindFramebuffer(GL_FRAMEBUFFER, id) checkGLError("glBindFramebuffer") @@ -25,20 +26,13 @@ data class FBO( checkGLError("glBindFramebuffer") } - private val destroyed = AtomicBoolean(false) - fun destroy() { - if (destroyed.compareAndExchange(false, true)) { - glDeleteFramebuffers(id) - colorAttachment.destroy() - depthStencilAttachment.destroy() - } else { - logger.trace("FBO $id is already destroyed", Exception()) - } + override fun destroyInternal() { + glDeleteFramebuffers(id) + colorAttachment.destroy() + depthStencilAttachment.destroy() } companion object { - private val logger = LoggerFactory.getLogger(FBO::class.java) - fun create( colorAttachment: Texture, depthStencilAttachment: Renderbuffer, diff --git a/src/main/java/dev/silenium/compose/gl/objects/Renderbuffer.kt b/src/main/java/dev/silenium/compose/gl/objects/Renderbuffer.kt index fd31400..5b59546 100644 --- a/src/main/java/dev/silenium/compose/gl/objects/Renderbuffer.kt +++ b/src/main/java/dev/silenium/compose/gl/objects/Renderbuffer.kt @@ -1,16 +1,15 @@ package dev.silenium.compose.gl.objects import androidx.compose.ui.unit.IntSize +import dev.silenium.compose.gl.util.DoubleDestructionProtection import dev.silenium.compose.gl.util.checkGLError import org.lwjgl.opengl.GL30.* -import org.slf4j.LoggerFactory -import java.util.concurrent.atomic.AtomicBoolean data class Renderbuffer( override val id: Int, override val size: IntSize, override val internalFormat: Int, -) : TextureOrRenderbuffer { +) : TextureOrRenderbuffer, DoubleDestructionProtection() { override val target: Int = GL_RENDERBUFFER override val binding: Int = GL_RENDERBUFFER_BINDING @@ -24,18 +23,11 @@ data class Renderbuffer( checkGLError("glBindRenderbuffer") } - private val destroyed = AtomicBoolean(false) - override fun destroy() { - if (destroyed.compareAndExchange(false, true)) { - glDeleteRenderbuffers(id) - } else { - logger.trace("Texture $id is already destroyed") - } + override fun destroyInternal() { + glDeleteRenderbuffers(id) } companion object { - private val logger = LoggerFactory.getLogger(Renderbuffer::class.java) - fun create(size: IntSize, internalFormat: Int): Renderbuffer { val id = glGenRenderbuffers() checkGLError("glGenRenderbuffers") diff --git a/src/main/java/dev/silenium/compose/gl/objects/Texture.kt b/src/main/java/dev/silenium/compose/gl/objects/Texture.kt index 159d46f..7d940b6 100644 --- a/src/main/java/dev/silenium/compose/gl/objects/Texture.kt +++ b/src/main/java/dev/silenium/compose/gl/objects/Texture.kt @@ -2,17 +2,16 @@ package dev.silenium.compose.gl.objects import androidx.compose.ui.unit.IntSize import dev.silenium.compose.gl.objects.TextureOrRenderbuffer.Companion.textureTargetBindings +import dev.silenium.compose.gl.util.DoubleDestructionProtection import dev.silenium.compose.gl.util.checkGLError import org.lwjgl.opengl.GL11.* -import org.slf4j.LoggerFactory -import java.util.concurrent.atomic.AtomicBoolean data class Texture( override val id: Int, override val size: IntSize, override val target: Int, override val internalFormat: Int, -) : TextureOrRenderbuffer { +) : TextureOrRenderbuffer, DoubleDestructionProtection() { init { require(target in textureTargetBindings) { "Unsupported texture target: $target" } } @@ -29,18 +28,11 @@ data class Texture( checkGLError("glBindTexture") } - private val destroyed = AtomicBoolean(false) - override fun destroy() { - if (destroyed.compareAndExchange(false, true)) { - glDeleteTextures(id) - } else { - logger.trace("Texture $id is already destroyed") - } + override fun destroyInternal() { + glDeleteTextures(id) } companion object { - private val logger = LoggerFactory.getLogger(Texture::class.java) - fun create( target: Int, size: IntSize, diff --git a/src/main/java/dev/silenium/compose/gl/util/DoubleDestructionProtection.kt b/src/main/java/dev/silenium/compose/gl/util/DoubleDestructionProtection.kt new file mode 100644 index 0000000..465bb5c --- /dev/null +++ b/src/main/java/dev/silenium/compose/gl/util/DoubleDestructionProtection.kt @@ -0,0 +1,31 @@ +package dev.silenium.compose.gl.util + +import org.slf4j.LoggerFactory +import java.util.concurrent.atomic.AtomicBoolean + +abstract class DoubleDestructionProtection { + abstract val id: ID + + private val destroyed = AtomicBoolean(false) + private var destructionPoint: Throwable? = null + fun destroy() { + if (destroyed.compareAndSet(false, true)) { + destroyInternal() + destructionPoint = Exception() + } else { + logger.trace( + "{} {} was already destroyed at: {}", + javaClass.simpleName, + id, + destroyed.get(), + Exception(), + ) + } + } + + protected abstract fun destroyInternal() + + companion object { + private val logger = LoggerFactory.getLogger(DoubleDestructionProtection::class.java) + } +}