A few fixes (#1994)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-08-08 18:29:26 -07:00
1 parent 8da561d3b6
commit 4b21bc77b6
13 files changed
+198 -5

No files matched your search

+1
View File
@@ -201,6 +201,7 @@ fun Test.configureAngelicaJava8() {
tasks.test {
useJUnitPlatform { excludeTags = setOf("gl-core") }
configureAngelicaJava8()
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
}
val downgradeRootMainClasses = tasks.named<DowngradeFiles>("downgradeMainClasses")
+1
View File
@@ -10,4 +10,5 @@ repositories {
// Re-export Gradle's generated typed catalog accessors so applied scripts (dependencies.gradle.kts) can use them.
dependencies {
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
implementation(libs.asm.build)
}
@@ -0,0 +1,49 @@
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import java.io.File
private const val LWJGL3_AWARE_DESC = "Lme/eigenraven/lwjgl3ify/api/Lwjgl3Aware;"
/**
* Stamps @Lwjgl3Aware onto every class under [classesDir] - classes that already carry the annotation are left untouched.
*/
fun injectLwjgl3Aware(classesDir: File) {
if (!classesDir.isDirectory) return
classesDir.walkTopDown()
.filter { it.isFile && it.extension == "class" && it.name != "module-info.class" }
.forEach { classFile ->
val reader = ClassReader(classFile.readBytes())
if (hasLwjgl3Aware(reader)) return@forEach
val writer = ClassWriter(reader, 0)
reader.accept(object : ClassVisitor(Opcodes.ASM9, writer) {
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, access, name, signature, superName, interfaces)
super.visitAnnotation(LWJGL3_AWARE_DESC, true)?.visitEnd()
}
}, 0)
classFile.writeBytes(writer.toByteArray())
}
}
private fun hasLwjgl3Aware(reader: ClassReader): Boolean {
var found = false
reader.accept(object : ClassVisitor(Opcodes.ASM9) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
if (descriptor == LWJGL3_AWARE_DESC) found = true
return null
}
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
return found
}
@@ -282,6 +282,7 @@ public class TessellatorStreamingDrawer {
}
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0);
} finally {
if (locked) GLStateManager.releaseDrawLock();
Tracy.endZone();
+2
View File
@@ -26,6 +26,7 @@ commonsLang3 = "3.17.0"
jvmDowngrader = "1.3.5"
junit = "5.14.1"
asmTest = "9.7"
asmBuild = "9.7"
mockito = "3.12.4"
archunit = "1.4.1"
ant = "1.8.2"
@@ -59,6 +60,7 @@ archunit-junit5 = { module = "com.tngtech.archunit:archunit-junit5", version.ref
ant = { module = "org.apache.ant:ant", version.ref = "ant" }
asm-tree-compile = { module = "org.ow2.asm:asm-tree", version.ref = "asmCompile" }
asm-tree-test = { module = "org.ow2.asm:asm-tree", version.ref = "asmTest" }
asm-build = { module = "org.ow2.asm:asm", version.ref = "asmBuild" }
lwjgl2 = { module = "org.lwjgl.lwjgl:lwjgl", version.ref = "lwjgl2" }
lwjgl2-util = { module = "org.lwjgl.lwjgl:lwjgl_util", version.ref = "lwjgl2" }
lwjgl3 = { module = "org.lwjgl:lwjgl", version.ref = "lwjgl3" }
+5
View File
@@ -74,6 +74,11 @@ dependencies {
compileOnly(libs.annotations)
}
tasks.named<JavaCompile>("compileJava") {
val classesDir = destinationDirectory
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
}
tasks.named<Jar>("jar") {
manifest {
attributes(
@@ -1613,9 +1613,9 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) {
if (GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_shader_image_load_store) {
if (caps.OpenGL42 || caps.GL_ARB_shader_image_load_store) {
GL42C.glBindImageTexture(unit, texture, level, layered, layer, access, format);
} else if (GLStateManager.capabilities.GL_EXT_shader_image_load_store) {
} else if (caps.GL_EXT_shader_image_load_store) {
EXTShaderImageLoadStore.glBindImageTextureEXT(unit, texture, level, layered, layer, access, format);
} else {
GLStateManager.warnOnce("bindImageTexture", "glBindImageTexture ignored: no image load/store support");
+5
View File
@@ -91,6 +91,11 @@ tasks.test {
}
}
tasks.named<JavaCompile>("compileJava") {
val classesDir = destinationDirectory
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
}
tasks.named<Jar>("jar") {
manifest {
attributes(
@@ -32,7 +32,7 @@ class ChunkCullSpirvCompileTest {
final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv);
assertEquals(0, map.samplerGlSlots().length, "the frustum cull samples no textures");
assertEquals(1, map.uboGlSlots().length, "Frustum UBO");
assertEquals(0, map.uboGlSlots()[0], "Frustum UBO is at binding 0");
assertEquals(1, map.uboGlSlots()[0], "Frustum UBO is at binding 1");
assertEquals(160, map.uboSizes()[0], "Frustum UBO is 160 bytes");
assertEquals(3, map.roSsboGlSlots().length + map.rwSsboGlSlots().length, "visible + meta + indirect");
assertTrue(IntStream.concat(Arrays.stream(map.roSsboGlSlots()), Arrays.stream(map.rwSsboGlSlots())).allMatch(s -> s >= 0 && s < 8), "a storage binding outside 0..7 exceeds the GL 4.3 guaranteed minimum");
@@ -30,7 +30,7 @@ public final class GpuDrivenChunkCuller {
static final int VISIBLE_SSBO_BINDING = 0;
static final int META_SSBO_BINDING = 1;
static final int INDIRECT_SSBO_BINDING = 2;
static final int FRUSTUM_UBO_BINDING = 0;
static final int FRUSTUM_UBO_BINDING = 1;
private boolean ready;
private boolean disabled;
@@ -33,7 +33,7 @@ layout(std430, binding = 2) writeonly buffer IndirectCmds {
DrawElementsIndirectCommand cmds[];
} indirect;
layout(std140, binding = 0) uniform Frustum {
layout(std140, binding = 1) uniform Frustum {
vec4 planes[6]; // camera-relative planes from `proj * mv` (no translate baked in)
// control.x = visibleCount (iteration bound)
// control.y = indexPointerMask (0 non-sorted -> firstIndex masked to 0; 0xFFFFFFFF sorted)
@@ -0,0 +1,127 @@
package com.gtnewhorizons.angelica.arch;
import com.tngtech.archunit.core.domain.AccessTarget;
import com.tngtech.archunit.core.domain.JavaAccess;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Enforces the lwjgl3ify remap boundary.
*/
class Lwjgl3AwareBoundaryTest {
private static final String AWARE = "me.eigenraven.lwjgl3ify.api.Lwjgl3Aware";
/** Packages whose classes lwjgl3ify never transforms, so their descriptors match on both sides. */
private static final List<String> UNTRANSFORMED_PACKAGES = List.of("org.lwjgl.", "org.lwjglx.");
/** Modules built against native LWJGL3; every class in them must be aware. */
private static final List<String> AWARE_MODULES = List.of(
"com.gtnewhorizons.angelica.sdlgpu.",
"com.gtnewhorizons.angelica.lwjgl3.");
private static final JavaClasses classes = new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPaths(
"build/classes/java/main",
"build/classes/java/mixin",
"glsm/build/classes/java/main",
"lwjgl3-backend/build/classes/java/main",
"sdl-gpu/build/classes/java/main");
@Test
void awareClassesDoNotReadRemappedMembers() {
final List<String> violations = new ArrayList<>();
for (JavaClass origin : classes) {
if (!isAware(origin)) continue;
for (JavaAccess<?> access : origin.getAccessesFromSelf()) {
final JavaClass owner = access.getTargetOwner();
if (isUntransformed(owner) || isAware(owner)) continue;
if (mentionsLwjgl3Type(access.getTarget())) violations.add(describe(access));
}
}
assertTrue(violations.isEmpty(), "@Lwjgl3Aware code referencing a remapped member with an org.lwjgl type in its descriptor:\n" + String.join("\n", violations));
}
@Test
void remappedClassesDoNotReadAwareMembers() {
final List<String> violations = new ArrayList<>();
for (JavaClass origin : classes) {
if (isAware(origin) || isUntransformed(origin)) continue;
for (JavaAccess<?> access : origin.getAccessesFromSelf()) {
if (!isAware(access.getTargetOwner())) continue;
if (mentionsLwjgl3Type(access.getTarget())) violations.add(describe(access));
}
}
assertTrue(violations.isEmpty(), "Remapped code referencing an @Lwjgl3Aware member with an org.lwjgl type in its descriptor:\n" + String.join("\n", violations));
}
/** Verifies the build-time injection in :sdl-gpu / :lwjgl3-backend actually ran over the class output. */
@Test
void nativeLwjgl3ModulesAreFullyAware() {
final List<String> missing = new ArrayList<>();
for (JavaClass c : classes) {
if (AWARE_MODULES.stream().anyMatch(p -> c.getName().startsWith(p)) && !isAware(c)) {
missing.add(c.getName());
}
}
assertTrue(missing.isEmpty(), "Classes missing @Lwjgl3Aware:\n" + String.join("\n", missing));
}
@Test
void renderBackendSignaturesAreRemapSafe() {
final JavaClass renderBackend = classes.get("com.gtnewhorizons.angelica.glsm.backend.RenderBackend");
final List<String> violations = new ArrayList<>();
for (JavaMethod method : renderBackend.getMethods()) {
final List<JavaClass> types = new ArrayList<>(method.getRawParameterTypes());
types.add(method.getRawReturnType());
if (types.stream().anyMatch(Lwjgl3AwareBoundaryTest::isLwjgl3Type)) {
violations.add(method.getFullName());
}
}
assertTrue(violations.isEmpty(), "RenderBackend methods with an org.lwjgl type in their signature:\n" + String.join("\n", violations));
}
private static boolean isAware(JavaClass c) {
return c.isAnnotatedWith(AWARE);
}
private static boolean isUntransformed(JavaClass c) {
return UNTRANSFORMED_PACKAGES.stream().anyMatch(p -> c.getName().startsWith(p));
}
private static boolean isLwjgl3Type(JavaClass type) {
return type.getName().startsWith("org.lwjgl.");
}
private static boolean mentionsLwjgl3Type(AccessTarget target) {
return descriptorTypes(target).stream().anyMatch(Lwjgl3AwareBoundaryTest::isLwjgl3Type);
}
private static List<JavaClass> descriptorTypes(AccessTarget target) {
if (target instanceof AccessTarget.FieldAccessTarget field) {
return List.of(field.getRawType());
}
if (target instanceof AccessTarget.CodeUnitCallTarget call) {
final List<JavaClass> types = new ArrayList<>(call.getRawParameterTypes());
if (target instanceof AccessTarget.MethodCallTarget method) {
types.add(method.getRawReturnType());
}
return types;
}
return List.of();
}
private static String describe(JavaAccess<?> access) {
return " " + access.getOwner().getFullName() + " -> " + access.getTarget().getFullName();
}
}
+2
View File
@@ -0,0 +1,2 @@
# The arch tests only need names and descriptors off the imported bytecode.
resolveMissingDependenciesFromClassPath=false