Expand SDL-GPU's GLSM redirector to GLSM and remove unredirected GL workarounds and move action back to master (#2268)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-10-17 17:58:50 -07:00
1 parent ab3bf17696
commit 482dece0f7
17 files changed
+146 -122

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs: jobs:
build-and-test: build-and-test:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@05e7cd7ce0ea015acda8c10c1c39fb3a20a49e60 uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
with: with:
disable-spotless-pr: true disable-spotless-pr: true
java-distribution: temurin java-distribution: temurin
+8
View File
@@ -170,7 +170,13 @@ val archScanAsm: Configuration by configurations.creating {
isCanBeResolved = true isCanBeResolved = true
} }
val glsmRedirectAgent: Configuration by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
dependencies { dependencies {
glsmRedirectAgent(project(":glsm", "redirectAgent"))
archScanAsm(libs.asm.tree.test) archScanAsm(libs.asm.tree.test)
archScanAsm(libs.asm.commons.test) archScanAsm(libs.asm.commons.test)
} }
@@ -212,6 +218,8 @@ val glCoreTest by tasks.registering(Test::class) {
.plus(downgraded(downgradeRootMainClasses)) .plus(downgraded(downgradeRootMainClasses))
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest)) .plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
javaAgent(glsmRedirectAgent)
configureAngelicaJava8() configureAngelicaJava8()
} }
+8
View File
@@ -0,0 +1,8 @@
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.testing.Test
import org.gradle.process.CommandLineArgumentProvider
fun Test.javaAgent(jar: FileCollection) {
inputs.files(jar).withPropertyName("javaAgent")
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${jar.singleFile.path}") })
}
+17
View File
@@ -267,6 +267,22 @@ tasks.test {
configureGlsmJava8() configureGlsmJava8()
} }
val redirectAgentJar by tasks.registering(Jar::class) {
archiveClassifier = "redirect-agent"
manifest {
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent")
}
}
val redirectAgent by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
}
artifacts {
add(redirectAgent.name, redirectAgentJar)
}
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) -> val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
tasks.register<Test>(taskName) { tasks.register<Test>(taskName) {
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile." description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
@@ -275,6 +291,7 @@ val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
includeTags = setOf(tag) includeTags = setOf(tag)
} }
configureGlsmJava8() configureGlsmJava8()
javaAgent(files(redirectAgentJar))
} }
} }
@@ -18,7 +18,7 @@ public class GLSM_VAO_UnitTest {
@AfterEach @AfterEach
void resetClientArrayState() { void resetClientArrayState() {
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false); GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false);
} }
@@ -66,7 +66,7 @@ public class GLSM_VAO_UnitTest {
int vao = UniversalVAO.genVertexArrays(); int vao = UniversalVAO.genVertexArrays();
Assertions.assertTrue(vao > 0, "GL_VERTEX_ARRAY_BINDING - VAO Created"); Assertions.assertTrue(vao > 0, "GL_VERTEX_ARRAY_BINDING - VAO Created");
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, vao, "GL_VERTEX_ARRAY_BINDING - Bound"); verifyState(GL30.GL_VERTEX_ARRAY_BINDING, vao, "GL_VERTEX_ARRAY_BINDING - Bound");
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
@@ -91,7 +91,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// Check if everything got reset to default // Check if everything got reset to default
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING - Unbound"); verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING - Unbound");
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, 0, "GL_ARRAY_BUFFER_BINDING - Unbound"); verifyState(GL15.GL_ARRAY_BUFFER_BINDING, 0, "GL_ARRAY_BUFFER_BINDING - Unbound");
@@ -103,18 +103,18 @@ public class GLSM_VAO_UnitTest {
verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Reset"); verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Reset");
// Check if the VAO states are still valid after re-binding it // Check if the VAO states are still valid after re-binding it
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Preserved"); verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Preserved");
verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Preserved"); verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Preserved");
verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Preserved"); verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Preserved");
verifyState(GL11.GL_VERTEX_ARRAY_STRIDE, vaoVertexStride, "GL_VERTEX_ARRAY_STRIDE - Preserved"); verifyState(GL11.GL_VERTEX_ARRAY_STRIDE, vaoVertexStride, "GL_VERTEX_ARRAY_STRIDE - Preserved");
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glPopAttrib(); GLStateManager.glPopAttrib();
@@ -123,7 +123,7 @@ public class GLSM_VAO_UnitTest {
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, vbo, "GL_ARRAY_BUFFER_BINDING - Not affected by glPushAttrib"); verifyState(GL15.GL_ARRAY_BUFFER_BINDING, vbo, "GL_ARRAY_BUFFER_BINDING - Not affected by glPushAttrib");
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glDeleteBuffers(vbo); GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao); UniversalVAO.deleteVertexArrays(vao);
@@ -132,7 +132,7 @@ public class GLSM_VAO_UnitTest {
@Test @Test
void testGenericAttributeSupersedesFfpConventional() { void testGenericAttributeSupersedesFfpConventional() {
final int vao = UniversalVAO.genVertexArrays(); final int vao = UniversalVAO.genVertexArrays();
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
final int vbo = GL15.glGenBuffers(); final int vbo = GL15.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
@@ -145,7 +145,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableVertexAttribArray(0); GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glDeleteBuffers(vbo); GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao); UniversalVAO.deleteVertexArrays(vao);
} }
@@ -153,7 +153,7 @@ public class GLSM_VAO_UnitTest {
@Test @Test
void testGenericAttributeWinsRegardlessOfOrder() { void testGenericAttributeWinsRegardlessOfOrder() {
final int vao = UniversalVAO.genVertexArrays(); final int vao = UniversalVAO.genVertexArrays();
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
final int vbo = GL15.glGenBuffers(); final int vbo = GL15.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
@@ -166,7 +166,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableVertexAttribArray(0); GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glDeleteBuffers(vbo); GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao); UniversalVAO.deleteVertexArrays(vao);
} }
@@ -174,7 +174,7 @@ public class GLSM_VAO_UnitTest {
@Test @Test
void testFfpConventionalAppliesWithoutGenericAttribute() { void testFfpConventionalAppliesWithoutGenericAttribute() {
final int vao = UniversalVAO.genVertexArrays(); final int vao = UniversalVAO.genVertexArrays();
glBindVertexArray(vao); UniversalVAO.bindVertexArray(vao);
final int vbo = GL15.glGenBuffers(); final int vbo = GL15.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
@@ -186,7 +186,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); UniversalVAO.bindVertexArray(0);
GLStateManager.glDeleteBuffers(vbo); GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao); UniversalVAO.deleteVertexArrays(vao);
} }
@@ -198,12 +198,4 @@ public class GLSM_VAO_UnitTest {
verifyState(GL11.GL_COLOR_ARRAY, false, "GL_COLOR_ARRAY Default State"); verifyState(GL11.GL_COLOR_ARRAY, false, "GL_COLOR_ARRAY Default State");
verifyState(GL11.GL_INDEX_ARRAY, false, "GL_INDEX_ARRAY Default State"); verifyState(GL11.GL_INDEX_ARRAY, false, "GL_INDEX_ARRAY Default State");
} }
private static void glBindVertexArray(int array) {
final GLContextState glCtx = GLStateManager.ctx();
if (array == 0) array = glCtx.defaultVAO;
glCtx.boundVAO = array;
glCtx.vaos.onBindVertexArrayPre(array);
UniversalVAO.bindVertexArray(array);
}
} }
@@ -35,7 +35,6 @@ class WideLineClipGLTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
FfpFixture.routeVertexFormats(true);
savedEmulation = GLStateManager.wideLineEmulationEnabled; savedEmulation = GLStateManager.wideLineEmulationEnabled;
GLStateManager.wideLineEmulationEnabled = true; GLStateManager.wideLineEmulationEnabled = true;
@@ -54,7 +53,6 @@ class WideLineClipGLTest {
@AfterEach @AfterEach
void tearDown() { void tearDown() {
FfpFixture.routeVertexFormats(false);
GLStateManager.wideLineEmulationEnabled = savedEmulation; GLStateManager.wideLineEmulationEnabled = savedEmulation;
GLStateManager.glLineWidth(1.0f); GLStateManager.glLineWidth(1.0f);
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; } if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"> <Configuration status="WARN">
<Appenders> <Appenders>
<Console name="Console" target="SYSTEM_ERR"> <Console name="Console" target="SYSTEM_ERR" follow="true">
<PatternLayout pattern="[%-5level] %logger{36} - %msg%n"/> <PatternLayout pattern="[%-5level] %logger{36} - %msg%n"/>
</Console> </Console>
</Appenders> </Appenders>
@@ -28,6 +28,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
@Override @Override
public void beforeAll(ExtensionContext context) throws LWJGLException { public void beforeAll(ExtensionContext context) throws LWJGLException {
GlsmRedirectAgent.requireInstalled();
if (startFailure != null) { if (startFailure != null) {
throw new IllegalStateException("GLSMCoreExtension context setup failed earlier in this JVM", startFailure); throw new IllegalStateException("GLSMCoreExtension context setup failed earlier in this JVM", startFailure);
} }
@@ -81,6 +82,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
public void afterEach(ExtensionContext context) { public void afterEach(ExtensionContext context) {
final int error = GL11.glGetError(); final int error = GL11.glGetError();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {} while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
GlsmRedirectAgent.assertNoFailures();
assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error)); assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error));
} }
@@ -61,6 +61,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
@Override @Override
public void beforeAll(ExtensionContext context) throws LWJGLException { public void beforeAll(ExtensionContext context) throws LWJGLException {
GlsmRedirectAgent.requireInstalled();
if (startFailure != null) { if (startFailure != null) {
throw new IllegalStateException("GLSMExtension context setup failed earlier in this JVM", startFailure); throw new IllegalStateException("GLSMExtension context setup failed earlier in this JVM", startFailure);
} }
@@ -179,6 +180,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
fail("Test leaked an open display-list recorder! Missing glEndList."); fail("Test leaked an open display-list recorder! Missing glEndList.");
} }
GlsmRedirectAgent.assertNoFailures();
final int error = GL11.glGetError(); final int error = GL11.glGetError();
assertEquals(GL11.GL_NO_ERROR, error, assertEquals(GL11.GL_NO_ERROR, error,
() -> "GL Error: 0x" + Integer.toHexString(error)); () -> "GL Error: 0x" + Integer.toHexString(error));
@@ -0,0 +1,76 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.loading.ClassDump;
import com.gtnewhorizons.angelica.glsm.loading.ModRedirector;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import static org.junit.jupiter.api.Assertions.fail;
public final class GlsmRedirectAgent {
private static final String GTNHLIB = "com/gtnewhorizon/gtnhlib/";
private static final List<Throwable> FAILURES = new CopyOnWriteArrayList<>();
private static final AtomicInteger TRANSFORMED = new AtomicInteger();
private static boolean installed;
private GlsmRedirectAgent() {}
public static void premain(String args, Instrumentation instrumentation) {
install(instrumentation);
}
public static void install(Instrumentation instrumentation) {
final ModRedirector redirector = new ModRedirector(ClassDump.DISABLED);
addTransformer(instrumentation, (className, classfile) -> {
if (!className.startsWith(GTNHLIB)) return null;
final byte[] out = redirector.transform(className.replace('/', '.'), classfile, null);
if (out == classfile) return null;
TRANSFORMED.incrementAndGet();
return out;
});
installed = true;
}
public static void addTransformer(Instrumentation instrumentation, BiFunction<String, byte[], byte[]> transform) {
instrumentation.addTransformer(new Guarded(transform));
}
public static void requireInstalled() {
if (!installed) throw new IllegalStateException("GL tests need the GLSM redirect agent");
}
public static void assertNoFailures() {
if (!FAILURES.isEmpty()) fail("agent transform failures: " + FAILURES, FAILURES.get(0));
}
public static int transformedCount() {
return TRANSFORMED.get();
}
private static final class Guarded implements ClassFileTransformer {
private final BiFunction<String, byte[], byte[]> transform;
private Guarded(BiFunction<String, byte[], byte[]> transform) {
this.transform = transform;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> redefined, ProtectionDomain domain, byte[] classfile) {
if (className == null) return null;
try {
return transform.apply(className, classfile);
} catch (Throwable t) {
FAILURES.add(new IllegalStateException("transforming " + className, t));
return null;
}
}
}
}
@@ -1,8 +1,5 @@
package com.gtnewhorizons.angelica.glsm.ffp; package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.GlTestSupport;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
@@ -136,27 +133,6 @@ public final class FfpFixture {
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, stride, offset); GLStateManager.glVertexAttribPointer(loc, size, type, normalized, stride, offset);
} }
private static boolean formatRoutingRegistered;
private static boolean routeFormats;
public static void routeVertexFormats(boolean enabled) {
routeFormats = enabled;
if (formatRoutingRegistered) return;
formatRoutingRegistered = true;
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
if (!routeFormats) return false;
final int stride = format.getVertexSize();
int elementOffset = (int) offset;
for (VertexFormatElement element : format.elementsArray) {
final Usage usage = element.getUsage();
final boolean normalized = usage == Usage.COLOR || usage == Usage.NORMAL;
attrib(usage.getAttributeLocation(), element.getCount(), element.getType().getGlType(), normalized, stride, elementOffset);
elementOffset += element.getByteSize();
}
return true;
});
}
public static void clear() { public static void clear() {
GLStateManager.glClearColor(0f, 0f, 0f, 1f); GLStateManager.glClearColor(0f, 0f, 0f, 1f);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
+3 -3
View File
@@ -108,6 +108,7 @@ val glsmSdlAgent = sourceSets.create("glsmSdlAgent") {
dependencies { dependencies {
"glsmSdlAgentCompileOnly"(project(":glsm")) "glsmSdlAgentCompileOnly"(project(":glsm"))
"glsmSdlAgentCompileOnly"(testFixtures(project(":glsm")))
"glsmSdlAgentCompileOnly"(libs.asm.tree.test) "glsmSdlAgentCompileOnly"(libs.asm.tree.test)
testImplementation(glsmSdlAgent.output) testImplementation(glsmSdlAgent.output)
} }
@@ -155,9 +156,8 @@ val glsmSdlTest by tasks.registering(Test::class) {
useJUnitPlatform { useJUnitPlatform {
includeTags(glsmSdlTag) includeTags(glsmSdlTag)
} }
dependsOn(tasks.named("glsmSdlStubsClasses"), glsmSdlRedirectAgentJar) dependsOn(tasks.named("glsmSdlStubsClasses"))
val agentJarPath = glsmSdlRedirectAgentJar.flatMap { it.archiveFile }.map { it.asFile.path } javaAgent(files(glsmSdlRedirectAgentJar))
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${agentJarPath.get()}") })
jvmArgs( jvmArgs(
"-Dangelica.sdlgpu.enable=true", "-Dangelica.sdlgpu.enable=true",
"-Dangelica.sdlgpu.disablePresenterThread=true", "-Dangelica.sdlgpu.disablePresenterThread=true",
@@ -1,60 +1,15 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm; package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector; import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public final class GlsmSdlRedirectAgent { public final class GlsmSdlRedirectAgent {
private static final String GTNHLIB = "com/gtnewhorizon/gtnhlib/";
private static final List<String> FAILURES = Collections.synchronizedList(new ArrayList<>());
private static final AtomicInteger TRANSFORMED_COUNT = new AtomicInteger();
private GlsmSdlRedirectAgent() {} private GlsmSdlRedirectAgent() {}
public static void premain(String args, Instrumentation instrumentation) { public static void premain(String args, Instrumentation instrumentation) {
instrumentation.addTransformer(new Redirector()); GlsmRedirectAgent.install(instrumentation);
} GlsmRedirectAgent.addTransformer(instrumentation, MovedSamplerProbe::instrument);
public static List<String> failures() {
return List.copyOf(FAILURES);
}
public static int transformedCount() {
return TRANSFORMED_COUNT.get();
}
private static final class Redirector implements ClassFileTransformer {
private final GLSMRedirector redirector = new GLSMRedirector();
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> redefined, ProtectionDomain domain, byte[] classfile) {
if (className == null) return null;
try {
final byte[] observed = MovedSamplerProbe.instrument(className, classfile);
if (observed != null) return observed;
if (!className.startsWith(GTNHLIB) || !redirector.shouldTransform(classfile)) return null;
final ClassNode node = new ClassNode();
new ClassReader(classfile).accept(node, 0);
if (!redirector.transformClassNode(className.replace('/', '.'), node)) return null;
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
node.accept(writer);
TRANSFORMED_COUNT.incrementAndGet();
return writer.toByteArray();
} catch (Throwable t) {
FAILURES.add(className + ": " + t);
return null;
}
}
} }
} }
@@ -1,12 +1,11 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm; package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.Assertions.assertTrue;
public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
@Override @Override
@@ -22,7 +21,6 @@ public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEac
@Override @Override
public void afterEach(ExtensionContext context) { public void afterEach(ExtensionContext context) {
GlsmSdlHeadlessRig.endFrame(); GlsmSdlHeadlessRig.endFrame();
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(), GlsmRedirectAgent.assertNoFailures();
() -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures());
} }
} }
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm; package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager; import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend; import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig; import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
@@ -71,9 +72,8 @@ public final class GlsmSdlHeadlessRig {
GLStateManager.setRunningSplash(false); GLStateManager.setRunningSplash(false);
GLStateManager.markSplashComplete("glsmSdlHeadlessRig"); GLStateManager.markSplashComplete("glsmSdlHeadlessRig");
assertTrue(GlsmSdlRedirectAgent.transformedCount() > 0, "redirect agent transformed no classes"); assertTrue(GlsmRedirectAgent.transformedCount() > 0, "redirect agent transformed no classes");
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(), GlsmRedirectAgent.assertNoFailures();
() -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures());
createTarget(); createTarget();
booted = true; booted = true;
@@ -1,12 +1,12 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm; package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
@@ -24,6 +24,6 @@ public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, Befo
@Override @Override
public void afterEach(ExtensionContext context) { public void afterEach(ExtensionContext context) {
GlsmSdlHeadlessRig.endFrame(); GlsmSdlHeadlessRig.endFrame();
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(), () -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures()); GlsmRedirectAgent.assertNoFailures();
} }
} }
@@ -60,8 +60,7 @@ class RetainedTesrGroupsGLTest {
private static final float[] TRIANGLE = { -0.1f, -0.1f, 0f, 0.1f, -0.1f, 0f, 0f, 0.15f, 0f }; private static final float[] TRIANGLE = { -0.1f, -0.1f, 0f, 0.1f, -0.1f, 0f, 0f, 0.15f, 0f };
private static final ResourceLocation GLINT_TEXTURE = new ResourceLocation("angelica", "test/glint"); private static final ResourceLocation GLINT_TEXTURE = new ResourceLocation("angelica", "test/glint");
private static boolean overridesRegistered; private static boolean probeRegistered;
private static boolean skipClientArrays;
private static Runnable onBufferSetup; private static Runnable onBufferSetup;
private AngelicaBufferSource source; private AngelicaBufferSource source;
@@ -82,14 +81,13 @@ class RetainedTesrGroupsGLTest {
private CubeParams[] glintCubes; private CubeParams[] glintCubes;
@BeforeAll @BeforeAll
static void registerClientArrayOverrides() { static void registerBufferSetupProbe() {
if (overridesRegistered) return; if (probeRegistered) return;
overridesRegistered = true; probeRegistered = true;
VertexFormat.registerSetupBufferStateOverride((format, offset) -> { VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
if (onBufferSetup != null) onBufferSetup.run(); if (onBufferSetup != null) onBufferSetup.run();
return skipClientArrays; return false;
}); });
VertexFormat.registerClearBufferStateOverride(format -> skipClientArrays);
} }
@BeforeEach @BeforeEach
@@ -119,7 +117,6 @@ class RetainedTesrGroupsGLTest {
@AfterEach @AfterEach
void cleanup() { void cleanup() {
onBufferSetup = null; onBufferSetup = null;
skipClientArrays = false;
source.discard(); source.discard();
GLStateManager.setFfpInstancing(Instancing.NONE); GLStateManager.setFfpInstancing(Instancing.NONE);
GLStateManager.glBindVertexArray(0); GLStateManager.glBindVertexArray(0);
@@ -221,12 +218,8 @@ class RetainedTesrGroupsGLTest {
state.setCurrentBlockEntity(83); state.setCurrentBlockEntity(83);
state.setCurrentEntityColor(0, 1, 0, 0.5f); state.setCurrentEntityColor(0, 1, 0, 0.5f);
draws.clear(); draws.clear();
skipClientArrays = true;
assertEquals(GL11.GL_QUADS, template.drawMode); assertEquals(GL11.GL_QUADS, template.drawMode);
groups.drawLayer(layer); groups.drawLayer(layer);
// FIXME each GL_QUADS fallback draw (VertexBuffer.draw inside MeshBuffer.render) raises this with the
// client arrays skipped; a GL_TRIANGLES template does not.
assertEquals(GL11.GL_INVALID_ENUM, GL11.glGetError());
assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback"); assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback");
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i, assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i,
@@ -339,7 +332,6 @@ class RetainedTesrGroupsGLTest {
assertEquals(2, group.cubeColumns.runs.size(), "sanity: cube runs A,B,A merge into two heads"); assertEquals(2, group.cubeColumns.runs.size(), "sanity: cube runs A,B,A merge into two heads");
assertNotNull(group.cubeColumns.runs.get(0).next, "sanity: the cube A head must carry a second segment"); assertNotNull(group.cubeColumns.runs.get(0).next, "sanity: the cube A head must carry a second segment");
skipClientArrays = true;
groups.drawLayer(layer); groups.drawLayer(layer);
assertEquals(0, pipeline.matrixBinds); assertEquals(0, pipeline.matrixBinds);