Fix NEI items vanishing with ModularUI2 screens open on SDL-GPU (#2153)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-09-27 12:26:53 -07:00
1 parent c9d46e70e0
commit e17b45c842
13 files changed
+146 -109

No files matched your search

@@ -649,8 +649,6 @@ public class SDLGPURenderBackend extends RenderBackend {
GLStateManager.replayStateToBackend();
}
ResourceManager.setSingleThreadedReads(GLStateManager.isSplashComplete());
st.clearedTexturesThisFrame.clear();
st.clearedStencilTexturesThisFrame.clear();
st.pendingColorTextures.clear();
st.pendingDepthTextures.clear();
st.pendingStencilTextures.clear();
@@ -3622,7 +3620,7 @@ public class SDLGPURenderBackend extends RenderBackend {
final int flipMode = (srcIsFbo0 != dstIsFbo0) ? SDL_FLIP_VERTICAL : SDL_FLIP_NONE;
if (blitColor) {
resolveBlitClears(cs, colorSrcTex, colorDstTex, colorDstGlId, dstX0, dstY, dstW, dstH);
resolveBlitClears(cs, colorSrcTex, colorDstTex, colorDstGlId, dstX0, dstY, dstW, dstH, true);
final ResourceManager.TextureMeta srcMeta = resourceManager.getTextureMeta(colorSrcGlId);
final ResourceManager.TextureMeta dstMeta = resourceManager.getTextureMeta(colorDstGlId);
if (flipMode == SDL_FLIP_NONE && TextureOps.canCopyInsteadOfBlit(srcMeta, dstMeta, srcW, srcH, dstW, dstH)) {
@@ -3632,14 +3630,15 @@ public class SDLGPURenderBackend extends RenderBackend {
}
}
if (blitDepth) {
resolveBlitClears(cs, depthSrcTex, depthDstTex, dstFbo.depthGlId, dstX0, dstY, dstW, dstH);
final boolean allAspects = !PixelOps.isDepthStencilFormat(dstFbo.depthFormat) || (mask & (GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT)) == (GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
resolveBlitClears(cs, depthSrcTex, depthDstTex, dstFbo.depthGlId, dstX0, dstY, dstW, dstH, allAspects);
textureOps.copyTexture(depthSrcTex, srcX0, srcY, depthDstTex, 0, dstX0, dstY, srcW, srcH);
}
}
private void resolveBlitClears(ContextState cs, long srcTex, long dstTex, int dstGlId, int dstX, int dstY, int dstW, int dstH) {
private void resolveBlitClears(ContextState cs, long srcTex, long dstTex, int dstGlId, int dstX, int dstY, int dstW, int dstH, boolean allAspects) {
fboClearTracker.materializePendingClearForTexture(cs, srcTex);
fboClearTracker.resolveDestinationForWrite(cs, dstTex, resourceManager.getTextureMeta(dstGlId), 0, dstX, dstY, 0, dstW, dstH);
fboClearTracker.resolveDestinationForWrite(cs, dstTex, resourceManager.getTextureMeta(dstGlId), 0, dstX, dstY, 0, dstW, dstH, allAspects);
}
@Override public int createBuffers() { return resourceManager.genBuffer(); }
@Override public void namedBufferData(int buffer, long size, int usage) {
@@ -3995,7 +3994,7 @@ public class SDLGPURenderBackend extends RenderBackend {
if (srcTex == 0 || dstTex == 0) return;
final ContextState copySt = s();
fboClearTracker.materializePendingClearForTexture(copySt, srcTex);
fboClearTracker.resolveDestinationForWrite(copySt, dstTex, resourceManager.getTextureMeta(dstName), dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight);
fboClearTracker.resolveDestinationForWrite(copySt, dstTex, resourceManager.getTextureMeta(dstName), dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, true);
final long cp = frameManager.ensureCopyPass();
if (cp == 0) return;
try (var stack = MemoryStack.stackPush()) {
@@ -332,9 +332,6 @@ public final class ContextState {
public int defaultFboId;
public GLContextState mirroredContext;
public final LongOpenHashSet clearedTexturesThisFrame = new LongOpenHashSet();
public final LongOpenHashSet clearedStencilTexturesThisFrame = new LongOpenHashSet();
public final LongOpenHashSet pendingColorTextures = new LongOpenHashSet();
public final Long2ObjectOpenHashMap<float[]> pendingColorValues = new Long2ObjectOpenHashMap<>();
public final LongOpenHashSet pendingDepthTextures = new LongOpenHashSet();
@@ -200,11 +200,6 @@ public final class AttachmentClear {
st.lastAppliedStencilRef = Integer.MIN_VALUE;
st.lastAppliedVboBindCb = 0;
if (t.depthTexture != 0) {
if (depth) st.clearedTexturesThisFrame.add(t.depthTexture);
if (stencil) st.clearedStencilTexturesThisFrame.add(t.depthTexture);
}
frameManager.noteInPassClear();
return true;
}
@@ -160,16 +160,11 @@ public final class PipelineApplier {
final boolean proposedStencilClear;
final boolean depthHasStencil = fbo.depthTexture != 0 && PixelOps.isDepthStencilFormat(fbo.depthFormat);
if (fbo.depthTexture != 0) {
final boolean defined = resourceManager.isTextureContentDefined(fbo.depthTexture);
final boolean pendingClear = st.pendingDepthTextures.contains(fbo.depthTexture);
final boolean firstUse = !pendingClear && !st.clearedTexturesThisFrame.contains(fbo.depthTexture);
proposedDepthClear = pendingClear || firstUse;
proposedDepthClear = pendingClear || !defined;
final boolean pendingStencil = depthHasStencil && st.pendingStencilTextures.contains(fbo.depthTexture);
if (depthHasStencil) {
final boolean firstStencilUse = !pendingStencil && !st.clearedStencilTexturesThisFrame.contains(fbo.depthTexture);
proposedStencilClear = pendingStencil || firstStencilUse;
} else {
proposedStencilClear = false;
}
proposedStencilClear = depthHasStencil && (pendingStencil || !defined);
layoutHash = Hashing.fmix64(layoutHash, fbo.depthTexture);
if (proposedDepthClear) {
layoutHash = foldClearDepth(layoutHash, pendingClear ? st.pendingDepthValues.get(fbo.depthTexture) : st.depthClearValue);
@@ -274,11 +269,8 @@ public final class PipelineApplier {
}
if (fbo.depthTexture == 0) return;
st.pendingDepthTextures.remove(fbo.depthTexture);
if (depthClear) st.clearedTexturesThisFrame.add(fbo.depthTexture);
if (depthHasStencil) {
st.pendingStencilTextures.remove(fbo.depthTexture);
if (stencilClear) st.clearedStencilTexturesThisFrame.add(fbo.depthTexture);
}
if (depthHasStencil) st.pendingStencilTextures.remove(fbo.depthTexture);
if (depthClear || stencilClear) rm.markTextureContentDefined(fbo.depthTexture);
}
static long foldClearColor(long h, float r, float g, float b, float a) {
@@ -38,7 +38,6 @@ public final class FBOClearTracker {
if (st.pendingColorTextures.add(tex)) {
st.pendingMutationGen++;
}
st.clearedTexturesThisFrame.remove(tex);
}
public static void recordPendingDepthClear(ContextState st, long tex, float value) {
@@ -46,7 +45,6 @@ public final class FBOClearTracker {
if (st.pendingDepthTextures.add(tex)) {
st.pendingMutationGen++;
}
st.clearedTexturesThisFrame.remove(tex);
}
public static void recordPendingStencilClear(ContextState st, long tex, int value) {
@@ -54,7 +52,6 @@ public final class FBOClearTracker {
if (st.pendingStencilTextures.add(tex)) {
st.pendingMutationGen++;
}
st.clearedStencilTexturesThisFrame.remove(tex);
}
public static boolean fboHasPendingClear(ContextState st, FboState fbo) {
@@ -179,7 +176,6 @@ public final class FBOClearTracker {
MemoryAccess.putFloat(ccAddr + SDL_FColor.G, color[1]);
MemoryAccess.putFloat(ccAddr + SDL_FColor.B, color[2]);
MemoryAccess.putFloat(ccAddr + SDL_FColor.A, color[3]);
st.clearedTexturesThisFrame.add(handle);
resourceManager.markTextureContentDefined(handle);
consumed[idx] = true;
}
@@ -217,35 +213,35 @@ public final class FBOClearTracker {
frameManager.beginRenderPass(null, dt);
frameManager.endRenderPassIfActive(FrameManager.PASS_END_CLEAR);
}
if (clearDepth) {
st.pendingDepthValues.remove(handle);
st.clearedTexturesThisFrame.add(handle);
}
if (clearStencil) {
st.pendingStencilValues.remove(handle);
st.clearedStencilTexturesThisFrame.add(handle);
}
if (clearDepth) st.pendingDepthValues.remove(handle);
if (clearStencil) st.pendingStencilValues.remove(handle);
if (clearDepth && clearStencil) resourceManager.markTextureContentDefined(handle);
}
public boolean discardPendingClearIfFullyCovered(ContextState st, long handle, int dstX, int dstY, int dstLevel, int width, int height, ResourceManager.TextureMeta meta) {
public boolean discardPendingClearIfFullyCovered(ContextState st, long handle, int dstX, int dstY, int dstLevel, int width, int height, ResourceManager.TextureMeta meta, boolean allAspects) {
if (handle == 0 || meta == null) return false;
if (dstLevel != 0 || dstX != 0 || dstY != 0) return false;
if (width != meta.width() || height != meta.height() || meta.depth() > 1) return false;
final boolean depth = st.pendingDepthTextures.remove(handle);
final boolean color = !depth && st.pendingColorTextures.remove(handle);
if (!depth && !color) return false;
if (depth) st.pendingDepthValues.remove(handle);
else st.pendingColorValues.remove(handle);
st.clearedTexturesThisFrame.add(handle);
resourceManager.markTextureContentDefined(handle);
if (st.pendingDepthTextures.contains(handle) || st.pendingStencilTextures.contains(handle)) {
if (!allAspects) return false;
st.pendingDepthTextures.remove(handle);
st.pendingDepthValues.remove(handle);
st.pendingStencilTextures.remove(handle);
st.pendingStencilValues.remove(handle);
} else if (st.pendingColorTextures.remove(handle)) {
st.pendingColorValues.remove(handle);
} else {
return false;
}
st.pendingMutationGen++;
return true;
}
public void resolveDestinationForWrite(ContextState st, long destTex, ResourceManager.TextureMeta destMeta, int level, int dx, int dy, int dz, int w, int h) {
if (dz != 0 || !discardPendingClearIfFullyCovered(st, destTex, dx, dy, level, w, h, destMeta)) {
public void resolveDestinationForWrite(ContextState st, long destTex, ResourceManager.TextureMeta destMeta, int level, int dx, int dy, int dz, int w, int h, boolean allAspects) {
if (dz != 0 || !discardPendingClearIfFullyCovered(st, destTex, dx, dy, level, w, h, destMeta, allAspects)) {
materializePendingClearForTexture(st, destTex);
}
resourceManager.markTextureContentDefined(destTex);
}
public void materializePendingClearForTexture(ContextState st, long handle) {
@@ -276,7 +272,6 @@ public final class FBOClearTracker {
frameManager.noteMaterializedClearPass();
frameManager.beginRenderPass(targets, null);
frameManager.endRenderPassIfActive(FrameManager.PASS_END_CLEAR);
st.clearedTexturesThisFrame.add(handle);
resourceManager.markTextureContentDefined(handle);
}
st.pendingMutationGen++;
@@ -305,8 +300,6 @@ public final class FBOClearTracker {
st.pendingDepthValues.remove(handle);
st.pendingStencilTextures.remove(handle);
st.pendingStencilValues.remove(handle);
st.clearedTexturesThisFrame.remove(handle);
st.clearedStencilTexturesThisFrame.remove(handle);
}
public boolean fbosHaveSameAttachments(ContextState st, int a, int b) {
@@ -364,6 +364,7 @@ public final class ResourceManager {
public void markTextureContentDefined(long handle) {
if (handle == 0) return;
if (isTextureContentDefined(handle)) return;
wLock.lock();
try { definedContentTextures.add(handle); } finally { wLock.unlock(); }
}
@@ -141,7 +141,7 @@ public final class TextureOps {
final int h = CopyRectClip.height(clip);
fboClearTracker.materializePendingClearForTexture(st, srcTex);
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h);
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h, true);
if (isDepthDest || (srcMeta != null && destMeta != null && srcMeta.sdlFormat() == destMeta.sdlFormat())) {
copyTexture(srcTex, sx, sy, destTex, level, dx, dy, w, h);
@@ -180,7 +180,7 @@ public final class TextureOps {
if (st.pendingSwapchainClear || st.pendingSwapchainDepthClear || st.pendingSwapchainStencilClear) {
frameManager.ensureFbo0RenderPass(frameManager.frame(), st);
}
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h);
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h, true);
blitTexture(srcTex, sx, srcFullH - sy - h, w, h, destTex, level, dx, dy, w, h, GL11.GL_NEAREST, SDL_FLIP_VERTICAL);
}
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Disabled;
@@ -29,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class GlsmSdlAttachmentClearTest {
private static final int SIZE = GlsmSdlHeadlessRig.SIZE;
private static final int RED = 0xFFFF0000;
private static final int BLACK = 0xFF000000;
@BeforeAll
static void boot() {
@@ -570,4 +573,31 @@ class GlsmSdlAttachmentClearTest {
GLStateManager.glDeleteTextures(tex);
GlsmSdlHeadlessRig.bindTarget();
}
@Test
void stencilWrittenLastFramePersists() {
GlsmSdlHeadlessRig.bindTarget();
GlsmSdlHeadlessRig.writeStencilOneInBottomHalf();
GlsmSdlHeadlessRig.endFrame();
GlsmSdlHeadlessRig.beginFrameAndReset();
GlsmSdlHeadlessRig.drawRedWhereStencilIsOne();
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, 4, RED, "stencil written last frame");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE - 5, BLACK, "outside last frame's stencil");
}
@Test
void aSingleAspectClearLeavesTheTextureUndefined() {
final int fbo = GlsmSdlHeadlessRig.createDepthStencilFbo();
GLStateManager.glDepthMask(true);
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT);
final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
final long depthTexture = resourceManager.getFbo(fbo).depthTexture;
GlsmSdlHeadlessRig.endFrame();
assertFalse(resourceManager.isTextureContentDefined(depthTexture), "the stencil aspect was never written, so the first pass must still clear it");
}
}
@@ -155,32 +155,13 @@ class GlsmSdlCopyBlitTest {
assertRow(pixels, SIZE - 1, SENTINEL, "last out-of-range row");
}
private static int createSecondaryFbo() {
final int color = GLStateManager.glGenTextures();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, color);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
final int depth = GLStateManager.glGenRenderbuffers();
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depth);
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE);
final int fbo = GLStateManager.glGenFramebuffers();
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, color, 0);
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depth);
return fbo;
}
@Test
void blitFramebufferWithColorAndDepthBitsMovesColor() {
GlsmSdlHeadlessRig.bindTarget();
GlsmSdlHeadlessRig.clearTo(0.0f, 1.0f, 0.0f, 1.0f);
GlsmSdlHeadlessRig.solidQuad(0.0f, 1.0f, 0.0f);
final int destination = createSecondaryFbo();
final int destination = GlsmSdlHeadlessRig.createDepthStencilFbo();
GLStateManager.glViewport(0, 0, SIZE, SIZE);
GLStateManager.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
@@ -243,4 +224,25 @@ class GlsmSdlCopyBlitTest {
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE / 2 - 4, GREEN, "blitted fbo0 below the split");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE / 2 + 4, RED, "blitted fbo0 above the split");
}
@Test
void blitFramebufferStencilSurvivesTheDestinationsFirstDraw() {
GlsmSdlHeadlessRig.bindTarget();
GlsmSdlHeadlessRig.writeStencilOneInBottomHalf();
final int destination = GlsmSdlHeadlessRig.createDepthStencilFbo();
GLStateManager.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, destination);
GLStateManager.glBlitFramebuffer(0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_NEAREST);
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, destination);
GlsmSdlHeadlessRig.drawRedWhereStencilIsOne();
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, 4, RED, "stencil carried by the blit");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE - 5, BLUE, "outside the blitted stencil");
}
}
@@ -116,6 +116,44 @@ public final class GlsmSdlHeadlessRig {
GLStateManager.glViewport(0, 0, SIZE, SIZE);
}
public static int createDepthStencilFbo() {
final int color = GLStateManager.glGenTextures();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, color);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
final int depth = GLStateManager.glGenRenderbuffers();
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depth);
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE);
final int fbo = GLStateManager.glGenFramebuffers();
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, color, 0);
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depth);
return fbo;
}
public static void writeStencilOneInBottomHalf() {
GLStateManager.glClearStencil(0);
GLStateManager.glStencilMask(0xFF);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
GLStateManager.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
halfQuad(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
}
public static void drawRedWhereStencilIsOne() {
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF);
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
solidQuad(1.0f, 0.0f, 0.0f);
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
}
public static void clearTo(float r, float g, float b, float a) {
GLStateManager.glClearColor(r, g, b, a);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
@@ -74,16 +74,16 @@ class ClearConsumptionTest {
@Test
void depthAndStencilAreConsumedIndependently() {
final ResourceManager rm = rm();
final ContextState st = new ContextState();
FBOClearTracker.recordPendingDepthClear(st, DEPTH, 1f);
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 0);
consumeClears(rm(), st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT), DUMMY, 0, true, true, true);
consumeClears(rm, st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT), DUMMY, 0, true, true, true);
assertFalse(st.pendingDepthTextures.contains(DEPTH));
assertFalse(st.pendingStencilTextures.contains(DEPTH));
assertTrue(st.clearedTexturesThisFrame.contains(DEPTH));
assertTrue(st.clearedStencilTexturesThisFrame.contains(DEPTH));
assertTrue(rm.isTextureContentDefined(DEPTH), "the clear pass defines the depth-stencil contents");
}
@Test
@@ -96,17 +96,17 @@ class ClearConsumptionTest {
assertFalse(st.pendingDepthTextures.contains(DEPTH));
assertTrue(st.pendingStencilTextures.contains(DEPTH), "the format has no stencil aspect to clear");
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH));
}
@Test
void anUnclearedDepthTargetIsStillConsumed() {
final ResourceManager rm = rm();
final ContextState st = new ContextState();
FBOClearTracker.recordPendingDepthClear(st, DEPTH, 1f);
consumeClears(rm(), st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM), DUMMY, 0, false, false, false);
consumeClears(rm, st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM), DUMMY, 0, false, false, false);
assertFalse(st.pendingDepthTextures.contains(DEPTH));
assertFalse(st.clearedTexturesThisFrame.contains(DEPTH), "no clear was baked, so nothing was cleared");
assertFalse(rm.isTextureContentDefined(DEPTH), "no clear was baked, so nothing was defined");
}
}
@@ -29,32 +29,34 @@ class FullCoverageClearDiscardTest {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
assertFalse(st.pendingColorTextures.contains(HANDLE), "pending clear must be dropped, not left queued");
assertFalse(st.pendingColorValues.containsKey(HANDLE));
assertTrue(st.clearedTexturesThisFrame.contains(HANDLE), "the overwrite counts as the clear");
}
@Test
void fullTargetColorBlitDefinesTheContents() {
void copyWithNoPendingClearDefinesTheDestination() {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
final SdlTestRig rig = SdlTestRig.create();
final FBOClearTracker tracker = new FBOClearTracker(rig.frameManager, rig.resourceManager, null);
assertTrue(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
tracker.resolveDestinationForWrite(st, HANDLE, meta(), 0, 0, 0, 0, W, H, true);
assertTrue(rig.resourceManager.isTextureContentDefined(HANDLE), "the overwrite defines the contents, so a later frame must not clear it back");
assertTrue(rig.resourceManager.isTextureContentDefined(HANDLE), "a copy is a write, so the next pass must load it, not clear it");
}
@Test
void fullTargetDepthBlitDropsThePendingClear() {
void depthStencilClearsDropOnlyForAnAllAspectOverwrite() {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingDepthClear(st, HANDLE, 1f);
FBOClearTracker.recordPendingStencilClear(st, HANDLE, 0);
final FBOClearTracker tracker = tracker();
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
assertFalse(st.pendingDepthTextures.contains(HANDLE));
assertTrue(st.clearedTexturesThisFrame.contains(HANDLE));
assertFalse(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), false));
assertTrue(st.pendingDepthTextures.contains(HANDLE) && st.pendingStencilTextures.contains(HANDLE), "a single-aspect overwrite must materialize, not drop, the other aspect's clear");
assertTrue(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
assertFalse(st.pendingDepthTextures.contains(HANDLE) || st.pendingStencilTextures.contains(HANDLE));
assertFalse(st.pendingDepthValues.containsKey(HANDLE) || st.pendingStencilValues.containsKey(HANDLE));
}
@Test
@@ -69,7 +71,7 @@ class FullCoverageClearDiscardTest {
for (int[] c : partial) {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, c[0], c[1], c[2], c[3], c[4], meta()),
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, c[0], c[1], c[2], c[3], c[4], meta(), true),
"x=" + c[0] + " y=" + c[1] + " level=" + c[2] + " w=" + c[3] + " h=" + c[4]);
assertTrue(st.pendingColorTextures.contains(HANDLE), "the clear must survive a partial overwrite");
}
@@ -80,7 +82,7 @@ class FullCoverageClearDiscardTest {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
final ResourceManager.TextureMeta volume = new ResourceManager.TextureMeta(0, 0, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, W, H, 4, 1, 0);
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, volume),
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, volume, true),
"one slice of a volume is not full coverage");
assertTrue(st.pendingColorTextures.contains(HANDLE));
}
@@ -88,15 +90,14 @@ class FullCoverageClearDiscardTest {
@Test
void noPendingClearIsNotAnOverwriteClaim() {
final ContextState st = new ContextState();
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
assertFalse(st.clearedTexturesThisFrame.contains(HANDLE));
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
}
@Test
void missingMetaFallsBackToMaterializing() {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, null));
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, null, true));
assertTrue(st.pendingColorTextures.contains(HANDLE));
}
@@ -107,7 +108,7 @@ class FullCoverageClearDiscardTest {
FBOClearTracker.snapshotFlushGenerations(st);
final int before = st.pendingMutationGen;
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
assertEquals(before + 1, st.pendingMutationGen);
}
}
@@ -56,15 +56,6 @@ class StencilClearTrackingTest {
assertTrue(FBOClearTracker.fboHasPendingClear(st, fbo), "the pass must be rebuilt to apply the stencil clear");
}
@Test
void recordingAStencilClearRetractsTheClearedThisFrameMark() {
final ContextState st = new ContextState();
st.clearedStencilTexturesThisFrame.add(DEPTH);
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 0);
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH), "a second clear in one frame must not be swallowed");
}
@Test
void recordingBumpsMutationGen() {
final ContextState st = new ContextState();
@@ -79,7 +70,6 @@ class StencilClearTrackingTest {
void scrubDropsStencilStateWithTheTexture() {
final ContextState st = new ContextState();
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 7);
st.clearedStencilTexturesThisFrame.add(DEPTH);
final SdlTestRig rig = SdlTestRig.create();
final int glId = rig.resourceManager.genTexture();
@@ -88,7 +78,6 @@ class StencilClearTrackingTest {
assertFalse(st.pendingStencilTextures.contains(DEPTH));
assertFalse(st.pendingStencilValues.containsKey(DEPTH));
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH));
}
@Test