From 25ebd3c3ea66fe1a3b84563d9d828f7e62d22a7c Mon Sep 17 00:00:00 2001 From: lowercasebtw Date: Sat, 8 Aug 2026 02:34:05 -0400 Subject: [PATCH 1/3] Fix desync in color attachments from render targets --- .../client/blur/motion/MotionBlurReproject.kt | 2 + .../polyblur/client/blur/phosphor/BlitPlan.kt | 36 ++++++++ .../blur/phosphor/HybridHandPhosphor.kt | 5 +- .../client/blur/phosphor/PhosphorBlur.kt | 5 +- .../blur/phosphor/RenderTargetTracker.kt | 83 ++++++++++++++++--- .../polyfrost/polyblur/test/BlitPlanTest.kt | 76 +++++++++++++++++ 6 files changed, 193 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/BlitPlan.kt create mode 100644 src/test/kotlin/org/polyfrost/polyblur/test/BlitPlanTest.kt diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt index bd8c6a4..9f66893 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt @@ -99,6 +99,8 @@ object MotionBlurReproject { val velTarget = VelocityTarget.current ?: return false if (!WorldCamera.hasPrev) return false + if (!RenderTargetTracker.isAttachmentInSync(renderTarget)) return false + val tempTarget = outTarget ?: run { InternalTargetTracker.updateSize(renderTarget.width, renderTarget.height) InternalTargetTracker.target ?: return false diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/BlitPlan.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/BlitPlan.kt new file mode 100644 index 0000000..b4e20d8 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/BlitPlan.kt @@ -0,0 +1,36 @@ +package org.polyfrost.polyblur.client.blur.phosphor + +enum class BlitPlan { + COPY, + DRAW, + SKIP, +} + +fun planBlit( + srcWidth: Int, + srcHeight: Int, + srcTextureWidth: Int, + srcTextureHeight: Int, + dstWidth: Int, + dstHeight: Int, + dstTextureWidth: Int, + dstTextureHeight: Int, +): BlitPlan { + if (srcTextureWidth <= 0 || srcTextureHeight <= 0 || dstTextureWidth <= 0 || dstTextureHeight <= 0) { + return BlitPlan.SKIP + } + + if (srcTextureWidth != srcWidth || srcTextureHeight != srcHeight) { + return BlitPlan.SKIP + } + + if (dstTextureWidth != dstWidth || dstTextureHeight != dstHeight) { + return BlitPlan.SKIP + } + + return if (srcTextureWidth == dstTextureWidth && srcTextureHeight == dstTextureHeight) { + BlitPlan.COPY + } else { + BlitPlan.DRAW + } +} diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt index d7d395f..9509a61 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt @@ -146,8 +146,9 @@ object HybridHandPhosphor { FullscreenPass.draw(renderPass) } - RenderTargetTracker.blit(tempTarget, renderTarget) - RenderTargetTracker.swap() + if (RenderTargetTracker.blit(tempTarget, renderTarget)) { + RenderTargetTracker.swap() + } } } //?} diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt index 56c4b45..3ac89dc 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt @@ -366,8 +366,9 @@ object PhosphorBlur { FullscreenPass.draw(renderPass) } - RenderTargetTracker.blit(tempTarget, renderTarget) - RenderTargetTracker.swap() + if (RenderTargetTracker.blit(tempTarget, renderTarget)) { + RenderTargetTracker.swap() + } } } //?} diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt index f8b04ec..717552a 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt @@ -102,6 +102,7 @@ import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.DefaultVertexFormat //? if <26.2 import com.mojang.blaze3d.vertex.VertexFormat +import org.apache.logging.log4j.LogManager import org.polyfrost.polyblur.client.blur.BlurPrewarm // import org.polyfrost.polyblur.client.blur.BlurProfiler //? if >=26.2 @@ -149,6 +150,8 @@ object RenderTargetTracker { //?} .build() + private val logger = LogManager.getLogger(RenderTargetTracker::class.java) + private var framebufferFactory: RenderTargetDescriptor? = null private var prevWidth = -1 private var prevHeight = -1 @@ -157,6 +160,8 @@ object RenderTargetTracker { private var parity = false private var bootstrap = true + private var skipLogged = false + private fun sized(target: RenderTarget?): RenderTarget? = target?.takeIf { it.width == prevWidth && it.height == prevHeight } @@ -182,8 +187,9 @@ object RenderTargetTracker { updateSize(sourceTarget.width, sourceTarget.height) val currentTarget = prevTarget ?: return - blit(sourceTarget, currentTarget) - bootstrap = false + if (blit(sourceTarget, currentTarget)) { + bootstrap = false + } } private fun updateSize(width: Int, height: Int) { @@ -212,21 +218,69 @@ object RenderTargetTracker { prevHeight = -1 } - fun blit(srcTarget: RenderTarget, dstTarget: RenderTarget) { + fun isAttachmentInSync(target: RenderTarget): Boolean { + val texture = target.getColorTexture() ?: return false + return !texture.isClosed() && + texture.getWidth(0) == target.width && + texture.getHeight(0) == target.height + } + + fun blit(srcTarget: RenderTarget, dstTarget: RenderTarget): Boolean { RenderSystem.assertOnRenderThread() // BlurProfiler.countBlit() - if (srcTarget.width == dstTarget.width && srcTarget.height == dstTarget.height) { + val srcTexture = srcTarget.getColorTexture() + val dstTexture = dstTarget.getColorTexture() + val srcView = srcTarget.getColorTextureView() + val dstView = dstTarget.getColorTextureView() + + if (srcTexture == null || dstTexture == null || srcView == null || dstView == null || + srcTexture.isClosed() || dstTexture.isClosed() + ) { + return skipBlit( + "colour attachment missing or closed " + + "(src ${srcTarget.width}x${srcTarget.height}, dst ${dstTarget.width}x${dstTarget.height})" + ) + } + + val srcTextureWidth = srcTexture.getWidth(0) + val srcTextureHeight = srcTexture.getHeight(0) + val dstTextureWidth = dstTexture.getWidth(0) + val dstTextureHeight = dstTexture.getHeight(0) + + val plan = planBlit( + srcWidth = srcTarget.width, + srcHeight = srcTarget.height, + srcTextureWidth = srcTextureWidth, + srcTextureHeight = srcTextureHeight, + dstWidth = dstTarget.width, + dstHeight = dstTarget.height, + dstTextureWidth = dstTextureWidth, + dstTextureHeight = dstTextureHeight, + ) + + if (plan == BlitPlan.SKIP) { + return skipBlit( + "attachments are out of sync with their render targets (src " + + "${srcTarget.width}x${srcTarget.height} backed by ${srcTextureWidth}x$srcTextureHeight, dst " + + "${dstTarget.width}x${dstTarget.height} backed by ${dstTextureWidth}x$dstTextureHeight); " + + "expected while the window is minimised or the swapchain is being recreated" + ) + } + + skipLogged = false + + if (plan == BlitPlan.COPY) { RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( - srcTarget.getColorTexture()!!, dstTarget.getColorTexture()!!, - 0, 0, 0, 0, 0, srcTarget.width, srcTarget.height + srcTexture, dstTexture, + 0, 0, 0, 0, 0, srcTextureWidth, srcTextureHeight ) - return + return true } RenderSystem.getDevice().createCommandEncoder().createRenderPass( { "PolyBlur/Previous Frame Tracker Blit" }, - dstTarget.getColorTextureView()!!, + dstView, //? if >=26.2 { /*Optional.empty() *///?} @@ -236,13 +290,22 @@ object RenderTargetTracker { ).use { renderPass -> renderPass.setPipeline(pipeline) //? if >=1.21.11 { - /*renderPass.bindTexture("InSampler", srcTarget.getColorTextureView()!!, BlurSampler.linearClamp) + /*renderPass.bindTexture("InSampler", srcView, BlurSampler.linearClamp) *///?} //? if <1.21.11 { - renderPass.bindSampler("InSampler", srcTarget.getColorTextureView()!!) + renderPass.bindSampler("InSampler", srcView) //?} FullscreenPass.draw(renderPass) } + return true + } + + private fun skipBlit(reason: String): Boolean { + if (!skipLogged) { + skipLogged = true + logger.debug("Skipping blit: {}", reason) + } + return false } } //?} diff --git a/src/test/kotlin/org/polyfrost/polyblur/test/BlitPlanTest.kt b/src/test/kotlin/org/polyfrost/polyblur/test/BlitPlanTest.kt new file mode 100644 index 0000000..fdba032 --- /dev/null +++ b/src/test/kotlin/org/polyfrost/polyblur/test/BlitPlanTest.kt @@ -0,0 +1,76 @@ +package org.polyfrost.polyblur.test + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.polyfrost.polyblur.client.blur.phosphor.BlitPlan +import org.polyfrost.polyblur.client.blur.phosphor.planBlit + +class BlitPlanTest { + + @Test + fun `matching attachments copy`() { + assertEquals( + BlitPlan.COPY, + planBlit( + srcWidth = 1920, srcHeight = 1080, srcTextureWidth = 1920, srcTextureHeight = 1080, + dstWidth = 1920, dstHeight = 1080, dstTextureWidth = 1920, dstTextureHeight = 1080, + ) + ) + } + + @Test + fun `differently sized but consistent attachments draw`() { + assertEquals( + BlitPlan.DRAW, + planBlit( + srcWidth = 1920, srcHeight = 1080, srcTextureWidth = 1920, srcTextureHeight = 1080, + dstWidth = 960, dstHeight = 540, dstTextureWidth = 960, dstTextureHeight = 540, + ) + ) + } + + /** The crash: VulkanMod's 10x10 placeholder swapchain framebuffer behind a 1920x1080 main target. */ + @Test + fun `destination smaller than it reports is skipped`() { + assertEquals( + BlitPlan.SKIP, + planBlit( + srcWidth = 1920, srcHeight = 1080, srcTextureWidth = 1920, srcTextureHeight = 1080, + dstWidth = 1920, dstHeight = 1080, dstTextureWidth = 10, dstTextureHeight = 10, + ) + ) + } + + @Test + fun `source smaller than it reports is skipped`() { + assertEquals( + BlitPlan.SKIP, + planBlit( + srcWidth = 1920, srcHeight = 1080, srcTextureWidth = 10, srcTextureHeight = 10, + dstWidth = 1920, dstHeight = 1080, dstTextureWidth = 1920, dstTextureHeight = 1080, + ) + ) + } + + @Test + fun `attachment larger than it reports is skipped`() { + assertEquals( + BlitPlan.SKIP, + planBlit( + srcWidth = 1920, srcHeight = 1080, srcTextureWidth = 1920, srcTextureHeight = 1080, + dstWidth = 1280, dstHeight = 720, dstTextureWidth = 1920, dstTextureHeight = 1080, + ) + ) + } + + @Test + fun `degenerate extents are skipped`() { + assertEquals( + BlitPlan.SKIP, + planBlit( + srcWidth = 0, srcHeight = 0, srcTextureWidth = 0, srcTextureHeight = 0, + dstWidth = 0, dstHeight = 0, dstTextureWidth = 0, dstTextureHeight = 0, + ) + ) + } +} From 87efed801dc25ffe92af5e0ce36de1131e1f4bac Mon Sep 17 00:00:00 2001 From: Julian Chang Date: Sat, 8 Aug 2026 20:37:38 +0700 Subject: [PATCH 2/3] fixes --- .../client/blur/motion/HybridWorldPass.kt | 8 +- .../polyblur/client/blur/motion/MotionBlur.kt | 2 + .../client/blur/motion/MotionBlurReproject.kt | 3 +- .../blur/motion/MotionBlurReproject1215.kt | 2 + .../blur/phosphor/HybridHandPhosphor.kt | 7 ++ .../client/blur/phosphor/PhosphorBlur.kt | 7 ++ .../blur/phosphor/RenderTargetTracker.kt | 91 +++++++++++++++---- .../blur/phosphor/WorldSnapshotTracker.kt | 20 +++- 8 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/HybridWorldPass.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/HybridWorldPass.kt index 194febc..7502e21 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/HybridWorldPass.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/HybridWorldPass.kt @@ -11,13 +11,15 @@ object HybridWorldPass { val snapshot = WorldSnapshotTracker.ensure(mainTarget) ?: return if (WorldCamera.velocitySettled) { - RenderTargetTracker.blit(mainTarget, snapshot) + WorldSnapshotTracker.markCaptured(RenderTargetTracker.blit(mainTarget, snapshot)) return } MotionVelocityPass.run(mainTarget) - if (!MotionBlurReproject.render(mainTarget, snapshot)) { - RenderTargetTracker.blit(mainTarget, snapshot) + if (MotionBlurReproject.render(mainTarget, snapshot)) { + WorldSnapshotTracker.markCaptured(true) + } else { + WorldSnapshotTracker.markCaptured(RenderTargetTracker.blit(mainTarget, snapshot)) } } } diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlur.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlur.kt index a017dbc..88d633d 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlur.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlur.kt @@ -225,6 +225,8 @@ object MotionBlur { @JvmStatic @Suppress("UNUSED_PARAMETER") fun render(renderTarget: RenderTarget, resourcePool: CrossFrameResourcePool) { + if (!RenderTargetTracker.isAttachmentInSync(renderTarget)) return + InternalTargetTracker.updateSize(renderTarget.width, renderTarget.height) val tempTarget = InternalTargetTracker.target ?: return diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt index 9f66893..ab084ae 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject.kt @@ -139,8 +139,7 @@ object MotionBlurReproject { FullscreenPass.draw(renderPass) } - RenderTargetTracker.blit(tempTarget, renderTarget) - return true + return RenderTargetTracker.blit(tempTarget, renderTarget) } } //?} diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject1215.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject1215.kt index 9c483b2..a5f5883 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject1215.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/motion/MotionBlurReproject1215.kt @@ -47,6 +47,8 @@ object MotionBlurReproject { val velTarget = VelocityTarget.current ?: return if (!WorldCamera.hasPrev) return + if (!RenderTargetTracker.isAttachmentInSync(renderTarget)) return + InternalTargetTracker.updateSize(renderTarget.width, renderTarget.height) val tempTarget = InternalTargetTracker.target ?: return diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt index 9509a61..faf0f9b 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/HybridHandPhosphor.kt @@ -101,6 +101,11 @@ object HybridHandPhosphor { renderInner(renderTarget) private fun renderInner(renderTarget: RenderTarget) { + if (!RenderTargetTracker.isAttachmentInSync(renderTarget)) { + RenderTargetTracker.requireBootstrap() + return + } + RenderTargetTracker.ensureSize(renderTarget.width, renderTarget.height) val prevTarget = RenderTargetTracker.prevTarget @@ -148,6 +153,8 @@ object HybridHandPhosphor { if (RenderTargetTracker.blit(tempTarget, renderTarget)) { RenderTargetTracker.swap() + } else { + RenderTargetTracker.requireBootstrap() } } } diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt index 3ac89dc..39b7fa8 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/PhosphorBlur.kt @@ -327,6 +327,11 @@ object PhosphorBlur { renderInner(renderTarget) private fun renderInner(renderTarget: RenderTarget) { + if (!RenderTargetTracker.isAttachmentInSync(renderTarget)) { + RenderTargetTracker.requireBootstrap() + return + } + RenderTargetTracker.ensureSize(renderTarget.width, renderTarget.height) val prevTarget = RenderTargetTracker.prevTarget @@ -368,6 +373,8 @@ object PhosphorBlur { if (RenderTargetTracker.blit(tempTarget, renderTarget)) { RenderTargetTracker.swap() + } else { + RenderTargetTracker.requireBootstrap() } } } diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt index 717552a..7726981 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/RenderTargetTracker.kt @@ -38,6 +38,13 @@ object RenderTargetTracker { blit(sourceTarget, currentTarget) } + fun isAttachmentInSync(target: RenderTarget): Boolean { + val texture = target.getColorTexture() ?: return false + return !texture.isClosed() && + texture.getWidth(0) == target.width && + texture.getHeight(0) == target.height + } + private fun updateSize(width: Int, height: Int) { if (width == prevWidth && height == prevHeight && internalPrevTarget != null) { return @@ -60,23 +67,46 @@ object RenderTargetTracker { prevHeight = -1 } - fun blit(srcTarget: RenderTarget, dstTarget: RenderTarget) { + fun blit(srcTarget: RenderTarget, dstTarget: RenderTarget): Boolean { RenderSystem.assertOnRenderThread() + val srcTexture = srcTarget.getColorTexture() + val dstTexture = dstTarget.getColorTexture() + + if (srcTexture == null || dstTexture == null || srcTexture.isClosed() || dstTexture.isClosed()) { + return false + } + + val plan = planBlit( + srcWidth = srcTarget.width, + srcHeight = srcTarget.height, + srcTextureWidth = srcTexture.getWidth(0), + srcTextureHeight = srcTexture.getHeight(0), + dstWidth = dstTarget.width, + dstHeight = dstTarget.height, + dstTextureWidth = dstTexture.getWidth(0), + dstTextureHeight = dstTexture.getHeight(0), + ) + + if (plan == BlitPlan.SKIP) { + return false + } + val autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS) val indexBuffer = autoStorageIndexBuffer.getBuffer(6) val vertexBuffer = FullscreenQuad.vertexBuffer RenderSystem.getDevice().createCommandEncoder().createRenderPass( - dstTarget.getColorTexture()!!, + dstTexture, OptionalInt.empty() ).use { renderPass -> renderPass.setPipeline(pipeline) renderPass.setVertexBuffer(0, vertexBuffer) renderPass.setIndexBuffer(indexBuffer, autoStorageIndexBuffer.type()) - renderPass.bindSampler("InSampler", srcTarget.getColorTexture()!!) + renderPass.bindSampler("InSampler", srcTexture) renderPass.drawIndexed(0, 6) } + return true } } *///?} @@ -161,6 +191,10 @@ object RenderTargetTracker { private var bootstrap = true private var skipLogged = false + private var skipWarned = false + + private const val DESYNC_HINT = + "; expected while the window is minimised or the swapchain is being recreated" private fun sized(target: RenderTarget?): RenderTarget? = target?.takeIf { it.width == prevWidth && it.height == prevHeight } @@ -178,6 +212,10 @@ object RenderTargetTracker { val needsBootstrap: Boolean get() = bootstrap + fun requireBootstrap() { + bootstrap = true + } + internal fun prewarm() = BlurPrewarm.compile(pipeline) fun ensureSize(width: Int, height: Int) = updateSize(width, height) @@ -219,10 +257,27 @@ object RenderTargetTracker { } fun isAttachmentInSync(target: RenderTarget): Boolean { - val texture = target.getColorTexture() ?: return false - return !texture.isClosed() && - texture.getWidth(0) == target.width && - texture.getHeight(0) == target.height + val texture = target.getColorTexture() + val view = target.getColorTextureView() + + if (texture == null || view == null || texture.isClosed() || view.isClosed()) { + return skipBlit { "colour attachment missing or closed (${target.width}x${target.height})" } + } + + val textureWidth = texture.getWidth(0) + val textureHeight = texture.getHeight(0) + + if (textureWidth <= 0 || textureHeight <= 0 || + textureWidth != target.width || textureHeight != target.height + ) { + return skipBlit { + "attachment is out of sync with its render target " + + "(${target.width}x${target.height} backed by ${textureWidth}x$textureHeight)$DESYNC_HINT" + } + } + + skipLogged = false + return true } fun blit(srcTarget: RenderTarget, dstTarget: RenderTarget): Boolean { @@ -235,12 +290,12 @@ object RenderTargetTracker { val dstView = dstTarget.getColorTextureView() if (srcTexture == null || dstTexture == null || srcView == null || dstView == null || - srcTexture.isClosed() || dstTexture.isClosed() + srcTexture.isClosed() || dstTexture.isClosed() || srcView.isClosed() || dstView.isClosed() ) { - return skipBlit( + return skipBlit { "colour attachment missing or closed " + "(src ${srcTarget.width}x${srcTarget.height}, dst ${dstTarget.width}x${dstTarget.height})" - ) + } } val srcTextureWidth = srcTexture.getWidth(0) @@ -260,12 +315,11 @@ object RenderTargetTracker { ) if (plan == BlitPlan.SKIP) { - return skipBlit( + return skipBlit { "attachments are out of sync with their render targets (src " + "${srcTarget.width}x${srcTarget.height} backed by ${srcTextureWidth}x$srcTextureHeight, dst " + - "${dstTarget.width}x${dstTarget.height} backed by ${dstTextureWidth}x$dstTextureHeight); " + - "expected while the window is minimised or the swapchain is being recreated" - ) + "${dstTarget.width}x${dstTarget.height} backed by ${dstTextureWidth}x$dstTextureHeight)$DESYNC_HINT" + } } skipLogged = false @@ -300,10 +354,15 @@ object RenderTargetTracker { return true } - private fun skipBlit(reason: String): Boolean { + private inline fun skipBlit(reason: () -> String): Boolean { if (!skipLogged) { skipLogged = true - logger.debug("Skipping blit: {}", reason) + if (skipWarned) { + logger.debug("Skipping blit: {}", reason()) + } else { + skipWarned = true + logger.warn("Skipping blit: {}. Further occurrences are logged at debug level.", reason()) + } } return false } diff --git a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/WorldSnapshotTracker.kt b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/WorldSnapshotTracker.kt index fcb66e6..1401f6d 100644 --- a/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/WorldSnapshotTracker.kt +++ b/src/main/kotlin/org/polyfrost/polyblur/client/blur/phosphor/WorldSnapshotTracker.kt @@ -10,22 +10,31 @@ object WorldSnapshotTracker { private var prevWidth = -1 private var prevHeight = -1 private var internalSnapshot: RenderTarget? = null + private var holdsFrame = false - val snapshot: RenderTarget? + private val buffer: RenderTarget? get() = internalSnapshot?.takeIf { it.width == prevWidth && it.height == prevHeight } + val snapshot: RenderTarget? + get() = if (holdsFrame) buffer else null + fun ensure(sourceTarget: RenderTarget): RenderTarget? { RenderSystem.assertOnRenderThread() updateSize(sourceTarget.width, sourceTarget.height) - return snapshot + return buffer } - fun capture(sourceTarget: RenderTarget) { + fun capture(sourceTarget: RenderTarget): Boolean { RenderSystem.assertOnRenderThread() - val target = ensure(sourceTarget) ?: return - RenderTargetTracker.blit(sourceTarget, target) + val target = ensure(sourceTarget) ?: return markCaptured(false) + return markCaptured(RenderTargetTracker.blit(sourceTarget, target)) + } + + fun markCaptured(captured: Boolean): Boolean { + holdsFrame = captured + return captured } private fun updateSize(width: Int, height: Int) { @@ -46,6 +55,7 @@ object WorldSnapshotTracker { private fun free() { internalSnapshot?.let { framebufferFactory?.free(it) } internalSnapshot = null + holdsFrame = false prevWidth = -1 prevHeight = -1 } From 5015f4d9285592804ddf34d0f88d08afe1a84a42 Mon Sep 17 00:00:00 2001 From: Julian Chang Date: Sat, 8 Aug 2026 22:44:04 +0700 Subject: [PATCH 3/3] 2.0.3 --- CHANGELOG.md | 5 +++-- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f783b..2ef7f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ -## 2.0.2 -- Fix rendering issues on NVIDIA graphics cards \ No newline at end of file +## 2.0.3 +- Fix iris compat on hand +- Fix other rare crashes \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 76d9202..dfba019 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ loom.ignoreDependencyLoomVersionValidation=true # Mod information mod.name=PolyBlur mod.id=polyblur -mod.version=2.0.2 +mod.version=2.0.3 mod.group=org.polyfrost # Loom settings, [VERSIONED] means to be overridden in each version's gradle.properties