mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 09:44:52 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
413df76ad4 | ||
|
|
18fe05eda8 | ||
|
|
4fa8cfe3e4 | ||
|
|
b5dd1c04ce | ||
|
|
6c5eedb7c1 | ||
|
|
9d5eb01091 | ||
|
|
0d54a65026 | ||
|
|
8148d8f927 | ||
|
|
5c441e4418 | ||
|
|
78562f12d9 | ||
|
|
031456f224 | ||
|
|
ff35c684ca | ||
|
|
5c4ef022d5 | ||
|
|
4eb837a2c0 | ||
|
|
6570b6a0a5 | ||
|
|
493b641d56 | ||
|
|
3f4995466c | ||
|
|
56d7568fd3 | ||
|
|
4910cfd6d3 | ||
|
|
d1c6677caf | ||
|
|
8cf0296284 | ||
|
|
4fd98e5026 | ||
|
|
8e293f13fb | ||
|
|
2db20ad866 | ||
|
|
10a5b550f2 | ||
|
|
c1403dcf13 | ||
|
|
5d1fabfca9 | ||
|
|
555b2b7dd2 | ||
|
|
7017786a7c | ||
|
|
1c49649685 | ||
|
|
2bae79c4ee | ||
|
|
c22935660a | ||
|
|
79c488102b | ||
|
|
cc1353b73a | ||
|
|
d1e6dc8c94 | ||
|
|
ecbf8fea2f | ||
|
|
610209605f | ||
|
|
112aae3735 | ||
|
|
5844b1b5c1 | ||
|
|
05f01c17e6 | ||
|
|
f07d96316a | ||
|
|
5b704d1bc2 | ||
|
|
26135124f4 | ||
|
|
3abc89b7b2 | ||
|
|
03d908d658 |
No files matched your search
@@ -1,4 +1,4 @@
|
||||
# Description or your *PR* and other info
|
||||
# Description of your *PR* and other info
|
||||
<!-- Please include a summary of the change -->
|
||||
<!-- Any details that you think are important to review this PR? -->
|
||||
<!-- Are there any Issues / PRs related to this one? -->
|
||||
|
||||
@@ -12,6 +12,7 @@ build/
|
||||
/config/
|
||||
/crash-reports/
|
||||
/logs/
|
||||
tmp/
|
||||
options.txt
|
||||
/saves/
|
||||
usernamecache.json
|
||||
|
||||
@@ -23,7 +23,7 @@ Targets GL 3.3+ core profile
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
|
||||
+8
-2
@@ -90,13 +90,17 @@ dependencies {
|
||||
compileOnly "org.apache.ant:ant:1.8.2"
|
||||
|
||||
// Celeritas (transitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.8-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
// BQ Testing
|
||||
// devOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.74-GTNH:dev")
|
||||
|
||||
|
||||
// Display List Testing
|
||||
/*
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-749-GTNH:dev")
|
||||
@@ -144,7 +148,7 @@ dependencies {
|
||||
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
|
||||
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
|
||||
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
|
||||
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
|
||||
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
|
||||
@@ -180,4 +184,6 @@ dependencies {
|
||||
}
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { transitive = false }
|
||||
}
|
||||
+44
-10
@@ -146,9 +146,13 @@ val downgradeTestClasses by tasks.registering(DowngradeFiles::class) {
|
||||
dependsOn(tasks.named("testClasses"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
val GL_TASK_TAGS = mapOf(
|
||||
"glCompatTest" to "gl-compat",
|
||||
"glCoreTest" to "gl-core",
|
||||
"glSharedTest" to "gl-shared",
|
||||
)
|
||||
|
||||
fun Test.configureGlsmJava8() {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")
|
||||
@@ -171,23 +175,53 @@ tasks.test {
|
||||
classpath = downgradedTest
|
||||
.plus(downgradedMain)
|
||||
.plus(downgradedDeps)
|
||||
.plus(classpath.minus(mainClassesDirs).minus(testClassesDirs).minus(depsToDowngrade))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(mainClassesDirs).minus(testClassesDirs).minus(depsToDowngrade))
|
||||
|
||||
val extractNatives = rootProject.tasks.named("extractNatives2")
|
||||
dependsOn(extractNatives)
|
||||
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags = GL_TASK_TAGS.values.toSet()
|
||||
}
|
||||
configureGlsmJava8()
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
tasks.register<Test>(taskName) {
|
||||
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
|
||||
group = "verification"
|
||||
useJUnitPlatform {
|
||||
includeTags = setOf(tag)
|
||||
}
|
||||
configureGlsmJava8()
|
||||
}
|
||||
}
|
||||
|
||||
(listOf(tasks.test) + glTestTasks).zipWithNext { earlier, later ->
|
||||
later.configure { mustRunAfter(earlier) }
|
||||
}
|
||||
tasks.test { finalizedBy(glTestTasks) }
|
||||
|
||||
val verifyTestsRan by tasks.registering {
|
||||
val resultsDir = layout.buildDirectory.dir("test-results/test")
|
||||
dependsOn(tasks.test)
|
||||
doLast {
|
||||
val dir = resultsDir.get().asFile
|
||||
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
|
||||
check(xmls.isNotEmpty()) {
|
||||
":glsm:test produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
|
||||
dependsOn(tasks.test, glTestTasks)
|
||||
}
|
||||
(listOf(tasks.test) + glTestTasks).forEach { testTask ->
|
||||
val name = testTask.name
|
||||
val verify = tasks.register("verify${name.replaceFirstChar { it.uppercase() }}Ran") {
|
||||
val resultsDir = layout.buildDirectory.dir("test-results/$name")
|
||||
dependsOn(testTask)
|
||||
doLast {
|
||||
val dir = resultsDir.get().asFile
|
||||
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
|
||||
check(xmls.isNotEmpty()) {
|
||||
":glsm:$name produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
|
||||
}
|
||||
}
|
||||
}
|
||||
verifyTestsRan.configure { dependsOn(verify) }
|
||||
}
|
||||
tasks.check { dependsOn(verifyTestsRan) }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
lwjglVersion = 2.9.4-nightly-20150209
|
||||
eventbusVersion = 7.0-beta.15
|
||||
celeritasVersion = 2.5.3-GTNH
|
||||
celeritasVersion = 2.5.8-GTNH
|
||||
lombokVersion = 1.18.42
|
||||
log4jVersion = 2.0-beta9
|
||||
guavaVersion = 18.0
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.ShaderParser;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
@@ -8,9 +13,11 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -182,8 +189,9 @@ public class CompatShaderTransformer {
|
||||
transformer.rename("gl_FrontColor", "angelica_FrontColor");
|
||||
transformer.prependMain("angelica_FrontColor = vec4(1.0);");
|
||||
|
||||
// Vertex attributes — replaces removed FFP vertex inputs with explicit in declarations
|
||||
transformVertexAttributes(transformer, source);
|
||||
final FFPInputs ffp = FFPInputs.detect(source);
|
||||
assignVertexInputLocations(transformer, ffp);
|
||||
transformVertexAttributes(transformer, source, ffp);
|
||||
} else {
|
||||
if (source.contains("gl_Color")) {
|
||||
transformer.injectVariable("in vec4 angelica_FrontColor;");
|
||||
@@ -310,25 +318,25 @@ public class CompatShaderTransformer {
|
||||
/**
|
||||
* Replace removed FFP vertex attributes with explicit {@code in} declarations at core profile attribute locations.
|
||||
*/
|
||||
private static void transformVertexAttributes(Transformer transformer, String source) {
|
||||
if (source.contains("gl_Vertex") || source.contains("ftransform")) {
|
||||
private static void transformVertexAttributes(Transformer transformer, String source, FFPInputs ffp) {
|
||||
if (ffp.position()) {
|
||||
transformer.injectVariable("layout(location = 0) in vec4 angelica_Vertex;");
|
||||
transformer.rename("gl_Vertex", "angelica_Vertex");
|
||||
}
|
||||
// gl_Color in vertex shaders is the per-vertex color attribute, distinct from the fragment gl_Color (interpolated gl_FrontColor) handled above
|
||||
if (source.contains("gl_Color")) {
|
||||
if (ffp.color()) {
|
||||
transformer.injectVariable("layout(location = 1) in vec4 angelica_Color;");
|
||||
transformer.rename("gl_Color", "angelica_Color");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord0")) {
|
||||
if (ffp.uv0()) {
|
||||
transformer.injectVariable("layout(location = 2) in vec4 angelica_MultiTexCoord0;");
|
||||
transformer.rename("gl_MultiTexCoord0", "angelica_MultiTexCoord0");
|
||||
}
|
||||
if (source.contains("gl_MultiTexCoord1")) {
|
||||
if (ffp.uv1()) {
|
||||
transformer.injectVariable("layout(location = 3) in vec4 angelica_MultiTexCoord1;");
|
||||
transformer.rename("gl_MultiTexCoord1", "angelica_MultiTexCoord1");
|
||||
}
|
||||
if (source.contains("gl_Normal")) {
|
||||
if (ffp.normal()) {
|
||||
transformer.injectVariable("layout(location = 4) in vec3 angelica_Normal;");
|
||||
transformer.rename("gl_Normal", "angelica_Normal");
|
||||
}
|
||||
@@ -337,6 +345,145 @@ public class CompatShaderTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_VERTEX_ATTRIBS = 16;
|
||||
|
||||
private record FFPInputs(boolean position, boolean color, boolean uv0, boolean uv1, boolean normal) {
|
||||
static FFPInputs detect(String source) {
|
||||
return new FFPInputs(
|
||||
source.contains("gl_Vertex") || source.contains("ftransform"),
|
||||
source.contains("gl_Color"),
|
||||
source.contains("gl_MultiTexCoord0"),
|
||||
source.contains("gl_MultiTexCoord1"),
|
||||
source.contains("gl_Normal"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Adaptation of Mesa's assign_attribute_or_color_locations */
|
||||
private static void assignVertexInputLocations(Transformer transformer, FFPInputs ffp) {
|
||||
int used = 0;
|
||||
if (ffp.position()) used |= 1 << Usage.POSITION.getAttributeLocation();
|
||||
if (ffp.color()) used |= 1 << Usage.COLOR.getAttributeLocation();
|
||||
if (ffp.uv0()) used |= 1 << Usage.PRIMARY_UV.getAttributeLocation();
|
||||
if (ffp.uv1()) used |= 1 << Usage.SECONDARY_UV.getAttributeLocation();
|
||||
if (ffp.normal()) used |= 1 << Usage.NORMAL.getAttributeLocation();
|
||||
|
||||
record Input(String name, String baseType, String arraySuffix, int slots, int index) {}
|
||||
final List<Input> toAssign = new ArrayList<>();
|
||||
int index = 0;
|
||||
|
||||
for (TerminalNode storage : transformer.collectStorage()) {
|
||||
final int tokenType = storage.getSymbol().getType();
|
||||
if (tokenType != GLSLLexer.ATTRIBUTE && tokenType != GLSLLexer.IN) continue;
|
||||
|
||||
ParseTree node = storage.getParent();
|
||||
while (node != null && !(node instanceof GLSLParser.Single_declarationContext)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
if (!(node instanceof GLSLParser.Single_declarationContext decl)) continue;
|
||||
if (decl.typeless_declaration() == null) continue;
|
||||
|
||||
final GLSLParser.Type_specifierContext typeSpec = decl.fully_specified_type().type_specifier();
|
||||
final String baseType = GlslTransformUtils.getFormattedShader(typeSpec.type_specifier_nonarray(), "").trim();
|
||||
final int typeArraySlots = arraySlots(typeSpec.array_specifier());
|
||||
final String typeArray = arraySuffix(typeSpec.array_specifier());
|
||||
|
||||
if (hasLayoutQualifier(decl)) {
|
||||
final int loc = explicitLocation(decl);
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(decl.typeless_declaration().array_specifier());
|
||||
if (loc >= 0) used |= slotMask(slots) << loc;
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
|
||||
declarators.add(decl.typeless_declaration());
|
||||
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
|
||||
declarators.addAll(list.typeless_declaration());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext d : declarators) {
|
||||
if (d.IDENTIFIER() == null) continue;
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
|
||||
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
|
||||
toAssign.add(new Input(d.IDENTIFIER().getText(), baseType, arraySuffix, slots, index++));
|
||||
}
|
||||
}
|
||||
|
||||
toAssign.sort((a, b) -> a.slots() != b.slots() ? b.slots() - a.slots() : a.index() - b.index());
|
||||
|
||||
for (Input in : toAssign) {
|
||||
final int loc = findAvailableSlots(used, in.slots());
|
||||
if (loc < 0) continue;
|
||||
used |= slotMask(in.slots()) << loc;
|
||||
transformer.removeVariable(in.name());
|
||||
transformer.variable = null;
|
||||
transformer.injectVariable("layout(location = " + loc + ") in " + in.baseType() + " " + in.name() + in.arraySuffix() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
private static String arraySuffix(GLSLParser.Array_specifierContext arraySpec) {
|
||||
return arraySpec != null ? GlslTransformUtils.getFormattedShader(arraySpec, "").trim() : "";
|
||||
}
|
||||
|
||||
private static int arraySlots(GLSLParser.Array_specifierContext arraySpec) {
|
||||
if (arraySpec == null) return 1;
|
||||
int product = 1;
|
||||
for (GLSLParser.DimensionContext dim : arraySpec.dimension()) {
|
||||
if (dim.constant_expression() == null) continue;
|
||||
try {
|
||||
product *= Integer.decode(GlslTransformUtils.getFormattedShader(dim.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private static int baseSlots(String type) {
|
||||
final String dims = type.startsWith("dmat") ? type.substring(4) : type.startsWith("mat") ? type.substring(3) : null;
|
||||
if (dims != null && !dims.isEmpty() && Character.isDigit(dims.charAt(0))) {
|
||||
return dims.charAt(0) - '0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int slotMask(int slots) {
|
||||
return (slots >= 32) ? -1 : (1 << slots) - 1;
|
||||
}
|
||||
|
||||
private static int findAvailableSlots(int used, int needed) {
|
||||
if (needed <= 0) return -1;
|
||||
final int mask = slotMask(needed);
|
||||
for (int i = 0; i <= MAX_VERTEX_ATTRIBS - needed; i++) {
|
||||
if (((mask << i) & ~used) == (mask << i)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int explicitLocation(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return -1;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
final GLSLParser.Layout_qualifierContext layout = single.layout_qualifier();
|
||||
if (layout == null) continue;
|
||||
for (GLSLParser.Layout_qualifier_idContext id : layout.layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() != null && "location".equals(id.IDENTIFIER().getText()) && id.constant_expression() != null) {
|
||||
try {
|
||||
return Integer.decode(GlslTransformUtils.getFormattedShader(id.constant_expression(), "").trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean hasLayoutQualifier(GLSLParser.Single_declarationContext decl) {
|
||||
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
|
||||
if (qualifier == null) return false;
|
||||
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
|
||||
if (single.layout_qualifier() != null) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void dumpShader(String original, String transformed, boolean isFragment, boolean wasTransformed) {
|
||||
if (DUMP_DIR == null) return;
|
||||
final int id = dumpCounter.getAndIncrement();
|
||||
|
||||
@@ -133,7 +133,10 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static UnsupportedOperationException unsupportedInList(String what) {
|
||||
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
final UnsupportedOperationException e = new UnsupportedOperationException( what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
GLStateManager.warnOnce("unsupported:" + what, "Aborting display list {} compilation: {} unsupported during recording", glListId, what);
|
||||
abortCompilation();
|
||||
return e;
|
||||
}
|
||||
|
||||
public static String describeCurrentCompilation() {
|
||||
@@ -798,7 +801,8 @@ public class DisplayListManager {
|
||||
*/
|
||||
public static void glEndList() {
|
||||
if (glListMode == 0) {
|
||||
throw new RuntimeException("glEndList called outside of a display list!");
|
||||
GLStateManager.warnOnce("endlist-outside", "glEndList called outside of a display list!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
finishCurrentList();
|
||||
@@ -873,9 +877,14 @@ public class DisplayListManager {
|
||||
// Empty display list - per OpenGL spec, still valid after glNewList/glEndList
|
||||
compiled = CompiledDisplayList.EMPTY;
|
||||
}
|
||||
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
|
||||
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
|
||||
previous.delete();
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
|
||||
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
|
||||
previous.delete();
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
|
||||
// Log compilation details if enabled (before context restoration changes glListId)
|
||||
@@ -906,17 +915,56 @@ public class DisplayListManager {
|
||||
// Parent's COMPILING callback is now active again
|
||||
} else {
|
||||
// Not nested - clear all compilation state
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
resetCompilationState();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear all root-level compilation state back to "not recording". */
|
||||
private static void resetCompilationState() {
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
}
|
||||
|
||||
public static void abortCompilation() {
|
||||
if (glListMode == 0 && compilationStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int levels = compilationStack.size() + (glListMode != 0 ? 1 : 0);
|
||||
for (int i = 0; i < levels; i++) {
|
||||
try {
|
||||
TessellatorManager.stopCapturingDirect();
|
||||
} catch (IllegalStateException ignored) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRecorder != null) {
|
||||
currentRecorder.delete();
|
||||
}
|
||||
for (CompilationContext ctx : compilationStack) {
|
||||
if (ctx.recorder() != null) {
|
||||
ctx.recorder().delete();
|
||||
}
|
||||
}
|
||||
compilationStack.clear();
|
||||
|
||||
resetCompilationState();
|
||||
}
|
||||
|
||||
public static void abortIfLeaked() {
|
||||
if (glListMode != 0 || !compilationStack.isEmpty()) {
|
||||
GLStateManager.warnOnce("frame-leak", "Display list {} left open across a frame boundary - aborting to recover", glListId);
|
||||
abortCompilation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,33 +1017,43 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
private static void executeDisplayList(int list) {
|
||||
final CompiledDisplayList compiled = displayListCache.get(list);
|
||||
if (compiled != null) {
|
||||
compiled.render(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to VBOManager or legacy display list
|
||||
if (list < 0) {
|
||||
// Negative IDs are VBOManager space (entity models, etc.)
|
||||
final VertexBuffer vbo = VBOManager.get(list);
|
||||
if (vbo != null) {
|
||||
vbo.render();
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
final CompiledDisplayList compiled = displayListCache.get(list);
|
||||
if (compiled != null) {
|
||||
compiled.render(list);
|
||||
return;
|
||||
}
|
||||
// Per OpenGL spec: if list is undefined, glCallList has no effect
|
||||
|
||||
// Fall back to VBOManager or legacy display list
|
||||
if (list < 0) {
|
||||
// Negative IDs are VBOManager space (entity models, etc.)
|
||||
final VertexBuffer vbo = VBOManager.get(list);
|
||||
if (vbo != null) {
|
||||
vbo.render();
|
||||
}
|
||||
// Per OpenGL spec: if list is undefined, glCallList has no effect
|
||||
}
|
||||
// An uncached positive-ID list = no-op (per OpenGL spec)
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
// An uncached positive-ID list = no-op (per OpenGL spec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete display lists and free their VBO resources.
|
||||
*/
|
||||
public static void glDeleteLists(int list, int range) {
|
||||
for (int i = list; i < list + range; i++) {
|
||||
CompiledDisplayList compiled = displayListCache.remove(i);
|
||||
if (compiled != null) {
|
||||
compiled.delete();
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
for (int i = list; i < list + range; i++) {
|
||||
CompiledDisplayList compiled = displayListCache.remove(i);
|
||||
if (compiled != null) {
|
||||
compiled.delete();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -572,6 +572,15 @@ public final class GLDebug {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload that lets you gate the String concat behind the debugState
|
||||
*/
|
||||
public static void pushGroup(String prefix, int value) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(prefix + value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void pushGroup(String group) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(group);
|
||||
|
||||
@@ -57,8 +57,8 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
@@ -89,12 +89,15 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
|
||||
@@ -120,6 +123,12 @@ public class GLStateManager {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
// Thread Checking - must be early in static init order so isMainThread() works for state initialization
|
||||
@Getter private static final Thread MainThread = Thread.currentThread();
|
||||
|
||||
@@ -244,6 +253,18 @@ public class GLStateManager {
|
||||
// Reference to DrawableGL (main display context) - works with both FML and BLS splash
|
||||
@Setter private static Drawable drawableGL = null;
|
||||
|
||||
private static final ReentrantLock DRAW_LOCK = new ReentrantLock();
|
||||
|
||||
public static boolean acquireDrawLock() {
|
||||
if (splashComplete) return false;
|
||||
DRAW_LOCK.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void releaseDrawLock() {
|
||||
DRAW_LOCK.unlock();
|
||||
}
|
||||
|
||||
public static boolean isCachingEnabled() {
|
||||
if (splashComplete) return true;
|
||||
return Thread.currentThread() == drawableGLHolder;
|
||||
@@ -360,16 +381,16 @@ public class GLStateManager {
|
||||
@Getter protected static final BooleanStateStack ditherState = new BooleanStateStack(GL11.GL_DITHER, true); // Defaults to true per OpenGL spec
|
||||
@Getter protected static final BooleanStateStack stencilTest = new BooleanStateStack(GL11.GL_STENCIL_TEST);
|
||||
@Getter protected static final BooleanStateStack lineSmoothState = new BooleanStateStack(GL11.GL_LINE_SMOOTH);
|
||||
@Getter protected static final BooleanStateStack lineStippleState = new BooleanStateStack(GL11.GL_LINE_STIPPLE);
|
||||
@Getter protected static final BooleanStateStack pointSmoothState = new BooleanStateStack(GL11.GL_POINT_SMOOTH);
|
||||
@Getter protected static final BooleanStateStack lineStippleState = new BooleanStateStack(GL11.GL_LINE_STIPPLE, false, true);
|
||||
@Getter protected static final BooleanStateStack pointSmoothState = new BooleanStateStack(GL11.GL_POINT_SMOOTH, false, true);
|
||||
@Getter protected static final BooleanStateStack polygonSmoothState = new BooleanStateStack(GL11.GL_POLYGON_SMOOTH);
|
||||
@Getter protected static final BooleanStateStack polygonStippleState = new BooleanStateStack(GL11.GL_POLYGON_STIPPLE);
|
||||
@Getter protected static final BooleanStateStack polygonStippleState = new BooleanStateStack(GL11.GL_POLYGON_STIPPLE, false, true);
|
||||
@Getter protected static final BooleanStateStack multisampleState = new BooleanStateStack(GL13.GL_MULTISAMPLE, true); // Defaults to true per OpenGL spec
|
||||
@Getter protected static final BooleanStateStack sampleAlphaToCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
@Getter protected static final BooleanStateStack sampleAlphaToOneState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_ONE);
|
||||
@Getter protected static final BooleanStateStack sampleCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_COVERAGE);
|
||||
@Getter protected static final BooleanStateStack colorLogicOpState = new BooleanStateStack(GL11.GL_COLOR_LOGIC_OP);
|
||||
@Getter protected static final BooleanStateStack indexLogicOpState = new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP);
|
||||
@Getter protected static final BooleanStateStack indexLogicOpState = new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP, false, true);
|
||||
|
||||
// Polygon offset states (enable bits)
|
||||
@Getter protected static final BooleanStateStack polygonOffsetPointState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_POINT);
|
||||
@@ -389,6 +410,7 @@ public class GLStateManager {
|
||||
* {@link com.gtnewhorizons.angelica.glsm.ffp.VertexKey}.
|
||||
*/
|
||||
public static boolean wideLineEmulationActive = false;
|
||||
public static boolean lineStippleActive = false;
|
||||
|
||||
// Point state (GL_POINT_BIT)
|
||||
@Getter protected static final PointStateStack pointState = new PointStateStack();
|
||||
@@ -400,25 +422,25 @@ public class GLStateManager {
|
||||
@Getter protected static final StencilStateStack stencilState = new StencilStateStack();
|
||||
private static int stencilBitMask = 0xFFFFFFFF;
|
||||
|
||||
@Getter protected static final BooleanStateStack autoNormalState = new BooleanStateStack(GL11.GL_AUTO_NORMAL);
|
||||
@Getter protected static final BooleanStateStack map1Color4State = new BooleanStateStack(GL11.GL_MAP1_COLOR_4);
|
||||
@Getter protected static final BooleanStateStack map1IndexState = new BooleanStateStack(GL11.GL_MAP1_INDEX);
|
||||
@Getter protected static final BooleanStateStack map1NormalState = new BooleanStateStack(GL11.GL_MAP1_NORMAL);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord1State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord2State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord3State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord4State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4);
|
||||
@Getter protected static final BooleanStateStack map1Vertex3State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_3);
|
||||
@Getter protected static final BooleanStateStack map1Vertex4State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_4);
|
||||
@Getter protected static final BooleanStateStack map2Color4State = new BooleanStateStack(GL11.GL_MAP2_COLOR_4);
|
||||
@Getter protected static final BooleanStateStack map2IndexState = new BooleanStateStack(GL11.GL_MAP2_INDEX);
|
||||
@Getter protected static final BooleanStateStack map2NormalState = new BooleanStateStack(GL11.GL_MAP2_NORMAL);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord1State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord2State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord3State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord4State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4);
|
||||
@Getter protected static final BooleanStateStack map2Vertex3State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_3);
|
||||
@Getter protected static final BooleanStateStack map2Vertex4State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_4);
|
||||
@Getter protected static final BooleanStateStack autoNormalState = new BooleanStateStack(GL11.GL_AUTO_NORMAL, false, true);
|
||||
@Getter protected static final BooleanStateStack map1Color4State = new BooleanStateStack(GL11.GL_MAP1_COLOR_4, false, true);
|
||||
@Getter protected static final BooleanStateStack map1IndexState = new BooleanStateStack(GL11.GL_MAP1_INDEX, false, true);
|
||||
@Getter protected static final BooleanStateStack map1NormalState = new BooleanStateStack(GL11.GL_MAP1_NORMAL, false, true);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord1State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1, false, true);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord2State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2, false, true);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord3State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3, false, true);
|
||||
@Getter protected static final BooleanStateStack map1TextureCoord4State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4, false, true);
|
||||
@Getter protected static final BooleanStateStack map1Vertex3State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_3, false, true);
|
||||
@Getter protected static final BooleanStateStack map1Vertex4State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_4, false, true);
|
||||
@Getter protected static final BooleanStateStack map2Color4State = new BooleanStateStack(GL11.GL_MAP2_COLOR_4, false, true);
|
||||
@Getter protected static final BooleanStateStack map2IndexState = new BooleanStateStack(GL11.GL_MAP2_INDEX, false, true);
|
||||
@Getter protected static final BooleanStateStack map2NormalState = new BooleanStateStack(GL11.GL_MAP2_NORMAL, false, true);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord1State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1, false, true);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord2State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2, false, true);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord3State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3, false, true);
|
||||
@Getter protected static final BooleanStateStack map2TextureCoord4State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4, false, true);
|
||||
@Getter protected static final BooleanStateStack map2Vertex3State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_3, false, true);
|
||||
@Getter protected static final BooleanStateStack map2Vertex4State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_4, false, true);
|
||||
|
||||
// Clip plane states
|
||||
@Getter protected static final BooleanStateStack[] clipPlaneStates = new BooleanStateStack[MAX_CLIP_PLANES];
|
||||
@@ -1029,6 +1051,7 @@ public class GLStateManager {
|
||||
case GL11.GL_SHADE_MODEL -> shadeModelState.getValue();
|
||||
// GL_TEXTURE_2D makes no sense here, but some mod still queries it...
|
||||
case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
|
||||
case GL13.GL_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + getActiveTextureUnitForServerState();
|
||||
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
|
||||
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
|
||||
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
|
||||
@@ -1037,15 +1060,18 @@ public class GLStateManager {
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> lightModel.colorControl;
|
||||
|
||||
case GL14.GL_BLEND_DST_ALPHA -> blendState.getDstAlpha();
|
||||
case GL14.GL_BLEND_DST_RGB -> blendState.getDstRgb();
|
||||
case GL11.GL_BLEND_DST, GL14.GL_BLEND_DST_RGB -> blendState.getDstRgb();
|
||||
case GL14.GL_BLEND_SRC_ALPHA -> blendState.getSrcAlpha();
|
||||
case GL14.GL_BLEND_SRC_RGB -> blendState.getSrcRgb();
|
||||
case GL11.GL_BLEND_SRC, GL14.GL_BLEND_SRC_RGB -> blendState.getSrcRgb();
|
||||
case GL14.GL_BLEND_EQUATION -> blendState.getEquationRgb();
|
||||
case GL20.GL_BLEND_EQUATION_ALPHA -> blendState.getEquationAlpha();
|
||||
|
||||
case GL11.GL_LOGIC_OP_MODE -> logicOpMode.getValue();
|
||||
case GL11.GL_DRAW_BUFFER -> drawBuffer.getValue();
|
||||
|
||||
case GL11.GL_CULL_FACE_MODE -> polygonState.getCullFaceMode();
|
||||
case GL11.GL_FRONT_FACE -> polygonState.getFrontFace();
|
||||
|
||||
case GL11.GL_STENCIL_FUNC -> stencilState.getFuncFront();
|
||||
case GL11.GL_STENCIL_REF -> stencilState.getRefFront();
|
||||
case GL11.GL_STENCIL_VALUE_MASK -> stencilState.getValueMaskFront();
|
||||
@@ -1084,6 +1110,10 @@ public class GLStateManager {
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_VIEWPORT -> viewportState.get(params);
|
||||
case GL11.GL_POLYGON_MODE -> {
|
||||
params.put(0, polygonState.getFrontMode());
|
||||
params.put(1, polygonState.getBackMode());
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params.put(0, glGetInteger(pname));
|
||||
@@ -2236,82 +2266,121 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void preDraw(int drawMode) {
|
||||
prepareWideLineEmulation(drawMode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
prepareLineEmulation(drawMode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void preDraw() {
|
||||
disableWideLineEmulation();
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
disableLineEmulation();
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_BYTE, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_INT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 2);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_SHORT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 1);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, count, type, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, count, type, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
@@ -2332,14 +2401,16 @@ public class GLStateManager {
|
||||
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
public static void glDrawBuffer(int mode) {
|
||||
@@ -2432,6 +2503,19 @@ public class GLStateManager {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
|
||||
if (VAOManager.isGenericPointerEnabled(index)) return;
|
||||
VAOManager.setAttribute(index, size, type, normalized, stride, offset, boundVBO);
|
||||
VAOManager.markConventional(index);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
if (VAOManager.isGenericPointerEnabled(index)) return;
|
||||
VAOManager.setAttribute(index, size, type, normalized, stride, pointer);
|
||||
VAOManager.markConventional(index);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, IntBuffer pointer) {
|
||||
glVertexPointer(size, GL11.GL_INT, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
@@ -2441,40 +2525,40 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer);
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, boolean unsigned, int stride, ByteBuffer pointer) {
|
||||
final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE;
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, FloatBuffer pointer) {
|
||||
@@ -2494,33 +2578,33 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ShortBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, IntBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, type, false, stride, pointer);
|
||||
ffpClientArrayPointer(loc, size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, FloatBuffer pointer) {
|
||||
@@ -2542,31 +2626,31 @@ public class GLStateManager {
|
||||
public static void glTexCoordPointer(int size, int type, int stride, FloatBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ShortBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, IntBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, DoubleBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
ffpClientArrayPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, type, false, stride, pointer_buffer_offset);
|
||||
ffpClientArrayPointer(loc, size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glEnableClientState(int cap) {
|
||||
@@ -3194,6 +3278,7 @@ public class GLStateManager {
|
||||
splashComplete = true;
|
||||
drawableGLHolder = null;
|
||||
drawableGL = null;
|
||||
ImmediateModeRecorder.cleanupOrphanTessellators();
|
||||
}
|
||||
|
||||
public static void glNewList(int list, int mode) {
|
||||
@@ -3244,7 +3329,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glCallList(int list) {
|
||||
GLDebug.pushGroup("glCallList " + list);
|
||||
GLDebug.pushGroup("glCallList ", list);
|
||||
try {
|
||||
DisplayListManager.glCallList(list);
|
||||
} finally {
|
||||
@@ -4003,8 +4088,8 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.getTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> info.getWidth();
|
||||
case GL11.GL_TEXTURE_HEIGHT -> info.getHeight();
|
||||
case GL11.GL_TEXTURE_WIDTH -> Math.max(info.getWidth() >> level, 1);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> Math.max(info.getHeight() >> level, 1);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||
if (info.needsInternalFormatResolve() && isRecordingDisplayList()) {
|
||||
throw new IllegalStateException(String.format("glGetTexLevelParameteri(GL_TEXTURE_INTERNAL_FORMAT) needs to resolve a generic compressed format for texture %d during display list recording", getBoundTextureForServerState()));
|
||||
@@ -4530,15 +4615,21 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void prepareWideLineEmulation(int drawMode) {
|
||||
wideLineEmulationActive =
|
||||
(drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
|
||||
&& lineState.getWidth() > 1.0f
|
||||
&& wideLineEmulationEnabled;
|
||||
public static void prepareLineEmulation(int drawMode) {
|
||||
final boolean isLine = drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP;
|
||||
wideLineEmulationActive = isLine && lineState.getWidth() > 1.0f && wideLineEmulationEnabled;
|
||||
setLineStippleActive(isLine && lineStippleState.isEnabled());
|
||||
}
|
||||
|
||||
public static void disableWideLineEmulation() {
|
||||
public static void disableLineEmulation() {
|
||||
wideLineEmulationActive = false;
|
||||
setLineStippleActive(false);
|
||||
}
|
||||
|
||||
private static void setLineStippleActive(boolean active) {
|
||||
if (lineStippleActive == active) return;
|
||||
lineStippleActive = active;
|
||||
RENDER_BACKEND.provokingVertex(active ? GL32.GL_FIRST_VERTEX_CONVENTION : GL32.GL_LAST_VERTEX_CONVENTION);
|
||||
}
|
||||
|
||||
public static void glFlush() {
|
||||
@@ -5123,31 +5214,44 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.deleteBuffers(buffers);
|
||||
}
|
||||
|
||||
public static void glDeleteBuffers(int[] buffers) {
|
||||
for (int buffer : buffers) {
|
||||
glDeleteBuffers(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void invalidateDeletedBuffer(int buffer) {
|
||||
if (buffer == 0) return;
|
||||
if (boundVBO == buffer) boundVBO = 0;
|
||||
if (VAOManager.boundEBO == buffer) VAOManager.boundEBO = 0;
|
||||
if (boundPixelUnpackBuffer == buffer) boundPixelUnpackBuffer = 0;
|
||||
if (boundPixelPackBuffer == buffer) boundPixelPackBuffer = 0;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (VAOManager.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
if (boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
// Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id.
|
||||
for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
|
||||
if (data.ebo == buffer) data.ebo = 0;
|
||||
VAOManager.onDeleteBuffer(buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBuffer(int target, int buffer) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
boundPixelPackBuffer = buffer;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
boundPixelPackBuffer = buffer;
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
public static void forcePixelUnpackState(PixelUnpackState target) {
|
||||
@@ -5158,9 +5262,21 @@ public class GLStateManager {
|
||||
PixelUnpackState.applyDiff(applied, pixelUnpackState);
|
||||
}
|
||||
|
||||
private static boolean warnedUntrackedPixelBuffer;
|
||||
|
||||
private static void warnUntrackedPixelBuffer(int binding, String name) {
|
||||
if (warnedUntrackedPixelBuffer || initConfig == null || !initConfig.isLwjglDebug()) return;
|
||||
final int actual = RENDER_BACKEND.getInteger(binding);
|
||||
if (actual == 0) return;
|
||||
warnedUntrackedPixelBuffer = true;
|
||||
LOGGER.warn("{} is bound to {} but GLSM's cache says 0; a call site bypassed GLStateManager.glBindBuffer", name, actual, new Throwable());
|
||||
}
|
||||
|
||||
static void suspendPixelUnpackBuffer() {
|
||||
if (boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
} else {
|
||||
warnUntrackedPixelBuffer(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING, "GL_PIXEL_UNPACK_BUFFER");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5173,6 +5289,8 @@ public class GLStateManager {
|
||||
static void suspendPixelPackBuffer() {
|
||||
if (boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
} else {
|
||||
warnUntrackedPixelBuffer(GL21.GL_PIXEL_PACK_BUFFER_BINDING, "GL_PIXEL_PACK_BUFFER");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5186,34 +5304,279 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static void glGenBuffers(IntBuffer buffers) {
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.genBuffers());
|
||||
}
|
||||
|
||||
public static void glGenBuffers(int[] buffers) {
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static int glCreateBuffers() {
|
||||
return RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(IntBuffer buffers) {
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.createBuffers());
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(int[] buffers) {
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, LongBuffer data, int usage) { glBufferData(target, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, java.nio.ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, java.nio.ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, java.nio.IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, java.nio.FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, java.nio.DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferData(int target, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferData(int target, long[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferData(int target, double[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglBufferData(int target, long size, long data, int usage) {
|
||||
if (data == 0L) {
|
||||
glBufferData(target, size, usage);
|
||||
return;
|
||||
}
|
||||
glBufferData(target, MemoryUtilities.memByteBuffer(data, checkedSize(size)), usage);
|
||||
}
|
||||
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, LongBuffer data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, int[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, long[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, float[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, double[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) { return RENDER_BACKEND.unmapBuffer(target); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.IntBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.FloatBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.DoubleBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
|
||||
public static void glGetBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, LongBuffer data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glGetBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 1);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asShortBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, int[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asIntBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, long[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asLongBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, float[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asFloatBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, double[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asDoubleBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglGetBufferSubData(int target, long offset, long size, long data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static int glGetBufferParameteri(int target, int pname) { return RENDER_BACKEND.getBufferParameteri(target, pname); }
|
||||
public static void glBufferStorage(int target, java.nio.ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static int glGetBufferParameter(int target, int pname) { return glGetBufferParameteri(target, pname); }
|
||||
public static void glGetBufferParameter(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, int[] params) { params[0] = glGetBufferParameteri(target, pname); }
|
||||
|
||||
public static void glBufferStorage(int target, long size, int flags) { RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, java.nio.ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, ShortBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, IntBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, FloatBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, DoubleBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, short[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, int[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, float[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, double[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, ShortBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, IntBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, LongBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, DoubleBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, int[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, long[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, float[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, double[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ShortBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, IntBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, LongBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, FloatBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, DoubleBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, int[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, long[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, float[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, double[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static boolean glIsBuffer(int buffer) { return RENDER_BACKEND.isBuffer(buffer); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
|
||||
private static int checkedSize(long size) {
|
||||
if (size < 0 || size > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Buffer range does not fit a ByteBuffer wrapper: " + size);
|
||||
}
|
||||
return (int) size;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(short[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 1);
|
||||
bb.asShortBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(int[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
|
||||
bb.asIntBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(long[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
|
||||
bb.asLongBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(float[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
|
||||
bb.asFloatBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(double[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
|
||||
bb.asDoubleBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
public static void glBindVertexArray(int array) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordBindVAO(array);
|
||||
@@ -5223,22 +5586,32 @@ public class GLStateManager {
|
||||
if (array == 0) {
|
||||
array = defaultVAO;
|
||||
}
|
||||
if (boundVAO != array) {
|
||||
boundVAO = array;
|
||||
VAOManager.onBindVertexArrayPre(array);
|
||||
RENDER_BACKEND.bindVertexArray(array);
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (boundVAO != array) {
|
||||
boundVAO = array;
|
||||
VAOManager.onBindVertexArrayPre(array);
|
||||
RENDER_BACKEND.bindVertexArray(array);
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDeleteVertexArrays(int array) {
|
||||
VAOManager.onDeleteVertexArray(array);
|
||||
if (array == boundVAO) {
|
||||
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
|
||||
boundVAO = defaultVAO;
|
||||
VAOManager.onBindVertexArrayPre(defaultVAO);
|
||||
RENDER_BACKEND.bindVertexArray(defaultVAO);
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
VAOManager.onDeleteVertexArray(array);
|
||||
if (array == boundVAO) {
|
||||
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
|
||||
boundVAO = defaultVAO;
|
||||
VAOManager.onBindVertexArrayPre(defaultVAO);
|
||||
RENDER_BACKEND.bindVertexArray(defaultVAO);
|
||||
}
|
||||
RENDER_BACKEND.deleteVertexArrays(array);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
RENDER_BACKEND.deleteVertexArrays(array);
|
||||
}
|
||||
|
||||
public static int glGenVertexArrays() {
|
||||
@@ -5792,6 +6165,9 @@ public class GLStateManager {
|
||||
generateVertexShaderIfNeeded(program);
|
||||
}
|
||||
RENDER_BACKEND.linkProgram(program);
|
||||
if (ShaderManager.getInstance().isEnabled() && RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
LOGGER.warn("Program {} failed to link: {}", program, RENDER_BACKEND.getProgramInfoLog(program));
|
||||
}
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
|
||||
@@ -6111,7 +6487,7 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.getUniformiv(program, location, params);
|
||||
}
|
||||
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
RENDER_BACKEND.getTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,21 +91,26 @@ public final class QuadConverter {
|
||||
}
|
||||
|
||||
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
|
||||
GLStateManager.preDraw();
|
||||
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
|
||||
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
|
||||
final int quadCount = vertexCount / 4;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
|
||||
final long indexOffset = (long) (first / 4) * 6 * 4;
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
|
||||
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
|
||||
final int quadCount = vertexCount / 4;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
|
||||
final long indexOffset = (long) (first / 4) * 6 * 4;
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,32 +128,37 @@ public final class QuadConverter {
|
||||
}
|
||||
|
||||
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
|
||||
GLStateManager.preDraw();
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
|
||||
if (scratchEboId == 0) {
|
||||
scratchEboId = RENDER_BACKEND.genBuffers();
|
||||
if (scratchEboId == 0) {
|
||||
scratchEboId = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
|
||||
|
||||
if (needed > scratchEboCapacity) {
|
||||
// Power-of-2 growth -- allocate full capacity, upload actual data
|
||||
int newCap = Math.max(4096, scratchEboCapacity);
|
||||
while (newCap < needed) newCap *= 2;
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
|
||||
scratchEboCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
|
||||
|
||||
if (needed > scratchEboCapacity) {
|
||||
// Power-of-2 growth — allocate full capacity, upload actual data
|
||||
int newCap = Math.max(4096, scratchEboCapacity);
|
||||
while (newCap < needed) newCap *= 2;
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
|
||||
scratchEboCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
memFree(dst);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +146,9 @@ public class RenderSystem {
|
||||
coreProfile = true;
|
||||
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
|
||||
ShaderManager.getInstance().enable();
|
||||
} else {
|
||||
throw new IllegalStateException("Non-core GL context (profile mask 0x" + Integer.toHexString(profileMask)
|
||||
+ "); FFP emulation requires a core profile and nothing would render. Context creation should have rejected this.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +304,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bufferData(int target, FloatBuffer data, int usage) {
|
||||
RENDER_BACKEND.bufferData(target, data, usage);
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
}
|
||||
|
||||
public static int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
@@ -447,7 +450,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int createBuffers() {
|
||||
return RENDER_BACKEND.genBuffers();
|
||||
return GLStateManager.glGenBuffers();
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
@@ -455,13 +458,13 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
RENDER_BACKEND.bufferStorage(target, size, flags);
|
||||
GLStateManager.glBufferStorage(target, size, flags);
|
||||
}
|
||||
|
||||
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
|
||||
final ByteBuffer buf = BufferUtils.createByteBuffer(data.length * 4);
|
||||
buf.asIntBuffer().put(data);
|
||||
RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
GLStateManager.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
}
|
||||
|
||||
public static void deleteBuffers(int buffer) {
|
||||
@@ -549,7 +552,7 @@ public class RenderSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Inlined from StandardMacros.getGlVersion(). */
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460" */
|
||||
static String parseGlVersionString(String info) {
|
||||
final Matcher matcher = SEMVER_PATTERN.matcher(Objects.requireNonNull(info));
|
||||
if (!matcher.matches()) {
|
||||
@@ -561,4 +564,18 @@ public class RenderSystem {
|
||||
if (bugfix == null || bugfix.isEmpty()) bugfix = "0";
|
||||
return major + minor + bugfix;
|
||||
}
|
||||
|
||||
/** Parse a GL version string into packed major*10+minor (e.g. "4.3.0 - Build ..." -> 43), or -1 if unparseable. */
|
||||
public static int parseMajorMinor(String glVersion) {
|
||||
if (glVersion == null) return -1;
|
||||
final Matcher matcher = SEMVER_PATTERN.matcher(glVersion.trim());
|
||||
if (!matcher.matches()) return -1;
|
||||
return Integer.parseInt(matcher.group("major")) * 10 + Integer.parseInt(matcher.group("minor"));
|
||||
}
|
||||
|
||||
public static boolean isContextValid(int requestedMajor, int requestedMinor, int actualVersion, boolean coreBitSet) {
|
||||
if (!coreBitSet) return false;
|
||||
if (actualVersion < 0) return true;
|
||||
return actualVersion >= requestedMajor * 10 + requestedMinor;
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -327,7 +327,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) {
|
||||
throw new UnsupportedOperationException("GLRenderBackend.copyBufferSubData should not be called — LWJGL service handles this");
|
||||
GL31.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -622,6 +622,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixelBufferOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
|
||||
return GL30.glGetFramebufferAttachmentParameteri(target, attachment, pname);
|
||||
@@ -1058,6 +1063,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return GL30.glMapBufferRange(target, offset, length, access, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushMappedBufferRange(int target, long offset, long length) {
|
||||
GL30.glFlushMappedBufferRange(target, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int genVertexArrays() {
|
||||
return GL30.glGenVertexArrays();
|
||||
@@ -1249,6 +1259,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return GL45.glCreateBuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedBufferData(int buffer, long size, int usage) {
|
||||
GL45.glNamedBufferData(buffer, size, usage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedBufferData(int buffer, ByteBuffer data, int usage) {
|
||||
GL45.glNamedBufferData(buffer, data, usage);
|
||||
|
||||
@@ -151,6 +151,7 @@ public abstract class RenderBackend {
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
|
||||
public abstract int getFramebufferAttachmentParameteri(int target, int attachment, int pname);
|
||||
|
||||
public abstract int createShader(int type);
|
||||
@@ -249,6 +250,7 @@ public abstract class RenderBackend {
|
||||
public abstract int getBufferParameteri(int target, int pname);
|
||||
public abstract boolean isBuffer(int buffer);
|
||||
public abstract ByteBuffer mapBufferRange(int target, long offset, long length, int access);
|
||||
public abstract void flushMappedBufferRange(int target, long offset, long length);
|
||||
|
||||
public abstract int genVertexArrays();
|
||||
public abstract void deleteVertexArrays(int array);
|
||||
@@ -292,6 +294,7 @@ public abstract class RenderBackend {
|
||||
public abstract void namedFramebufferDrawBuffers(int framebuffer, IntBuffer bufs);
|
||||
public abstract void blitNamedFramebuffer(int readFramebuffer, int drawFramebuffer, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
||||
public abstract int createBuffers();
|
||||
public abstract void namedBufferData(int buffer, long size, int usage);
|
||||
public abstract void namedBufferData(int buffer, ByteBuffer data, int usage);
|
||||
public abstract void namedBufferData(int buffer, FloatBuffer data, int usage);
|
||||
public abstract void namedBufferSubData(int buffer, long offset, ByteBuffer data);
|
||||
|
||||
@@ -104,8 +104,8 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = RENDER_BACKEND.createBuffers();
|
||||
RENDER_BACKEND.namedBufferData(buffer, data, usage);
|
||||
final int buffer = GLStateManager.glCreateBuffers();
|
||||
GLStateManager.glNamedBufferData(buffer, data, usage);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,9 +121,9 @@ public class DSAUnsupported implements DSAAccess {
|
||||
|
||||
@Override
|
||||
public int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
final int buffer = RENDER_BACKEND.genBuffers();
|
||||
final int buffer = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
RENDER_BACKEND.bufferData(target, data, usage);
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
|
||||
return buffer;
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (11) + unit 0 (47 bits at offset 11)
|
||||
* long[0]: global bits (12) + unit 0 (47 bits at offset 12)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,13 +63,14 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 11;
|
||||
private static final int GLOBAL_BITS = 12;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -125,6 +126,10 @@ public final class FragmentKey {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
global |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
// Separate specular
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
@@ -135,7 +140,8 @@ public final class FragmentKey {
|
||||
int highestEnabled = -1;
|
||||
long unit0Bits = 0;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled();
|
||||
// Per Mesa - an enabled unit with no complete texture bound counts as disabled.
|
||||
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled() && GLStateManager.getTextures().getTextureUnitBindings(i).getBinding() != 0;
|
||||
if (texEnabled) highestEnabled = i;
|
||||
|
||||
final long unitBits = packUnit(i, texEnabled);
|
||||
@@ -232,6 +238,7 @@ public final class FragmentKey {
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
|
||||
sb.append("void main() {\n");
|
||||
emitLineStipple(sb, key);
|
||||
emitTextureSampling(sb, key);
|
||||
emitTexEnvChain(sb, key);
|
||||
emitSpecularAdd(sb, key);
|
||||
@@ -46,9 +47,19 @@ public final class FragmentShaderGenerator {
|
||||
if (key.fogMode() != FragmentKey.FOG_NONE) {
|
||||
sb.append("in float v_FogCoord;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat in vec2 v_LineStart;\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitLineStipple(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.lineStipple()) return;
|
||||
sb.append(" vec2 stippleDelta = abs(gl_FragCoord.xy - v_LineStart);\n");
|
||||
sb.append(" int stippleBit = int(floor(max(stippleDelta.x, stippleDelta.y) / float(u_LineStipple >> 16))) & 15;\n");
|
||||
sb.append(" if (((u_LineStipple >> stippleBit) & 1) == 0) discard;\n\n");
|
||||
}
|
||||
|
||||
private static void emitUniforms(StringBuilder sb, FragmentKey key) {
|
||||
// Samplers for each enabled unit
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
@@ -59,6 +70,9 @@ public final class FragmentShaderGenerator {
|
||||
if (key.alphaTestEnabled()) {
|
||||
sb.append("uniform float u_AlphaRef;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("uniform int u_LineStipple;\n");
|
||||
}
|
||||
// Per-unit texEnvColor uniforms
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i) && key.unitNeedsEnvColor(i)) {
|
||||
|
||||
@@ -29,6 +29,9 @@ public final class GeometryShaderGenerator {
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("in float v_FogCoord_gs[];\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat in vec2 v_LineStart_gs[];\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
@@ -42,6 +45,9 @@ public final class GeometryShaderGenerator {
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("out float v_FogCoord;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
|
||||
sb.append("void main() {\n");
|
||||
@@ -85,6 +91,9 @@ public final class GeometryShaderGenerator {
|
||||
if (key.fogEnabled()) {
|
||||
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
|
||||
}
|
||||
|
||||
if (key.clipPlanesEnabled()) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
||||
@@ -33,6 +33,9 @@ public class Program {
|
||||
int texGenGen = -1, clipPlaneGen = -1;
|
||||
float lightmapX = Float.NaN, lightmapY = Float.NaN, lineWidth = Float.NaN;
|
||||
int viewportWidth = -1, viewportHeight = -1;
|
||||
int stippleViewportX = Integer.MIN_VALUE, stippleViewportY = Integer.MIN_VALUE;
|
||||
int stippleViewportW = -1, stippleViewportH = -1;
|
||||
int lineStipple = -1;
|
||||
}
|
||||
final UploadState uploadState = new UploadState();
|
||||
|
||||
@@ -98,6 +101,10 @@ public class Program {
|
||||
public int locViewportSize = -1;
|
||||
public int locLineWidth = -1;
|
||||
|
||||
// Line stipple emulation
|
||||
public int locViewport = -1;
|
||||
public int locLineStipple = -1;
|
||||
|
||||
// Fragment uniforms
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
public int locAlphaRef = -1;
|
||||
@@ -174,6 +181,10 @@ public class Program {
|
||||
locViewportSize = loc("u_ViewportSize");
|
||||
locLineWidth = loc("u_LineWidth");
|
||||
|
||||
// Line stipple emulation
|
||||
locViewport = loc("u_Viewport");
|
||||
locLineStipple = loc("u_LineStipple");
|
||||
|
||||
// Fragment
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locSampler[i] = loc("u_Sampler" + i);
|
||||
|
||||
@@ -6,8 +6,10 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec3fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import lombok.Getter;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
@@ -66,11 +68,33 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
warmUp();
|
||||
enabled = true;
|
||||
|
||||
GLStateManager.LOGGER.info("FFP shader emulation enabled");
|
||||
}
|
||||
|
||||
// Force loading of classes before the SplashThread kicks off
|
||||
// Ensure it's GL free
|
||||
private static void warmUp() {
|
||||
try {
|
||||
final long[] fkScratch = new long[FragmentKey.MAX_UNITS];
|
||||
final int fkLen = FragmentKey.packFromState(fkScratch);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(fkScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
VertexShaderGenerator.generate(vk);
|
||||
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
|
||||
GeometryShaderGenerator.generate(vk);
|
||||
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
|
||||
for (Class<?> c : touched) {
|
||||
c.getName();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
GLStateManager.LOGGER.warn("FFP warmup failed; draw-path classes will resolve lazily", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LineState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix3f;
|
||||
@@ -136,6 +138,31 @@ public class Uniforms {
|
||||
if (program.locLineWidth != -1 && program.locViewportSize != -1) {
|
||||
uploadWideLineUniforms(program);
|
||||
}
|
||||
|
||||
if (program.locLineStipple != -1) {
|
||||
uploadLineStippleUniforms(program);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadLineStippleUniforms(Program program) {
|
||||
final Program.UploadState st = program.uploadState;
|
||||
final LineState line = GLStateManager.getLineState();
|
||||
|
||||
final int factor = line.getStippleFactor() < 1 ? 1 : line.getStippleFactor();
|
||||
final int packed = (line.getStipplePattern() & 0xFFFF) | (factor << 16);
|
||||
if (packed != st.lineStipple) {
|
||||
RENDER_BACKEND.uniform1i(program.locLineStipple, packed);
|
||||
st.lineStipple = packed;
|
||||
}
|
||||
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
if (vp.x != st.stippleViewportX || vp.y != st.stippleViewportY || vp.width != st.stippleViewportW || vp.height != st.stippleViewportH) {
|
||||
RENDER_BACKEND.uniform4f(program.locViewport, vp.x, vp.y, vp.width, vp.height);
|
||||
st.stippleViewportX = vp.x;
|
||||
st.stippleViewportY = vp.y;
|
||||
st.stippleViewportW = vp.width;
|
||||
st.stippleViewportH = vp.height;
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadMatrices(Program program, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
|
||||
@@ -15,6 +15,8 @@ public final class VAOManager {
|
||||
|
||||
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static VAOData current = null;
|
||||
private static int currentVertexFlags = 0;
|
||||
private static Attrib[] currentAttribs = null;
|
||||
@@ -38,8 +40,13 @@ public final class VAOManager {
|
||||
// Save old VAO data
|
||||
if (current != null) {
|
||||
current.vertexFlags = currentVertexFlags;
|
||||
current.ebo = boundEBO;
|
||||
if (current.ebo != boundEBO) {
|
||||
removeEboOwner(current.ebo, current);
|
||||
current.ebo = boundEBO;
|
||||
addEboOwner(current.ebo, current);
|
||||
}
|
||||
current.attribs = currentAttribs;
|
||||
current.clientSideEnabledCount = clientSideEnabledCount;
|
||||
}
|
||||
|
||||
VAOData data = vaoMap.get(vaoId);
|
||||
@@ -51,12 +58,50 @@ public final class VAOManager {
|
||||
currentVertexFlags = data.vertexFlags;
|
||||
currentAttribs = data.attribs;
|
||||
boundEBO = data.ebo;
|
||||
|
||||
recomputeClientSideCount();
|
||||
clientSideEnabledCount = data.clientSideEnabledCount;
|
||||
}
|
||||
|
||||
public static void onDeleteVertexArray(int vaoId) {
|
||||
vaoMap.remove(vaoId);
|
||||
final VAOData data = vaoMap.remove(vaoId);
|
||||
if (data == null) return;
|
||||
removeEboOwner(data.ebo, data);
|
||||
if (data == current) current = null;
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int buffer) {
|
||||
if (buffer == 0) return;
|
||||
VAOData owner = eboOwners.remove(buffer);
|
||||
while (owner != null) {
|
||||
final VAOData next = owner.nextEboOwner;
|
||||
owner.nextEboOwner = null;
|
||||
owner.ebo = 0;
|
||||
owner = next;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
data.nextEboOwner = eboOwners.put(ebo, data);
|
||||
}
|
||||
|
||||
private static void removeEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
VAOData node = eboOwners.get(ebo);
|
||||
if (node == data) {
|
||||
final VAOData next = data.nextEboOwner;
|
||||
if (next == null) eboOwners.remove(ebo); else eboOwners.put(ebo, next);
|
||||
data.nextEboOwner = null;
|
||||
return;
|
||||
}
|
||||
while (node != null) {
|
||||
final VAOData next = node.nextEboOwner;
|
||||
if (next == data) {
|
||||
node.nextEboOwner = data.nextEboOwner;
|
||||
data.nextEboOwner = null;
|
||||
return;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
|
||||
@@ -68,6 +113,8 @@ public final class VAOManager {
|
||||
public Attrib[] attribs;
|
||||
public int vertexFlags;
|
||||
public int ebo;
|
||||
public int clientSideEnabledCount;
|
||||
private VAOData nextEboOwner;
|
||||
|
||||
public VAOData() {
|
||||
attribs = new Attrib[MAX_ATTRIBS];
|
||||
@@ -85,6 +132,7 @@ public final class VAOManager {
|
||||
a.offset = offset;
|
||||
a.vboId = vboId;
|
||||
a.clientPointer = null;
|
||||
a.genericPointer = true;
|
||||
if (wasClient && a.enabled) {
|
||||
clientSideEnabledCount--;
|
||||
}
|
||||
@@ -101,11 +149,24 @@ public final class VAOManager {
|
||||
a.offset = 0;
|
||||
a.vboId = 0;
|
||||
a.clientPointer = pointer;
|
||||
a.genericPointer = true;
|
||||
if (!wasClient && a.enabled) {
|
||||
clientSideEnabledCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGenericPointerEnabled(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return false;
|
||||
final Attrib a = currentAttribs[index];
|
||||
return a != null && a.genericPointer && a.enabled;
|
||||
}
|
||||
|
||||
public static void markConventional(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = currentAttribs[index];
|
||||
if (a != null) a.genericPointer = false;
|
||||
}
|
||||
|
||||
public static void setEnabled(int index, boolean enabled) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
@@ -169,14 +230,6 @@ public final class VAOManager {
|
||||
|
||||
|
||||
|
||||
private static void recomputeClientSideCount() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < MAX_ATTRIBS; i++) {
|
||||
if (isClientSideAttrib(i)) count++;
|
||||
}
|
||||
clientSideEnabledCount = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any currently enabled vertex attribute was registered without a VBO
|
||||
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
|
||||
@@ -239,6 +292,7 @@ public final class VAOManager {
|
||||
public long offset;
|
||||
public int vboId;
|
||||
public ByteBuffer clientPointer;
|
||||
public boolean genericPointer;
|
||||
|
||||
public boolean isClientSide() {
|
||||
return enabled && clientPointer != null;
|
||||
@@ -253,6 +307,7 @@ public final class VAOManager {
|
||||
offset = 0;
|
||||
vboId = 0;
|
||||
clientPointer = null;
|
||||
genericPointer = false;
|
||||
}
|
||||
|
||||
public int effectiveStride() {
|
||||
|
||||
@@ -50,6 +50,7 @@ public final class VertexKey {
|
||||
// Per-unit texmat enable: bits 37-40 (unit 0..3). Unit 1 is the lightmap matrix.
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = 37;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = 41;
|
||||
private static final int BIT_LINE_STIPPLE = 42;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -98,6 +99,7 @@ public final class VertexKey {
|
||||
public boolean texGenEnabled() { return texGenModeS() != TG_NONE || texGenModeT() != TG_NONE || texGenModeR() != TG_NONE || texGenModeQ() != TG_NONE; }
|
||||
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
|
||||
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
|
||||
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
|
||||
|
||||
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
public boolean cmReplacesDiffuse() { final int m = colorMaterialMode(); return m == CM_DIFFUSE || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
@@ -227,6 +229,10 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_WIDE_LINE);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ public final class VertexShaderGenerator {
|
||||
emitColorPassthrough(sb, key);
|
||||
}
|
||||
emitTexCoordPassthrough(sb, key);
|
||||
if (key.lineStipple()) {
|
||||
emitLineStart(sb);
|
||||
}
|
||||
if (key.fogEnabled()) {
|
||||
emitFogDistance(sb, key);
|
||||
}
|
||||
@@ -45,6 +48,7 @@ public final class VertexShaderGenerator {
|
||||
.replace("v_TexCoord2", "v_TexCoord2_gs")
|
||||
.replace("v_TexCoord3", "v_TexCoord3_gs")
|
||||
.replace("v_FogCoord", "v_FogCoord_gs")
|
||||
.replace("v_LineStart", "v_LineStart_gs")
|
||||
.replace("v_Color", "v_Color_gs");
|
||||
}
|
||||
return source;
|
||||
@@ -68,6 +72,11 @@ public final class VertexShaderGenerator {
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitLineStart(StringBuilder sb) {
|
||||
sb.append(" vec2 ndc = gl_Position.xy / gl_Position.w;\n");
|
||||
sb.append(" v_LineStart = u_Viewport.xy + (ndc * 0.5 + 0.5) * u_Viewport.zw;\n\n");
|
||||
}
|
||||
|
||||
private static void emitUniforms(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Matrices\n");
|
||||
sb.append("uniform mat4 u_ModelViewMatrix;\n");
|
||||
@@ -77,6 +86,9 @@ public final class VertexShaderGenerator {
|
||||
if (key.lightingEnabled()) {
|
||||
sb.append("uniform mat3 u_NormalMatrix;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("uniform vec4 u_Viewport;\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (i == 1) continue;
|
||||
@@ -182,6 +194,9 @@ public final class VertexShaderGenerator {
|
||||
private static void emitOutputs(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Outputs\n");
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
|
||||
@@ -62,11 +62,14 @@ public final class DisplayListVBO {
|
||||
}
|
||||
|
||||
public void render() {
|
||||
vao.bind();
|
||||
vao.draw(drawMode, start, count);
|
||||
vao.unbind();
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
vao.draw(drawMode, start, count);
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
|
||||
public IVertexArrayObject getVAO() {
|
||||
return vao;
|
||||
}
|
||||
|
||||
+22
-11
@@ -10,6 +10,8 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* Records immediate mode GL calls (glBegin/glEnd/glVertex) via {@link DirectTessellator}.
|
||||
@@ -18,28 +20,37 @@ import java.util.Arrays;
|
||||
public final class ImmediateModeRecorder {
|
||||
private static final CallbackTessellator tessellator = new CallbackTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
|
||||
/** Separate tessellator for the splash thread. Nulled after splash completes. */
|
||||
private static DirectTessellator splashTessellator;
|
||||
private record ThreadTessellator(Thread thread, DirectTessellator tessellator) {}
|
||||
|
||||
private static final Queue<ThreadTessellator> threadTessellators = new ConcurrentLinkedQueue<>();
|
||||
private static final ThreadLocal<DirectTessellator> threadTessellator = ThreadLocal.withInitial(() -> {
|
||||
final DirectTessellator t = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
threadTessellators.add(new ThreadTessellator(Thread.currentThread(), t));
|
||||
cleanupOrphanTessellators();
|
||||
return t;
|
||||
});
|
||||
|
||||
private ImmediateModeRecorder() {
|
||||
|
||||
}
|
||||
|
||||
public static void initSplashTessellator() {
|
||||
splashTessellator = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
public static int getThreadTessellatorCount() {
|
||||
return threadTessellators.size();
|
||||
}
|
||||
|
||||
public static void destroySplashTessellator() {
|
||||
splashTessellator.delete();
|
||||
splashTessellator = null;
|
||||
public static synchronized void cleanupOrphanTessellators() {
|
||||
threadTessellators.removeIf(tt -> {
|
||||
if (tt.thread().isAlive()) return false;
|
||||
tt.tessellator().delete();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static DirectTessellator tessellator() {
|
||||
final DirectTessellator splash = splashTessellator;
|
||||
if (splash != null && Thread.currentThread() != GLStateManager.getMainThread()) {
|
||||
return splash;
|
||||
if (Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
return tessellator;
|
||||
}
|
||||
return tessellator;
|
||||
return threadTessellator.get();
|
||||
}
|
||||
|
||||
public static DirectTessellator getInternalTessellator() {
|
||||
|
||||
+53
-33
@@ -9,8 +9,6 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
|
||||
@@ -52,20 +50,14 @@ public final class IndexedDrawCapture {
|
||||
return freed;
|
||||
}
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture create(int mode, int indicesCount, int srcIndexType, long indicesOffset, int eboId) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (eboId == 0) {
|
||||
warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
GLStateManager.warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
return null;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
@@ -74,29 +66,53 @@ public final class IndexedDrawCapture {
|
||||
final int prevEBO = GLStateManager.getBoundEBO();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
ByteBuffer eboSrc = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
GLStateManager.warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
GLStateManager.warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
return bake(mode, indicesCount, srcIndexType, MemoryUtilities.memAddress0(eboSrc));
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture createFromClientIndices(int mode, int indicesCount, int srcIndexType, long indexSrcAddr, long srcByteLength) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (indexSrcAddr == 0L) return null;
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int idxSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
if (idxSize <= 0) return null;
|
||||
if ((long) indicesCount * idxSize > srcByteLength) {
|
||||
GLStateManager.warnOnce("client-idx-range", "[IndexedDrawCapture] index count {} ({} bytes) exceeds client buffer {} bytes - skipping", indicesCount, (long) indicesCount * idxSize, srcByteLength);
|
||||
return null;
|
||||
}
|
||||
return bake(mode, indicesCount, srcIndexType, indexSrcAddr);
|
||||
}
|
||||
|
||||
private static IndexedDrawCapture bake(int mode, int indicesCount, int srcIndexType, long indexSrcAddr) {
|
||||
AttribSnapshot snap = null;
|
||||
ByteBuffer vertexData = null;
|
||||
ByteBuffer indexData = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
final long eboSrcAddr = MemoryUtilities.memAddress0(eboSrc);
|
||||
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(eboSrcAddr, srcIndexType, indicesCount);
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(indexSrcAddr, srcIndexType, indicesCount);
|
||||
if (minMax == -1L) {
|
||||
warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
GLStateManager.warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
return null;
|
||||
}
|
||||
final int minVtx = (int) (minMax & 0xFFFFFFFFL);
|
||||
@@ -110,7 +126,7 @@ public final class IndexedDrawCapture {
|
||||
if (snap.get(i) != null) enabledCount++;
|
||||
}
|
||||
if (enabledCount == 0) {
|
||||
warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
GLStateManager.warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
return null;
|
||||
}
|
||||
final int[] locations = new int[enabledCount];
|
||||
@@ -137,6 +153,13 @@ public final class IndexedDrawCapture {
|
||||
for (int a = 0; a < enabledCount; a++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
|
||||
final int relOffset = (int) (d.offset() - d.readBufferBaseOffset());
|
||||
|
||||
final long readStart = relOffset + (long) minVtx * d.effectiveStride();
|
||||
final long readEnd = relOffset + (long) maxVtx * d.effectiveStride() + (long) d.size() * d.typeSizeBytes();
|
||||
if (readStart < 0 || readEnd > d.readBuffer().capacity()) {
|
||||
GLStateManager.warnOnce("vtx-oob", "[IndexedDrawCapture] attrib {} read extent {} exceeds snapshot {} bytes - skipping", d.location(), readEnd, d.readBuffer().capacity());
|
||||
return null;
|
||||
}
|
||||
if (a == 0) {
|
||||
sharedSrcBuffer = d.readBuffer();
|
||||
sharedSrcStride = d.effectiveStride();
|
||||
@@ -169,12 +192,12 @@ public final class IndexedDrawCapture {
|
||||
final int quadCount = indicesCount / 4;
|
||||
bakedIndexCount = quadCount * 6;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.triangulateQuads(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
QuadConverter.triangulateQuads(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
bakedDrawMode = GL11.GL_TRIANGLES;
|
||||
} else {
|
||||
bakedIndexCount = indicesCount;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.widenIndices(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
QuadConverter.widenIndices(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
bakedDrawMode = mode;
|
||||
}
|
||||
|
||||
@@ -184,14 +207,11 @@ public final class IndexedDrawCapture {
|
||||
success = true;
|
||||
return out;
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
if (snap != null) snap.free();
|
||||
if (!success) {
|
||||
if (vertexData != null) MemoryUtilities.memFree(vertexData);
|
||||
if (indexData != null) MemoryUtilities.memFree(indexData);
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ public class GLSMRedirector {
|
||||
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
|
||||
private static final String ARBDrawInstanced = "org/lwjgl/opengl/ARBDrawInstanced";
|
||||
private static final String EXTDrawInstanced = "org/lwjgl/opengl/EXTDrawInstanced";
|
||||
private static final String ARBBufferObject = "org/lwjgl/opengl/ARBBufferObject";
|
||||
private static final String ARBVertexBufferObject = "org/lwjgl/opengl/ARBVertexBufferObject";
|
||||
private static final String ARBBufferStorage = "org/lwjgl/opengl/ARBBufferStorage";
|
||||
private static final String ARBCopyBuffer = "org/lwjgl/opengl/ARBCopyBuffer";
|
||||
private static final String ARBMapBufferRange = "org/lwjgl/opengl/ARBMapBufferRange";
|
||||
|
||||
// Redirect VAO related calls from NHLib
|
||||
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
|
||||
@@ -285,11 +290,16 @@ public class GLSMRedirector {
|
||||
.add("glBindBuffer")
|
||||
.add("glDeleteBuffers")
|
||||
.add("glBufferData")
|
||||
.add("nglBufferData")
|
||||
.add("glBufferSubData")
|
||||
.add("nglBufferSubData")
|
||||
.add("glMapBuffer")
|
||||
.add("glUnmapBuffer")
|
||||
.add("glGetBufferSubData")
|
||||
.add("nglGetBufferSubData")
|
||||
.add("glGetBufferParameteri")
|
||||
.add("glGetBufferParameter")
|
||||
.add("glGetBufferParameteriv")
|
||||
.add("glIsBuffer");
|
||||
final var gl20 = RedirectMap.newMap()
|
||||
.add("glBlendEquationSeparate")
|
||||
@@ -356,10 +366,13 @@ public class GLSMRedirector {
|
||||
.add("glVertexAttribIPointer")
|
||||
.add("glGenerateMipmap")
|
||||
.add("glGetFramebufferAttachmentParameteri")
|
||||
.add("glBlitFramebuffer");
|
||||
.add("glBlitFramebuffer")
|
||||
.add("glMapBufferRange")
|
||||
.add("glFlushMappedBufferRange");
|
||||
final var gl31 = RedirectMap.newMap()
|
||||
.add("glDrawElementsInstanced")
|
||||
.add("glDrawArraysInstanced");
|
||||
.add("glDrawArraysInstanced")
|
||||
.add("glCopyBufferSubData");
|
||||
final var gl32 = RedirectMap.newMap()
|
||||
.add("glFramebufferTexture");
|
||||
final var gl33 = RedirectMap.newMap()
|
||||
@@ -383,6 +396,10 @@ public class GLSMRedirector {
|
||||
final var gl44 = RedirectMap.newMap()
|
||||
.add("glBufferStorage")
|
||||
.add("glClearTexImage");
|
||||
final var gl45 = RedirectMap.newMap()
|
||||
.add("glCreateBuffers")
|
||||
.add("glNamedBufferData")
|
||||
.add("glNamedBufferSubData");
|
||||
|
||||
// Merge all GL version maps into the flat lookup
|
||||
glMethodRedirects.putAll(gl11);
|
||||
@@ -398,6 +415,7 @@ public class GLSMRedirector {
|
||||
glMethodRedirects.putAll(gl42);
|
||||
glMethodRedirects.putAll(gl43);
|
||||
glMethodRedirects.putAll(gl44);
|
||||
glMethodRedirects.putAll(gl45);
|
||||
|
||||
// MINECRAFT
|
||||
methodRedirects.put(OpenGlHelper, RedirectMap.newMap()
|
||||
@@ -470,6 +488,31 @@ public class GLSMRedirector {
|
||||
.add("glDrawArraysInstancedEXT", "glDrawArraysInstanced")
|
||||
.add("glDrawElementsInstancedEXT", "glDrawElementsInstanced")
|
||||
);
|
||||
final var arbBufferObject = RedirectMap.newMap()
|
||||
.add("glBindBufferARB", "glBindBuffer")
|
||||
.add("glGenBuffersARB", "glGenBuffers")
|
||||
.add("glDeleteBuffersARB", "glDeleteBuffers")
|
||||
.add("glIsBufferARB", "glIsBuffer")
|
||||
.add("glBufferDataARB", "glBufferData")
|
||||
.add("glBufferSubDataARB", "glBufferSubData")
|
||||
.add("glGetBufferSubDataARB", "glGetBufferSubData")
|
||||
.add("glMapBufferARB", "glMapBuffer")
|
||||
.add("glUnmapBufferARB", "glUnmapBuffer")
|
||||
.add("glGetBufferParameterARB", "glGetBufferParameter")
|
||||
.add("glGetBufferParameteriARB", "glGetBufferParameteri")
|
||||
.add("glGetBufferParameterivARB", "glGetBufferParameteriv");
|
||||
methodRedirects.put(ARBBufferObject, arbBufferObject);
|
||||
methodRedirects.put(ARBVertexBufferObject, arbBufferObject);
|
||||
methodRedirects.put(ARBBufferStorage, RedirectMap.newMap()
|
||||
.add("glBufferStorage")
|
||||
);
|
||||
methodRedirects.put(ARBCopyBuffer, RedirectMap.newMap()
|
||||
.add("glCopyBufferSubData")
|
||||
);
|
||||
methodRedirects.put(ARBMapBufferRange, RedirectMap.newMap()
|
||||
.add("glMapBufferRange")
|
||||
.add("glFlushMappedBufferRange")
|
||||
);
|
||||
|
||||
// APPLE
|
||||
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
|
||||
|
||||
+3
-1
@@ -167,7 +167,9 @@ public class PersistentStreamingBuffer implements StreamingBuffer {
|
||||
private boolean syncOldest() {
|
||||
if (fenceQueue.isEmpty()) return false;
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
region.fence.sync();
|
||||
if (!region.fence.sync()) {
|
||||
GLStateManager.LOGGER.warn("Streaming buffer fence wait did not signal; reclaiming {} bytes anyway", region.bytes);
|
||||
}
|
||||
region.fence.delete();
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
|
||||
+30
-20
@@ -88,13 +88,18 @@ public class TessellatorStreamingDrawer {
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
final int requiredBytes = vertexCount * vertexSize;
|
||||
ensureRepackCapacity(requiredBytes);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
ensureRepackCapacity(requiredBytes);
|
||||
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
|
||||
// Shrink rawBuffer if oversized
|
||||
if (tess.rawBufferSize > 0x20000 && tess.rawBufferIndex < (tess.rawBufferSize << 3)) {
|
||||
@@ -187,24 +192,29 @@ public class TessellatorStreamingDrawer {
|
||||
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
|
||||
*/
|
||||
private static void uploadAndDraw(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
|
||||
ensureVAO(flags, format);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
ensureVAO(flags, format);
|
||||
|
||||
int firstVertex = -1;
|
||||
int firstVertex = -1;
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize);
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize);
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
|
||||
} else {
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(packed);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
|
||||
} else {
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(packed);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
@@ -12,7 +11,7 @@ import java.nio.IntBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
@GLCoreTest
|
||||
class ArrayUniformUploadGLTest {
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -613,4 +615,239 @@ class CompatShaderTransformerTest {
|
||||
assertFalse(result.matches("(?s).*\\bnew\\b(?!\\s*\\().*"), "'new' as identifier must be renamed in core profile output\n\n" + result);
|
||||
assertTrue(result.contains("angelica_renamed_new"), "'new' should be renamed to angelica_renamed_new\n\n" + result);
|
||||
}
|
||||
|
||||
private static String compact(String s) {
|
||||
return s.replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static String core(String src) {
|
||||
return compact(CompatShaderTransformer.transform(src, false));
|
||||
}
|
||||
|
||||
private static void assertLoc(String c, int loc, String decl) {
|
||||
assertTrue(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " expected at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
private static void assertNoLoc(String c, int loc, String decl) {
|
||||
assertFalse(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " must not be at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomPositionAttributePinnedToLocationZero() {
|
||||
// Betweenlands blshader fix
|
||||
String src = """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
uniform mat4 u_projMat;
|
||||
uniform vec2 u_outSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main(){
|
||||
gl_Position = u_projMat * vec4(a_position.xy, 0.0, 1.0);
|
||||
v_texCoord = a_position.xy / u_outSize;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertLoc(compact(result), 0, "vec4 a_position");
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleLocationlessInputsGetSequentialLocations() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_pos;
|
||||
attribute vec2 a_uv;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_uv;
|
||||
gl_Position = vec4(a_pos.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_pos");
|
||||
assertLoc(c, 1, "vec2 a_uv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputReservesAroundGlVertex() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = ftransform() + a_foo;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertLoc(c, 1, "vec4 a_foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputSkipsFfpSlots() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = vec4(a_foo.xy, 0.0, 1.0) + gl_Color;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_foo");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExplicitLowLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_x");
|
||||
assertLoc(c, 1, "vec4 a_y");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatrixInputSortedFirst() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_a;
|
||||
attribute mat4 a_m;
|
||||
void main() {
|
||||
gl_Position = a_m * a_a;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "mat4 a_m"); // 4-slot matrix sorted first
|
||||
assertLoc(c, 4, "vec4 a_a");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputNamedLocationStillPinned() {
|
||||
assertLoc(core("""
|
||||
#version 120
|
||||
attribute vec4 a_location;
|
||||
void main() {
|
||||
gl_Position = vec4(a_location.xy, 0.0, 1.0);
|
||||
}
|
||||
"""), 0, "vec4 a_location");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputWithExplicitLayoutLeftAlone() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 3) in vec4 a_data;
|
||||
attribute vec4 a_position;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_data.xy;
|
||||
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_position"); // sole location-less input
|
||||
assertLoc(c, 3, "vec4 a_data"); // explicit location preserved
|
||||
assertNoLoc(c, 0, "vec4 a_data");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPinnedShaderRemainsWellFormed() {
|
||||
String src = """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
void main() {
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewMatrix * vec4(a_position.xyz, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a_position");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
assertTrue(c.contains("uniformmat4angelica_ModelViewMatrix"), "matrix uniform injected\n\n" + result);
|
||||
assertFalse(c.contains("gl_ModelViewMatrix"), "builtin rewritten (not fallback)\n\n" + result);
|
||||
|
||||
final int mainIdx = result.indexOf("void main");
|
||||
assertTrue(mainIdx > 0, result);
|
||||
assertTrue(result.indexOf("angelica_ModelViewMatrix") < mainIdx, "uniform before main\n\n" + result);
|
||||
assertTrue(result.indexOf("a_position") < mainIdx, "pinned input before main\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlVertexShaderDoesNotPinCustomAttribute() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute float activelights;
|
||||
void main() {
|
||||
gl_Position = ftransform();
|
||||
float x = activelights;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertNoLoc(c, 0, "float activelights");
|
||||
}
|
||||
|
||||
@ParameterizedTest // array on the declarator (vec4 a[2]) and on the type (vec4[2] a) must both round-trip
|
||||
@ValueSource(strings = {"attribute vec4 positions[2];", "attribute vec4[2] positions;"})
|
||||
void testArrayAttributePreservedEitherSyntax(String decl) {
|
||||
String c = core("""
|
||||
#version 120
|
||||
%s
|
||||
attribute vec4 a_tail;
|
||||
void main() {
|
||||
gl_Position = positions[0] + positions[1] + a_tail;
|
||||
}
|
||||
""".formatted(decl));
|
||||
assertLoc(c, 0, "vec4 positions[2]"); // 2-slot array pinned to 0..1
|
||||
assertLoc(c, 2, "vec4 a_tail");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorSplitIntoSeparateLocations() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b;
|
||||
void main() {
|
||||
gl_Position = a + b;
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a");
|
||||
assertLoc(c, 1, "vec4 b");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorWithMixedArray() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b[2];
|
||||
void main() {
|
||||
gl_Position = a + b[0] + b[1];
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
// b takes 2 slots; sorted before the 1-slot a, so b -> 0..1, a -> 2.
|
||||
assertLoc(c, 0, "vec4 b[2]");
|
||||
assertLoc(c, 2, "vec4 a");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHexExplicitLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0x1) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertTrue(c.contains("layout(location=0x1)invec4a_x"), "hex explicit location preserved\n\n" + c);
|
||||
assertNoLoc(c, 1, "vec4 a_y"); // must not collide with the hex-reserved slot
|
||||
assertLoc(c, 0, "vec4 a_y");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -12,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class CompatUniformManagerTest {
|
||||
|
||||
private int program = 0;
|
||||
|
||||
+29
-2
@@ -3,19 +3,20 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** Tests GLSM display list behavior matches OpenGL spec. */
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class DisplayListCompilationGLSMTest {
|
||||
|
||||
private int testList = -1;
|
||||
@@ -257,4 +258,30 @@ class DisplayListCompilationGLSMTest {
|
||||
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_unsupportedOpInList_abortsCleanly() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording after glNewList");
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "recording state must be reset after an in-list throw");
|
||||
assertEquals(DisplayListManager.RecordMode.NONE, DisplayListManager.getRecordMode());
|
||||
|
||||
assertDoesNotThrow(() -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
assertDoesNotThrow(GLStateManager::glEndList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_abortIfLeaked_recoversOpenList() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording());
|
||||
|
||||
DisplayListManager.abortIfLeaked();
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list");
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -3,16 +3,17 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** Tests raw GL11 display list behavior (reference for GLSM). */
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class DisplayListCompilationRawGLTest {
|
||||
|
||||
private int testList = -1;
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -11,9 +10,11 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class FeedbackManagerTest {
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Tag(GLTestTags.COMPAT)
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
public @interface GLCompatTest {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Tag(GLTestTags.CORE)
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
public @interface GLCoreTest {
|
||||
}
|
||||
@@ -1,40 +1,73 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
|
||||
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
|
||||
|
||||
/**
|
||||
* 3.3 core-profile context extension
|
||||
*/
|
||||
public class GLSMCoreExtension implements BeforeAllCallback, AfterAllCallback {
|
||||
private boolean ownsDisplay = false;
|
||||
public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, ExtensionContext.Store.CloseableResource {
|
||||
|
||||
private static boolean started = false;
|
||||
private static Throwable startFailure;
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
if (Display.isCreated()) return;
|
||||
Display.setDisplayModeAndFullscreen(new DisplayMode(800, 600));
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
Display.create(
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(3, 3).withProfileCore(true).withForwardCompatible(true));
|
||||
ownsDisplay = true;
|
||||
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(800, 600).build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete();
|
||||
GLStateManager.BYPASS_CACHE = false;
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMCoreExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
if (started) return;
|
||||
try {
|
||||
Display.setDisplayModeAndFullscreen(new DisplayMode(800, 600));
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
Display.create(
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(3, 3).withProfileCore(true).withForwardCompatible(true));
|
||||
|
||||
GLSMExtension.setMainThread(Thread.currentThread());
|
||||
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(800, 600).build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete();
|
||||
GLStateManager.BYPASS_CACHE = false;
|
||||
|
||||
GLSMExtension.glVendor = GL11.glGetString(GL11.GL_VENDOR);
|
||||
GLSMExtension.glRenderer = GL11.glGetString(GL11.GL_RENDERER);
|
||||
GLSMExtension.glVersion = GL11.glGetString(GL11.GL_VERSION);
|
||||
System.out.println("OpenGL Vendor: " + GLSMExtension.glVendor);
|
||||
System.out.println("OpenGL Renderer: " + GLSMExtension.glRenderer);
|
||||
System.out.println("OpenGL Version: " + GLSMExtension.glVersion);
|
||||
|
||||
final int profileMask = GL11.glGetInteger(GL32.GL_CONTEXT_PROFILE_MASK);
|
||||
if ((profileMask & GL32.GL_CONTEXT_CORE_PROFILE_BIT) == 0) {
|
||||
throw new IllegalStateException("Expected a GL core context, got profile mask 0x" + Integer.toHexString(profileMask) + " (" + GLSMExtension.glVersion + ")");
|
||||
}
|
||||
context.getRoot().getStore(GLOBAL).put("GLSMCoreExtension", this);
|
||||
started = true;
|
||||
} catch (LWJGLException | RuntimeException | Error e) {
|
||||
startFailure = e;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAll(ExtensionContext context) {
|
||||
if (ownsDisplay && Display.isCreated()) Display.destroy();
|
||||
ownsDisplay = false;
|
||||
public void beforeEach(ExtensionContext context) {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (Display.isCreated()) Display.destroy();
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
||||
import org.junit.jupiter.api.extension.ExecutionCondition;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
@@ -45,6 +47,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
}
|
||||
|
||||
private static boolean started = false;
|
||||
private static Throwable startFailure;
|
||||
private static DisplayMode displayMode;
|
||||
public static String glVendor;
|
||||
public static String glRenderer;
|
||||
@@ -55,21 +58,23 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
if (!started) {
|
||||
started = true;
|
||||
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
if (started) return;
|
||||
try {
|
||||
displayMode = new DisplayMode(800, 600);
|
||||
final GLSMInitConfig config = GLSMInitConfig.builder().displaySize(displayMode.getWidth(), displayMode.getHeight()).build();
|
||||
setMainThread(Thread.currentThread());
|
||||
|
||||
latchRenderSystemOnCoreContext(config);
|
||||
|
||||
Display.setDisplayModeAndFullscreen(displayMode);
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
final PixelFormat format = new PixelFormat().withDepthBits(24).withStencilBits(8);
|
||||
Display.create(format);
|
||||
Display.create(new PixelFormat().withDepthBits(24).withStencilBits(8));
|
||||
|
||||
setMainThread(Thread.currentThread());
|
||||
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(displayMode.getWidth(), displayMode.getHeight())
|
||||
.build());
|
||||
GLStateManager.initialize(config);
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete();
|
||||
GLStateManager.BYPASS_CACHE = false;
|
||||
@@ -81,6 +86,30 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
System.out.println("OpenGL Vendor: " + glVendor);
|
||||
System.out.println("OpenGL Renderer: " + glRenderer);
|
||||
System.out.println("OpenGL Version: " + glVersion);
|
||||
|
||||
final int profileMask = GL11.glGetInteger(GL32.GL_CONTEXT_PROFILE_MASK);
|
||||
if ((profileMask & GL32.GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) == 0) {
|
||||
throw new IllegalStateException("Expected a GL compatibility context, got profile mask 0x" + Integer.toHexString(profileMask) + " (" + glVersion + ")");
|
||||
}
|
||||
started = true;
|
||||
} catch (LWJGLException | RuntimeException | Error e) {
|
||||
startFailure = e;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void latchRenderSystemOnCoreContext(GLSMInitConfig config) throws LWJGLException {
|
||||
Display.setDisplayModeAndFullscreen(displayMode);
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
Display.create(
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(3, 3).withProfileCore(true).withForwardCompatible(true));
|
||||
try {
|
||||
GLStateManager.initialize(config);
|
||||
} finally {
|
||||
Display.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +124,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMainThread(Thread thread) {
|
||||
static void setMainThread(Thread thread) {
|
||||
try {
|
||||
final Field f = GLStateManager.class.getDeclaredField("MainThread");
|
||||
final Object base = theUnsafe.staticFieldBase(f);
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
@@ -16,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
* Verifies that per-buffer blend operations (glEnablei/glDisablei) properly invalidate
|
||||
* the GLSM blend cache so subsequent global blend enable/disable calls are not skipped.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_BlendUnknownState_UnitTest {
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_CoreRemovedCaps_GLTest {
|
||||
|
||||
static Stream<Arguments> removedCaps() {
|
||||
return Stream.of(
|
||||
cap("GL_LINE_STIPPLE", GL11.GL_LINE_STIPPLE),
|
||||
cap("GL_POINT_SMOOTH", GL11.GL_POINT_SMOOTH),
|
||||
cap("GL_POLYGON_STIPPLE", GL11.GL_POLYGON_STIPPLE),
|
||||
cap("GL_INDEX_LOGIC_OP", GL11.GL_INDEX_LOGIC_OP),
|
||||
cap("GL_AUTO_NORMAL", GL11.GL_AUTO_NORMAL),
|
||||
cap("GL_MAP1_COLOR_4", GL11.GL_MAP1_COLOR_4),
|
||||
cap("GL_MAP1_INDEX", GL11.GL_MAP1_INDEX),
|
||||
cap("GL_MAP1_NORMAL", GL11.GL_MAP1_NORMAL),
|
||||
cap("GL_MAP1_TEXTURE_COORD_1", GL11.GL_MAP1_TEXTURE_COORD_1),
|
||||
cap("GL_MAP1_TEXTURE_COORD_2", GL11.GL_MAP1_TEXTURE_COORD_2),
|
||||
cap("GL_MAP1_TEXTURE_COORD_3", GL11.GL_MAP1_TEXTURE_COORD_3),
|
||||
cap("GL_MAP1_TEXTURE_COORD_4", GL11.GL_MAP1_TEXTURE_COORD_4),
|
||||
cap("GL_MAP1_VERTEX_3", GL11.GL_MAP1_VERTEX_3),
|
||||
cap("GL_MAP1_VERTEX_4", GL11.GL_MAP1_VERTEX_4),
|
||||
cap("GL_MAP2_COLOR_4", GL11.GL_MAP2_COLOR_4),
|
||||
cap("GL_MAP2_INDEX", GL11.GL_MAP2_INDEX),
|
||||
cap("GL_MAP2_NORMAL", GL11.GL_MAP2_NORMAL),
|
||||
cap("GL_MAP2_TEXTURE_COORD_1", GL11.GL_MAP2_TEXTURE_COORD_1),
|
||||
cap("GL_MAP2_TEXTURE_COORD_2", GL11.GL_MAP2_TEXTURE_COORD_2),
|
||||
cap("GL_MAP2_TEXTURE_COORD_3", GL11.GL_MAP2_TEXTURE_COORD_3),
|
||||
cap("GL_MAP2_TEXTURE_COORD_4", GL11.GL_MAP2_TEXTURE_COORD_4),
|
||||
cap("GL_MAP2_VERTEX_3", GL11.GL_MAP2_VERTEX_3),
|
||||
cap("GL_MAP2_VERTEX_4", GL11.GL_MAP2_VERTEX_4));
|
||||
}
|
||||
|
||||
private static Arguments cap(String name, int value) {
|
||||
return Arguments.of(name, value);
|
||||
}
|
||||
|
||||
private static void assertNoGlError(String what) {
|
||||
final int error = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> what + " raised 0x" + Integer.toHexString(error));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("removedCaps")
|
||||
void enableDisableIsCacheOnly(String name, int cap) {
|
||||
try {
|
||||
GLStateManager.glEnable(cap);
|
||||
assertNoGlError("glEnable(" + name + ")");
|
||||
assertTrue(GLStateManager.glIsEnabled(cap), name + " must still round-trip through the cache");
|
||||
|
||||
GLStateManager.glDisable(cap);
|
||||
assertNoGlError("glDisable(" + name + ")");
|
||||
assertFalse(GLStateManager.glIsEnabled(cap));
|
||||
} finally {
|
||||
GLStateManager.glDisable(cap);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("removedCaps")
|
||||
void pushPopAttribIsCacheOnly(String name, int cap) {
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glEnable(cap);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertNoGlError("glPushAttrib/glEnable(" + name + ")/glPopAttrib");
|
||||
assertFalse(GLStateManager.glIsEnabled(cap));
|
||||
} finally {
|
||||
GLStateManager.glDisable(cap);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void displayListReplayIsCacheOnly() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glEnable(GL11.GL_LINE_STIPPLE);
|
||||
GLStateManager.glLineStipple(2, (short) 0xF0F0);
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
GLStateManager.glEndList();
|
||||
assertNoGlError("display list compile");
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
assertNoGlError("display list replay");
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/** Deleting a bound buffer must not strand an id in LWJGL2's StateTracker */
|
||||
@GLCompatTest
|
||||
public class GLSM_DeletedBufferBinding_GLTest {
|
||||
|
||||
private static int makeBoundBuffer(int target) {
|
||||
final int buffer = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
GLStateManager.glBufferData(target, 64L, GL15.GL_STREAM_DRAW);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void assertUnboundAfterDelete(int target, int binding, int buffer) {
|
||||
GLStateManager.glDeleteBuffers(buffer);
|
||||
assertEquals(0, GL11.glGetInteger(binding), "GL unbinds a deleted buffer; the cache must not be the only place that knows");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedPixelUnpackBufferDoesNotBlockTexImage2D() {
|
||||
final int pbo = makeBoundBuffer(GL21.GL_PIXEL_UNPACK_BUFFER);
|
||||
assertUnboundAfterDelete(GL21.GL_PIXEL_UNPACK_BUFFER, GL21.GL_PIXEL_UNPACK_BUFFER_BINDING, pbo);
|
||||
|
||||
final int tex = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 1, 1, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(4));
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedPixelPackBufferDoesNotBlockReadPixels() {
|
||||
final int pbo = makeBoundBuffer(GL21.GL_PIXEL_PACK_BUFFER);
|
||||
assertUnboundAfterDelete(GL21.GL_PIXEL_PACK_BUFFER, GL21.GL_PIXEL_PACK_BUFFER_BINDING, pbo);
|
||||
|
||||
GL11.glReadPixels(0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedArrayBufferDoesNotBlockClientVertexPointer() {
|
||||
final int vbo = makeBoundBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
assertUnboundAfterDelete(GL15.GL_ARRAY_BUFFER, GL15.GL_ARRAY_BUFFER_BINDING, vbo);
|
||||
|
||||
final int prevVao = GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING);
|
||||
GL30.glBindVertexArray(0);
|
||||
try {
|
||||
GL11.glVertexPointer(3, 0, BufferUtils.createFloatBuffer(9));
|
||||
} finally {
|
||||
GL30.glBindVertexArray(prevVao);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedElementArrayBufferDoesNotBlockClientDrawElements() {
|
||||
final int ebo = makeBoundBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER);
|
||||
assertUnboundAfterDelete(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING, ebo);
|
||||
|
||||
GL11.glDrawElements(GL11.GL_TRIANGLES, BufferUtils.createIntBuffer(3));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import org.joml.Vector4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -13,7 +12,7 @@ import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
@GLCoreTest
|
||||
public class GLSM_DirtyTracking_UnitTest {
|
||||
|
||||
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
||||
+6
-4
@@ -6,13 +6,15 @@ import com.gtnewhorizons.angelica.glsm.recording.DisplayListVBO;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.createColorDraw;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.createDisplayList;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.createSimpleDraw;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.createTextureDraw;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for display list format-based batching behavior.
|
||||
@@ -24,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
* <li>Different formats produce separate VBOs</li>
|
||||
* </ul>
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_Batching_Test {
|
||||
|
||||
private static int getVBOCount(CompiledDisplayList compiledDisplayList) {
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
@@ -12,7 +11,7 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
* Verifies that display lists work correctly with different vertex attribute combinations
|
||||
* and that optimal formats are selected.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_FormatOptimization_Test {
|
||||
|
||||
private int displayList = -1;
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -15,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
* Tests for nested display list execution.
|
||||
* Verifies that child display lists compose correctly with parent transforms.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_Nesting_Test {
|
||||
|
||||
private int parentList = -1;
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -10,7 +9,7 @@ import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
@GLCoreTest
|
||||
public class GLSM_DisplayList_PixelStore_Test {
|
||||
|
||||
private static final int SRC = 8;
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -23,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
* need the full Minecraft environment. These tests focus on matrix
|
||||
* operations and display list lifecycle which work with raw GL calls.</p>
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_Stress_Test {
|
||||
|
||||
private final List<Integer> testLists = new ArrayList<>();
|
||||
|
||||
+1
-2
@@ -7,7 +7,6 @@ import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -18,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** Tests for transform collapsing optimization in display lists. */
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
|
||||
private int testList = -1;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_UnitTest {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -14,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
/**
|
||||
* Tests that GLStateManager's GLU implementations produce the same matrices as the equivalent JOML operations.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_GLU_UnitTest {
|
||||
|
||||
private static final FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
@@ -10,10 +9,10 @@ import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyLightState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyMaterialState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_Lighting_UnitTest {
|
||||
|
||||
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_LineStipple_GLTest {
|
||||
|
||||
private static final int W = 32;
|
||||
|
||||
private static final int POSITION_LOC = VertexFormatElement.Usage.POSITION.getAttributeLocation();
|
||||
|
||||
private final ByteBuffer pixels = BufferUtils.createByteBuffer(W * 4);
|
||||
private final ByteBuffer verts = ByteBuffer.allocateDirect(3 * 12).order(ByteOrder.nativeOrder());
|
||||
private int savedX, savedY, savedW, savedH;
|
||||
private int vao, vbo;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
savedX = vp.x;
|
||||
savedY = vp.y;
|
||||
savedW = vp.width;
|
||||
savedH = vp.height;
|
||||
|
||||
GLStateManager.glUseProgram(0);
|
||||
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glVertexAttribPointer(POSITION_LOC, 3, GL11.GL_FLOAT, false, 12, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(POSITION_LOC);
|
||||
|
||||
GLStateManager.glViewport(0, 0, W, 1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glOrtho(0, W, 0, 1, -1, 1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glColor4f(1f, 1f, 1f, 1f);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
GLStateManager.glLineStipple(1, (short) 0xFFFF);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glViewport(savedX, savedY, savedW, savedH);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (vao != 0) GLStateManager.glDeleteVertexArrays(vao);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private boolean[] renderLine(float x0, float x1) {
|
||||
clear();
|
||||
drawLines(x0, x1);
|
||||
return readCoverage();
|
||||
}
|
||||
|
||||
private void drawLines(float... xs) {
|
||||
verts.clear();
|
||||
for (float x : xs) verts.putFloat(x).putFloat(0.5f).putFloat(0f);
|
||||
verts.flip();
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, verts, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glDrawArrays(xs.length == 2 ? GL11.GL_LINES : GL11.GL_LINE_STRIP, 0, xs.length);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private boolean[] readCoverage() {
|
||||
pixels.clear();
|
||||
GLStateManager.glReadPixels(0, 0, W, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL error during readback");
|
||||
|
||||
final boolean[] covered = new boolean[W];
|
||||
for (int i = 0; i < W; i++) {
|
||||
covered[i] = (pixels.get(i * 4) & 0xFF) > 127;
|
||||
}
|
||||
return covered;
|
||||
}
|
||||
|
||||
private static boolean[] expected(IntPredicate on) {
|
||||
final boolean[] e = new boolean[W];
|
||||
for (int i = 0; i < W; i++) e[i] = on.test(i);
|
||||
return e;
|
||||
}
|
||||
|
||||
private static void stipple(int factor, int pattern) {
|
||||
GLStateManager.glEnable(GL11.GL_LINE_STIPPLE);
|
||||
GLStateManager.glLineStipple(factor, (short) pattern);
|
||||
}
|
||||
|
||||
@Test
|
||||
void solidPatternCoversEveryPixel() {
|
||||
stipple(1, 0xFFFF);
|
||||
assertArrayEquals(expected(i -> true), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyPatternCoversNothing() {
|
||||
stipple(1, 0x0000);
|
||||
assertArrayEquals(expected(i -> false), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void alternatingPatternDashesEveryOtherPixel() {
|
||||
stipple(1, 0xAAAA);
|
||||
assertArrayEquals(expected(i -> (i & 1) == 1), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void factorStretchesEachBitAcrossThatManyPixels() {
|
||||
stipple(2, 0xAAAA);
|
||||
assertArrayEquals(expected(i -> ((i / 2) & 1) == 1), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disabledStippleCoversEveryPixel() {
|
||||
GLStateManager.glLineStipple(1, (short) 0xAAAA);
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
assertArrayEquals(expected(i -> true), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void patternIsAnchoredAtTheSegmentStart() {
|
||||
stipple(1, 0xAAAA);
|
||||
final boolean[] covered = renderLine(8f, W);
|
||||
for (int i = 8; i < W; i++) {
|
||||
assertEquals(((i - 8) & 1) == 1, covered[i], "pixel " + i + " must be phased from x=8, not x=0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineStripRestartsThePatternPerSegment() {
|
||||
stipple(1, 0xAAAA);
|
||||
|
||||
clear();
|
||||
drawLines(0f, 9f, W);
|
||||
|
||||
final boolean[] covered = readCoverage();
|
||||
for (int i = 0; i < 9; i++) {
|
||||
assertEquals((i & 1) == 1, covered[i], "segment 1 pixel " + i);
|
||||
}
|
||||
for (int i = 10; i < W; i++) {
|
||||
assertEquals(((i - 9) & 1) == 1, covered[i], "segment 2 pixel " + i + " must restart the phase at x=9");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonStippleDrawAfterStippleIsUnaffected() {
|
||||
stipple(1, 0xAAAA);
|
||||
renderLine(0f, W);
|
||||
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
assertArrayEquals(expected(i -> true), renderLine(0f, W));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
@@ -12,7 +11,7 @@ import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_MatrixStack_UnitTest {
|
||||
|
||||
final static FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.gtnewhorizons.angelica.util.GLBit;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -21,7 +20,7 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyLightState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class GLSM_PushPop_UnitTest {
|
||||
|
||||
@BeforeEach
|
||||
@@ -1132,4 +1131,52 @@ class GLSM_PushPop_UnitTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBlendFuncGL11AliasesMatchGL14() {
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
verifyState(GL11.GL_BLEND_SRC, GL11.GL_SRC_ALPHA, "Blend Source");
|
||||
verifyState(GL14.GL_BLEND_SRC_RGB, GL11.GL_SRC_ALPHA, "Blend Source RGB");
|
||||
verifyState(GL11.GL_BLEND_DST, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination");
|
||||
verifyState(GL14.GL_BLEND_DST_RGB, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination RGB");
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBlendFuncSeparateGL11AliasesTrackRgb() {
|
||||
GLStateManager.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
verifyState(GL11.GL_BLEND_SRC, GL11.GL_SRC_ALPHA, "Blend Source");
|
||||
verifyState(GL11.GL_BLEND_DST, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination");
|
||||
verifyState(GL14.GL_BLEND_SRC_ALPHA, GL11.GL_ONE, "Blend Source Alpha");
|
||||
verifyState(GL14.GL_BLEND_DST_ALPHA, GL11.GL_ZERO, "Blend Destination Alpha");
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPolygonStateReadBack() {
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glFrontFace(GL11.GL_CW);
|
||||
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
|
||||
|
||||
verifyState(GL11.GL_CULL_FACE_MODE, GL11.GL_FRONT, "Cull Face Mode");
|
||||
verifyState(GL11.GL_FRONT_FACE, GL11.GL_CW, "Front Face");
|
||||
verifyState(GL11.GL_POLYGON_MODE, new int[] { GL11.GL_LINE, GL11.GL_LINE }, "Polygon Mode");
|
||||
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glFrontFace(GL11.GL_CCW);
|
||||
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTextureReadBack() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture - Reset");
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -15,7 +14,10 @@ import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests for server-side GL state synchronization between SharedDrawable and DrawableGL contexts.
|
||||
@@ -24,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
* be properly synchronized in the GLSM cache. Client-side state (texture bindings, active unit)
|
||||
* is per-context and should NOT be shared.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_ServerSideState_UnitTest {
|
||||
|
||||
private SharedDrawable sharedDrawable;
|
||||
|
||||
+8
-4
@@ -3,20 +3,24 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.EXTTextureCompressionS3TC;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.EXTTextureCompressionS3TC;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_TextureInfoCache_UnitTest {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.util.GLSMUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -13,7 +12,7 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public class GLSM_VAO_UnitTest {
|
||||
|
||||
@Test
|
||||
@@ -122,6 +121,68 @@ public class GLSM_VAO_UnitTest {
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeSupersedesFfpConventional() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "enabled generic attribute must supersede the FFP conventional array at the same location");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeWinsRegardlessOfOrder() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "generic attribute set after the FFP conventional array must still win");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFfpConventionalAppliesWithoutGenericAttribute() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
Assertions.assertEquals(3, VAOManager.get(0).size, "plain FFP conventional array must apply when no generic attribute occupies the location");
|
||||
Assertions.assertFalse(VAOManager.get(0).genericPointer, "location must be conventional-sourced");
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
private static void testClientStates() {
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, false, "GL_VERTEX_ARRAY Default State");
|
||||
verifyState(GL11.GL_TEXTURE_COORD_ARRAY, false, "GL_TEXTURE_COORD_ARRAY Default State");
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
public final class GLTestTags {
|
||||
public static final String COMPAT = "gl-compat";
|
||||
public static final String CORE = "gl-core";
|
||||
public static final String SHARED = "gl-shared";
|
||||
|
||||
private GLTestTags() {}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLVersionParseTest {
|
||||
|
||||
@Test
|
||||
void parseMajorMinor() {
|
||||
assertEquals(43, RenderSystem.parseMajorMinor("4.3.0 - Build 20.19.15.4835"));
|
||||
assertEquals(46, RenderSystem.parseMajorMinor("4.6.0 NVIDIA 551.61"));
|
||||
assertEquals(41, RenderSystem.parseMajorMinor("4.1 Metal - 88"));
|
||||
assertEquals(33, RenderSystem.parseMajorMinor("3.3"));
|
||||
assertEquals(46, RenderSystem.parseMajorMinor("4.6 (Core Profile) Mesa 24.0.3"));
|
||||
assertEquals(-1, RenderSystem.parseMajorMinor(null));
|
||||
assertEquals(-1, RenderSystem.parseMajorMinor(""));
|
||||
assertEquals(-1, RenderSystem.parseMajorMinor("garbage"));
|
||||
assertEquals(-1, RenderSystem.parseMajorMinor("OpenGL ES 3.2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isContextValid() {
|
||||
assertTrue(RenderSystem.isContextValid(4, 4, 46, true));
|
||||
assertTrue(RenderSystem.isContextValid(4, 4, 44, true));
|
||||
assertFalse(RenderSystem.isContextValid(4, 4, 43, true));
|
||||
assertFalse(RenderSystem.isContextValid(4, 4, 44, false));
|
||||
assertTrue(RenderSystem.isContextValid(4, 4, -1, true));
|
||||
assertFalse(RenderSystem.isContextValid(4, 4, -1, false));
|
||||
assertTrue(RenderSystem.isContextValid(3, 3, 33, true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class HugeFarPlaneFrustumTest {
|
||||
|
||||
private static final float SECTION_HALF = 8.0f + 1.0f + 0.125f;
|
||||
private static final float DEGENERATE_FAR = 2.147483648e9f;
|
||||
|
||||
private static Matrix4f combined(float zFar) {
|
||||
final Matrix4f proj = new Matrix4f().perspective((float) Math.toRadians(70.0), 854.0f / 480.0f, 0.05f, zFar);
|
||||
final Matrix4f mv = new Matrix4f()
|
||||
.rotate((float) Math.toRadians(25.0), 1, 0, 0)
|
||||
.rotate((float) Math.toRadians(123.0), 0, 1, 0)
|
||||
.translate(0.0f, -1.62f, 0.0f);
|
||||
return new Matrix4f().set(proj).mul(mv);
|
||||
}
|
||||
|
||||
private static boolean sectionVisible(FrustumIntersection f, int sx, int sy, int sz) {
|
||||
final float cx = sx * 16.0f + 8.0f;
|
||||
final float cy = sy * 16.0f + 8.0f;
|
||||
final float cz = sz * 16.0f + 8.0f;
|
||||
return f.testAab(cx - SECTION_HALF, cy - SECTION_HALF, cz - SECTION_HALF, cx + SECTION_HALF, cy + SECTION_HALF, cz + SECTION_HALF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnormalizedFrustumMatchesNormalFarPlane() {
|
||||
final FrustumIntersection normal = new FrustumIntersection(combined(512.0f), false);
|
||||
final FrustumIntersection huge = new FrustumIntersection(combined(DEGENERATE_FAR), false);
|
||||
|
||||
int visible = 0;
|
||||
int mismatches = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
final boolean vNormal = sectionVisible(normal, x, y, z);
|
||||
if (vNormal) visible++;
|
||||
if (vNormal != sectionVisible(huge, x, y, z)) mismatches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue(visible > 100, "sanity check broken: normal frustum accepted only " + visible + " sections");
|
||||
assertEquals(0, mismatches, "sections culled differently under the degenerate far plane");
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalizedSetCullsEverythingOnDegenerateFarPlane() {
|
||||
final FrustumIntersection normalized = new FrustumIntersection();
|
||||
normalized.set(combined(DEGENERATE_FAR));
|
||||
|
||||
int visible = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
if (sectionVisible(normalized, x, y, z)) visible++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(0, visible, "set(m) with a degenerate far plane no longer culls everything");
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityFrustumOnlyPassesCameraSection() {
|
||||
final FrustumIntersection identity = new FrustumIntersection(new Matrix4f());
|
||||
assertTrue(sectionVisible(identity, 0, 0, 0), "identity frustum rejected the camera's own section");
|
||||
int visible = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
if (Math.abs(x) <= 1 && Math.abs(y) <= 1 && Math.abs(z) <= 1) continue;
|
||||
if (sectionVisible(identity, x, y, z)) visible++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(0, visible, "non-adjacent sections accepted by the identity frustum");
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class ImmediateModeThreadIsolationTest {
|
||||
|
||||
@Test
|
||||
void backgroundThreadsGetDistinctTessellators() throws Exception {
|
||||
final DirectTessellator mainTess = ImmediateModeRecorder.getInternalTessellator();
|
||||
final AtomicReference<DirectTessellator> bg1 = new AtomicReference<>();
|
||||
final AtomicReference<DirectTessellator> bg2 = new AtomicReference<>();
|
||||
|
||||
final Thread t1 = new Thread(() -> bg1.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-1");
|
||||
final Thread t2 = new Thread(() -> bg2.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-2");
|
||||
t1.start(); t2.start();
|
||||
t1.join(); t2.join();
|
||||
|
||||
assertNotNull(bg1.get());
|
||||
assertNotNull(bg2.get());
|
||||
assertNotSame(mainTess, bg1.get(), "background thread must not share the main-thread tessellator");
|
||||
assertNotSame(mainTess, bg2.get(), "background thread must not share the main-thread tessellator");
|
||||
assertNotSame(bg1.get(), bg2.get(), "background threads must not share a tessellator");
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentImmediateModeDoesNotCorruptRecording() throws Exception {
|
||||
final List<Throwable> failures = new CopyOnWriteArrayList<>();
|
||||
final CountDownLatch start = new CountDownLatch(1);
|
||||
final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
|
||||
final Runnable spam = () -> {
|
||||
try {
|
||||
start.await();
|
||||
while (!stop.get()) {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
failures.add(t);
|
||||
}
|
||||
};
|
||||
final Thread t1 = new Thread(spam, "iso-spam-1");
|
||||
final Thread t2 = new Thread(spam, "iso-spam-2");
|
||||
t1.start(); t2.start();
|
||||
start.countDown();
|
||||
|
||||
final List<Integer> lists = new ArrayList<>();
|
||||
try {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
GLStateManager.glEndList();
|
||||
lists.add(list);
|
||||
}
|
||||
} finally {
|
||||
stop.set(true);
|
||||
t1.join(5000); t2.join(5000);
|
||||
}
|
||||
|
||||
assertTrue(failures.isEmpty(), "background immediate mode threw: " + failures);
|
||||
for (int list : lists) {
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(list);
|
||||
assertNotNull(compiled, "list " + list + " should be compiled");
|
||||
assertEquals(1, compiled.getCommandCounts().getOrDefault(GLCommand.DRAW_RANGE, 0), "list " + list + " should contain exactly the main thread's draw");
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void sweepFreesDeadThreadTessellators() throws Exception {
|
||||
final Thread t = new Thread(() -> ImmediateModeRecorder.getInternalTessellator(), "iso-sweep");
|
||||
t.start();
|
||||
t.join();
|
||||
final int before = ImmediateModeRecorder.getThreadTessellatorCount();
|
||||
assertTrue(before >= 1);
|
||||
ImmediateModeRecorder.cleanupOrphanTessellators();
|
||||
assertTrue(ImmediateModeRecorder.getThreadTessellatorCount() < before, "dead thread's tessellator should be swept");
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawLockGatesOnSplashComplete() throws Exception {
|
||||
assertFalse(GLStateManager.acquireDrawLock(), "post-splash acquire must be a no-op");
|
||||
|
||||
final Field f = GLStateManager.class.getDeclaredField("splashComplete");
|
||||
f.setAccessible(true);
|
||||
f.set(null, false);
|
||||
try {
|
||||
assertTrue(GLStateManager.acquireDrawLock(), "during splash the lock must engage");
|
||||
GLStateManager.releaseDrawLock();
|
||||
} finally {
|
||||
f.set(null, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -15,7 +14,7 @@ import java.nio.IntBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
@GLCoreTest
|
||||
class InstancedQuadDrawGLTest {
|
||||
|
||||
private static final String VS = "#version 330 core\nlayout(location=0) in vec2 aPos;\nvoid main(){ gl_Position = vec4(aPos, 0.0, 1.0); }\n";
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
@@ -16,6 +17,7 @@ import org.lwjgl.opengl.SharedDrawable;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@Tag(GLTestTags.SHARED)
|
||||
class SharedDrawableVaoTest {
|
||||
|
||||
private static final String VERT_330 = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n";
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
@@ -10,7 +9,7 @@ import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class VertexAttribDefaultBridgeTest {
|
||||
|
||||
private static final int COLOR_ATTRIB = 1; // VertexFormatElement.Usage.COLOR
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
@@ -17,17 +16,21 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests for FFP TexEnv COMBINE / multi-unit features:
|
||||
* TexEnvState tracking, FragmentKey packing, FragmentShaderGenerator COMBINE codegen, and glMultiTexCoord routing.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class FFPCombineShaderTest {
|
||||
|
||||
private final List<Integer> shadersToDelete = new ArrayList<>();
|
||||
private final List<Integer> programsToDelete = new ArrayList<>();
|
||||
private final List<Integer> texturesToDelete = new ArrayList<>();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
@@ -37,15 +40,25 @@ class FFPCombineShaderTest {
|
||||
programsToDelete.clear();
|
||||
shadersToDelete.clear();
|
||||
|
||||
// Reset texture units to defaults
|
||||
// Reset texture units to defaults (unbind so a later test does not see a stale binding)
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.getTextures().getTexEnvState(i).reset();
|
||||
if (i > 0) {
|
||||
GLStateManager.getTextures().getTextureUnitStates(i).disable();
|
||||
}
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (int t : texturesToDelete) GL11.glDeleteTextures(t);
|
||||
texturesToDelete.clear();
|
||||
}
|
||||
|
||||
private int bindUnitTexture() {
|
||||
final int tex = GL11.glGenTextures();
|
||||
texturesToDelete.add(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,6 +126,7 @@ class FFPCombineShaderTest {
|
||||
void testSingleUnitCombineCompiles() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
@@ -169,6 +183,7 @@ class FFPCombineShaderTest {
|
||||
void testAllCombineFunctionsCompile(String name, int glFunc, int numArgs) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, glFunc);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -202,6 +217,7 @@ class FFPCombineShaderTest {
|
||||
void testCombineScopeDeclaresColorOutsideBlock() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
@@ -258,6 +274,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 0: COMBINE / SUBTRACT
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL13.GL_SUBTRACT);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -273,6 +290,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 2: COMBINE / ADD
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_ADD);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
@@ -286,6 +304,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 3: simple MODULATE
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE3);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
|
||||
// Restore active unit
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class FFPUnboundTextureUnitTest {
|
||||
|
||||
private final List<Integer> texturesToDelete = new ArrayList<>();
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
if (i > 0) GLStateManager.getTextures().getTextureUnitStates(i).disable();
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (int t : texturesToDelete) GL11.glDeleteTextures(t);
|
||||
texturesToDelete.clear();
|
||||
}
|
||||
|
||||
private void bindUnitTexture() {
|
||||
final int tex = GL11.glGenTextures();
|
||||
texturesToDelete.add(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
void enabledUnitWithoutTextureIsExcluded() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture(); // enabled, nothing bound
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE3);
|
||||
GLStateManager.enableTexture(); // enabled, nothing bound
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
assertEquals(1, fk.nrEnabledUnits(), "units 2/3 have no texture bound -> excluded -> only unit 0");
|
||||
assertEquals(0b0001, fk.enabledUnitMask(), "only unit 0 in the key; enabled-but-unbound units 2/3 excluded");
|
||||
assertTrue(fk.unitEnabled(0), "unit 0 (texture bound) stays enabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void enabledUnitsWithTextureAreCounted() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
assertTrue(fk.unitEnabled(0));
|
||||
assertTrue(fk.unitEnabled(2), "unit 2 with a bound texture must be counted");
|
||||
assertEquals(3, fk.nrEnabledUnits(), "highest enabled is unit 2 -> nrEnabledUnits=3");
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -1,16 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCoreExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
@GLCoreTest
|
||||
class ProgramUniformTrackingTest {
|
||||
|
||||
private static Program buildProgram(boolean hasColor) {
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class VAOManagerEboIndexTest {
|
||||
|
||||
private static final int DEFAULT_VAO = 0;
|
||||
private static final int NO_VAO = -1;
|
||||
|
||||
private final Int2IntOpenHashMap expectedEbo = new Int2IntOpenHashMap();
|
||||
private int currentVao;
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
drainManagerState();
|
||||
expectedEbo.clear();
|
||||
VAOManager.init(DEFAULT_VAO);
|
||||
expectedEbo.put(DEFAULT_VAO, 0);
|
||||
currentVao = DEFAULT_VAO;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void drain() {
|
||||
drainManagerState();
|
||||
}
|
||||
|
||||
private static void drainManagerState() {
|
||||
for (int vao : VAOManager.vaoMap.keySet().toIntArray()) {
|
||||
VAOManager.onDeleteVertexArray(vao);
|
||||
}
|
||||
VAOManager.boundEBO = 0;
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "draining every VAO left entries in the index");
|
||||
}
|
||||
|
||||
private void bindVao(int vao) {
|
||||
if (currentVao != NO_VAO) expectedEbo.put(currentVao, VAOManager.boundEBO);
|
||||
VAOManager.onBindVertexArrayPre(vao);
|
||||
expectedEbo.putIfAbsent(vao, 0);
|
||||
currentVao = vao;
|
||||
}
|
||||
|
||||
private void bindEbo(int ebo) {
|
||||
VAOManager.onBindEBO(ebo);
|
||||
}
|
||||
|
||||
private int countOwners(int buffer) {
|
||||
int owners = 0;
|
||||
for (int vao : expectedEbo.keySet().toIntArray()) {
|
||||
if (expectedEbo.get(vao) == buffer) owners++;
|
||||
}
|
||||
return owners;
|
||||
}
|
||||
|
||||
private void deleteBuffer(int buffer) {
|
||||
VAOManager.onDeleteBuffer(buffer);
|
||||
for (int vao : expectedEbo.keySet().toIntArray()) {
|
||||
if (expectedEbo.get(vao) == buffer) expectedEbo.put(vao, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteVao(int vao) {
|
||||
VAOManager.onDeleteVertexArray(vao);
|
||||
expectedEbo.remove(vao);
|
||||
if (vao == currentVao) currentVao = NO_VAO;
|
||||
}
|
||||
|
||||
private void assertExpectedEbos(int op) {
|
||||
for (var entry : VAOManager.vaoMap.int2ObjectEntrySet()) {
|
||||
final int vao = entry.getIntKey();
|
||||
if (vao == currentVao) continue;
|
||||
assertEquals(expectedEbo.get(vao), entry.getValue().ebo, () -> "op " + op + ": vao " + vao + " ebo mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyVaoKeepsItsExpectedEboUnderRandomOps() {
|
||||
final Random random = new Random(0xC0FFEE);
|
||||
final IntArrayList liveVaos = new IntArrayList();
|
||||
final IntArrayList liveBuffers = new IntArrayList();
|
||||
int nextVao = 1;
|
||||
int nextBuffer = 1;
|
||||
int sharedEboDeletes = 0;
|
||||
|
||||
for (int op = 0; op < 200_000; op++) {
|
||||
switch (random.nextInt(5)) {
|
||||
case 0 -> {
|
||||
final int vao = nextVao++;
|
||||
liveVaos.add(vao);
|
||||
bindVao(vao);
|
||||
}
|
||||
case 1 -> {
|
||||
if (liveVaos.isEmpty()) break;
|
||||
bindVao(liveVaos.getInt(random.nextInt(liveVaos.size())));
|
||||
}
|
||||
case 2 -> {
|
||||
if (currentVao == NO_VAO) break;
|
||||
final int buffer = (random.nextBoolean() && nextBuffer > 8) ? 1 + random.nextInt(nextBuffer - 1) : nextBuffer++;
|
||||
liveBuffers.add(buffer);
|
||||
bindEbo(buffer);
|
||||
}
|
||||
case 3 -> {
|
||||
if (liveBuffers.isEmpty()) break;
|
||||
final int buffer = liveBuffers.removeInt(random.nextInt(liveBuffers.size()));
|
||||
if (countOwners(buffer) > 1) sharedEboDeletes++;
|
||||
deleteBuffer(buffer);
|
||||
}
|
||||
case 4 -> {
|
||||
if (liveVaos.isEmpty()) break;
|
||||
final int vao = liveVaos.removeInt(random.nextInt(liveVaos.size()));
|
||||
final boolean wasBound = vao == currentVao;
|
||||
deleteVao(vao);
|
||||
if (wasBound) bindVao(DEFAULT_VAO);
|
||||
}
|
||||
}
|
||||
assertExpectedEbos(op);
|
||||
}
|
||||
assertTrue(sharedEboDeletes > 20, "op stream never freed a multi-owner EBO (hits: " + sharedEboDeletes + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedEboClearsEveryOwner() {
|
||||
final int ebo = 77;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
bindVao(2);
|
||||
bindEbo(ebo);
|
||||
bindVao(3);
|
||||
bindEbo(ebo);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
deleteBuffer(ebo);
|
||||
|
||||
assertEquals(0, VAOManager.vaoMap.get(1).ebo, "vao 1 kept a freed shared EBO");
|
||||
assertEquals(0, VAOManager.vaoMap.get(2).ebo, "vao 2 kept a freed shared EBO");
|
||||
assertEquals(0, VAOManager.vaoMap.get(3).ebo, "vao 3 kept a freed shared EBO");
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept owners for a freed EBO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedEboEntryDropsOnlyAfterItsLastOwnerGoes() {
|
||||
final int ebo = 88;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
bindVao(2);
|
||||
bindEbo(ebo);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
deleteVao(1);
|
||||
assertTrue(VAOManager.eboOwners.containsKey(ebo), "index dropped a still-live owner");
|
||||
deleteVao(2);
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept an entry after its last owner went away");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingTheBoundVaoDoesNotOrphanAnIndexEntry() {
|
||||
final int ebo = 99;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
|
||||
deleteVao(1);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "deleting the bound VAO left an unreachable owner in the index");
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "index leaked an entry for a deleted VAO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexEmptiesOutWhenEverythingIsDeleted() {
|
||||
for (int vao = 1; vao <= 64; vao++) {
|
||||
bindVao(vao);
|
||||
bindEbo(1000 + vao);
|
||||
}
|
||||
bindVao(DEFAULT_VAO);
|
||||
for (int vao = 1; vao <= 64; vao++) {
|
||||
deleteVao(vao);
|
||||
}
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "index leaked entries after every VAO was deleted");
|
||||
}
|
||||
}
|
||||
+154
-1
@@ -9,6 +9,9 @@ import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -17,7 +20,10 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -25,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
|
||||
@@ -521,7 +528,153 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
"listBA: batch[1] must match the second-recorded layout");
|
||||
}
|
||||
|
||||
// === local helpers ===
|
||||
private static Stream<Arguments> clientIndexedDrawCases() {
|
||||
return Stream.of(
|
||||
arguments("byte indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3);
|
||||
idx.put(new byte[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("short indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ShortBuffer idx = BufferUtils.createShortBuffer(3);
|
||||
idx.put(new short[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("int indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("explicit count+type", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("quads triangulate at record", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, idx);
|
||||
}, GL11.GL_TRIANGLES, 6),
|
||||
arguments("compile-and-execute fires live and bakes", GL11.GL_COMPILE_AND_EXECUTE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("clientIndexedDrawCases")
|
||||
void clientIndexedDraw_capturedAndWidenedToUnsignedInt(String name, int listMode, Runnable draw, int expectedMode, int expectedCount) {
|
||||
final int list = recordClientIndexDraw(listMode, draw);
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(expectedMode, cmd.getDrawMode());
|
||||
assertEquals(expectedCount, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType(), "client indices always widen to UNSIGNED_INT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientVerticesAndIndices_pointersSetDuringRecording_bake() {
|
||||
final float[][] verts = {{0f, 0f, 0f}, {1f, 0f, 0f}, {1f, 1f, 0f}, {0f, 1f, 0f}};
|
||||
final ByteBuffer clientVerts = BufferUtils.createByteBuffer(verts.length * 12).order(ByteOrder.nativeOrder());
|
||||
for (float[] v : verts) {
|
||||
clientVerts.putFloat(v[0]);
|
||||
clientVerts.putFloat(v[1]);
|
||||
clientVerts.putFloat(v[2]);
|
||||
}
|
||||
clientVerts.flip();
|
||||
final IntBuffer indices = BufferUtils.createIntBuffer(6);
|
||||
indices.put(new int[]{0, 1, 2, 2, 3, 0}).flip();
|
||||
|
||||
final int vao = newVao();
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
VAOManager.setAttribute(0, 3, GL11.GL_FLOAT, false, 0, clientVerts);
|
||||
VAOManager.enableAttribute(0);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, indices);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(GL11.GL_TRIANGLES, cmd.getDrawMode());
|
||||
assertEquals(6, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType());
|
||||
|
||||
final ByteBuffer owned = readBufferBytes(GL15.GL_ARRAY_BUFFER, firstBatch(list).getSharedVBO(), verts.length * 12);
|
||||
for (int i = 0; i < verts.length; i++) {
|
||||
assertEquals(verts[i][0], owned.getFloat(i * 12), 0f, "pos.x v=" + i);
|
||||
assertEquals(verts[i][1], owned.getFloat(i * 12 + 4), 0f, "pos.y v=" + i);
|
||||
assertEquals(verts[i][2], owned.getFloat(i * 12 + 8), 0f, "pos.z v=" + i);
|
||||
}
|
||||
MemoryUtilities.memFree(owned);
|
||||
}
|
||||
|
||||
@Test
|
||||
void quadOffsetDraw_compileAndExecute_doesNotPauseRecordingForRestOfList() {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int quadEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int triEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, quadEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording must resume after a GL_QUADS offset draw");
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, triEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertEquals(2, allBatchedCmds(list).size(), "both the QUADS draw and the following triangle draw must be recorded");
|
||||
}
|
||||
|
||||
private enum MalformedClientDraw { OVERSIZED_COUNT, INDEX_PAST_VERTEX_BUFFER }
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@EnumSource(MalformedClientDraw.class)
|
||||
void malformedClientDraw_skippedAndRecordingSurvives(MalformedClientDraw scenario) {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
switch (scenario) {
|
||||
case OVERSIZED_COUNT -> {
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(defaultQuadVbo()));
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 100, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}
|
||||
case INDEX_PAST_VERTEX_BUFFER -> {
|
||||
final int vbo = uploadPosColorVbo(new float[]{0f, 0f, 1f, 0f}, new byte[][]{{1, 1, 1, 1}, {2, 2, 2, 2}});
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(vbo));
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}
|
||||
}
|
||||
assertTrue(DisplayListManager.isRecording(), "recording survives a skipped malformed draw");
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertNull(firstBatchedCmd(list), "malformed draw must be skipped, not captured or over-read");
|
||||
}
|
||||
|
||||
private int recordClientIndexDraw(int listMode, Runnable draw) {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, listMode);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
draw.run();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void assertUintIndices(ByteBuffer ib, int... expected) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
|
||||
+8
-10
@@ -2,26 +2,24 @@ package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit tests for display list recording data structures.
|
||||
*/
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class RecordingDataStructuresTest {
|
||||
|
||||
private int testList = -1;
|
||||
|
||||
+2
-3
@@ -2,13 +2,12 @@ package com.gtnewhorizons.angelica.glsm.recording.support;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.BatchedIndexedDrawCmd;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
@@ -20,7 +19,7 @@ import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
public abstract class DisplayListTestFixture {
|
||||
|
||||
public static final float[] DEFAULT_QUAD_POSITIONS = {0f, 0f, 1f, 0f, 1f, 1f, 0f, 1f};
|
||||
|
||||
+6
-6
@@ -1,13 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader.UploadStrategy;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader.UploadStrategy;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
@@ -15,9 +14,10 @@ import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
@GLCompatTest
|
||||
class StreamingBufferTest {
|
||||
|
||||
@ParameterizedTest
|
||||
|
||||
@@ -9,11 +9,13 @@ import org.lwjgl.opengl.GL13;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
public class GLSMUtil {
|
||||
@@ -39,7 +41,10 @@ public class GLSMUtil {
|
||||
GL11.GL_MAP2_VERTEX_3, GL11.GL_MAP2_VERTEX_4
|
||||
};
|
||||
|
||||
private static final Set<Integer> FFP_ONLY_CAPS = Set.of(
|
||||
private static final Set<Integer> FFP_ONLY_CAPS = ffpOnlyCaps();
|
||||
|
||||
private static Set<Integer> ffpOnlyCaps() {
|
||||
final Set<Integer> caps = new HashSet<>(Set.of(
|
||||
// Enable/disable caps (BooleanState ffpStateOnly=true)
|
||||
GL11.GL_FOG, GL11.GL_ALPHA_TEST, GL11.GL_LIGHTING,
|
||||
GL12.GL_RESCALE_NORMAL, GL11.GL_NORMALIZE, GL11.GL_COLOR_MATERIAL,
|
||||
@@ -68,8 +73,15 @@ public class GLSMUtil {
|
||||
// Fog params
|
||||
GL11.GL_FOG_COLOR, GL11.GL_FOG_DENSITY,
|
||||
GL11.GL_FOG_START, GL11.GL_FOG_END,
|
||||
GL11.GL_FOG_MODE, GL11.GL_FOG_INDEX
|
||||
);
|
||||
GL11.GL_FOG_MODE, GL11.GL_FOG_INDEX,
|
||||
// Also removed from core profile
|
||||
GL11.GL_POINT_SMOOTH, GL11.GL_POLYGON_STIPPLE,
|
||||
GL11.GL_INDEX_LOGIC_OP, GL11.GL_AUTO_NORMAL));
|
||||
|
||||
for (int cap : MAP1_STATES) caps.add(cap);
|
||||
for (int cap : MAP2_STATES) caps.add(cap);
|
||||
return caps;
|
||||
}
|
||||
|
||||
private static boolean isFFPOnly(int glCap) {
|
||||
return FFP_ONLY_CAPS.contains(glCap);
|
||||
@@ -81,7 +93,10 @@ public class GLSMUtil {
|
||||
|
||||
public static void verifyIsEnabled(int glCap, boolean expected, String message) {
|
||||
if (isFFPOnly(glCap)) {
|
||||
assertEquals(expected, GLStateManager.glIsEnabled(glCap), message + " - GLSM State Mismatch");
|
||||
assertAll(message,
|
||||
() -> assertFalse(GL11.glIsEnabled(glCap), "GL State Mismatch - cache-only cap reached the driver"),
|
||||
() -> assertEquals(expected, GLStateManager.glIsEnabled(glCap), "GLSM State Mismatch")
|
||||
);
|
||||
} else {
|
||||
assertAll(message,
|
||||
() -> assertEquals(expected, GL11.glIsEnabled(glCap), "GL State Mismatch"),
|
||||
@@ -96,7 +111,10 @@ public class GLSMUtil {
|
||||
|
||||
public static void verifyState(int glCap, boolean expected, String message) {
|
||||
if (isFFPOnly(glCap)) {
|
||||
assertEquals(expected, GLStateManager.glGetBoolean(glCap), message + " - GLSM State Mismatch");
|
||||
assertAll(message,
|
||||
() -> assertFalse(GL11.glGetBoolean(glCap), "GL State Mismatch - cache-only cap reached the driver"),
|
||||
() -> assertEquals(expected, GLStateManager.glGetBoolean(glCap), "GLSM State Mismatch")
|
||||
);
|
||||
} else {
|
||||
assertAll(message,
|
||||
() -> assertEquals(expected, GL11.glGetBoolean(glCap), "GL State Mismatch"),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e1118b4c7c2858d28154d086dbff010e/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/97d46b5ff4df0bf61e95652a190a5567/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/9ef54a08e06b0754cee9fc95ed3a3134/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/97d46b5ff4df0bf61e95652a190a5567/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/9ef54a08e06b0754cee9fc95ed3a3134/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1df82b76031285ef2e6a4f59ffe9299d/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/c7d5be6a02945ac2e045048ee54a7d32/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/97d46b5ff4df0bf61e95652a190a5567/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/9ef54a08e06b0754cee9fc95ed3a3134/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/643f404f769811f5f265fd66f7af5d89/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/38256085a73e137cb4937ea9c4807fec/redirect
|
||||
toolchainVersion=25
|
||||
@@ -60,8 +60,8 @@ repositories {
|
||||
dependencies {
|
||||
api(project(":glsm"))
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:lwjgl3ify:3.0.15:dev") { isTransitive = false }
|
||||
compileOnly("org.embeddedt.celeritas:celeritas-common:2.5.3-GTNH") { isTransitive = false }
|
||||
compileOnly("com.github.GTNewHorizons:lwjgl3ify:3.0.31:dev") { isTransitive = false }
|
||||
compileOnly("org.embeddedt.celeritas:celeritas-common:2.5.8-GTNH") { isTransitive = false }
|
||||
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val osArch = System.getProperty("os.arch").lowercase()
|
||||
|
||||
+15
@@ -659,6 +659,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL11C.glGetTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
|
||||
GL11C.glGetTexImage(target, level, format, type, pixelBufferOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
|
||||
return GL30C.glGetFramebufferAttachmentParameteri(target, attachment, pname);
|
||||
@@ -1096,6 +1101,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return GL30C.glMapBufferRange(target, offset, length, access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushMappedBufferRange(int target, long offset, long length) {
|
||||
GL30C.glFlushMappedBufferRange(target, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int genVertexArrays() {
|
||||
return GL30C.glGenVertexArrays();
|
||||
@@ -1281,6 +1291,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return GL45C.glCreateBuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedBufferData(int buffer, long size, int usage) {
|
||||
GL45C.glNamedBufferData(buffer, size, usage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void namedBufferData(int buffer, ByteBuffer data, int usage) {
|
||||
GL45C.glNamedBufferData(buffer, data, usage);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.26")
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.27")
|
||||
}
|
||||
|
||||
include("glsm")
|
||||
|
||||
@@ -16,7 +16,7 @@ import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
name = "Angelica",
|
||||
version = Tags.VERSION,
|
||||
dependencies = """
|
||||
required-after:gtnhlib@[0.8.21,);\
|
||||
required-after:gtnhlib@[0.11.19,);\
|
||||
before:lwjgl3ify@[1.5.3,);\
|
||||
after:hodgepodge@[2.4.4,);\
|
||||
after:CodeChickenCore@[1.2.0,);\
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
public interface ExtCeleritasRenderBlocks {
|
||||
|
||||
boolean angelica$shouldApplyCeleritasAO();
|
||||
|
||||
void angelica$setApplyingCeleritasAO(boolean val);
|
||||
|
||||
}
|
||||
@@ -327,6 +327,15 @@ public class BatchingFontRenderer {
|
||||
private int fontAAStrengthLast = -1;
|
||||
|
||||
private void flushBatch() {
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
flushBatchInner();
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBatchInner() {
|
||||
if (vertexDataPos == 0) {
|
||||
clearBatch();
|
||||
return;
|
||||
|
||||
@@ -2,17 +2,25 @@ package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManagerReloadListener;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class FontProviderUnicode implements FontProvider {
|
||||
public final class FontProviderUnicode implements FontProvider, IResourceManagerReloadListener {
|
||||
|
||||
private FontProviderUnicode() {
|
||||
onResourceManagerReload(Minecraft.getMinecraft().getResourceManager());
|
||||
((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager) {
|
||||
try {
|
||||
InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
|
||||
.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
|
||||
InputStream inputstream = resourceManager.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
inputstream.read(this.glyphWidth);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,13 @@ import net.minecraft.client.resources.DefaultResourcePack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -27,13 +31,13 @@ public class FontStrategist {
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
static {
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Custom fonts will be unavailable. This is likely a MacOS issue.");
|
||||
availableFonts = new Font[0];
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Only bundled fonts will be available. This is likely a MacOS issue.");
|
||||
} else {
|
||||
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
|
||||
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
|
||||
|
||||
for (Font font : availableFontsDirty) {
|
||||
@@ -57,11 +61,14 @@ public class FontStrategist {
|
||||
sb.append(". Some fonts may be missing from the font selection menu.");
|
||||
LOGGER.warn(sb.toString());
|
||||
}
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, availableFonts.length);
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size());
|
||||
}
|
||||
|
||||
loadBundledFonts(fontSet);
|
||||
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
// create and add the resource pack that provides fonts
|
||||
HashMap<String, File> packMap = new HashMap<>();
|
||||
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
|
||||
@@ -74,6 +81,44 @@ public class FontStrategist {
|
||||
defaultResourcePacks.add(fontResourcePack);
|
||||
}
|
||||
|
||||
// Load .ttf/.otf shipped with the pack so customFontName* can point at a font that isn't installed
|
||||
// on the system. fontfiles/ is SmoothFont's folder, kept so packs built for it work unchanged.
|
||||
private static void loadBundledFonts(HashMap<String, Font> fontSet) {
|
||||
File configDir = Loader.instance().getConfigDir();
|
||||
if (configDir == null) {
|
||||
LOGGER.warn("Loader.instance().getConfigDir() returned null. Bundled fonts will not be loaded.");
|
||||
return;
|
||||
}
|
||||
File parent = configDir.getParentFile();
|
||||
File[] fontDirs = { new File(parent, "fontfiles"), new File(configDir, "angelica/fonts") };
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.isHeadless() ? null : GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
int loaded = 0;
|
||||
for (File dir : fontDirs) {
|
||||
File[] files = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".ttf") || name.toLowerCase().endsWith(".otf"));
|
||||
if (files == null) continue;
|
||||
for (File file : files) {
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, file);
|
||||
String name = font.getFontName();
|
||||
if (fontSet.containsKey(name)) {
|
||||
String deduped = name;
|
||||
int n = 1;
|
||||
while (fontSet.containsKey(deduped)) deduped = name + "_" + n++;
|
||||
LOGGER.warn("Font name {} (from {}) is already registered; registering this one as {}", name, file.getName(), deduped);
|
||||
name = deduped;
|
||||
}
|
||||
if (ge != null) ge.registerFont(font);
|
||||
fontSet.put(name, font);
|
||||
loaded++;
|
||||
LOGGER.info("Loaded font {} from {}/{}", name, dir.getName(), file.getName());
|
||||
} catch (FontFormatException | IOException e) {
|
||||
LOGGER.error("Couldn't load font {}", file.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loaded > 0) LOGGER.info("Loaded {} bundled font(s) from the pack", loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether
|
||||
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.falsepattern.endlessids.config.GeneralConfig;
|
||||
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
|
||||
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CubeStatusEvents;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
|
||||
import mods.battlegear2.Battlegear;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class ModStatus {
|
||||
public static final Logger LOGGER = LogManager.getLogger("ModCompat");
|
||||
@@ -29,6 +30,7 @@ public class ModStatus {
|
||||
public static boolean isBaublesLoaded;
|
||||
public static boolean isCosmeticArmorReworkedLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isCubicChunksLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
public static boolean isEtFuturumLoaded;
|
||||
|
||||
@@ -44,23 +46,27 @@ public class ModStatus {
|
||||
isChunkAPILoaded = Loader.isModLoaded("chunkapi");
|
||||
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
|
||||
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
|
||||
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
isCubicChunksLoaded = Loader.isModLoaded("cubicchunks");
|
||||
isEtFuturumLoaded = Loader.isModLoaded("etfuturum");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBOPLoaded = Loader.isModLoaded("BiomesOPlenty");
|
||||
|
||||
// remove compat with original release of BG2
|
||||
if (isBattlegearLoaded){
|
||||
isBattlegearLoaded = new DefaultArtifactVersion("1.2.0")
|
||||
.compareTo(
|
||||
LoadControllerHelper.getOwningMod(Battlegear.class).getProcessedVersion()
|
||||
) <= 0;
|
||||
// Angelica's compat relies on GTNH extensions, so this should only be true for NH Battlegear2, 1.2.0+
|
||||
if (Loader.isModLoaded("battlegear2")) {
|
||||
// Don't ask me why battlegear2 reports as 1.7.10, I don't know
|
||||
final var OG_BG2_VER = new DefaultArtifactVersion("1.7.10");
|
||||
final var NH_BG2_VER = new DefaultArtifactVersion("1.2.0");
|
||||
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
var battlegearVersion = LoadControllerHelper.getOwningMod(Battlegear.class).getProcessedVersion();
|
||||
// We can be sure the NH version will never be 1.7.10, because that garbage got archived.
|
||||
isBattlegearLoaded = battlegearVersion.compareTo(OG_BG2_VER) != 0 && battlegearVersion.compareTo(NH_BG2_VER) >= 0;
|
||||
}
|
||||
|
||||
isNEIDMetadataExtended = false;
|
||||
@@ -71,6 +77,10 @@ public class ModStatus {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCubicChunksLoaded) {
|
||||
CubeStatusEvents.init();
|
||||
}
|
||||
|
||||
boolean eidBlockItemExtended = false;
|
||||
boolean eidBiomeExtended = false;
|
||||
if (isEIDLoaded) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.compat.cubicchunks;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.IColumn;
|
||||
import com.cardinalstar.cubicchunks.api.ICube;
|
||||
import com.cardinalstar.cubicchunks.world.ICubicWorld;
|
||||
import com.cardinalstar.cubicchunks.world.cube.Cube;
|
||||
import com.cardinalstar.cubicchunks.world.cube.ICubeProviderInternal;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
|
||||
public class CubicChunksAPI {
|
||||
|
||||
public static ExtendedBlockStorage getCubeStorage(World world, int x, int y, int z) {
|
||||
Cube cube = ((ICubeProviderInternal) world.getChunkProvider()).getLoadedCube(x, y, z);
|
||||
|
||||
if (cube == null || cube.isEmpty()) return null;
|
||||
|
||||
return cube.getStorage();
|
||||
}
|
||||
|
||||
public static int[] getLoadedCubeLevelsInColumn(World world, int chunkX, int chunkZ) {
|
||||
IColumn column = (IColumn) world.getChunkFromChunkCoords(chunkX, chunkZ);
|
||||
|
||||
return column.getLoadedCubes().stream().mapToInt(ICube::getY).toArray();
|
||||
}
|
||||
|
||||
public static int getMinHeight(World world) {
|
||||
return ((ICubicWorld) world).getMinHeight();
|
||||
}
|
||||
|
||||
public static int getMaxHeight(World world) {
|
||||
return ((ICubicWorld) world).getMaxHeight();
|
||||
}
|
||||
|
||||
public static int getMinSectionY(World world) {
|
||||
return ChunkSectionPos.getSectionCoord(getMinHeight(world));
|
||||
}
|
||||
|
||||
public static int getMaxSectionYExclusive(World world) {
|
||||
return ChunkSectionPos.getSectionCoord(getMaxHeight(world) - 1) + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.compat.etfuturum;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import ganymedes01.etfuturum.api.client.EndFlashAPI;
|
||||
|
||||
/**
|
||||
* Use EFR to power End flash uniforms.
|
||||
*/
|
||||
public final class EndFlashCompat {
|
||||
|
||||
private static final boolean AVAILABLE = Loader.isModLoaded("etfuturum") && EndFlashCompat.class.getResource("/ganymedes01/etfuturum/api/client/EndFlashAPI.class") != null;
|
||||
|
||||
private EndFlashCompat() {}
|
||||
|
||||
public static boolean isAvailable() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
public static float getIntensity(float partialTicks) {
|
||||
return AVAILABLE ? EndFlashAPI.getIntensity(partialTicks) : 0.0F;
|
||||
}
|
||||
|
||||
public static float getXAngle() {
|
||||
return AVAILABLE ? EndFlashAPI.getXAngle() : 0.0F;
|
||||
}
|
||||
|
||||
public static float getYAngle() {
|
||||
return AVAILABLE ? EndFlashAPI.getYAngle() : 0.0F;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.compat.lotr;
|
||||
|
||||
import net.minecraft.world.WorldProvider;
|
||||
|
||||
public final class LOTRCompat {
|
||||
|
||||
private static final Class<?> LOTR_WORLD_PROVIDER_CLASS;
|
||||
|
||||
static {
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = Class.forName("lotr.common.world.LOTRWorldProvider", false, LOTRCompat.class.getClassLoader());
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
LOTR_WORLD_PROVIDER_CLASS = clazz;
|
||||
}
|
||||
|
||||
private LOTRCompat() {}
|
||||
|
||||
public static boolean isLotrProvider(WorldProvider provider) {
|
||||
return LOTR_WORLD_PROVIDER_CLASS != null && LOTR_WORLD_PROVIDER_CLASS.isInstance(provider);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
|
||||
// See if we can merge/mixin/extend ChunkPosition maybe?
|
||||
public class ChunkSectionPos extends Vector3i {
|
||||
|
||||
@@ -25,11 +27,8 @@ public class ChunkSectionPos extends Vector3i {
|
||||
}
|
||||
|
||||
public static long asLong(int x, int y, int z) {
|
||||
long l = 0L;
|
||||
l |= ((long)x & 4194303L) << 42;
|
||||
l |= ((long)y & 1048575L) << 0;
|
||||
l |= ((long)z & 4194303L) << 20;
|
||||
return l;
|
||||
// Use CoordinatePacker so that cubic chunks can change the bit packing layout
|
||||
return CoordinatePacker.pack(x, y, z);
|
||||
}
|
||||
|
||||
public static int getLocalCoord(int coord) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import net.minecraft.util.MathHelper;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
|
||||
public class CompatMathHelper {
|
||||
public static int smallestEncompassingPowerOfTwo(int value) {
|
||||
int j = value - 1;
|
||||
@@ -24,4 +28,13 @@ public class CompatMathHelper {
|
||||
return k == 0 ? value : value + divisor - k;
|
||||
}
|
||||
}
|
||||
|
||||
public static int clampHeight(int y) {
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
// no clamping
|
||||
return y;
|
||||
} else {
|
||||
return MathHelper.clamp_int(y, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,8 +620,7 @@ public class DynamicLights {
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
}
|
||||
}
|
||||
else if (ModStatus.isBackhandLoaded){
|
||||
} else if (ModStatus.isBackhandLoaded){
|
||||
final ItemStack offhand = BackhandReflectionCompat.getOffhandItem(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
|
||||
public class RenderChunkEvent implements RecordEvent {
|
||||
public static final EventBus<RenderChunkEvent> BUS = EventBus.create(RenderChunkEvent.class);
|
||||
|
||||
private static final RenderChunkEvent INSTANCE = new RenderChunkEvent();
|
||||
|
||||
public static void post() {
|
||||
if (!BUS.hasListeners()) return;
|
||||
BUS.post(INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,10 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/com/gtnewhorizon/gtnhlib/client/renderer/VertexCallbackManager.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/it/unimi/dsi/fastutil/objects/ObjectBooleanBiConsumer.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.11.19 or newer (fastutil 8.5.18+)! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
} catch (RuntimeException ex) {
|
||||
fatalDependencyError(ex.getMessage());
|
||||
|
||||
@@ -83,6 +83,13 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins("angelica.vbo.MixinRenderGlobal")
|
||||
),
|
||||
|
||||
ANGELICA_VBO_CLOUDS_FAR_PLANE(
|
||||
new MixinBuilder()
|
||||
.setApplyIf(() -> AngelicaConfig.enableVBOClouds && !AngelicaConfig.enableNotFineFeatures)
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins("notfine.clouds.MixinEntityRenderer")
|
||||
),
|
||||
|
||||
ANGELICA_PANORAMA_BLUR(
|
||||
new MixinBuilder("Replace main menu panorama with modern equivalent")
|
||||
.setPhase(Phase.EARLY)
|
||||
@@ -150,6 +157,7 @@ public enum Mixins implements IMixins {
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
"angelica.itemrenderer.MixinItemRenderer",
|
||||
"angelica.itemrenderer.MixinRenderItemFrame",
|
||||
"angelica.itemrenderer.MixinRenderBlocks"
|
||||
)
|
||||
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)),
|
||||
@@ -204,6 +212,8 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins(
|
||||
"rendering.MixinBlock"
|
||||
, "rendering.MixinBlockFluidBase"
|
||||
, "rendering.MixinBlockLiquid"
|
||||
, "rendering.BlockLiquidFlowInvoker"
|
||||
, "rendering.AccessorBiomeColorEvent"
|
||||
, "rendering.MixinBiomeGenBase"
|
||||
, "rendering.MixinChunk"
|
||||
@@ -231,8 +241,7 @@ public enum Mixins implements IMixins {
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableCeleritas)
|
||||
.addClientMixins(
|
||||
"celeritas.terrain.ChunkTrackerAccessor"
|
||||
, "celeritas.terrain.MixinChunkProviderClient"
|
||||
"celeritas.terrain.MixinChunkProviderClient"
|
||||
, "celeritas.terrain.MixinMinecraft_ChunkUpdates"
|
||||
, "celeritas.terrain.MixinRenderGlobal"
|
||||
, "celeritas.terrain.MixinRenderSectionManager"
|
||||
@@ -242,7 +251,8 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.frustum.MixinFrustrum"
|
||||
, "celeritas.features.culling.MixinEffectRenderer"
|
||||
, "celeritas.features.mipmaps.MixinTextureAtlasSprite"
|
||||
, "celeritas.features.mipmaps.MixinTextureUtil"
|
||||
, "celeritas.features.mipmaps.MixinTextureMetadataSection"
|
||||
, "celeritas.features.mipmaps.MixinTextureMetadataSectionSerializer"
|
||||
, "celeritas.features.textures.MixinTextureMap"
|
||||
, "celeritas.features.textures.MixinTextureAtlasSprite"
|
||||
, "celeritas.biome_blending.MixinBlockGrass"
|
||||
@@ -283,8 +293,12 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinLocale"
|
||||
, "shaders.MixinRender"
|
||||
, "shaders.MixinRenderBiped"
|
||||
, "shaders.MixinRenderBlocks_FaceNormals"
|
||||
, "shaders.MixinRenderBlocks_FallingShading"
|
||||
, "shaders.MixinRenderBlocks_ItemId"
|
||||
, "shaders.MixinRenderDragon"
|
||||
, "shaders.MixinRenderEntityFlame"
|
||||
, "shaders.MixinRenderFallingBlock"
|
||||
, "shaders.MixinRendererLivingEntity"
|
||||
, "shaders.MixinRenderGlobal"
|
||||
, "shaders.AccessorEntityHorse"
|
||||
@@ -299,6 +313,8 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
, "shaders.MixinTextureManager_ReloadCount"
|
||||
, "shaders.AccessorModelBox"
|
||||
, "shaders.MixinModelBiped"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -407,6 +423,12 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.removeUnicodeEvenScaling)
|
||||
.addClientMixins("angelica.bugfixes.MixinScaledResolution_UnicodeFix")),
|
||||
|
||||
FARSEEK_WORLDSLICE_COMPAT(new MixinBuilder("Let Farseek resolve celeritas' WorldSlice so Streams' water renders properly")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.FARSEEK)
|
||||
.setApplyIf(() -> AngelicaConfig.enableCeleritas)
|
||||
.addClientMixins("client.farseek.MixinFarseekIBlockAccessValue")),
|
||||
|
||||
SECURITYCRAFT_COMPAT(new MixinBuilder("Fix reflection in SecurityCraft for compat with Angelica")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.SECURITYCRAFT)
|
||||
@@ -473,7 +495,7 @@ public enum Mixins implements IMixins {
|
||||
"clouds.MixinEntityRenderer",
|
||||
"clouds.MixinGameSettings",
|
||||
//"clouds.MixinRenderGlobal",
|
||||
"clouds.MixinWorldType",
|
||||
"clouds.MixinWorldProvider",
|
||||
|
||||
"fix.MixinRenderItem",
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
DRAGON_API("Reika.DragonAPI.Auxiliary.DragonAPIASMHandler", "DragonAPI"),
|
||||
ET_FUTURUM_REQUIEM(null, "etfuturum"),
|
||||
EXTRAUTILS(null, "ExtraUtilities"),
|
||||
FARSEEK("farseek.core.FarseekCoreMod", "farseek"),
|
||||
FALSETWEAKS("com.falsepattern.falsetweaks.asm.CoreLoadingPlugin", "falsetweaks"),
|
||||
MINEFACTORY_RELOADED(null, "MineFactoryReloaded"),
|
||||
NTM_SPACE("com.hbm.dim.SolarSystem"),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
public interface ChunkTrackerAccessor {
|
||||
Long2IntOpenHashMap angelica$getChunkStatus();
|
||||
LongOpenHashSet angelica$getChunkReady();
|
||||
LongSet angelica$getUnloadQueue();
|
||||
LongSet angelica$getLoadQueue();
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4fc;
|
||||
|
||||
public interface ClippingHelperExt {
|
||||
FrustumIntersection celeritas$getFrustumIntersection();
|
||||
void celeritas$updateFrustumIntersection();
|
||||
|
||||
Matrix4fc celeritas$getCombinedMatrix();
|
||||
}
|
||||
+4
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
|
||||
public interface IChunkTileEntityMapHolder {
|
||||
ConcurrentTileEntityMap angelica$getConcurrentTEMap();
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import com.gtnewhorizons.angelica.utils.MipmapStrategy;
|
||||
|
||||
public interface TextureMetadataExtension {
|
||||
|
||||
void angelica$setMipmapStrategy(MipmapStrategy strategy);
|
||||
|
||||
MipmapStrategy angelica$getMipmapStrategy();
|
||||
}
|
||||
Loaded 100 of 222 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user