mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dd60f6a75 | ||
|
|
87c83f0645 | ||
|
|
c578970a66 | ||
|
|
ef68af5850 | ||
|
|
b6a4497fb3 | ||
|
|
939e31461a | ||
|
|
02f0b0fca1 | ||
|
|
b7d439e6b1 | ||
|
|
c36647af34 | ||
|
|
f787801a0e | ||
|
|
f192793395 | ||
|
|
3876f7e693 | ||
|
|
94ee37bfed | ||
|
|
26c161f0e8 | ||
|
|
75639d3b73 | ||
|
|
a07cecb879 | ||
|
|
81b82748ce | ||
|
|
8d0c90c423 | ||
|
|
8ce6a2f8c6 | ||
|
|
2aa4ac7509 | ||
|
|
8f98151049 | ||
|
|
2b70318327 | ||
|
|
3b4ebcf611 | ||
|
|
2845c9e078 | ||
|
|
6f054a09a9 |
No files matched your search
@@ -22,6 +22,8 @@ indent_size = 2
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.java]
|
||||
max_line_length = 240
|
||||
ij_java_wrap_long_lines = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_class_count_to_use_import_on_demand = 999
|
||||
ij_java_names_count_to_use_import_on_demand = 999
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
cd "$REPO_ROOT/umbra"
|
||||
|
||||
gradle_args=(:compileJava :compileMixinJava :test --no-daemon --console=plain --stacktrace)
|
||||
if [[ -n "${UMBRA_GRADLE_ARGS:-}" ]]; then
|
||||
read -ra extra_args <<< "$UMBRA_GRADLE_ARGS"
|
||||
gradle_args+=("${extra_args[@]}")
|
||||
fi
|
||||
|
||||
sh ./gradlew "${gradle_args[@]}"
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Umbra build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main ]
|
||||
push:
|
||||
branches: [ master, main ]
|
||||
|
||||
jobs:
|
||||
umbra-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: |
|
||||
21
|
||||
25
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
validate-wrappers: true
|
||||
- run: .github/scripts/umbra-build.sh
|
||||
@@ -1,5 +1,6 @@
|
||||
.gradle
|
||||
.kotlin/
|
||||
.lwjgl/
|
||||
.settings
|
||||
/.idea/
|
||||
/.vs/
|
||||
@@ -43,5 +44,6 @@ tracy-client/src/main/resources/natives/
|
||||
tracy-client/angelica/
|
||||
dist/tracy-natives/
|
||||
*.tracy
|
||||
*.jfr
|
||||
|
||||
tracy-client/angelica/
|
||||
@@ -101,3 +101,5 @@ Targets GL 3.3+ core profile
|
||||
* FFP vertex shader generation inspired by ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* FFP fragment shader generation inspired by ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
* Fog parameter optimization inspired by st_nir_lower_fog.c (Copyright 2023 Google LLC) and prog_statevars.c (Copyright 1999-2007 Brian Paul)
|
||||
* RGSS Terrain Texture Filtering
|
||||
* Indiesoft for [defold-sharp-sprite](https://github.com/indiesoftby/defold-sharp-sprite), used as the reference implementation under the MIT license
|
||||
+4
-82
@@ -1,19 +1,16 @@
|
||||
import com.modrinth.minotaur.TaskModrinthUpload
|
||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
||||
import xyz.wagyourtail.jvmdg.gradle.flags.DowngradeFlags
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeJar>().configureEach { logLevel.set("FATAL") }
|
||||
tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
val lwjglDebug = false
|
||||
val gpuHud = false
|
||||
val renderdoc = false
|
||||
val rgp = false
|
||||
extra["lwjglDebug"] = lwjglDebug
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.debug.testBlocks=true")
|
||||
@@ -24,24 +21,13 @@ minecraft {
|
||||
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultConnectTimeout=5000")
|
||||
extraRunJvmArguments.add("-Dsun.net.client.defaultReadTimeout=5000")
|
||||
// TODO: Remove once GTNHGradle includes shaderc/spvc in the default lwjgl3Bindings list
|
||||
lwjgl3Bindings.addAll("shaderc", "spvc")
|
||||
lwjgl3Version = libs.versions.lwjgl3.get()
|
||||
// extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
// extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.debug.redirectorLogspam=true")
|
||||
|
||||
}
|
||||
|
||||
// Forward -Dangelica.* from the gradle invocation to the client
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
for ((key, value) in System.getProperties()) {
|
||||
val name = key.toString()
|
||||
if (name.startsWith("angelica.")) {
|
||||
jvmArgs("-D$name=$value")
|
||||
}
|
||||
}
|
||||
}
|
||||
apply(from = "gradle/angelica-run-common.gradle.kts")
|
||||
|
||||
if (gpuHud) {
|
||||
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
|
||||
@@ -57,47 +43,12 @@ if (gpuHud) {
|
||||
|
||||
configurations.all {
|
||||
exclude(group = "com.github.GTNewHorizons", module = "Angelica")
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.github.GTNewHorizons:lwjgl3ify"))
|
||||
.using(module("com.github.GTNewHorizons:lwjgl3ify:${libs.versions.lwjgl3ify.get()}"))
|
||||
}
|
||||
}
|
||||
|
||||
// lwjgl3ify pulls a pinned lwjgl 3.4.2 snapshot transitively from here.
|
||||
repositories {
|
||||
maven {
|
||||
name = "Maven Central Snapshots"
|
||||
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
|
||||
content { includeGroup("org.lwjgl") }
|
||||
}
|
||||
}
|
||||
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
val isMacOsArm64 = isMacOs && System.getProperty("os.arch") == "aarch64"
|
||||
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && name != "runClient") {
|
||||
if (isMacOs) {
|
||||
// SDL3 / Cocoa / Metal must initialize on the JVM main thread.
|
||||
jvmArgs("-XstartOnFirstThread")
|
||||
jvmArgs("-Dangelica.sdlgpu.encoderAssertions=" + if (lwjglDebug) "fatal" else "warn")
|
||||
if (lwjglDebug) {
|
||||
environment("METAL_DEVICE_WRAPPER_TYPE", "1")
|
||||
environment("METAL_DEBUG_ERROR_MODE", "0")
|
||||
environment("MTL_SHADER_VALIDATION", "1")
|
||||
environment("MTL_SHADER_VALIDATION_REPORT_TO_STDERR", "1")
|
||||
environment("MTL_DEBUG_LAYER", "1")
|
||||
environment("MallocScribble", "1")
|
||||
environment("MallocPreScribble", "1")
|
||||
environment("MallocGuardEdges", "1")
|
||||
environment("MallocStackLogging", "1")
|
||||
environment("MallocStackLoggingNoCompact", "1")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linux runClient: RenderDoc frame capture (F12) OR RADV RGP/SQTT capture. Mutually exclusive; RGP wins
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.startsWith("runClient") && !isMacOs && !osName.contains("windows")) {
|
||||
@@ -126,14 +77,6 @@ tasks.withType<JavaExec>().configureEach {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
val projectVersion = project.version.toString()
|
||||
inputs.property("version", projectVersion)
|
||||
filesMatching("META-INF/rfb-plugin/*") {
|
||||
expand("version" to projectVersion)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyDependencies") {
|
||||
group = "Angelica"
|
||||
description = "Collect dependencies into the testDependencies folder"
|
||||
@@ -206,6 +149,7 @@ fun Test.configureAngelicaJava8() {
|
||||
tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
jvmArgs("-Dceleritas.lwjglService=org.taumc.celeritas.lwjgl.HeadlessTestLWJGLService")
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
}
|
||||
|
||||
@@ -233,29 +177,7 @@ val glCoreTest by tasks.registering(Test::class) {
|
||||
|
||||
tasks.test { finalizedBy(glCoreTest) }
|
||||
|
||||
tasks.shadowJar {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mergeServiceFiles()
|
||||
|
||||
minimize {
|
||||
exclude(project(rootProject.path))
|
||||
exclude(project(":glsm"))
|
||||
exclude(project(":lwjgl3-backend"))
|
||||
exclude(project(":sdl-gpu"))
|
||||
exclude(dependency("org.taumc:.*:.*"))
|
||||
exclude(dependency("org.antlr:.*:.*"))
|
||||
}
|
||||
|
||||
relocate("com.mitchej123", "com.mitchej123")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
relocate("com.gtnewhorizons.angelica.config", "com.gtnewhorizons.angelica.config")
|
||||
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
|
||||
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
|
||||
relocate("com.gtnewhorizons.angelica.sdlgpu", "com.gtnewhorizons.angelica.sdlgpu")
|
||||
}
|
||||
apply(from = "gradle/angelica-shadow-common.gradle.kts")
|
||||
|
||||
tasks.withType<TaskPublishCurseForge>().configureEach {
|
||||
uploadArtifacts.forEach { it.addGameVersion("Client") }
|
||||
|
||||
@@ -52,8 +52,8 @@ configurations.configureEach {
|
||||
}
|
||||
|
||||
DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.197:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.197:dev")
|
||||
|
||||
runtimeOnlyNonPublishable(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
@@ -93,6 +93,7 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
shadowImplementation(project(":lwjgl3-backend")) { isTransitive = false }
|
||||
shadowImplementation(project(":sdl-gpu")) { isTransitive = false }
|
||||
runtimeOnlyNonPublishable(project(":tracy-client")) { isTransitive = false }
|
||||
devOnlyNonPublishable(libs.async.profiler)
|
||||
shadowImplementation(libs.eventbus)
|
||||
|
||||
shadowImplementation(libs.jcpp) // Apache 2.0
|
||||
@@ -104,7 +105,6 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
|
||||
// Celeritas (isTransitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly(libs.celeritas.common) { isTransitive = false }
|
||||
embedOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.109-GTNH:dev")
|
||||
@@ -177,7 +177,7 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.5-GTNH:dev")
|
||||
|
||||
// Distant Horizons
|
||||
compileOnly("maven.modrinth:DistantHorizonsApi:5.1.0")
|
||||
compileOnly(libs.distant.horizons.api)
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
@@ -208,5 +208,6 @@ afterEvaluate {
|
||||
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
testImplementation(libs.distant.horizons.api)
|
||||
}
|
||||
}
|
||||
+29
-2
@@ -46,6 +46,27 @@ tasks.withType<Jar>().configureEach {
|
||||
exclude("net/minecraft/**")
|
||||
}
|
||||
|
||||
val stubsElements by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY))
|
||||
attribute(rfgObfAttr, "mcp")
|
||||
attribute(rfgTransformedAttr, true)
|
||||
}
|
||||
outgoing {
|
||||
capability("${project.group}:glsm-stubs:${project.version}")
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(stubsElements.name, sourceSets["stubs"].java.classesDirectory) {
|
||||
type = "java-classes-directory"
|
||||
builtBy(tasks.named("stubsClasses"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Mojang"
|
||||
@@ -82,6 +103,11 @@ repositories {
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
val spirvTestRuntime by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// MC compile stubs
|
||||
compileOnly(sourceSets["stubs"].output)
|
||||
@@ -112,8 +138,10 @@ dependencies {
|
||||
testRuntimeOnly(libs.lwjgl3.spvc) { artifact { classifier = lwjglNatives } }
|
||||
testRuntimeOnly(libs.lwjgl3)
|
||||
testRuntimeOnly(libs.lwjgl3) { artifact { classifier = lwjglNatives } }
|
||||
spirvTestRuntime(libs.lwjgl3.opengl)
|
||||
// @Lwjgl3Aware annotation
|
||||
compileOnly(libs.lwjgl3ify) { artifact { classifier = "dev" }; isTransitive = false }
|
||||
compileOnly(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
|
||||
compileOnly(libs.lombok) { isTransitive = false }
|
||||
annotationProcessor(libs.lombok)
|
||||
@@ -140,7 +168,6 @@ dependencies {
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.log4j.core)
|
||||
testImplementation(libs.celeritas.common) { isTransitive = false }
|
||||
testRuntimeOnly(libs.celeritas.lwjgl2.service) { isTransitive = false }
|
||||
testRuntimeOnly(libs.jvmdowngrader.java.api) { artifact { classifier = "downgraded-8" } }
|
||||
testRuntimeOnly(libs.commons.lang3)
|
||||
}
|
||||
@@ -288,7 +315,7 @@ val spirvTest by tasks.registering(Test::class) {
|
||||
classpath = sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
// Strip the LWJGL2 jars - they collide with LWJGL3's sealed org.lwjgl package.
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
}.plus(spirvTestRuntime)
|
||||
|
||||
filter {
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
|
||||
@@ -37,6 +37,7 @@ public final class SystemProperties {
|
||||
public static final boolean SDL_VERIFY_PER_FRAME_UNIFORM_BLOCK = Boolean.getBoolean("angelica.sdlgpu.verifyPerFrameUniformBlock");
|
||||
public static final int SDL_FRAMES_IN_FLIGHT = Integer.getInteger("angelica.sdlgpu.framesInFlight", 2);
|
||||
public static final boolean DISABLE_SDL_PRESENTER_THREAD = Boolean.getBoolean("angelica.sdlgpu.disablePresenterThread");
|
||||
public static final boolean SDL_DISABLE_IN_PASS_CLEAR = Boolean.getBoolean("angelica.sdlgpu.disableInPassClear");
|
||||
|
||||
// Tracy
|
||||
public static final boolean TRACY = Boolean.getBoolean("angelica.tracy");
|
||||
@@ -44,6 +45,11 @@ public final class SystemProperties {
|
||||
public static final String TRACY_DIR = System.getProperty("angelica.tracy.dir", "angelica" + File.separator + "natives" + File.separator + "tracy");
|
||||
public static final int TRACY_MAX_SRC_LOCS = Math.max(16, Integer.getInteger("angelica.tracy.maxSrcLocs", 4096));
|
||||
|
||||
// Profiling
|
||||
public static final String PROFILE_OPTS = System.getProperty("angelica.profile.opts", "event=wall,interval=5ms,alloc=512k,lock=10ms");
|
||||
public static final String PROFILE_DIR = System.getProperty("angelica.profile.dir", "angelica" + File.separator + "profiles");
|
||||
public static final String PROFILE_OUTPUT = System.getProperty("angelica.profile.output", "");
|
||||
|
||||
// Flyby
|
||||
public static final String FLYBY_ROUTE = System.getProperty("angelica.flyby.route", "");
|
||||
public static final boolean FLYBY_WAIT_FOR_TRACY = Boolean.getBoolean("angelica.flyby.waitForTracy");
|
||||
@@ -52,6 +58,11 @@ public final class SystemProperties {
|
||||
public static final int FLYBY_LENGTH = Integer.getInteger("angelica.flyby.length", 0); // Blocks for moving routes, ticks for stationary
|
||||
public static final double FLYBY_SPEED = parseDouble("angelica.flyby.speed"); // Travel speed in blocks per tick
|
||||
public static final boolean FLYBY_EXIT_WHEN_DONE = Boolean.getBoolean("angelica.flyby.exitWhenDone");
|
||||
public static final long FLYBY_TIME_OF_DAY = Math.floorMod(Long.getLong("angelica.flyby.timeOfDay", 6000L), 24000L);
|
||||
public static final String FLYBY_COMMANDS = System.getProperty("angelica.flyby.commands", "");
|
||||
public static final String FLYBY_ORIGIN = System.getProperty("angelica.flyby.origin", "");
|
||||
public static final FlybyPacing FLYBY_PACING = parseEnum("angelica.flyby.pacing", FlybyPacing.UNCAPPED, FlybyPacing.UNCAPPED, FlybyPacing.class);
|
||||
public static final boolean FLYBY_JFR = Boolean.getBoolean("angelica.flyby.jfr");
|
||||
|
||||
// Debug
|
||||
public static final boolean LWJGL_DEBUG = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
@@ -60,19 +71,26 @@ public final class SystemProperties {
|
||||
public static final boolean ENABLE_TEST_BLOCKS = Boolean.getBoolean("angelica.debug.testBlocks");
|
||||
public static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.debug.dumpClass");
|
||||
private static final boolean DUMP_SHADERS_PROP = Boolean.getBoolean("angelica.debug.dumpShaders");
|
||||
private static final boolean DEBUG_TOOLING_PROP = Boolean.getBoolean("angelica.debug.tooling");
|
||||
public static final boolean REDIRECTOR_LOGSPAM = Boolean.getBoolean("angelica.debug.redirectorLogspam");
|
||||
public static final boolean DEBUG_DISPLAY_LISTS = Boolean.getBoolean("angelica.debug.displayLists");
|
||||
public static final boolean FFP_TRACE = Boolean.getBoolean("angelica.debug.ffpTrace");
|
||||
public static final boolean LOG_DISPLAY_LIST_COMPILATION = Boolean.getBoolean("angelica.debug.displayLists.compilation");
|
||||
public static final boolean FORCE_ORPHAN_STREAMING = Boolean.getBoolean("angelica.debug.forceOrphanStreaming");
|
||||
public static final String SHADER_DUMP_ROOT = "angelica_dumps";
|
||||
|
||||
// Set by us, read by celeritas
|
||||
public static final String KEY_CELERITAS_ENABLE_GL_DEBUG = "celeritas.enableGLDebug";
|
||||
public static final String KEY_CELERITAS_LWJGL_SERVICE = "celeritas.lwjglService";
|
||||
|
||||
public static boolean dumpShaders() {
|
||||
return DUMP_SHADERS_PROP || isDeobf();
|
||||
}
|
||||
|
||||
public static boolean debugTooling() {
|
||||
return DEBUG_TOOLING_PROP || isDeobf();
|
||||
}
|
||||
|
||||
public static Path shaderDumpDir(String phase) {
|
||||
return dumpShaders() ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
|
||||
}
|
||||
@@ -102,6 +120,11 @@ public final class SystemProperties {
|
||||
FATAL
|
||||
}
|
||||
|
||||
public enum FlybyPacing {
|
||||
UNCAPPED,
|
||||
CONFIGURED
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> E parseEnum(String key, E whenAbsent, E whenInvalid, Class<E> type) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return whenAbsent;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
|
||||
public final class ClipPlaneBooleanState extends BooleanStateStack {
|
||||
|
||||
private final GLContextState owner;
|
||||
private final int index;
|
||||
|
||||
ClipPlaneBooleanState(GLContextState owner, int index, int glCap) {
|
||||
super(glCap);
|
||||
this.owner = owner;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabledChanged() {
|
||||
if (isEnabled()) {
|
||||
owner.clipPlaneEnabledMask |= (1 << index);
|
||||
} else {
|
||||
owner.clipPlaneEnabledMask &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,13 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Feature {
|
||||
@@ -23,52 +18,49 @@ public class Feature {
|
||||
GL13.GL_MULTISAMPLE_BIT, GL11.GL_PIXEL_MODE_BIT, GL11.GL_POINT_BIT, GL11.GL_POLYGON_BIT, GL11.GL_POLYGON_STIPPLE_BIT, GL11.GL_SCISSOR_BIT,
|
||||
GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_TEXTURE_BIT, GL11.GL_TRANSFORM_BIT, GL11.GL_VIEWPORT_BIT };
|
||||
|
||||
static final Int2ObjectMap<List<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
|
||||
static final Int2ObjectMap<IStateStack<?>[]> maskToNonBooleanStacksMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final long[] NO_MEMBERS = new long[0];
|
||||
|
||||
static List<IStateStack<?>> maskToFeatures(int mask) {
|
||||
if(maskToFeaturesMap.containsKey(mask)) {
|
||||
return maskToFeaturesMap.get(mask);
|
||||
}
|
||||
private static final Int2ObjectMap<long[]> maskToMembersMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
final Set<IStateStack<?>> features = new HashSet<>();
|
||||
|
||||
for(int attrib : Feature.supportedAttribs) {
|
||||
if((mask & attrib) == attrib) {
|
||||
features.addAll(getFeatures(attrib));
|
||||
}
|
||||
}
|
||||
|
||||
final List<IStateStack<?>> asList = new ArrayList<>(features);
|
||||
|
||||
maskToFeaturesMap.put(mask, asList);
|
||||
return asList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only non-BooleanStateStack instances for the given mask.
|
||||
* These use traditional push/pop without global depth tracking.
|
||||
*/
|
||||
static IStateStack<?>[] maskToNonBooleanStacks(int mask) {
|
||||
IStateStack<?>[] cached = maskToNonBooleanStacksMap.get(mask);
|
||||
static long[] maskToMembers(int mask) {
|
||||
final long[] cached = maskToMembersMap.get(mask);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final List<IStateStack<?>> all = maskToFeatures(mask);
|
||||
final List<IStateStack<?>> nonBooleans = new ArrayList<>();
|
||||
for (int i = 0; i < all.size(); i++) {
|
||||
final IStateStack<?> stack = all.get(i);
|
||||
if (!(stack instanceof BooleanStateStack)) {
|
||||
nonBooleans.add(stack);
|
||||
long[] result = NO_MEMBERS;
|
||||
for (int attrib : Feature.supportedAttribs) {
|
||||
if ((mask & attrib) == attrib) {
|
||||
result = union(result, attribToMembers.get(attrib));
|
||||
}
|
||||
}
|
||||
cached = nonBooleans.toArray(new IStateStack<?>[0]);
|
||||
maskToNonBooleanStacksMap.put(mask, cached);
|
||||
return cached;
|
||||
|
||||
maskToMembersMap.put(mask, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Map<Integer, Set<IStateStack<?>>> attribToFeatures = new HashMap<>();
|
||||
private static long[] union(long[] a, long[] b) {
|
||||
if (b == null || b.length == 0) return a;
|
||||
if (a.length == 0) return b.clone();
|
||||
final long[] result = new long[Math.max(a.length, b.length)];
|
||||
System.arraycopy(a, 0, result, 0, a.length);
|
||||
for (int i = 0; i < b.length; i++) result[i] |= b[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
private static long[] toMembers(Set<IStateStack<?>> stacks) {
|
||||
if (stacks.isEmpty()) return NO_MEMBERS;
|
||||
int max = 0;
|
||||
for (IStateStack<?> s : stacks) max = Math.max(max, ((CowStateStack<?>) s).stackId());
|
||||
final long[] bits = new long[(max >> 6) + 1];
|
||||
for (IStateStack<?> s : stacks) {
|
||||
final int id = ((CowStateStack<?>) s).stackId();
|
||||
bits[id >> 6] |= 1L << id;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static final Int2ObjectMap<long[]> attribToMembers = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Helper method to add all texture gen states (S, T, R, Q) for all texture units to a collection.
|
||||
@@ -84,6 +76,8 @@ public class Feature {
|
||||
}
|
||||
|
||||
static {
|
||||
final Int2ObjectMap<Set<IStateStack<?>>> attribToFeatures = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.getAlphaTest()
|
||||
, GLStateManager.getAlphaState()
|
||||
@@ -191,11 +185,25 @@ public class Feature {
|
||||
|
||||
attribToFeatures.put(GL11.GL_ENABLE_BIT, enableBits);
|
||||
attribToFeatures.put(GL11.GL_EVAL_BIT, ImmutableSet.of(
|
||||
// GL_MAP1_x enable bits, where x is a map type
|
||||
// GL_MAP2_x enable bits, where x is a map type
|
||||
// 1D grid endpoints and divisions
|
||||
// 2D grid endpoints and divisions
|
||||
// GL_AUTO_NORMAL enable bit
|
||||
GLStateManager.getAutoNormalState()
|
||||
, GLStateManager.getMap1Color4State()
|
||||
, GLStateManager.getMap1IndexState()
|
||||
, GLStateManager.getMap1NormalState()
|
||||
, GLStateManager.getMap1TextureCoord1State()
|
||||
, GLStateManager.getMap1TextureCoord2State()
|
||||
, GLStateManager.getMap1TextureCoord3State()
|
||||
, GLStateManager.getMap1TextureCoord4State()
|
||||
, GLStateManager.getMap1Vertex3State()
|
||||
, GLStateManager.getMap1Vertex4State()
|
||||
, GLStateManager.getMap2Color4State()
|
||||
, GLStateManager.getMap2IndexState()
|
||||
, GLStateManager.getMap2NormalState()
|
||||
, GLStateManager.getMap2TextureCoord1State()
|
||||
, GLStateManager.getMap2TextureCoord2State()
|
||||
, GLStateManager.getMap2TextureCoord3State()
|
||||
, GLStateManager.getMap2TextureCoord4State()
|
||||
, GLStateManager.getMap2Vertex3State()
|
||||
, GLStateManager.getMap2Vertex4State()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
|
||||
GLStateManager.getFogMode()
|
||||
@@ -258,10 +266,10 @@ public class Feature {
|
||||
// GL_LIST_BASE setting
|
||||
));
|
||||
attribToFeatures.put(GL13.GL_MULTISAMPLE_BIT, ImmutableSet.of(
|
||||
// GL_MULTISAMPLE enable bit
|
||||
// GL_SAMPLE_ALPHA_TO_COVERAGE flag
|
||||
// GL_SAMPLE_ALPHA_TO_ONE flag
|
||||
// GL_SAMPLE_COVERAGE flag
|
||||
GLStateManager.getMultisampleState()
|
||||
, GLStateManager.getSampleAlphaToCoverageState()
|
||||
, GLStateManager.getSampleAlphaToOneState()
|
||||
, GLStateManager.getSampleCoverageState()
|
||||
// GL_SAMPLE_COVERAGE_VALUE value
|
||||
// GL_SAMPLE_COVERAGE_INVERT value
|
||||
));
|
||||
@@ -327,22 +335,24 @@ public class Feature {
|
||||
|
||||
attribToFeatures.put(GL11.GL_TEXTURE_BIT, textureAttribs);
|
||||
|
||||
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
|
||||
final Set<IStateStack<?>> transformAttribs = new HashSet<>(ImmutableSet.of(
|
||||
// Coefficients of the six clipping planes
|
||||
|
||||
|
||||
GLStateManager.getMatrixMode()
|
||||
, GLStateManager.getNormalizeState()
|
||||
, GLStateManager.getRescaleNormalState()
|
||||
));
|
||||
for(int i = 0; i < GLStateManager.getClipPlaneStates().length; i++) {
|
||||
transformAttribs.add(GLStateManager.getClipPlaneStates()[i]);
|
||||
}
|
||||
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, transformAttribs);
|
||||
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
|
||||
|
||||
|
||||
GLStateManager.getViewportState()
|
||||
));
|
||||
|
||||
for (Int2ObjectMap.Entry<Set<IStateStack<?>>> entry : attribToFeatures.int2ObjectEntrySet()) {
|
||||
attribToMembers.put(entry.getIntKey(), toMembers(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<IStateStack<?>> getFeatures(int attrib) {
|
||||
return attribToFeatures.getOrDefault(attrib, Collections.emptySet());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Color4Stack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowDepths;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowDispatch;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.DepthStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
|
||||
@@ -20,16 +23,18 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntStack;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -42,11 +47,20 @@ import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class GLContextState {
|
||||
public class GLContextState {
|
||||
private final List<IStateStack<?>> allStacksBuilder = new ArrayList<>();
|
||||
public IStateStack<?>[] allStacks;
|
||||
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
|
||||
|
||||
private BooleanStateStack track(BooleanStateStack s) {
|
||||
s.setStackId(StackIdAllocator.nextId());
|
||||
allBooleanStates.add(s);
|
||||
allStacksBuilder.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private <T extends IStateStack<?>> T member(T s) {
|
||||
allStacksBuilder.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -67,6 +81,7 @@ class GLContextState {
|
||||
public int colorGeneration;
|
||||
// clip plane equation changes
|
||||
public int clipPlaneGeneration;
|
||||
public int clipPlaneEnabledMask;
|
||||
public float overlayR = 0.0f;
|
||||
public float overlayG = 0.0f;
|
||||
public float overlayB = 0.0f;
|
||||
@@ -83,10 +98,15 @@ class GLContextState {
|
||||
public boolean unit23TexCoordSetDuringDraw = false;
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
public final IntStack attribs = new IntArrayList(GLStateManager.MAX_ATTRIB_STACK_DEPTH);
|
||||
public final StateSet[] attribSets = new StateSet[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public int attribDepth = 0;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final List<IStateStack<?>>[] modifiedAtDepth = new List[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public CowStateStack<?>[] stackById;
|
||||
public CowDepths[] depthsById;
|
||||
public int[] restoreBitById;
|
||||
public int[] restoreUnitById;
|
||||
public byte[] kindById;
|
||||
public int[][] modifiedIds;
|
||||
public int[] modifiedCount;
|
||||
public final int[] savedMvGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedMvLinearGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedProjGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
@@ -96,28 +116,30 @@ class GLContextState {
|
||||
public final int[] savedColorGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedNormalGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final int[] savedTexCoordGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public final IntegerStateStack activeTextureUnit = new IntegerStateStack(0);
|
||||
public final IntegerStateStack activeTextureUnit = member(new IntegerStateStack(0, StackIdAllocator.nextId()).restoreBit(StateSet.R_ACTIVE_UNIT));
|
||||
public int clientActiveTextureUnit = 0;
|
||||
public final IntegerStateStack shadeModelState = new IntegerStateStack(GL11.GL_SMOOTH);
|
||||
public final IntegerStateStack shadeModelState = member(new IntegerStateStack(GL11.GL_SMOOTH, StackIdAllocator.nextId()));
|
||||
public final TextureUnitArray textures = new TextureUnitArray();
|
||||
public final ImageUnitArray imageUnits = new ImageUnitArray();
|
||||
public final BlendStateStack blendState = new BlendStateStack();
|
||||
public final SamplerUnitArray samplerUnits = new SamplerUnitArray();
|
||||
public final BlendStateStack blendState = member(new BlendStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack blendMode = track(new BooleanStateStack(GL11.GL_BLEND));
|
||||
public final BooleanStateStack scissorTest = track(new BooleanStateStack(GL11.GL_SCISSOR_TEST));
|
||||
public final DepthStateStack depthState = new DepthStateStack();
|
||||
public final DepthStateStack depthState = member(new DepthStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack depthTest = track(new BooleanStateStack(GL11.GL_DEPTH_TEST));
|
||||
public final FogStateStack fogState = new FogStateStack();
|
||||
public final FogStateStack fogState = member(new FogStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
|
||||
public final Color4Stack color = new Color4Stack();
|
||||
public final Color4Stack color = member(new Color4Stack(StackIdAllocator.nextId()));
|
||||
// GL_CURRENT_SECONDARY_COLOR - alpha is always 1 and unused, the color sum only adds RGB
|
||||
public final Color4Stack secondaryColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 1.0F));
|
||||
public final Color4Stack secondaryColor = member(new Color4Stack(StackIdAllocator.nextId(), new Color4(0.0F, 0.0F, 0.0F, 1.0F)).restoreBit(StateSet.R_SECONDARY_COLOR));
|
||||
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
|
||||
public final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
public final ColorMaskStack colorMask = new ColorMaskStack();
|
||||
public final IntegerStateStack drawBuffer = new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER);
|
||||
public final IntegerStateStack logicOpMode = new IntegerStateStack(GL11.GL_COPY);
|
||||
public final Color4Stack clearColor = member(new Color4Stack(StackIdAllocator.nextId(), new Color4(0.0F, 0.0F, 0.0F, 0.0F)).restoreBit(StateSet.R_CLEAR_COLOR));
|
||||
public final ColorMaskStack colorMask = member(new ColorMaskStack(StackIdAllocator.nextId()));
|
||||
public final IntegerStateStack drawBuffer = member(new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER, StackIdAllocator.nextId()).restoreBit(StateSet.R_DRAW_BUFFER));
|
||||
public final IntegerStateStack logicOpMode = member(new IntegerStateStack(GL11.GL_COPY, StackIdAllocator.nextId()).restoreBit(StateSet.R_LOGIC_OP));
|
||||
public final BooleanStateStack cullState = track(new BooleanStateStack(GL11.GL_CULL_FACE));
|
||||
public final AlphaStateStack alphaState = new AlphaStateStack();
|
||||
public final AlphaState restoredAlphaScratch = new AlphaState();
|
||||
public final AlphaStateStack alphaState = member(new AlphaStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack alphaTest = track(new BooleanStateStack(GL11.GL_ALPHA_TEST, false, true));
|
||||
public final BooleanStateStack lightingState = track(new BooleanStateStack(GL11.GL_LIGHTING, false, true));
|
||||
public final BooleanStateStack rescaleNormalState = track(new BooleanStateStack(GL12.GL_RESCALE_NORMAL, false, true));
|
||||
@@ -138,10 +160,10 @@ class GLContextState {
|
||||
public final BooleanStateStack polygonOffsetPointState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_POINT));
|
||||
public final BooleanStateStack polygonOffsetLineState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_LINE));
|
||||
public final BooleanStateStack polygonOffsetFillState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL));
|
||||
public final LineStateStack lineState = new LineStateStack();
|
||||
public final PointStateStack pointState = new PointStateStack();
|
||||
public final PolygonStateStack polygonState = new PolygonStateStack();
|
||||
public final StencilStateStack stencilState = new StencilStateStack();
|
||||
public final LineStateStack lineState = member(new LineStateStack(StackIdAllocator.nextId()));
|
||||
public final PointStateStack pointState = member(new PointStateStack(StackIdAllocator.nextId()));
|
||||
public final PolygonStateStack polygonState = member(new PolygonStateStack(StackIdAllocator.nextId()));
|
||||
public final StencilStateStack stencilState = member(new StencilStateStack(StackIdAllocator.nextId()));
|
||||
public int stencilBitMask = 0xFFFFFFFF;
|
||||
public final BooleanStateStack autoNormalState = track(new BooleanStateStack(GL11.GL_AUTO_NORMAL, false, true));
|
||||
public final BooleanStateStack map1Color4State = track(new BooleanStateStack(GL11.GL_MAP1_COLOR_4, false, true));
|
||||
@@ -166,19 +188,20 @@ class GLContextState {
|
||||
public final ClipPlaneState clipPlaneState = new ClipPlaneState();
|
||||
public final FloatBuffer queryScratch = BufferUtils.createFloatBuffer(16);
|
||||
public final DoubleBuffer queryScratchDouble = BufferUtils.createDoubleBuffer(16);
|
||||
public final MatrixModeStack matrixMode = new MatrixModeStack();
|
||||
public final MatrixModeStack matrixMode = member(new MatrixModeStack(StackIdAllocator.nextId()));
|
||||
public final Matrix4fStack modelViewMatrix = new Matrix4fStack(GLStateManager.MAX_MODELVIEW_STACK_DEPTH);
|
||||
public final Matrix4fStack projectionMatrix = new Matrix4fStack(GLStateManager.MAX_PROJECTION_STACK_DEPTH);
|
||||
public final BooleanStateStack[] lightStates = new BooleanStateStack[8];
|
||||
public final LightStateStack[] lightDataStates = new LightStateStack[8];
|
||||
public final BooleanStateStack colorMaterial = track(new BooleanStateStack(GL11.GL_COLOR_MATERIAL, false, true));
|
||||
public final IntegerStateStack colorMaterialFace = new IntegerStateStack(GL11.GL_FRONT_AND_BACK);
|
||||
public final IntegerStateStack colorMaterialParameter = new IntegerStateStack(GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
public final LightModelStateStack lightModel = new LightModelStateStack();
|
||||
public final MaterialStateStack frontMaterial = new MaterialStateStack(GL11.GL_FRONT);
|
||||
public final MaterialStateStack backMaterial = new MaterialStateStack(GL11.GL_BACK);
|
||||
public final ViewPortStateStack viewportState = new ViewPortStateStack();
|
||||
public final IntegerStateStack colorMaterialFace = member(new IntegerStateStack(GL11.GL_FRONT_AND_BACK, StackIdAllocator.nextId()));
|
||||
public final IntegerStateStack colorMaterialParameter = member(new IntegerStateStack(GL11.GL_AMBIENT_AND_DIFFUSE, StackIdAllocator.nextId()));
|
||||
public final LightModelStateStack lightModel = member(new LightModelStateStack(StackIdAllocator.nextId()));
|
||||
public final MaterialStateStack frontMaterial = member(new MaterialStateStack(GL11.GL_FRONT, StackIdAllocator.nextId()));
|
||||
public final MaterialStateStack backMaterial = member(new MaterialStateStack(GL11.GL_BACK, StackIdAllocator.nextId()));
|
||||
public final ViewPortStateStack viewportState = member(new ViewPortStateStack(StackIdAllocator.nextId()));
|
||||
public int activeProgram = 0;
|
||||
public final IntegerStateStack programStack = member(new IntegerStateStack(0, StackIdAllocator.nextId()));
|
||||
public int listBase = 0;
|
||||
public int boundVBO;
|
||||
public int boundVAO;
|
||||
@@ -192,8 +215,8 @@ class GLContextState {
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged, restoreSecondaryColorChanged;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
public final BlendState vanillaBlendAfter = new BlendState();
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
@@ -202,18 +225,40 @@ class GLContextState {
|
||||
public int texGenGeneration;
|
||||
|
||||
GLContextState() {
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) modifiedAtDepth[i] = new ArrayList<>();
|
||||
allStacksBuilder.addAll(textures.idStacks());
|
||||
for (int i = 0; i < lightStates.length; i++) {
|
||||
lightStates[i] = track(new BooleanStateStack(GL11.GL_LIGHT0 + i, false, true));
|
||||
}
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
clipPlaneStates[i] = track(new BooleanStateStack(GL11.GL_CLIP_PLANE0 + i));
|
||||
clipPlaneStates[i] = track(new ClipPlaneBooleanState(this, i, GL11.GL_CLIP_PLANE0 + i));
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
for (int i = 0; i < lightDataStates.length; i++) {
|
||||
lightDataStates[i] = new LightStateStack(GL11.GL_LIGHT0 + i);
|
||||
lightDataStates[i] = member(new LightStateStack(GL11.GL_LIGHT0 + i, StackIdAllocator.nextId()));
|
||||
}
|
||||
allStacksBuilder.add(ShaderManager.getNormalStack());
|
||||
allStacksBuilder.add(ShaderManager.getTexCoordStack());
|
||||
allStacks = allStacksBuilder.toArray(new IStateStack<?>[0]);
|
||||
|
||||
final int capacity = StackIdAllocator.capacity();
|
||||
stackById = new CowStateStack<?>[capacity];
|
||||
depthsById = new CowDepths[capacity];
|
||||
restoreBitById = new int[capacity];
|
||||
restoreUnitById = new int[capacity];
|
||||
kindById = new byte[capacity];
|
||||
for (final IStateStack<?> stack : allStacks) {
|
||||
final CowStateStack<?> s = (CowStateStack<?>) stack;
|
||||
final int id = s.stackId();
|
||||
stackById[id] = s;
|
||||
depthsById[id] = s.cowDepths();
|
||||
restoreBitById[id] = s.restoreBit();
|
||||
restoreUnitById[id] = s.restoreUnit();
|
||||
kindById[id] = CowDispatch.kindOf(s);
|
||||
}
|
||||
|
||||
modifiedIds = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH][allStacks.length];
|
||||
modifiedCount = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
}
|
||||
}
|
||||
@@ -564,7 +564,7 @@ public final class GLDebug {
|
||||
/**
|
||||
* Overload that lets you gate the String concat behind the debugState
|
||||
*/
|
||||
public static void pushGroup(String prefix, int value) {
|
||||
public static void pushGroup(String prefix, long value) {
|
||||
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
|
||||
debugState.pushGroup(prefix + value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.OpenGLException;
|
||||
|
||||
final class GLErrorReporter {
|
||||
|
||||
private GLErrorReporter() {}
|
||||
|
||||
static void reportError(int error) {
|
||||
throw new OpenGLException(error);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -9,7 +9,7 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.mitchej123.lwjgl.LWJGLServiceProvider;
|
||||
import org.taumc.celeritas.lwjgl.LWJGLServiceProvider;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -49,6 +49,7 @@ public class RenderSystem {
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static int maxImageUnits;
|
||||
@Getter private static int maxCombinedTextureImageUnits;
|
||||
private static int maxSSBOBindings;
|
||||
private static int maxGlslVersion;
|
||||
private static boolean supportsGpuShader4;
|
||||
@@ -64,9 +65,6 @@ public class RenderSystem {
|
||||
private static volatile boolean isLTW;
|
||||
private static volatile boolean ltwDetected;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
|
||||
private static final Pattern SEMVER_PATTERN = Pattern.compile("(?<major>\\d+)\\.(?<minor>\\d+)\\.*(?<bugfix>\\d*)(.*)");
|
||||
|
||||
private RenderSystem() {}
|
||||
@@ -158,7 +156,7 @@ public class RenderSystem {
|
||||
// Check for sampler objects support (GL 3.3+ or ARB extension)
|
||||
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
if (supportsSamplerObjects) {
|
||||
samplers = new int[RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)];
|
||||
maxCombinedTextureImageUnits = RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
}
|
||||
|
||||
maxGlslVersion = Integer.parseInt(parseGlVersionString(RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION)));
|
||||
@@ -486,17 +484,15 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void disableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.disablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
GLStateManager.glDisablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void enableBufferBlend(int buffer) {
|
||||
RENDER_BACKEND.enablei(GL11.GL_BLEND, buffer);
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
GLStateManager.glEnablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void blendFuncSeparatei(int buffer, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
RENDER_BACKEND.blendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
GLStateManager.glBlendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
@@ -628,7 +624,7 @@ public class RenderSystem {
|
||||
|
||||
public static int genSampler() {
|
||||
if (!supportsSamplerObjects) return 0;
|
||||
return RENDER_BACKEND.genSamplers();
|
||||
return GLStateManager.glGenSamplers();
|
||||
}
|
||||
|
||||
public static void destroySampler(int sampler) {
|
||||
@@ -638,22 +634,25 @@ public class RenderSystem {
|
||||
|
||||
public static void samplerParameteri(int sampler, int pname, int param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
RENDER_BACKEND.samplerParameteri(sampler, pname, param);
|
||||
GLStateManager.glSamplerParameteri(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void samplerParameterf(int sampler, int pname, float param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glSamplerParameterf(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void bindSamplerToUnit(int unit, int sampler) {
|
||||
if (!supportsSamplerObjects) return;
|
||||
if (samplers[unit] == sampler) return;
|
||||
RENDER_BACKEND.bindSampler(unit, sampler);
|
||||
samplers[unit] = sampler;
|
||||
GLStateManager.glBindSampler(unit, sampler);
|
||||
}
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
for (int i = 0; i < samplers.length; i++) {
|
||||
if (samplers[i] != 0) {
|
||||
RENDER_BACKEND.bindSampler(i, 0);
|
||||
samplers[i] = 0;
|
||||
final int count = GLStateManager.getSamplerUnitCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (GLStateManager.getSamplerBinding(i) != 0) {
|
||||
GLStateManager.glBindSampler(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class StateSet {
|
||||
|
||||
public static final int R_DEPTH = 1;
|
||||
public static final int R_BLEND = 1 << 1;
|
||||
public static final int R_COLOR_MASK = 1 << 2;
|
||||
public static final int R_CLEAR_COLOR = 1 << 3;
|
||||
public static final int R_DRAW_BUFFER = 1 << 4;
|
||||
public static final int R_LOGIC_OP = 1 << 5;
|
||||
public static final int R_SECONDARY_COLOR = 1 << 6;
|
||||
public static final int R_STENCIL = 1 << 7;
|
||||
public static final int R_VIEWPORT = 1 << 8;
|
||||
public static final int R_LINE = 1 << 9;
|
||||
public static final int R_POINT = 1 << 10;
|
||||
public static final int R_POLYGON = 1 << 11;
|
||||
public static final int R_TEXTURE = 1 << 12;
|
||||
public static final int R_ACTIVE_UNIT = 1 << 13;
|
||||
public static final int R_PROGRAM = 1 << 14;
|
||||
|
||||
public static final int B_FRAG_COLORBUF = 1;
|
||||
public static final int B_FRAG_TEXTURE = 1 << 1;
|
||||
public static final int B_FRAG_FOG = 1 << 2;
|
||||
public static final int B_CURRENT = 1 << 3;
|
||||
public static final int B_LIGHTING = 1 << 4;
|
||||
public static final int B_TRANSFORM = 1 << 5;
|
||||
|
||||
private static final long[] NO_MEMBERS = new long[0];
|
||||
private static final Int2ObjectMap<StateSet> maskToStateSetMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public final int glMask;
|
||||
public final long[] members;
|
||||
public final int restore;
|
||||
public final int bump;
|
||||
public final boolean blendSnapshot;
|
||||
|
||||
StateSet(int glMask, long[] members, int restore, int bump, boolean blendSnapshot) {
|
||||
this.glMask = glMask;
|
||||
this.members = members;
|
||||
this.restore = restore;
|
||||
this.bump = bump;
|
||||
this.blendSnapshot = blendSnapshot;
|
||||
}
|
||||
|
||||
public boolean contains(int id) {
|
||||
if (id < 0) return false;
|
||||
final int word = id >> 6;
|
||||
return word < members.length && (members[word] & (1L << id)) != 0;
|
||||
}
|
||||
|
||||
private static long[] ids(CowStateStack<?>... stacks) {
|
||||
if (stacks.length == 0) return NO_MEMBERS;
|
||||
int max = 0;
|
||||
for (CowStateStack<?> s : stacks) max = Math.max(max, s.stackId());
|
||||
final long[] bits = new long[(max >> 6) + 1];
|
||||
for (CowStateStack<?> s : stacks) {
|
||||
final int id = s.stackId();
|
||||
bits[id >> 6] |= 1L << id;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
public static final StateSet BATCH = buildBatch();
|
||||
public static final StateSet FONT = buildFont();
|
||||
public static final StateSet FONT_PIPELINE = buildFontPipeline();
|
||||
public static final StateSet HAND = buildHand();
|
||||
public static final StateSet BLEND = buildBlend();
|
||||
public static final StateSet ALPHA = buildAlpha();
|
||||
public static final StateSet CUTOUT = buildCutout();
|
||||
public static final StateSet MASKS = buildMasks();
|
||||
public static final StateSet CULL = buildCull();
|
||||
|
||||
private static StateSet buildBatch() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLStateManager.getBlendState(), GLStateManager.getAlphaState(), GLStateManager.getDepthState(),
|
||||
GLStateManager.getColorMask(), GLStateManager.getPolygonState(),
|
||||
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
|
||||
GLStateManager.getActiveTextureUnitStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getLightingState(), GLStateManager.getCullState(), GLStateManager.getPolygonOffsetFillState(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildFont() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLStateManager.getBlendState(), GLStateManager.getDepthState(), GLStateManager.getShadeModelState(),
|
||||
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
|
||||
GLStateManager.getActiveTextureUnitStack(), GLStateManager.getProgramStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildFontPipeline() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLStateManager.getBlendState(), GLStateManager.getDepthState(),
|
||||
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
|
||||
GLStateManager.getActiveTextureUnitStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildHand() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLStateManager.getBlendState(), GLStateManager.getAlphaState(), GLStateManager.getDepthState(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getTextures().getTextureUnitStates(1)
|
||||
),
|
||||
R_BLEND | R_DEPTH,
|
||||
B_FRAG_COLORBUF,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildBlend() {
|
||||
return new StateSet(0,
|
||||
ids(GLStateManager.getBlendState(), GLStateManager.getBlendMode()),
|
||||
R_BLEND,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildAlpha() {
|
||||
return new StateSet(0,
|
||||
ids(GLStateManager.getAlphaState(), GLStateManager.getAlphaTest()),
|
||||
0,
|
||||
B_FRAG_COLORBUF,
|
||||
false);
|
||||
}
|
||||
|
||||
private static StateSet buildCutout() {
|
||||
return new StateSet(0,
|
||||
ids(GLStateManager.getAlphaState(), GLStateManager.getAlphaTest(), GLStateManager.getTextures().getTextureUnitStates(1)),
|
||||
0,
|
||||
B_FRAG_COLORBUF,
|
||||
false);
|
||||
}
|
||||
|
||||
private static StateSet buildMasks() {
|
||||
return new StateSet(0,
|
||||
ids(GLStateManager.getDepthState(), GLStateManager.getColorMask()),
|
||||
R_DEPTH | R_COLOR_MASK,
|
||||
0,
|
||||
false);
|
||||
}
|
||||
|
||||
private static StateSet buildCull() {
|
||||
return new StateSet(0,
|
||||
ids(GLStateManager.getPolygonState(), GLStateManager.getCullState()),
|
||||
R_POLYGON,
|
||||
0,
|
||||
false);
|
||||
}
|
||||
|
||||
public static StateSet forMask(int mask) {
|
||||
StateSet cached = maskToStateSetMap.get(mask);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
cached = buildForMask(mask);
|
||||
maskToStateSetMap.put(mask, cached);
|
||||
return cached;
|
||||
}
|
||||
|
||||
private static StateSet buildForMask(int mask) {
|
||||
int restore = 0;
|
||||
int bump = 0;
|
||||
final boolean blendSnapshot = (mask & (GL11.GL_COLOR_BUFFER_BIT | GL11.GL_ENABLE_BIT)) != 0;
|
||||
|
||||
if ((mask & GL11.GL_DEPTH_BUFFER_BIT) != 0) {
|
||||
restore |= R_DEPTH;
|
||||
}
|
||||
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
|
||||
restore |= R_BLEND | R_COLOR_MASK | R_CLEAR_COLOR | R_DRAW_BUFFER | R_LOGIC_OP;
|
||||
bump |= B_FRAG_COLORBUF;
|
||||
}
|
||||
if ((mask & GL11.GL_CURRENT_BIT) != 0) {
|
||||
restore |= R_SECONDARY_COLOR;
|
||||
bump |= B_CURRENT;
|
||||
}
|
||||
if ((mask & GL11.GL_STENCIL_BUFFER_BIT) != 0) {
|
||||
restore |= R_STENCIL;
|
||||
}
|
||||
if ((mask & GL11.GL_VIEWPORT_BIT) != 0) {
|
||||
restore |= R_VIEWPORT;
|
||||
}
|
||||
if ((mask & GL11.GL_LINE_BIT) != 0) {
|
||||
restore |= R_LINE;
|
||||
}
|
||||
if ((mask & GL11.GL_POINT_BIT) != 0) {
|
||||
restore |= R_POINT;
|
||||
}
|
||||
if ((mask & GL11.GL_POLYGON_BIT) != 0) {
|
||||
restore |= R_POLYGON;
|
||||
}
|
||||
if ((mask & GL11.GL_TEXTURE_BIT) != 0) {
|
||||
restore |= R_TEXTURE | R_ACTIVE_UNIT;
|
||||
bump |= B_FRAG_TEXTURE;
|
||||
}
|
||||
if ((mask & GL11.GL_LIGHTING_BIT) != 0) {
|
||||
bump |= B_LIGHTING;
|
||||
}
|
||||
if ((mask & GL11.GL_FOG_BIT) != 0) {
|
||||
bump |= B_FRAG_FOG;
|
||||
}
|
||||
if ((mask & GL11.GL_TRANSFORM_BIT) != 0) {
|
||||
bump |= B_TRANSFORM;
|
||||
}
|
||||
|
||||
final long[] members = Feature.maskToMembers(mask);
|
||||
|
||||
return new StateSet(mask, members, restore, bump, blendSnapshot);
|
||||
}
|
||||
}
|
||||
+10
-4
@@ -40,7 +40,7 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.mitchej123.lwjgl.LWJGLServiceProvider.LWJGL;
|
||||
import static org.taumc.celeritas.lwjgl.LWJGLServiceProvider.LWJGL;
|
||||
|
||||
/**
|
||||
* LWJGL2 GL implementation of {@link RenderBackend}.
|
||||
@@ -122,11 +122,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int queryDisplayRefreshRateHz() {
|
||||
protected long queryRefreshPeriodNanos() {
|
||||
final DisplayMode mode = Display.getDisplayMode();
|
||||
if (mode != null && mode.getFrequency() > 0) return mode.getFrequency();
|
||||
if (mode != null && mode.getFrequency() > 0) return periodFromIntegerHz(mode.getFrequency());
|
||||
final DisplayMode desktop = Display.getDesktopDisplayMode();
|
||||
return desktop == null ? 0 : desktop.getFrequency();
|
||||
return desktop == null ? 0L : periodFromIntegerHz(desktop.getFrequency());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,6 +134,12 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return caps != null && caps.GL_EXT_texture_filter_anisotropic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayFromWorkerThread(boolean processMessages) {
|
||||
if (processMessages) pumpDisplayMessages();
|
||||
Display.update(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {GL11.glFlush();}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
public final class MainThreadPump {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("MainThreadPump");
|
||||
|
||||
private static final Tracy.ZoneId Z_DISPLAY_MESSAGES = Tracy.zoneId("displayMessages", Tracy.COLOR_CLIENT);
|
||||
|
||||
private static final long PARK_MILLIS = 20L;
|
||||
private static final long STALL_WARN_NANOS = 5_000_000_000L;
|
||||
|
||||
public static void pollInput() {
|
||||
Keyboard.poll();
|
||||
Mouse.poll();
|
||||
}
|
||||
|
||||
private final Executor mainExecutor;
|
||||
private final BooleanSupplier isMainThread;
|
||||
private final Runnable pumpBody;
|
||||
private final Runnable pollBody;
|
||||
private final Runnable task = this::runPump;
|
||||
private final Object lock = new Object();
|
||||
|
||||
private boolean done;
|
||||
private Throwable failure;
|
||||
private ClassLoader callerLoader;
|
||||
|
||||
public MainThreadPump(Executor mainExecutor, BooleanSupplier isMainThread, Runnable pumpBody, Runnable pollBody) {
|
||||
this.mainExecutor = mainExecutor;
|
||||
this.isMainThread = isMainThread;
|
||||
this.pumpBody = pumpBody;
|
||||
this.pollBody = pollBody;
|
||||
}
|
||||
|
||||
public void pumpMessages() {
|
||||
Tracy.beginZone(Z_DISPLAY_MESSAGES);
|
||||
try {
|
||||
if (isMainThread.getAsBoolean()) {
|
||||
pumpBody.run();
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
runPumpOnMainThread();
|
||||
}
|
||||
}
|
||||
pollBody.run();
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
private void runPumpOnMainThread() {
|
||||
final Thread self = Thread.currentThread();
|
||||
callerLoader = self.getContextClassLoader();
|
||||
failure = null;
|
||||
done = false;
|
||||
mainExecutor.execute(task);
|
||||
boolean interrupted = false;
|
||||
boolean stallReported = false;
|
||||
final long stallAt = System.nanoTime() + STALL_WARN_NANOS;
|
||||
while (!done) {
|
||||
try {
|
||||
lock.wait(PARK_MILLIS);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
if (!done && !stallReported && System.nanoTime() - stallAt >= 0L) {
|
||||
stallReported = true;
|
||||
LOGGER.warn("Display message pump has been waiting on the RFB main thread for over {} ms", STALL_WARN_NANOS / 1_000_000L);
|
||||
}
|
||||
}
|
||||
if (interrupted) self.interrupt();
|
||||
final Throwable t = failure;
|
||||
if (t != null) {
|
||||
throw asUnchecked(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPump() {
|
||||
final Thread self = Thread.currentThread();
|
||||
final ClassLoader saved = self.getContextClassLoader();
|
||||
try {
|
||||
self.setContextClassLoader(callerLoader);
|
||||
pumpBody.run();
|
||||
} catch (Throwable t) {
|
||||
failure = t;
|
||||
} finally {
|
||||
try {
|
||||
self.setContextClassLoader(saved);
|
||||
} finally {
|
||||
synchronized (lock) {
|
||||
done = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RuntimeException asUnchecked(Throwable t) {
|
||||
if (t instanceof RuntimeException runtime) return runtime;
|
||||
if (t instanceof Error error) throw error;
|
||||
return new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm.backend;
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -12,6 +14,7 @@ import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,9 @@ public abstract class RenderBackend {
|
||||
|
||||
public final void setVSyncMode(VSyncMode preferred) {
|
||||
setVSyncPreference(preferred);
|
||||
publishVSyncMode(applyVSyncMode(preferred));
|
||||
final VSyncMode mode = applyVSyncMode(preferred);
|
||||
vsyncHonored = applyHonored();
|
||||
publishVSyncMode(mode);
|
||||
}
|
||||
|
||||
public final void setVSyncPreference(VSyncMode preferred) {
|
||||
@@ -90,50 +95,146 @@ public abstract class RenderBackend {
|
||||
|
||||
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
|
||||
|
||||
protected boolean applyHonored() { return true; }
|
||||
|
||||
private boolean vsyncHonored = true;
|
||||
|
||||
public final boolean vsyncHonored() { return vsyncHonored; }
|
||||
|
||||
public boolean supportsVSyncMode(VSyncMode mode) { return mode != VSyncMode.MAILBOX; }
|
||||
|
||||
protected final void publishVSyncMode(VSyncMode mode) { effectiveVSyncMode = mode; }
|
||||
protected final void publishVSyncMode(VSyncMode mode) {
|
||||
effectiveVSyncMode = mode;
|
||||
bumpDisplayGeneration();
|
||||
}
|
||||
|
||||
public final VSyncMode getEffectiveVSyncMode() { return effectiveVSyncMode; }
|
||||
|
||||
private int displayGeneration;
|
||||
|
||||
public final int displayGeneration() { return displayGeneration; }
|
||||
|
||||
protected final void bumpDisplayGeneration() { displayGeneration++; }
|
||||
|
||||
private static final long REFRESH_POLL_NANOS = 1_000_000_000L;
|
||||
private static final long MIN_PLAUSIBLE_PERIOD_NANOS = 1_000_000L;
|
||||
private static final long MAX_PLAUSIBLE_PERIOD_NANOS = 50_000_000L;
|
||||
|
||||
private long refreshPollNanos;
|
||||
private int cachedRefreshHz;
|
||||
private long cachedRefreshPeriodNanos;
|
||||
|
||||
public final int getDisplayRefreshRateHz() {
|
||||
public final long refreshPeriodNanos() {
|
||||
final long now = System.nanoTime();
|
||||
if (refreshPollNanos == 0L || now - refreshPollNanos >= REFRESH_POLL_NANOS) {
|
||||
refreshPollNanos = now;
|
||||
cachedRefreshHz = queryDisplayRefreshRateHz();
|
||||
final long fresh = plausiblePeriodNanos(queryRefreshPeriodNanos());
|
||||
if (fresh != cachedRefreshPeriodNanos) bumpDisplayGeneration();
|
||||
cachedRefreshPeriodNanos = fresh;
|
||||
}
|
||||
return cachedRefreshHz;
|
||||
return cachedRefreshPeriodNanos;
|
||||
}
|
||||
|
||||
protected int queryDisplayRefreshRateHz() { return 0; }
|
||||
protected long queryRefreshPeriodNanos() { return 0L; }
|
||||
|
||||
public static int refreshHzFrom(int numerator, int denominator, float fallbackHz) {
|
||||
if (numerator > 0 && denominator > 0) return (numerator + denominator - 1) / denominator;
|
||||
return fallbackHz > 0.0f ? (int) Math.ceil(fallbackHz) : 0;
|
||||
static long plausiblePeriodNanos(long periodNanos) {
|
||||
return (periodNanos >= MIN_PLAUSIBLE_PERIOD_NANOS && periodNanos <= MAX_PLAUSIBLE_PERIOD_NANOS) ? periodNanos : 0L;
|
||||
}
|
||||
|
||||
public static long periodFromRational(int numerator, int denominator, float fallbackHz) {
|
||||
if (numerator > 0 && denominator > 0) {
|
||||
final long scaledDenominator = 1_000_000_000L * denominator;
|
||||
return (scaledDenominator + numerator / 2) / numerator;
|
||||
}
|
||||
return fallbackHz > 0.0f ? Math.round(1_000_000_000.0 / fallbackHz) : 0L;
|
||||
}
|
||||
|
||||
private static final int[] TRUNCATED_NTSC_HZ = {23, 29, 59, 71, 119, 143, 239};
|
||||
|
||||
private static boolean isTruncatedNtscRate(int hz) {
|
||||
for (int candidate : TRUNCATED_NTSC_HZ) {
|
||||
if (candidate == hz) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static long periodFromIntegerHz(int hz) {
|
||||
if (hz <= 0) return 0L;
|
||||
if (isTruncatedNtscRate(hz)) {
|
||||
final long denominator = (hz + 1) * 1000L;
|
||||
return Math.round(1_000_000_000.0 * 1001 / denominator);
|
||||
}
|
||||
return Math.round(1_000_000_000.0 / hz);
|
||||
}
|
||||
|
||||
public static int hzFromPeriod(long periodNanos) {
|
||||
return periodNanos <= 0L ? 0 : (int) Math.round(1_000_000_000.0 / periodNanos);
|
||||
}
|
||||
|
||||
public boolean hasSwapchainBackpressure() { return false; }
|
||||
|
||||
private long frameGateNanos;
|
||||
private long frameGateEndNanos;
|
||||
public static final class GateSample {
|
||||
public long durationNanos;
|
||||
public long endNanos;
|
||||
public long gpuWaitNanos;
|
||||
}
|
||||
|
||||
public boolean gateAnchorsNextFrameStart() { return false; }
|
||||
static final class GateRecorder {
|
||||
|
||||
private long gateDurationNanos;
|
||||
private long gateEndNanos;
|
||||
private long gpuWaitNanos;
|
||||
|
||||
synchronized void recordGate(long durationNanos, long endNanos) {
|
||||
if (gateEndNanos != 0L && gateDurationNanos >= durationNanos) return;
|
||||
gateDurationNanos = durationNanos;
|
||||
gateEndNanos = endNanos;
|
||||
}
|
||||
|
||||
synchronized void recordGpuWait(long nanos) {
|
||||
gpuWaitNanos += nanos;
|
||||
}
|
||||
|
||||
synchronized void readGateSample(GateSample out) {
|
||||
final long end = gateEndNanos;
|
||||
out.durationNanos = end == 0L ? 0L : gateDurationNanos;
|
||||
out.endNanos = end;
|
||||
out.gpuWaitNanos = gpuWaitNanos;
|
||||
gpuWaitNanos = 0L;
|
||||
gateEndNanos = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private final GateRecorder gateRecorder = new GateRecorder();
|
||||
|
||||
public boolean wantsDisplayUpdateGateTiming() { return true; }
|
||||
|
||||
public void recordFrameGate(long durationNanos, long endNanos) {
|
||||
frameGateNanos = durationNanos;
|
||||
frameGateEndNanos = endNanos;
|
||||
public final void recordGate(long durationNanos, long endNanos) {
|
||||
gateRecorder.recordGate(durationNanos, endNanos);
|
||||
}
|
||||
|
||||
public long lastFrameGateNanos() { return frameGateNanos; }
|
||||
public final void recordGpuWait(long nanos) {
|
||||
gateRecorder.recordGpuWait(nanos);
|
||||
}
|
||||
|
||||
public long lastFrameGateEndNanos() { return frameGateEndNanos; }
|
||||
public final void readGateSample(GateSample out) {
|
||||
gateRecorder.readGateSample(out);
|
||||
}
|
||||
|
||||
public void awaitPresent() {}
|
||||
|
||||
public void parkNanos(long nanos) {
|
||||
LockSupport.parkNanos(nanos);
|
||||
}
|
||||
|
||||
public void pumpDisplayMessages() {
|
||||
Display.processMessages();
|
||||
}
|
||||
|
||||
private boolean presentSuppressed;
|
||||
|
||||
public final void setPresentSuppressed(boolean suppressed) { presentSuppressed = suppressed; }
|
||||
|
||||
public final boolean isPresentSuppressed() { return presentSuppressed; }
|
||||
|
||||
public void onFrameBegin() {
|
||||
if (GLStateManager.takeStateSeedPending()) {
|
||||
@@ -144,16 +245,23 @@ public abstract class RenderBackend {
|
||||
public void onFrameEnd() {}
|
||||
|
||||
/** Fired by lwjgl3ify Display before a mutation that recreates the swapchain. */
|
||||
public void onPreSwapchainInvalidatingChange(Object change) {}
|
||||
public void onPreSwapchainInvalidatingChange(Object change) { bumpDisplayGeneration(); }
|
||||
|
||||
public boolean handleMakeCurrent(Object drawable) { return false; }
|
||||
public boolean handleReleaseContext(Object drawable) { return false; }
|
||||
public boolean handleSwapBuffers() { return false; }
|
||||
public void onRenderThreadReleased(Thread thread) {}
|
||||
|
||||
public void updateDisplayFromWorkerThread(boolean processMessages) throws LWJGLException {
|
||||
GLStateManager.swapBuffers();
|
||||
if (processMessages) {
|
||||
pumpDisplayMessages();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTransferDebugInfo() { return null; }
|
||||
|
||||
public boolean bindVoxelizationRegion(int ssboBinding, int vertexBufferGlId, long openPass, float x, float y, float z) { return false; }
|
||||
public boolean bindVoxelizationRegion(int ssboBinding, long openPass, float x, float y, float z) { return false; }
|
||||
public long beginVoxelizationBatch(int ssboBinding) { return 0L; }
|
||||
public void voxelizeRange(long pass, int vertexOffset, int vertexCount) {}
|
||||
public void endVoxelizationBatch(long pass) {}
|
||||
|
||||
@@ -104,7 +104,9 @@ public class DSAARB extends DSAUnsupported {
|
||||
super.bindTextureToUnit(target != 0 ? target : GL11.GL_TEXTURE_2D, unit, texture);
|
||||
} else {
|
||||
RENDER_BACKEND.bindTextureUnit(unit, texture);
|
||||
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture, target);
|
||||
final var binding = GLStateManager.getTextures().getTextureUnitBindings(unit);
|
||||
binding.beforeModify();
|
||||
binding.setBinding(texture, target);
|
||||
GLStateManager.trackMaxBoundTextureUnit(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class CubeInstancedAttribs {
|
||||
|
||||
public static final int LOC_LIGHTMAP_SCALE = InstancedAttribs.LOC_LIGHTMAP;
|
||||
public static final int LOC_CUBE_TEX = 15;
|
||||
|
||||
public static final int OFFSET_LIGHTMAP_SCALE = InstancedAttribs.HEAD_SIZE;
|
||||
public static final int OFFSET_CUBE_TEX = OFFSET_LIGHTMAP_SCALE + 16;
|
||||
public static final int STRIDE = OFFSET_CUBE_TEX + 16;
|
||||
|
||||
public static void pointInstanceAttribs(long base) {
|
||||
InstancedAttribs.pointHead(base, STRIDE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP_SCALE, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP_SCALE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_CUBE_TEX, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CUBE_TEX);
|
||||
}
|
||||
|
||||
static void enableInstanceArrays() {
|
||||
InstancedAttribs.enableHeadArrays();
|
||||
InstancedAttribs.enableInstanced(LOC_CUBE_TEX);
|
||||
}
|
||||
|
||||
private CubeInstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
|
||||
public record CubeParams(float minX, float minY, float minZ, float spanX, float spanY, float spanZ, float u0, float v0, float uScaleX, float vScaleY, float uScaleZ, float vScaleZ) {
|
||||
|
||||
private static final float MIN_SPAN = 1.0e-4f;
|
||||
|
||||
public CubeParams {
|
||||
if (!(spanX > MIN_SPAN && spanY > MIN_SPAN && spanZ > MIN_SPAN)) throw new IllegalArgumentException("degenerate cube span");
|
||||
}
|
||||
|
||||
public static CubeParams of(float texWidth, float texHeight, boolean mirror, int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate) {
|
||||
if (texWidth <= 0.0f || texHeight <= 0.0f) return null;
|
||||
if (sizeX < 0 || sizeY < 0 || sizeZ < 0) return null;
|
||||
if (mirror && sizeX == 0) return null;
|
||||
final float minX = x - inflate;
|
||||
final float minY = y - inflate;
|
||||
final float minZ = z - inflate;
|
||||
final float spanX = ((x + sizeX) + inflate) - minX;
|
||||
final float spanY = ((y + sizeY) + inflate) - minY;
|
||||
final float spanZ = ((z + sizeZ) + inflate) - minZ;
|
||||
if (spanX <= MIN_SPAN || spanY <= MIN_SPAN || spanZ <= MIN_SPAN) return null;
|
||||
return new CubeParams(minX, minY, minZ, spanX, spanY, spanZ, texU / texWidth, texV / texHeight, (mirror ? -sizeX : sizeX) / texWidth, sizeY / texHeight, sizeZ / texWidth, sizeZ / texHeight);
|
||||
}
|
||||
|
||||
public void writeRows(long ptr, float[] mv, int off, float scale) {
|
||||
final float tx = minX * scale;
|
||||
final float ty = minY * scale;
|
||||
final float tz = minZ * scale;
|
||||
final float sx = spanX * scale;
|
||||
final float sy = spanY * scale;
|
||||
final float sz = spanZ * scale;
|
||||
for (int r = 0; r < 3; r++) {
|
||||
final float c0 = mv[off + r];
|
||||
final float c1 = mv[off + 4 + r];
|
||||
final float c2 = mv[off + 8 + r];
|
||||
final long row = ptr + InstancedAttribs.OFFSET_ROWS + r * 16L;
|
||||
memPutFloat(row, c0 * sx);
|
||||
memPutFloat(row + 4, c1 * sy);
|
||||
memPutFloat(row + 8, c2 * sz);
|
||||
memPutFloat(row + 12, mv[off + 12 + r] + c0 * tx + c1 * ty + c2 * tz);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, u0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, v0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -12,7 +14,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
|
||||
* long[0]: global bits (14) + unit 0 (47 bits at offset 14)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,7 +65,7 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 13;
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
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
|
||||
@@ -72,6 +74,7 @@ public final class FragmentKey {
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
private static final int BIT_OVERLAY_INSTANCED = 13;
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -103,11 +106,15 @@ public final class FragmentKey {
|
||||
* @return number of significant longs (1..4)
|
||||
*/
|
||||
public static int packFromState(long[] out) {
|
||||
return packFromState(out, GLStateManager.ctx());
|
||||
}
|
||||
|
||||
public static int packFromState(long[] out, GLContextState glCtx) {
|
||||
long global = 0;
|
||||
|
||||
// Fog
|
||||
if (GLStateManager.getFogMode().isEnabled()) {
|
||||
final int fogMode = switch (GLStateManager.getFogState().getFogMode()) {
|
||||
if (glCtx.fogMode.isEnabled()) {
|
||||
final int fogMode = switch (glCtx.fogState.getFogMode()) {
|
||||
case GL11.GL_LINEAR -> FOG_LINEAR;
|
||||
case GL11.GL_EXP -> FOG_EXP;
|
||||
case GL11.GL_EXP2 -> FOG_EXP2;
|
||||
@@ -117,13 +124,15 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
// Alpha test
|
||||
if (GLStateManager.getAlphaTest().isEnabled()) {
|
||||
if (glCtx.alphaTest.isEnabled()) {
|
||||
global |= (1L << BIT_ALPHA_TEST);
|
||||
global |= ((long) (encodeAlphaFunc(GLStateManager.getAlphaState().getFunction()) & 0x7)) << BIT_ALPHA_FUNC;
|
||||
global |= ((long) (encodeAlphaFunc(glCtx.alphaState.getFunction()) & 0x7)) << BIT_ALPHA_FUNC;
|
||||
}
|
||||
|
||||
// Damage overlay
|
||||
if (GLStateManager.getOverlayA() != 0.0f) {
|
||||
if (GLStateManager.ffpInstancing.hasInstanceHead()) {
|
||||
global |= (1L << BIT_OVERLAY_INSTANCED);
|
||||
} else if (glCtx.overlayA != 0.0f) {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
@@ -132,22 +141,23 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
// Separate specular / color sum
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
if (glCtx.lightingState.isEnabled()
|
||||
&& glCtx.lightModel.colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
global |= (1L << BIT_SEPARATE_SPECULAR);
|
||||
} else if (GLStateManager.getColorSumState().isEnabled()) {
|
||||
} else if (glCtx.colorSumState.isEnabled()) {
|
||||
global |= (1L << BIT_COLOR_SUM);
|
||||
}
|
||||
|
||||
// Per-unit state
|
||||
final TextureUnitArray textures = glCtx.textures;
|
||||
int highestEnabled = -1;
|
||||
long unit0Bits = 0;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
// 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;
|
||||
final boolean texEnabled = textures.getTextureUnitStates(i).isEnabled() && textures.getTextureUnitBindings(i).getBinding() != 0;
|
||||
if (texEnabled) highestEnabled = i;
|
||||
|
||||
final long unitBits = packUnit(i, texEnabled);
|
||||
final long unitBits = packUnit(i, texEnabled, textures);
|
||||
if (i == 0) {
|
||||
unit0Bits = unitBits;
|
||||
} else {
|
||||
@@ -163,10 +173,10 @@ public final class FragmentKey {
|
||||
return Math.max(1, nrEnabled);
|
||||
}
|
||||
|
||||
private static long packUnit(int unitIndex, boolean texEnabled) {
|
||||
private static long packUnit(int unitIndex, boolean texEnabled, TextureUnitArray textures) {
|
||||
if (!texEnabled) return 0;
|
||||
|
||||
final TexEnvState envState = GLStateManager.getTextures().getTexEnvState(unitIndex);
|
||||
final TexEnvState envState = textures.getTexEnvState(unitIndex);
|
||||
final int mode = encodeTexEnvMode(envState.mode);
|
||||
|
||||
long bits = 1L; // enabled
|
||||
@@ -245,6 +255,7 @@ public final class FragmentKey {
|
||||
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; }
|
||||
public boolean overlayInstanced() { return ((packed[0] >> BIT_OVERLAY_INSTANCED) & 1) != 0; }
|
||||
|
||||
private long unitBits(int i) {
|
||||
return (i == 0) ? (packed[0] >>> GLOBAL_BITS) : packed[i];
|
||||
@@ -378,10 +389,7 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d",
|
||||
fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-",
|
||||
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(), alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(), overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -35,6 +35,9 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitInputs(StringBuilder sb, FragmentKey key) {
|
||||
sb.append("// Inputs from vertex shader\n");
|
||||
sb.append("in vec4 v_Color;\n");
|
||||
if (key.overlayInstanced()) {
|
||||
sb.append("in vec4 v_Overlay;\n");
|
||||
}
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor;\n");
|
||||
}
|
||||
@@ -116,8 +119,11 @@ public final class FragmentShaderGenerator {
|
||||
* Emit the modern-style damage overlay mix.
|
||||
*/
|
||||
private static void emitOverlay(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.overlayEnabled()) return;
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
if (key.overlayInstanced()) {
|
||||
sb.append(" color.rgb = mix(color.rgb, v_Overlay.rgb, v_Overlay.a);\n");
|
||||
} else if (key.overlayEnabled()) {
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitSimpleUnit(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, String assign) {
|
||||
|
||||
@@ -1,18 +1,84 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutLong;
|
||||
|
||||
public final class InstancedAttribs {
|
||||
|
||||
public static final int LOC_MATRIX_COL0 = 5;
|
||||
public static final int LOC_MATRIX_COL1 = 6;
|
||||
public static final int LOC_MATRIX_COL2 = 7;
|
||||
public static final int LOC_MATRIX_COL3 = 8;
|
||||
public static final int LOC_COLOR = 9;
|
||||
public static final int LOC_ROW0 = 5;
|
||||
public static final int LOC_ROW1 = 6;
|
||||
public static final int LOC_ROW2 = 7;
|
||||
public static final int LOC_COLOR = 8;
|
||||
public static final int LOC_OVERLAY = 9;
|
||||
public static final int LOC_LIGHTMAP = 10;
|
||||
public static final int LOC_ENTITY = 11;
|
||||
|
||||
public static final int OFFSET_MATRIX = 0;
|
||||
public static final int OFFSET_COLOR = 64;
|
||||
public static final int OFFSET_LIGHTMAP = 68;
|
||||
public static final int STRIDE = 80;
|
||||
public static final int OFFSET_ROWS = 0;
|
||||
public static final int OFFSET_COLOR = 48;
|
||||
public static final int OFFSET_OVERLAY = 52;
|
||||
public static final int OFFSET_ENTITY = 56;
|
||||
public static final int HEAD_SIZE = 64;
|
||||
public static final int OFFSET_LIGHTMAP = HEAD_SIZE;
|
||||
public static final int STRIDE = 72;
|
||||
|
||||
public static void writeHead(long ptr, float[] colMajor, int off, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
writeRows(ptr, colMajor, off);
|
||||
writeTail(ptr, colorABGR, overlayABGR, entityInfo);
|
||||
}
|
||||
|
||||
private static void writeRows(long ptr, float[] colMajor, int off) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
final long row = ptr + OFFSET_ROWS + r * 16L;
|
||||
memPutFloat(row, colMajor[off + r]);
|
||||
memPutFloat(row + 4, colMajor[off + 4 + r]);
|
||||
memPutFloat(row + 8, colMajor[off + 8 + r]);
|
||||
memPutFloat(row + 12, colMajor[off + 12 + r]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeTail(long ptr, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
memPutInt(ptr + OFFSET_COLOR, colorABGR);
|
||||
memPutInt(ptr + OFFSET_OVERLAY, overlayABGR);
|
||||
memPutLong(ptr + OFFSET_ENTITY, entityInfo);
|
||||
}
|
||||
|
||||
public static void writeLightmap(long ptr, int packedLight) {
|
||||
memPutFloat(ptr, packedLight & 0xFFFF);
|
||||
memPutFloat(ptr + 4, (packedLight >>> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
public static long packEntityInfo(int entity, int blockEntity, int item) {
|
||||
return (entity & 0xFFFFL) | ((blockEntity & 0xFFFFL) << 16) | ((item & 0xFFFFL) << 32);
|
||||
}
|
||||
|
||||
public static void pointHead(long base, int stride) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
|
||||
}
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_SHORT, stride, base + OFFSET_ENTITY);
|
||||
}
|
||||
|
||||
public static void pointTemplate(long base) {
|
||||
pointHead(base, STRIDE);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
|
||||
}
|
||||
|
||||
public static void enableHeadArrays() {
|
||||
for (int loc = LOC_ROW0; loc <= LOC_ENTITY; loc++) {
|
||||
enableInstanced(loc);
|
||||
}
|
||||
}
|
||||
|
||||
static void enableInstanced(int loc) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
}
|
||||
|
||||
private InstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class InstancedGlslHelpers {
|
||||
|
||||
private InstancedGlslHelpers() {}
|
||||
|
||||
public static String mat4FromRows(String row0, String row1, String row2) {
|
||||
return "mat4("
|
||||
+ "vec4(" + row0 + ".x, " + row1 + ".x, " + row2 + ".x, 0.0), "
|
||||
+ "vec4(" + row0 + ".y, " + row1 + ".y, " + row2 + ".y, 0.0), "
|
||||
+ "vec4(" + row0 + ".z, " + row1 + ".z, " + row2 + ".z, 0.0), "
|
||||
+ "vec4(" + row0 + ".w, " + row1 + ".w, " + row2 + ".w, 1.0))";
|
||||
}
|
||||
|
||||
public static String mat3FromRows(String row0, String row1, String row2) {
|
||||
return "mat3("
|
||||
+ "normalize(vec3(" + row0 + ".x, " + row1 + ".x, " + row2 + ".x)), "
|
||||
+ "normalize(vec3(" + row0 + ".y, " + row1 + ".y, " + row2 + ".y)), "
|
||||
+ "normalize(vec3(" + row0 + ".z, " + row1 + ".z, " + row2 + ".z)))";
|
||||
}
|
||||
|
||||
public static String particlePos(String centerHalf, String offset) {
|
||||
return "vec4(" + centerHalf + ".xyz + " + offset + " * " + centerHalf + ".w, 1.0)";
|
||||
}
|
||||
|
||||
public static String particleUv(String uv, String corner) {
|
||||
return "vec4("
|
||||
+ "mix(" + uv + ".x, " + uv + ".z, " + corner + ".x), "
|
||||
+ "mix(" + uv + ".y, " + uv + ".w, " + corner + ".y), "
|
||||
+ "0.0, 1.0)";
|
||||
}
|
||||
|
||||
public static String cubePrelude(String prefix, String mid, String delta, String normal, String tex, String sep) {
|
||||
return "float " + prefix + "cubeMirror = " + tex + ".z < 0.0 ? 1.0 : 0.0;" + sep
|
||||
+ "vec2 " + prefix + "cubeMidU = " + mid + ".xy - " + prefix + "cubeMirror * " + normal + ".x;" + sep
|
||||
+ "vec2 " + prefix + "cubeU = " + prefix + "cubeMidU + (1.0 - 2.0 * " + prefix + "cubeMirror) * " + delta + ".xy;" + sep
|
||||
+ "vec2 " + prefix + "cubeV = " + mid + ".zw + " + delta + ".zw;";
|
||||
}
|
||||
|
||||
public static String cubeUv(String tex, String lightScale, String uA, String uZ, String vA, String vZ) {
|
||||
return "vec4("
|
||||
+ tex + ".x + " + uA + " * abs(" + tex + ".z) + " + uZ + " * " + lightScale + ".z, "
|
||||
+ tex + ".y + " + vA + " * " + tex + ".w + " + vZ + " * " + lightScale + ".w, "
|
||||
+ "0.0, 1.0)";
|
||||
}
|
||||
|
||||
public static String[] attributeDecls(String prefix, Instancing kind) {
|
||||
final ArrayList<String> decls = new ArrayList<>(9);
|
||||
switch (kind) {
|
||||
case NONE -> {}
|
||||
case TEMPLATE -> addHead(decls, prefix, "vec2", "InstLightmap");
|
||||
case CUBE -> {
|
||||
decls.add(decl(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), "vec4", prefix + "CubeMid"));
|
||||
decls.add(decl(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation(), "vec4", prefix + "CubeDelta"));
|
||||
addHead(decls, prefix, "vec4", "InstLightmapScale");
|
||||
decls.add(decl(CubeInstancedAttribs.LOC_CUBE_TEX, "vec4", prefix + "CubeTex"));
|
||||
}
|
||||
case PARTICLE -> {
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_CENTER_HALF, "vec4", prefix + "InstCenterHalf"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_UV, "vec4", prefix + "InstUv"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_COLOR, "vec4", prefix + "InstColor"));
|
||||
decls.add(decl(ParticleInstancedAttribs.LOC_LIGHTMAP, "vec2", prefix + "InstLightmap"));
|
||||
}
|
||||
}
|
||||
return decls.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static void addHead(ArrayList<String> decls, String prefix, String lightmapType, String lightmapName) {
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW0, "vec4", prefix + "InstRow0"));
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW1, "vec4", prefix + "InstRow1"));
|
||||
decls.add(decl(InstancedAttribs.LOC_ROW2, "vec4", prefix + "InstRow2"));
|
||||
decls.add(decl(InstancedAttribs.LOC_COLOR, "vec4", prefix + "InstColor"));
|
||||
decls.add(decl(InstancedAttribs.LOC_OVERLAY, "vec4", prefix + "InstOverlay"));
|
||||
decls.add(decl(InstancedAttribs.LOC_LIGHTMAP, lightmapType, prefix + lightmapName));
|
||||
}
|
||||
|
||||
private static String decl(int loc, String type, String name) {
|
||||
return "layout(location = " + loc + ") in " + type + " " + name + ";";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public enum Instancing {
|
||||
NONE(""),
|
||||
TEMPLATE("_instanced"),
|
||||
CUBE("_cubeinstanced"),
|
||||
PARTICLE("_particleinstanced");
|
||||
|
||||
public static final Instancing[] VALUES = values();
|
||||
|
||||
public final String variantSuffix;
|
||||
|
||||
Instancing(String variantSuffix) {
|
||||
this.variantSuffix = variantSuffix;
|
||||
}
|
||||
|
||||
public boolean hasInstanceHead() {
|
||||
return this == TEMPLATE || this == CUBE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class ParticleInstancedAttribs {
|
||||
|
||||
public static final int LOC_CENTER_HALF = 5;
|
||||
public static final int LOC_UV = 6;
|
||||
public static final int LOC_COLOR = InstancedAttribs.LOC_COLOR;
|
||||
public static final int LOC_LIGHTMAP = InstancedAttribs.LOC_LIGHTMAP;
|
||||
|
||||
public static final int OFFSET_CENTER_HALF = 0;
|
||||
public static final int OFFSET_UV = 16;
|
||||
public static final int OFFSET_COLOR = 32;
|
||||
public static final int OFFSET_LIGHTMAP = 36;
|
||||
public static final int STRIDE = 48;
|
||||
|
||||
public static void pointInstanceAttribs(long base) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_CENTER_HALF, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CENTER_HALF);
|
||||
GLStateManager.glVertexAttribPointer(LOC_UV, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_UV);
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
|
||||
}
|
||||
|
||||
static void enableInstanceArrays() {
|
||||
InstancedAttribs.enableInstanced(LOC_CENTER_HALF);
|
||||
InstancedAttribs.enableInstanced(LOC_UV);
|
||||
InstancedAttribs.enableInstanced(LOC_COLOR);
|
||||
InstancedAttribs.enableInstanced(LOC_LIGHTMAP);
|
||||
}
|
||||
|
||||
public static void writeInstance(long ptr, float cx, float cy, float cz, float half, float u0, float v0, float u1, float v1, int colorABGR, int packedLight) {
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF, cx);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 4, cy);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 8, cz);
|
||||
memPutFloat(ptr + OFFSET_CENTER_HALF + 12, half);
|
||||
memPutFloat(ptr + OFFSET_UV, u0);
|
||||
memPutFloat(ptr + OFFSET_UV + 4, v0);
|
||||
memPutFloat(ptr + OFFSET_UV + 8, u1);
|
||||
memPutFloat(ptr + OFFSET_UV + 12, v1);
|
||||
memPutInt(ptr + OFFSET_COLOR, colorABGR);
|
||||
InstancedAttribs.writeLightmap(ptr + OFFSET_LIGHTMAP, packedLight);
|
||||
}
|
||||
|
||||
private ParticleInstancedAttribs() {}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class ParticleQuadMesh {
|
||||
|
||||
public static final int VERTEX_FLAGS = VertexFlags.TEXTURE_BIT;
|
||||
|
||||
public static final int VERTEX_COUNT = 4;
|
||||
private static final int VERTEX_STRIDE = 24;
|
||||
|
||||
private static final int OFFSET_OFFSET = 0;
|
||||
private static final int OFFSET_CORNER = 12;
|
||||
private static final int OFFSET_TANGENT = 20;
|
||||
|
||||
private static final float[] CORNER_A = { -1.0f, -1.0f, 1.0f, 1.0f };
|
||||
private static final float[] CORNER_B = { -1.0f, 1.0f, 1.0f, -1.0f };
|
||||
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
private static ByteBuffer staging;
|
||||
private static boolean written;
|
||||
private static float lastRotX, lastRotXZ, lastRotZ, lastRotYZ, lastRotXY;
|
||||
|
||||
private ParticleQuadMesh() {}
|
||||
|
||||
public static float cornerA(int corner) {
|
||||
return CORNER_A[corner];
|
||||
}
|
||||
|
||||
public static float cornerB(int corner) {
|
||||
return CORNER_B[corner];
|
||||
}
|
||||
|
||||
public static float offsetX(float a, float b, float rotX, float rotYZ) {
|
||||
return a * rotX + b * rotYZ;
|
||||
}
|
||||
|
||||
public static float offsetY(float b, float rotXZ) {
|
||||
return b * rotXZ;
|
||||
}
|
||||
|
||||
public static float offsetZ(float a, float b, float rotZ, float rotXY) {
|
||||
return a * rotZ + b * rotXY;
|
||||
}
|
||||
|
||||
public static int vao() {
|
||||
if (vao == 0) build();
|
||||
return vao;
|
||||
}
|
||||
|
||||
public static void update(float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
if (vao == 0) build();
|
||||
if (written && rotX == lastRotX && rotXZ == lastRotXZ && rotZ == lastRotZ && rotYZ == lastRotYZ && rotXY == lastRotXY) {
|
||||
return;
|
||||
}
|
||||
lastRotX = rotX;
|
||||
lastRotXZ = rotXZ;
|
||||
lastRotZ = rotZ;
|
||||
lastRotYZ = rotYZ;
|
||||
lastRotXY = rotXY;
|
||||
written = true;
|
||||
|
||||
write(memAddress0(staging), rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
staging.position(0).limit(VERTEX_COUNT * VERTEX_STRIDE);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, staging);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
private static void write(long dst, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
|
||||
final int tangent = packTangent(rotX, 0.0f, rotZ);
|
||||
long ptr = dst;
|
||||
for (int i = 0; i < VERTEX_COUNT; i++) {
|
||||
final float a = cornerA(i);
|
||||
final float b = cornerB(i);
|
||||
memPutFloat(ptr + OFFSET_OFFSET, offsetX(a, b, rotX, rotYZ));
|
||||
memPutFloat(ptr + OFFSET_OFFSET + 4, offsetY(b, rotXZ));
|
||||
memPutFloat(ptr + OFFSET_OFFSET + 8, offsetZ(a, b, rotZ, rotXY));
|
||||
memPutFloat(ptr + OFFSET_CORNER, (a + 1.0f) * 0.5f);
|
||||
memPutFloat(ptr + OFFSET_CORNER + 4, (b + 1.0f) * 0.5f);
|
||||
memPutInt(ptr + OFFSET_TANGENT, tangent);
|
||||
ptr += VERTEX_STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
private static int packTangent(float x, float y, float z) {
|
||||
final float lenSq = x * x + y * y + z * z;
|
||||
final float nx, ny, nz;
|
||||
if (lenSq < 1.0e-12f) {
|
||||
nx = 1.0f;
|
||||
ny = 0.0f;
|
||||
nz = 0.0f;
|
||||
} else {
|
||||
final float inv = (float) (1.0 / Math.sqrt(lenSq));
|
||||
nx = x * inv;
|
||||
ny = y * inv;
|
||||
nz = z * inv;
|
||||
}
|
||||
return NormI8.pack(nx, ny, nz) | (127 << 24);
|
||||
}
|
||||
|
||||
private static void build() {
|
||||
final int savedVAO = GLStateManager.getBoundVAO();
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) VERTEX_COUNT * VERTEX_STRIDE, GL15.GL_DYNAMIC_DRAW);
|
||||
|
||||
attrib(VertexFormatElement.Usage.POSITION.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_OFFSET);
|
||||
attrib(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), 2, GL11.GL_FLOAT, false, OFFSET_CORNER);
|
||||
attrib(ImmediateExtendedAttribHandler.LOC_TANGENT, 4, GL11.GL_BYTE, true, OFFSET_TANGENT);
|
||||
|
||||
ParticleInstancedAttribs.enableInstanceArrays();
|
||||
|
||||
GLStateManager.glBindVertexArray(savedVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
|
||||
if (staging == null) staging = memAlloc(VERTEX_COUNT * VERTEX_STRIDE);
|
||||
written = false;
|
||||
}
|
||||
|
||||
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
if (vao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
vao = 0;
|
||||
}
|
||||
if (staging != null) {
|
||||
memFree(staging);
|
||||
staging = null;
|
||||
}
|
||||
written = false;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
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.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec3fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
@@ -50,8 +52,8 @@ public final class ShaderManager {
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
@Getter private static final Vec3fStack normalStack = new Vec3fStack(currentNormal);
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0]);
|
||||
@Getter private static final Vec3fStack normalStack = new Vec3fStack(currentNormal, StackIdAllocator.nextStaticId());
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0], StackIdAllocator.nextStaticId());
|
||||
@Getter private static int normalGeneration;
|
||||
@Getter private static int texCoordGeneration;
|
||||
|
||||
@@ -97,6 +99,8 @@ public final class ShaderManager {
|
||||
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
|
||||
final VertexKey vk = VertexKey.fromPacked(vkPacked);
|
||||
VertexShaderGenerator.generate(vk);
|
||||
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.CUBE)));
|
||||
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.PARTICLE)));
|
||||
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
|
||||
GeometryShaderGenerator.generate(vk);
|
||||
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
|
||||
@@ -127,6 +131,7 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
@@ -148,15 +153,16 @@ public final class ShaderManager {
|
||||
if (!active) return;
|
||||
|
||||
preDrawCalls++;
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch);
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch, glCtx);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(currentFKScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask);
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask, glCtx);
|
||||
|
||||
if (vkPacked != currentVertexKeyPacked || !Arrays.equals(currentFKScratch, 0, fkLen, currentFKPacked, 0, currentFKLen)) {
|
||||
commitVariant(vkPacked, fkLen);
|
||||
}
|
||||
|
||||
uploadUniforms();
|
||||
uploadUniforms(glCtx);
|
||||
}
|
||||
|
||||
private void commitVariant(long vkPacked, int fkLen) {
|
||||
@@ -172,9 +178,9 @@ public final class ShaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadUniforms() {
|
||||
private void uploadUniforms(GLContextState glCtx) {
|
||||
if (currentProgram != null) {
|
||||
uniforms.upload();
|
||||
uniforms.upload(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,16 +193,19 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public static void setCurrentNormal(float x, float y, float z) {
|
||||
normalStack.beforeModify();
|
||||
currentNormal.set(x, y, z);
|
||||
normalGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(float s, float t, float r, float q) {
|
||||
texCoordStack.beforeModify();
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(int unit, float s, float t, float r, float q) {
|
||||
if (unit == 0) texCoordStack.beforeModify();
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
@@ -7,6 +8,7 @@ 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.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
@@ -81,43 +83,43 @@ public class Uniforms {
|
||||
private static final double LN2 = java.lang.Math.log(2.0);
|
||||
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
|
||||
|
||||
public void upload() {
|
||||
public void upload(GLContextState glCtx) {
|
||||
boolean dirty = false;
|
||||
contentChanged = false;
|
||||
|
||||
final int mvG = GLStateManager.getMvGeneration();
|
||||
final int projG = GLStateManager.getProjGeneration();
|
||||
final int mvG = glCtx.mvGeneration;
|
||||
final int projG = glCtx.projGeneration;
|
||||
final boolean mvChanged = mvG != mvGen;
|
||||
final boolean projChanged = projG != projGen;
|
||||
if (mvChanged || projChanged) {
|
||||
stageMatrices(mvChanged, projChanged);
|
||||
stageMatrices(mvChanged, projChanged, glCtx);
|
||||
mvGen = mvG;
|
||||
projGen = projG;
|
||||
stagedMatrices++;
|
||||
dirty = true;
|
||||
}
|
||||
final int mvLinG = GLStateManager.getMvLinearGeneration();
|
||||
final int mvLinG = glCtx.mvLinearGeneration;
|
||||
if (mvLinG != mvLinearGen) {
|
||||
stageNormalMatrix();
|
||||
stageNormalMatrix(glCtx);
|
||||
mvLinearGen = mvLinG;
|
||||
dirty = true;
|
||||
}
|
||||
final int texMatG = GLStateManager.getTexMatrixGeneration();
|
||||
final int texMatG = glCtx.texMatrixGeneration;
|
||||
if (texMatG != texMatGen) {
|
||||
stageTextureMatrices();
|
||||
stageTextureMatrices(glCtx);
|
||||
texMatGen = texMatG;
|
||||
dirty = true;
|
||||
}
|
||||
final int litG = GLStateManager.getLightingGeneration();
|
||||
final int litG = glCtx.lightingGeneration;
|
||||
if (litG != lightingGen) {
|
||||
stageLighting();
|
||||
stageLighting(glCtx);
|
||||
lightingGen = litG;
|
||||
stagedLighting++;
|
||||
dirty = true;
|
||||
}
|
||||
final int colG = GLStateManager.getColorGeneration();
|
||||
final int colG = glCtx.colorGeneration;
|
||||
if (colG != colorGen) {
|
||||
stageCurrentColor();
|
||||
stageCurrentColor(glCtx);
|
||||
colorGen = colG;
|
||||
stagedColor++;
|
||||
dirty = true;
|
||||
@@ -149,35 +151,36 @@ public class Uniforms {
|
||||
stagedLightmap++;
|
||||
dirty = true;
|
||||
}
|
||||
final int tgG = GLStateManager.getTexGenGeneration();
|
||||
final int tgG = glCtx.texGenGeneration;
|
||||
if (tgG != texGenGen) {
|
||||
stageTexGen();
|
||||
stageTexGen(glCtx);
|
||||
texGenGen = tgG;
|
||||
stagedTexGen++;
|
||||
dirty = true;
|
||||
}
|
||||
final int cpG = GLStateManager.getClipPlaneGeneration();
|
||||
final int cpG = glCtx.clipPlaneGeneration;
|
||||
if (cpG != clipPlaneGen) {
|
||||
stageClipPlanes();
|
||||
stageClipPlanes(glCtx);
|
||||
clipPlaneGen = cpG;
|
||||
stagedClipPlanes++;
|
||||
dirty = true;
|
||||
}
|
||||
final int fragG = GLStateManager.getFragmentGeneration();
|
||||
final int fragG = glCtx.fragmentGeneration;
|
||||
if (fragG != fragmentGen) {
|
||||
stageFragment();
|
||||
stageFragment(glCtx);
|
||||
fragmentGen = fragG;
|
||||
stagedFragment++;
|
||||
dirty = true;
|
||||
}
|
||||
final float lw = GLStateManager.getLineState().getWidth();
|
||||
final LineState line = glCtx.lineState;
|
||||
final float lw = line.getWidth();
|
||||
if (lw != lineWidth) {
|
||||
putFloat(FFPUniformBlock.LINE_WIDTH, lw);
|
||||
lineWidth = lw;
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
final ViewportState vp = glCtx.viewportState;
|
||||
if (vp.x != viewportX || vp.y != viewportY || vp.width != viewportWidth || vp.height != viewportHeight) {
|
||||
putFloat(FFPUniformBlock.VIEWPORT_SIZE, vp.width);
|
||||
putFloat(FFPUniformBlock.VIEWPORT_SIZE + 4, vp.height);
|
||||
@@ -189,8 +192,7 @@ public class Uniforms {
|
||||
stagedMisc++;
|
||||
dirty = true;
|
||||
}
|
||||
final LineState line = GLStateManager.getLineState();
|
||||
final int stippleFactor = line.getStippleFactor() < 1 ? 1 : line.getStippleFactor();
|
||||
final int stippleFactor = Math.max(line.getStippleFactor(), 1);
|
||||
final int stipplePacked = (line.getStipplePattern() & 0xFFFF) | (stippleFactor << 16);
|
||||
if (stipplePacked != lineStipple) {
|
||||
putInt(FFPUniformBlock.LINE_STIPPLE, stipplePacked);
|
||||
@@ -215,9 +217,9 @@ public class Uniforms {
|
||||
}
|
||||
}
|
||||
|
||||
private void stageMatrices(boolean mvChanged, boolean projChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
private void stageMatrices(boolean mvChanged, boolean projChanged, GLContextState glCtx) {
|
||||
final Matrix4f mv = glCtx.modelViewMatrix;
|
||||
final Matrix4f proj = glCtx.projectionMatrix;
|
||||
if (mvChanged) {
|
||||
putMat4(FFPUniformBlock.MODEL_VIEW_MATRIX, mv);
|
||||
}
|
||||
@@ -228,8 +230,8 @@ public class Uniforms {
|
||||
putMat4(FFPUniformBlock.MVP_MATRIX, mvpMatrix);
|
||||
}
|
||||
|
||||
private void stageNormalMatrix() {
|
||||
GLStateManager.getModelViewMatrix().normal(normalMatrix);
|
||||
private void stageNormalMatrix(GLContextState glCtx) {
|
||||
glCtx.modelViewMatrix.normal(normalMatrix);
|
||||
putMat3(FFPUniformBlock.NORMAL_MATRIX, normalMatrix);
|
||||
putFloat(FFPUniformBlock.NORMAL_SCALE, rescaleFactor(normalMatrix, tempVec3));
|
||||
}
|
||||
@@ -239,18 +241,19 @@ public class Uniforms {
|
||||
return f > 1.0e-12f ? 1.0f / (float) Math.sqrt(f) : 1.0f;
|
||||
}
|
||||
|
||||
private void stageTextureMatrices() {
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_0, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_2, GLStateManager.getTextures().getTextureUnitMatrix(2));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_3, GLStateManager.getTextures().getTextureUnitMatrix(3));
|
||||
putMat4(FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, GLStateManager.getTextures().getTextureUnitMatrix(1));
|
||||
private void stageTextureMatrices(GLContextState glCtx) {
|
||||
final TextureUnitArray textures = glCtx.textures;
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_0, textures.getTextureUnitMatrix(0));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_2, textures.getTextureUnitMatrix(2));
|
||||
putMat4(FFPUniformBlock.TEXTURE_MATRIX_3, textures.getTextureUnitMatrix(3));
|
||||
putMat4(FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, textures.getTextureUnitMatrix(1));
|
||||
}
|
||||
|
||||
private void stageLighting() {
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
final Vector4f lmAmbient = GLStateManager.getLightModel().ambient;
|
||||
final LightState light0 = GLStateManager.getLightDataStates()[0];
|
||||
final LightState light1 = GLStateManager.getLightDataStates()[1];
|
||||
private void stageLighting(GLContextState glCtx) {
|
||||
final MaterialState mat = glCtx.frontMaterial;
|
||||
final Vector4f lmAmbient = glCtx.lightModel.ambient;
|
||||
final LightState light0 = glCtx.lightDataStates[0];
|
||||
final LightState light1 = glCtx.lightDataStates[1];
|
||||
|
||||
putVec4(FFPUniformBlock.LIGHT_MODEL_AMBIENT, lmAmbient);
|
||||
putVec4(FFPUniformBlock.MATERIAL_EMISSION, mat.emission);
|
||||
@@ -284,8 +287,8 @@ public class Uniforms {
|
||||
putVec3(offset, lightVal.x * materialVal.x, lightVal.y * materialVal.y, lightVal.z * materialVal.z);
|
||||
}
|
||||
|
||||
private void stageCurrentColor() {
|
||||
final var color = GLStateManager.getColor();
|
||||
private void stageCurrentColor(GLContextState glCtx) {
|
||||
final var color = glCtx.color;
|
||||
putVec4(FFPUniformBlock.CURRENT_COLOR,
|
||||
Math.clamp(0f, 1f, color.getRed()),
|
||||
Math.clamp(0f, 1f, color.getGreen()),
|
||||
@@ -293,8 +296,8 @@ public class Uniforms {
|
||||
Math.clamp(0f, 1f, color.getAlpha()));
|
||||
}
|
||||
|
||||
private void stageTexGen() {
|
||||
final TexGenState tg = GLStateManager.getTextures().getTexGenState(0);
|
||||
private void stageTexGen(GLContextState glCtx) {
|
||||
final TexGenState tg = glCtx.textures.getTexGenState(0);
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_S, tg.getObjectPlane(GL11.GL_S));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_T, tg.getObjectPlane(GL11.GL_T));
|
||||
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_R, tg.getObjectPlane(GL11.GL_R));
|
||||
@@ -305,8 +308,8 @@ public class Uniforms {
|
||||
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_Q, tg.getEyePlane(GL11.GL_Q));
|
||||
}
|
||||
|
||||
private void stageClipPlanes() {
|
||||
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
|
||||
private void stageClipPlanes(GLContextState glCtx) {
|
||||
final ClipPlaneState cps = glCtx.clipPlaneState;
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
@@ -317,24 +320,24 @@ public class Uniforms {
|
||||
}
|
||||
}
|
||||
|
||||
private void stageFragment() {
|
||||
putFloat(FFPUniformBlock.ALPHA_REF, GLStateManager.getAlphaState().getReference());
|
||||
private void stageFragment(GLContextState glCtx) {
|
||||
putFloat(FFPUniformBlock.ALPHA_REF, glCtx.alphaState.getReference());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final var envState = GLStateManager.getTextures().getTexEnvState(i);
|
||||
final var envState = glCtx.textures.getTexEnvState(i);
|
||||
putVec4(FFPUniformBlock.TEX_ENV_COLOR_0 + i * 16,
|
||||
envState.envColorR, envState.envColorG, envState.envColorB, envState.envColorA);
|
||||
}
|
||||
|
||||
putVec4(FFPUniformBlock.OVERLAY_COLOR,
|
||||
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
|
||||
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
glCtx.overlayR, glCtx.overlayG,
|
||||
glCtx.overlayB, glCtx.overlayA);
|
||||
|
||||
final var secondary = GLStateManager.getSecondaryColor();
|
||||
final var secondary = glCtx.secondaryColor;
|
||||
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
final FogState fog = GLStateManager.getFogState();
|
||||
final FogState fog = glCtx.fogState;
|
||||
final float start = fog.getStart();
|
||||
final float end = fog.getEnd();
|
||||
final float density = fog.getDensity();
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
public final class UnitCubeMesh {
|
||||
|
||||
public static final int VERTEX_FLAGS = VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT | VertexFlags.NORMAL_BIT;
|
||||
|
||||
public static final int FACE_COUNT = 6;
|
||||
public static final int VERTEX_COUNT = 24;
|
||||
public static final int VERTEX_STRIDE = 64;
|
||||
|
||||
public static final int OFFSET_POSITION = 0;
|
||||
private static final int OFFSET_COLOR = 12;
|
||||
public static final int OFFSET_MID = 16;
|
||||
public static final int OFFSET_DELTA = 32;
|
||||
public static final int OFFSET_NORMAL = 48;
|
||||
public static final int OFFSET_TANGENT = 60;
|
||||
|
||||
public static final float[][] CORNERS = {
|
||||
{ 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
|
||||
{ 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 },
|
||||
};
|
||||
|
||||
public record Span(int a, int z) {
|
||||
public int texels(int sizeA, int sizeZ) {
|
||||
return a * sizeA + z * sizeZ;
|
||||
}
|
||||
}
|
||||
|
||||
public record Net(Span uLow, Span uHigh, Span vLow, Span vHigh) {
|
||||
public Span u(int vertex) {
|
||||
return vertex == 0 || vertex == 3 ? uHigh : uLow;
|
||||
}
|
||||
|
||||
public Span v(int vertex) {
|
||||
return vertex >= 2 ? vHigh : vLow;
|
||||
}
|
||||
}
|
||||
|
||||
public record Face(int[] corners, float[] normal, float[] tangent, Net net) {}
|
||||
|
||||
public static final Face[] FACES = {
|
||||
new Face(new int[] { 5, 1, 2, 6 }, new float[] { 1, 0, 0 }, new float[] { 0, 0, 1, 1 }, new Net(new Span(1, 1), new Span(1, 2), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 0, 4, 7, 3 }, new float[] { -1, 0, 0 }, new float[] { 0, 0, -1, 1 }, new Net(new Span(0, 0), new Span(0, 1), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 5, 4, 0, 1 }, new float[] { 0, -1, 0 }, new float[] { 1, 0, 0, 1 }, new Net(new Span(0, 1), new Span(1, 1), new Span(0, 0), new Span(0, 1))),
|
||||
new Face(new int[] { 2, 3, 7, 6 }, new float[] { 0, 1, 0 }, new float[] { 1, 0, 0, -1 }, new Net(new Span(1, 1), new Span(2, 1), new Span(0, 1), new Span(0, 0))),
|
||||
new Face(new int[] { 1, 0, 3, 2 }, new float[] { 0, 0, -1 }, new float[] { 1, 0, 0, 1 }, new Net(new Span(0, 1), new Span(1, 1), new Span(0, 1), new Span(1, 1))),
|
||||
new Face(new int[] { 4, 5, 6, 7 }, new float[] { 0, 0, 1 }, new float[] { -1, 0, 0, 1 }, new Net(new Span(1, 2), new Span(2, 2), new Span(0, 1), new Span(1, 1))),
|
||||
};
|
||||
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
|
||||
private UnitCubeMesh() {}
|
||||
|
||||
public static void write(long dst) {
|
||||
long ptr = dst;
|
||||
for (int f = 0; f < FACE_COUNT; f++) {
|
||||
final Face face = FACES[f];
|
||||
final Net net = face.net();
|
||||
final float midUA = (net.uLow().a() + net.uHigh().a()) * 0.5f;
|
||||
final float midUZ = (net.uLow().z() + net.uHigh().z()) * 0.5f;
|
||||
final float midVA = (net.vLow().a() + net.vHigh().a()) * 0.5f;
|
||||
final float midVZ = (net.vLow().z() + net.vHigh().z()) * 0.5f;
|
||||
for (int c = 0; c < 4; c++) {
|
||||
final float[] corner = CORNERS[face.corners()[c]];
|
||||
final Span u = net.u(c);
|
||||
final Span v = net.v(c);
|
||||
memPutFloat(ptr + OFFSET_POSITION, corner[0]);
|
||||
memPutFloat(ptr + OFFSET_POSITION + 4, corner[1]);
|
||||
memPutFloat(ptr + OFFSET_POSITION + 8, corner[2]);
|
||||
memPutInt(ptr + OFFSET_COLOR, 0xFFFFFFFF);
|
||||
memPutFloat(ptr + OFFSET_MID, midUA);
|
||||
memPutFloat(ptr + OFFSET_MID + 4, midUZ);
|
||||
memPutFloat(ptr + OFFSET_MID + 8, midVA);
|
||||
memPutFloat(ptr + OFFSET_MID + 12, midVZ);
|
||||
memPutFloat(ptr + OFFSET_DELTA, u.a() - midUA);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 4, u.z() - midUZ);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 8, v.a() - midVA);
|
||||
memPutFloat(ptr + OFFSET_DELTA + 12, v.z() - midVZ);
|
||||
memPutFloat(ptr + OFFSET_NORMAL, face.normal()[0]);
|
||||
memPutFloat(ptr + OFFSET_NORMAL + 4, face.normal()[1]);
|
||||
memPutFloat(ptr + OFFSET_NORMAL + 8, face.normal()[2]);
|
||||
final float[] tangent = face.tangent();
|
||||
memPutInt(ptr + OFFSET_TANGENT, packTangent(tangent[0], tangent[1], tangent[2], tangent[3]));
|
||||
ptr += VERTEX_STRIDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int packTangent(float x, float y, float z, float w) {
|
||||
return NormI8.pack(x, y, z) | (((int) (w * 127.0f) & 0xFF) << 24);
|
||||
}
|
||||
|
||||
public static int vao() {
|
||||
if (vao == 0) build();
|
||||
return vao;
|
||||
}
|
||||
|
||||
private static void build() {
|
||||
final int savedVAO = GLStateManager.getBoundVAO();
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
final int bytes = VERTEX_COUNT * VERTEX_STRIDE;
|
||||
final ByteBuffer data = memAlloc(bytes);
|
||||
try {
|
||||
write(memAddress0(data));
|
||||
data.position(0).limit(bytes);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
} finally {
|
||||
memFree(data);
|
||||
}
|
||||
|
||||
attrib(VertexFormatElement.Usage.POSITION.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_POSITION);
|
||||
attrib(VertexFormatElement.Usage.COLOR.getAttributeLocation(), 4, GL11.GL_UNSIGNED_BYTE, true, OFFSET_COLOR);
|
||||
attrib(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), 4, GL11.GL_FLOAT, false, OFFSET_MID);
|
||||
attrib(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation(), 4, GL11.GL_FLOAT, false, OFFSET_DELTA);
|
||||
attrib(VertexFormatElement.Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_NORMAL);
|
||||
attrib(ImmediateExtendedAttribHandler.LOC_TANGENT, 4, GL11.GL_BYTE, true, OFFSET_TANGENT);
|
||||
|
||||
CubeInstancedAttribs.enableInstanceArrays();
|
||||
|
||||
GLStateManager.glBindVertexArray(savedVAO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
|
||||
}
|
||||
|
||||
public static void delete() {
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
if (vao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
vao = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
@@ -49,7 +50,7 @@ public final class VertexKey {
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = BIT_WIDE_LINE + 1;
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = BIT_UNIT_TEXMAT_BASE + MAX_UNITS;
|
||||
private static final int BIT_LINE_STIPPLE = BIT_UNIT23_UV_FROM_UNIT0 + 1;
|
||||
private static final int BIT_INSTANCED = BIT_LINE_STIPPLE + 1;
|
||||
private static final int BIT_INSTANCING = BIT_LINE_STIPPLE + 1;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -97,7 +98,7 @@ public final class VertexKey {
|
||||
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 instancedDraw() { return bit(BIT_INSTANCED); }
|
||||
public Instancing instancing() { return Instancing.VALUES[(int) ((packed >> BIT_INSTANCING) & 0x3)]; }
|
||||
|
||||
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; }
|
||||
@@ -126,54 +127,66 @@ public final class VertexKey {
|
||||
}
|
||||
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
return packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragUnitMask, GLStateManager.ctx());
|
||||
}
|
||||
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask, GLContextState glCtx) {
|
||||
long bits = 0;
|
||||
|
||||
final boolean lighting = GLStateManager.getLightingState().isEnabled();
|
||||
final boolean lighting = glCtx.lightingState.isEnabled();
|
||||
if (lighting) {
|
||||
bits |= (1L << BIT_LIGHTING);
|
||||
|
||||
for (int i = 0; i < FFP_LIGHT_COUNT; i++) {
|
||||
if (!GLStateManager.getLightStates()[i].isEnabled()) continue;
|
||||
if (!glCtx.lightStates[i].isEnabled()) continue;
|
||||
bits |= (1L << (BIT_LIGHT_BASE + i));
|
||||
// position.w == 0 means directional
|
||||
if (GLStateManager.getLightDataStates()[i].position.w == 0.0f) {
|
||||
if (glCtx.lightDataStates[i].position.w == 0.0f) {
|
||||
bits |= (1L << (BIT_LIGHT_DIR_BASE + i));
|
||||
}
|
||||
}
|
||||
|
||||
if (GLStateManager.getColorMaterial().isEnabled()) {
|
||||
if (glCtx.colorMaterial.isEnabled()) {
|
||||
bits |= (1L << BIT_COLOR_MATERIAL);
|
||||
final int cmMode = encodeColorMaterialMode(GLStateManager.getColorMaterialParameter().getValue());
|
||||
final int cmMode = encodeColorMaterialMode(glCtx.colorMaterialParameter.getValue());
|
||||
bits |= ((long) cmMode & 0x7) << BIT_COLOR_MAT_MODE;
|
||||
}
|
||||
|
||||
if (GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
if (glCtx.lightModel.colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
bits |= (1L << BIT_SEPARATE_SPECULAR);
|
||||
}
|
||||
}
|
||||
|
||||
if (GLStateManager.getFogMode().isEnabled()) {
|
||||
if (glCtx.fogMode.isEnabled()) {
|
||||
bits |= (1L << BIT_FOG);
|
||||
final int fogDistMode = GLStateManager.getFogState().getFogDistanceMode();
|
||||
final int fogDistMode = glCtx.fogState.getFogDistanceMode();
|
||||
bits |= ((long) fogDistMode & 0x3) << BIT_FOG_DIST_MODE;
|
||||
}
|
||||
|
||||
if (GLStateManager.getNormalizeState().isEnabled()) {
|
||||
if (glCtx.normalizeState.isEnabled()) {
|
||||
bits |= (1L << BIT_NORMALIZE);
|
||||
}
|
||||
if (GLStateManager.getRescaleNormalState().isEnabled()) {
|
||||
if (glCtx.rescaleNormalState.isEnabled()) {
|
||||
bits |= (1L << BIT_RESCALE_NORMAL);
|
||||
}
|
||||
|
||||
final TextureUnitArray texUnit = GLStateManager.getTextures();
|
||||
final TextureUnitArray texUnit = glCtx.textures;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
if ((fragUnitMask & (1 << i)) != 0) {
|
||||
bits |= (1L << (BIT_UNIT_TEX_BASE + i));
|
||||
}
|
||||
if (!MatrixHelper.isIdentity(texUnit.getTextureUnitMatrix(i))) {
|
||||
bits |= (1L << (BIT_UNIT_TEXMAT_BASE + i));
|
||||
}
|
||||
}
|
||||
if (texUnit.texMatIdentityGen != glCtx.texMatrixGeneration) {
|
||||
int mask = 0;
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
if (!MatrixHelper.isIdentity(texUnit.getTextureUnitMatrix(i))) {
|
||||
mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
texUnit.texMatIdentityMask = mask;
|
||||
texUnit.texMatIdentityGen = glCtx.texMatrixGeneration;
|
||||
}
|
||||
bits |= ((long) texUnit.texMatIdentityMask) << BIT_UNIT_TEXMAT_BASE;
|
||||
|
||||
// TexGen (unit 0 only) - per-coordinate mode if enabled.
|
||||
// TODO: per-unit texgen for units 2/3. Deferred - no MC code path uses glTexGen on those.
|
||||
@@ -210,11 +223,11 @@ public final class VertexKey {
|
||||
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
|
||||
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
|
||||
|
||||
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw() && hasTexCoord) {
|
||||
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw(glCtx) && hasTexCoord) {
|
||||
bits |= (1L << BIT_UNIT23_UV_FROM_UNIT0);
|
||||
}
|
||||
|
||||
if (GLStateManager.anyClipPlaneEnabled()) {
|
||||
if (GLStateManager.anyClipPlaneEnabled(glCtx)) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
@@ -226,13 +239,15 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
if (GLStateManager.instancedFfpDrawActive) {
|
||||
bits |= (1L << BIT_INSTANCED);
|
||||
}
|
||||
bits |= (long) GLStateManager.ffpInstancing.ordinal() << BIT_INSTANCING;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
static long withInstancing(long packed, Instancing kind) {
|
||||
return (packed & ~(0x3L << BIT_INSTANCING)) | ((long) kind.ordinal() << BIT_INSTANCING);
|
||||
}
|
||||
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragUnitMask));
|
||||
}
|
||||
@@ -255,12 +270,6 @@ public final class VertexKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%b]",
|
||||
packed, lightingEnabled(), lightEnabled(0), lightEnabled(1),
|
||||
colorMaterialEnabled(), fogEnabled(),
|
||||
unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0,
|
||||
unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0,
|
||||
hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(),
|
||||
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancedDraw());
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%s]", packed,lightingEnabled(), lightEnabled(0), lightEnabled(1), colorMaterialEnabled(), fogEnabled(), unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0, unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0, hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(), texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancing());
|
||||
}
|
||||
}
|
||||
@@ -55,31 +55,43 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
private static void emitAttributes(StringBuilder sb, VertexKey key) {
|
||||
final Instancing kind = key.instancing();
|
||||
sb.append("// Vertex attributes\n");
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.POSITION.getAttributeLocation()).append(") in vec3 a_Position;\n");
|
||||
if (key.hasVertexColor()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.COLOR.getAttributeLocation()).append(") in vec4 a_Color;\n");
|
||||
}
|
||||
if (key.hasVertexTexCoord()) {
|
||||
if (kind == Instancing.PARTICLE) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord0;\n");
|
||||
} else {
|
||||
if (key.hasVertexColor()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.COLOR.getAttributeLocation()).append(") in vec4 a_Color;\n");
|
||||
}
|
||||
if (kind != Instancing.CUBE) {
|
||||
if (key.hasVertexTexCoord()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord0;\n");
|
||||
}
|
||||
if (key.hasVertexLightmap()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord1;\n");
|
||||
}
|
||||
}
|
||||
if (key.hasVertexNormal() || kind == Instancing.CUBE) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
|
||||
}
|
||||
}
|
||||
if (key.hasVertexLightmap()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.SECONDARY_UV.getAttributeLocation()).append(") in vec2 a_TexCoord1;\n");
|
||||
}
|
||||
if (key.hasVertexNormal()) {
|
||||
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
|
||||
}
|
||||
if (key.instancedDraw()) {
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL0).append(") in vec4 a_InstCol0;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL1).append(") in vec4 a_InstCol1;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL2).append(") in vec4 a_InstCol2;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_MATRIX_COL3).append(") in vec4 a_InstCol3;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_COLOR).append(") in vec4 a_InstColor;\n");
|
||||
sb.append("layout(location = ").append(InstancedAttribs.LOC_LIGHTMAP).append(") in vec2 a_InstLightmap;\n");
|
||||
for (String d : InstancedGlslHelpers.attributeDecls("a_", kind)) {
|
||||
sb.append(d).append('\n');
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static final String PARTICLE_UV = InstancedGlslHelpers.particleUv("a_InstUv", "a_TexCoord0");
|
||||
|
||||
private static String unit0Source(VertexKey key) {
|
||||
return switch (key.instancing()) {
|
||||
case CUBE -> "cubeUv";
|
||||
case PARTICLE -> PARTICLE_UV;
|
||||
case NONE, TEMPLATE -> key.hasVertexTexCoord() ? "vec4(a_TexCoord0, 0.0, 1.0)" : "u_CurrentTexCoord0";
|
||||
};
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -93,6 +105,10 @@ 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.instancing().hasInstanceHead()) {
|
||||
sb.append("out vec4 v_Overlay;\n");
|
||||
sb.append("invariant gl_Position;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
@@ -116,11 +132,16 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitPositionTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Position transform\n");
|
||||
sb.append(" vec4 pos4 = vec4(a_Position, 1.0);\n");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" mat4 instMV = mat4(a_InstCol0, a_InstCol1, a_InstCol2, a_InstCol3);\n");
|
||||
if (key.instancing() == Instancing.PARTICLE) {
|
||||
sb.append(" vec4 pos4 = ").append(InstancedGlslHelpers.particlePos("a_InstCenterHalf", "a_Position")).append(";\n");
|
||||
} else {
|
||||
sb.append(" vec4 pos4 = vec4(a_Position, 1.0);\n");
|
||||
}
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" mat4 instMV = ").append(InstancedGlslHelpers.mat4FromRows("a_InstRow0", "a_InstRow1", "a_InstRow2")).append(";\n");
|
||||
sb.append(" vec4 eyePos = instMV * pos4;\n");
|
||||
sb.append(" gl_Position = u_ProjectionMatrix * eyePos;\n");
|
||||
sb.append(" v_Overlay = a_InstOverlay;\n");
|
||||
} else {
|
||||
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
|
||||
|
||||
@@ -134,8 +155,8 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitNormalTransform(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Normal transform\n");
|
||||
final String srcNormal = key.hasVertexNormal() ? "a_Normal" : "u_CurrentNormal";
|
||||
if (key.instancedDraw()) {
|
||||
final String srcNormal = key.hasVertexNormal() && key.instancing() != Instancing.PARTICLE ? "a_Normal" : "u_CurrentNormal";
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" vec3 normal = normalize(mat3(instMV) * ").append(srcNormal).append(");\n");
|
||||
sb.append('\n');
|
||||
return;
|
||||
@@ -156,9 +177,13 @@ public final class VertexShaderGenerator {
|
||||
if (key.colorMaterialEnabled()) {
|
||||
// Color material: vertex color replaces specific material properties per glColorMaterial mode (Mesa ffvertex_prog.c get_material() bitmask approach)
|
||||
sb.append(" vec4 matColor = ");
|
||||
sb.append(key.hasVertexColor() ? "a_Color" : "u_CurrentColor");
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" * a_InstColor");
|
||||
if (key.instancing() == Instancing.PARTICLE) {
|
||||
sb.append("a_InstColor");
|
||||
} else {
|
||||
sb.append(key.hasVertexColor() ? "a_Color" : "u_CurrentColor");
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append(" * a_InstColor");
|
||||
}
|
||||
}
|
||||
sb.append(";\n");
|
||||
|
||||
@@ -255,22 +280,27 @@ public final class VertexShaderGenerator {
|
||||
private static void emitColorPassthrough(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Color passthrough (no lighting)\n");
|
||||
final String src = key.hasVertexColor() ? "a_Color" : "u_CurrentColor";
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_Color = ").append(src).append(" * a_InstColor;\n");
|
||||
} else {
|
||||
sb.append(" v_Color = ").append(src).append(";\n");
|
||||
switch (key.instancing()) {
|
||||
case PARTICLE -> sb.append(" v_Color = a_InstColor;\n");
|
||||
case TEMPLATE, CUBE -> sb.append(" v_Color = ").append(src).append(" * a_InstColor;\n");
|
||||
case NONE -> sb.append(" v_Color = ").append(src).append(";\n");
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitTexCoordPassthrough(StringBuilder sb, VertexKey key) {
|
||||
final boolean cube = key.instancing() == Instancing.CUBE;
|
||||
final boolean unit0FromCube = cube && key.unitTexCoordEnabled(0) && !key.texGenEnabled();
|
||||
final boolean unit23FromCube = cube && key.unit23UvFromUnit0() && (key.unitTexCoordEnabled(2) || key.unitTexCoordEnabled(3));
|
||||
if (unit0FromCube || unit23FromCube) {
|
||||
emitCubeTexCoord(sb);
|
||||
}
|
||||
|
||||
if (key.texGenEnabled()) {
|
||||
emitTexGenCoordGeneration(sb, key);
|
||||
} else if (key.unitTexCoordEnabled(0)) {
|
||||
sb.append(" // Texture coordinates - unit 0\n");
|
||||
final String src = key.hasVertexTexCoord()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord0";
|
||||
final String src = unit0Source(key);
|
||||
if (key.unitTexMatEnabled(0)) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * ").append(src).append(";\n");
|
||||
} else {
|
||||
@@ -279,20 +309,17 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled()) {
|
||||
if (key.instancedDraw()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_InstLightmap, 0.0, 1.0);\n");
|
||||
} else if (key.hasVertexLightmap()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_TexCoord1, 0.0, 1.0);\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(u_CurrentLightmapCoord, 0.0, 1.0);\n");
|
||||
}
|
||||
final String lightmap = switch (key.instancing()) {
|
||||
case PARTICLE, TEMPLATE -> "a_InstLightmap";
|
||||
case CUBE -> "a_InstLightmapScale.xy";
|
||||
case NONE -> key.hasVertexLightmap() ? "a_TexCoord1" : "u_CurrentLightmapCoord";
|
||||
};
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(").append(lightmap).append(", 0.0, 1.0);\n");
|
||||
}
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
final String src = key.unit23UvFromUnit0()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord" + i;
|
||||
final String src = key.unit23UvFromUnit0() ? unit0Source(key) : "u_CurrentTexCoord" + i;
|
||||
if (!key.unit23UvFromUnit0() && key.unitTexMatEnabled(i)) {
|
||||
sb.append(" v_TexCoord").append(i).append(" = u_TextureMatrix").append(i).append(" * ").append(src).append(";\n");
|
||||
} else {
|
||||
@@ -301,6 +328,12 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitCubeTexCoord(StringBuilder sb) {
|
||||
sb.append(" // Cube texture net\n");
|
||||
sb.append(" ").append(InstancedGlslHelpers.cubePrelude("", "a_CubeMid", "a_CubeDelta", "a_Normal", "a_CubeTex", "\n ")).append('\n');
|
||||
sb.append(" vec4 cubeUv = ").append(InstancedGlslHelpers.cubeUv("a_CubeTex", "a_InstLightmapScale", "cubeU.x", "cubeU.y", "cubeV.x", "cubeV.y")).append(";\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit per-coordinate texgen code following Mesa's ffvertex_prog.c build order.
|
||||
* OBJ_LINEAR: dot(pos4, objPlane), EYE_LINEAR: dot(eyePos, eyePlane).
|
||||
|
||||
@@ -2,6 +2,6 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredAlphaHandler {
|
||||
boolean isAlphaTestLocked();
|
||||
void deferAlphaTestToggle(boolean enabled);
|
||||
void deferAlphaFunc(int function, float reference);
|
||||
boolean deferAlphaTestToggle(boolean enabled);
|
||||
boolean deferAlphaFunc(int function, float reference);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class FrameHooks {
|
||||
|
||||
private FrameHooks() {}
|
||||
|
||||
private static boolean gameLoopStarted;
|
||||
private static long frameCounter;
|
||||
private static boolean frameGroupActive;
|
||||
|
||||
public static void bootstrapFirstFrame() {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
if (!gameLoopStarted) {
|
||||
gameLoopStarted = true;
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void frameEnd() {
|
||||
popFrameGroup();
|
||||
RENDER_BACKEND.onFrameEnd();
|
||||
}
|
||||
|
||||
public static void frameBegin() {
|
||||
if (gameLoopStarted) {
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
BackendManager.shutdown();
|
||||
}
|
||||
|
||||
public static void bindSplashVao() {
|
||||
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
|
||||
}
|
||||
|
||||
public static void splashFinished() {
|
||||
GLStateManager.markSplashComplete("SplashProgress.finish");
|
||||
}
|
||||
|
||||
private static void pushFrameGroup() {
|
||||
CaptureGate.refresh();
|
||||
GLDebug.pushGroup("frame:", frameCounter);
|
||||
frameGroupActive = true;
|
||||
}
|
||||
|
||||
private static void popFrameGroup() {
|
||||
if (frameGroupActive) {
|
||||
GLDebug.popGroup();
|
||||
frameGroupActive = false;
|
||||
frameCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.events.*;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
|
||||
@@ -14,6 +15,12 @@ public final class GLSMHooks {
|
||||
public static ShaderTransformPostProcessor postTransformProcessor;
|
||||
public static PerFrameUniformBlock perFrameUniformBlock;
|
||||
public static PerFrameUniformBlock perPassUniformBlock;
|
||||
public static PendingProgramSelection pendingProgramSelection;
|
||||
|
||||
public static void resolvePendingProgram() {
|
||||
final PendingProgramSelection p = pendingProgramSelection;
|
||||
if (p != null && !DisplayListManager.isRecording()) p.resolvePendingProgram();
|
||||
}
|
||||
|
||||
// EventBus instances
|
||||
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
@@ -14,7 +16,7 @@ public final class GLSMInitConfig {
|
||||
@Getter private final int displayWidth;
|
||||
@Getter private final int displayHeight;
|
||||
@Getter private final Runnable postInitCallback;
|
||||
@Getter private final boolean DSAEnabled;
|
||||
private final boolean dsaEnabled;
|
||||
@Getter private final boolean noErrorChecks;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
@@ -26,7 +28,11 @@ public final class GLSMInitConfig {
|
||||
this.displayWidth = builder.displayWidth;
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.DSAEnabled = builder.enableDSA;
|
||||
this.dsaEnabled = builder.dsaEnabled;
|
||||
}
|
||||
|
||||
public boolean isDSAEnabled() {
|
||||
return dsaEnabled;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -34,14 +40,14 @@ public final class GLSMInitConfig {
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private boolean lwjglDebug = false;
|
||||
private boolean lwjglDebug = SystemProperties.LWJGL_DEBUG;
|
||||
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
|
||||
private Consumer<DirectTessellator> directDrawer = null;
|
||||
private Runnable streamingDrawerDestroy = null;
|
||||
private Consumer<DirectTessellator> directDrawer = TessellatorStreamingDrawer::drawDirect;
|
||||
private Runnable streamingDrawerDestroy = TessellatorStreamingDrawer::destroy;
|
||||
private int displayWidth = 0;
|
||||
private int displayHeight = 0;
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean enableDSA = false;
|
||||
private boolean dsaEnabled = true;
|
||||
private boolean noErrorChecks;
|
||||
|
||||
private Builder() {}
|
||||
@@ -83,7 +89,7 @@ public final class GLSMInitConfig {
|
||||
}
|
||||
|
||||
public Builder enableDSA(boolean enableDSA) {
|
||||
this.enableDSA = enableDSA;
|
||||
this.dsaEnabled = enableDSA;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface PendingProgramSelection {
|
||||
void resolvePendingProgram();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
public final class TextureUtilHooks {
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
|
||||
private static int savedFilterTextureId;
|
||||
|
||||
public static void setMaxLevel(int mipmapLevels) {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmapLevels);
|
||||
}
|
||||
|
||||
public static void captureBoundFilterTexture() {
|
||||
savedFilterTextureId = GLStateManager.getBoundTextureForServerState();
|
||||
}
|
||||
|
||||
public static void clearSavedFilterTexture() {
|
||||
savedFilterTextureId = 0;
|
||||
}
|
||||
|
||||
public static void restoreFilter(int min, int mag) {
|
||||
final int id = savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
} else {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
}
|
||||
}
|
||||
|
||||
public static void restoreAniso(float aniso) {
|
||||
final int id = savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameterf(id, GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
} else {
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
|
||||
public final class ClassDump {
|
||||
|
||||
public static final ClassDump DISABLED = new ClassDump(false);
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
public ClassDump(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
|
||||
if (!enabled) return;
|
||||
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
|
||||
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
|
||||
}
|
||||
|
||||
public void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
|
||||
if (!enabled) return;
|
||||
classNode.markDirty();
|
||||
dumpClass(className, classNode.getOriginalBytes(), classNode.computeBytes(), transformer);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -15,17 +20,64 @@ public final class DependencyVerifier {
|
||||
*/
|
||||
public record Check(String resourcePath, String errorMessage) {}
|
||||
|
||||
public interface FatalDialog {
|
||||
void show(String title, String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify all checks pass. Throws RuntimeException on first failure.
|
||||
* @param anchor Class whose classloader to use for resource lookups
|
||||
* @param checks Dependency checks to run
|
||||
*/
|
||||
public static void verify(Class<?> anchor, List<Check> checks) {
|
||||
final Check missing = firstMissing(anchor, checks);
|
||||
if (missing != null) {
|
||||
throw new RuntimeException(missing.errorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the first check whose resource is absent, or null when all pass. */
|
||||
public static Check firstMissing(Class<?> anchor, List<Check> checks) {
|
||||
for (Check check : checks) {
|
||||
if (anchor.getResource(check.resourcePath()) == null) {
|
||||
throw new RuntimeException(check.errorMessage());
|
||||
return check;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Check> gtnhLibChecks(String productName) {
|
||||
return List.of(
|
||||
new Check("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class", "Missing dependency: " + productName + " requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new Check("/com/gtnewhorizon/gtnhlib/client/renderer/VertexCallbackManager.class", "GTNHLib is outdated: " + productName + " requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new Check("/it/unimi/dsi/fastutil/objects/ObjectBooleanBiConsumer.class", "GTNHLib is outdated: " + productName + " requires GTNHLib 0.11.19 or newer (fastutil 8.5.18+)! Download: https://modrinth.com/mod/gtnhlib"));
|
||||
}
|
||||
|
||||
public static void verifyOrHalt(Class<?> anchor, String productName, List<Check> checks, Logger logger, FatalDialog sdlDialog) {
|
||||
final String message;
|
||||
try {
|
||||
verify(anchor, checks);
|
||||
return;
|
||||
} catch (RuntimeException ex) {
|
||||
message = ex.getMessage();
|
||||
}
|
||||
|
||||
final String title = productName + " - Missing Dependency";
|
||||
System.err.println("FATAL: " + message);
|
||||
try {
|
||||
logger.fatal(message);
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
final int lwjgl3ifyMajor = ((Integer) Launch.blackboard.getOrDefault("lwjgl3ify:major-version", Integer.MIN_VALUE));
|
||||
try {
|
||||
if (lwjgl3ifyMajor >= 3) {
|
||||
sdlDialog.show(title, message);
|
||||
} else if (!GraphicsEnvironment.isHeadless()) {
|
||||
JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
Runtime.getRuntime().halt(1);
|
||||
}
|
||||
|
||||
private DependencyVerifier() {}
|
||||
|
||||
+2
-48
@@ -1,54 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class EarlyRedirectorCore {
|
||||
|
||||
private final GLSMRedirector core;
|
||||
private final String[] targets;
|
||||
private final String[] exclusions;
|
||||
public final class EarlyRedirectorCore extends ModRedirector {
|
||||
|
||||
public EarlyRedirectorCore(String... extraExclusions) {
|
||||
this.core = new GLSMRedirector();
|
||||
this.targets = EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone();
|
||||
|
||||
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
if (extraExclusions != null && extraExclusions.length > 0) {
|
||||
Collections.addAll(excl, extraExclusions);
|
||||
}
|
||||
this.exclusions = excl.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (!startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, exclusions)) return basicClass;
|
||||
if (!core.shouldTransform(basicClass)) return basicClass;
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (core.transformClassNode(transformedName, cn)) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String p : prefixes) {
|
||||
if (name.startsWith(p)) return true;
|
||||
}
|
||||
return false;
|
||||
super(ClassDump.DISABLED, EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone(), extraExclusions);
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -91,9 +91,8 @@ public final class EcosystemNarrowRules {
|
||||
public static final String[] LWJGL3IFY_EXCLUSIONS_SHARED = {
|
||||
"org.embeddedt.embeddium",
|
||||
"org.taumc.celeritas",
|
||||
"com.mitchej123.lwjgl.lwjgl3",
|
||||
"com.mitchej123.glsm",
|
||||
"com.gtnewhorizons.angelica.lwjgl3",
|
||||
"com.gtnewhorizons.angelica.sdlgpu",
|
||||
};
|
||||
|
||||
public static final String[] EARLY_REDIRECTOR_TARGETS = {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
|
||||
public final class Lwjgl3ifyExclusions {
|
||||
|
||||
public static void apply() {
|
||||
final var handle = SharedConfig.getRfbTransformers().stream()
|
||||
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (handle == null) return;
|
||||
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
|
||||
handle.exclusions().add(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
private Lwjgl3ifyExclusions() {}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ModRedirector {
|
||||
|
||||
private final GLSMRedirector core = new GLSMRedirector();
|
||||
private final ClassDump dump;
|
||||
private final String[] targets;
|
||||
private final String[] allExclusions;
|
||||
|
||||
public ModRedirector(ClassDump dump, String... extraExclusions) {
|
||||
this(dump, (String[]) null, extraExclusions);
|
||||
}
|
||||
|
||||
public ModRedirector(ClassDump dump, String[] targets, String[] extraExclusions) {
|
||||
this.dump = dump;
|
||||
this.targets = targets;
|
||||
final List<String> exclusions = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
exclusions.add("com.gtnewhorizons.angelica.lwjgl3.");
|
||||
Collections.addAll(exclusions, extraExclusions);
|
||||
this.allExclusions = exclusions.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public ClassDump getDump() {
|
||||
return dump;
|
||||
}
|
||||
|
||||
public String[] getTransformerExclusions() {
|
||||
return allExclusions;
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return core.shouldTransform(basicClass);
|
||||
}
|
||||
|
||||
public boolean transformClassNode(String transformedName, ClassNode cn, boolean lwjgl3Aware) {
|
||||
return core.transformClassNode(transformedName, cn, lwjgl3Aware);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass, Object transformer) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (targets != null && !startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, allExclusions)) return basicClass;
|
||||
|
||||
if (!core.shouldTransform(basicClass)) {
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (!core.transformClassNode(transformedName, cn)) {
|
||||
return basicClass;
|
||||
}
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
dump.dumpClass(transformedName, basicClass, bytes, transformer);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String prefix : prefixes) {
|
||||
if (name.startsWith(prefix)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public abstract class RfbModRedirector implements RfbClassTransformer {
|
||||
|
||||
private static final Attributes.Name LWJGL3_AWARE = new Attributes.Name("Lwjgl3ify-Aware");
|
||||
|
||||
private final String id;
|
||||
private final ModRedirector inner;
|
||||
|
||||
protected RfbModRedirector(@Pattern("[a-z0-9-]+") String id, ModRedirector inner) {
|
||||
this.id = id;
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private static boolean isLwjgl3Aware(@Nullable Manifest manifest) {
|
||||
return manifest != null && "true".equals(manifest.getMainAttributes().getValue(LWJGL3_AWARE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortAfter() {
|
||||
return new String[] {"*", "mixin:mixin"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortBefore() {
|
||||
return new String[] {"lwjgl3ify:redirect"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] additionalExclusions() {
|
||||
return inner.getTransformerExclusions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldTransformClass(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
if (!classNode.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return inner.shouldTransform(classNode.getOriginalBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(className, classNode.getNode(), isLwjgl3Aware(manifest));
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
inner.getDump().dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
transformClassIfNeeded(classLoader, context, manifest, className, classNode);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +164,8 @@ public class GLSMRedirector {
|
||||
private static final String EXTGPUShader4 = "org/lwjgl/opengl/EXTGPUShader4";
|
||||
private static final String ARBTimerQuery = "org/lwjgl/opengl/ARBTimerQuery";
|
||||
private static final String EXTTimerQuery = "org/lwjgl/opengl/EXTTimerQuery";
|
||||
private static final String ARBSamplerObjects = "org/lwjgl/opengl/ARBSamplerObjects";
|
||||
private static final String ARBMultiBind = "org/lwjgl/opengl/ARBMultiBind";
|
||||
|
||||
// Redirect VAO related calls from NHLib
|
||||
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
|
||||
@@ -555,6 +557,7 @@ public class GLSMRedirector {
|
||||
.add("glMultiDrawElementsIndirect");
|
||||
final var gl44 = RedirectMap.newMap()
|
||||
.add("glBufferStorage")
|
||||
.add("glBindSamplers")
|
||||
.add("glClearTexImage");
|
||||
final var gl45 = RedirectMap.newMap()
|
||||
.add("glCreateBuffers")
|
||||
@@ -767,6 +770,17 @@ public class GLSMRedirector {
|
||||
.add("glMapBufferRange")
|
||||
.add("glFlushMappedBufferRange")
|
||||
);
|
||||
methodRedirects.put(ARBSamplerObjects, RedirectMap.newMap()
|
||||
.add("glGenSamplers")
|
||||
.add("glDeleteSamplers")
|
||||
.add("glIsSampler")
|
||||
.add("glBindSampler")
|
||||
.add("glSamplerParameteri")
|
||||
.add("glSamplerParameterf")
|
||||
);
|
||||
methodRedirects.put(ARBMultiBind, RedirectMap.newMap()
|
||||
.add("glBindSamplers")
|
||||
);
|
||||
|
||||
// APPLE
|
||||
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
|
||||
@@ -796,6 +810,8 @@ public class GLSMRedirector {
|
||||
.add("gluBuild2DMipmaps")
|
||||
.add("gluErrorString")
|
||||
);
|
||||
// Must honor disableErrorChecks like glGetError, or it throws on errors those checks left latched
|
||||
methodRedirects.put("org/lwjgl/opengl/Util", RedirectMap.newMap().add("checkGLError"));
|
||||
|
||||
// Quadric type replacements
|
||||
typeRedirects.put("org/lwjgl/util/glu/Sphere", "com/gtnewhorizons/angelica/glsm/compat/lwjgl/AngelicaSphere");
|
||||
|
||||
@@ -1,45 +1,46 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class AlphaStateStack extends AlphaState implements IStateStack<AlphaState> {
|
||||
public final class AlphaStateStack extends AlphaState implements CowStateStack<AlphaStateStack> {
|
||||
|
||||
protected final AlphaState[] stack;
|
||||
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
@Setter private VanillaStateLayer<AlphaState> vanillaLayer;
|
||||
|
||||
public AlphaStateStack() {
|
||||
public AlphaStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new AlphaState();
|
||||
}
|
||||
}
|
||||
|
||||
public AlphaStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public AlphaStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
}
|
||||
|
||||
final AlphaState saved = stack[--pointer];
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
final AlphaState saved = stack[s];
|
||||
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public AlphaState readEffective(AlphaState out) {
|
||||
@@ -48,7 +49,8 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,63 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class BlendStateStack extends BlendState implements IStateStack<BlendStateStack> {
|
||||
public final class BlendStateStack extends BlendState implements CowStateStack<BlendStateStack> {
|
||||
|
||||
protected final BlendState[] stack;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_BLEND);
|
||||
|
||||
protected int pointer;
|
||||
private boolean funcUnknown;
|
||||
|
||||
@Setter private VanillaStateLayer<BlendState> vanillaLayer;
|
||||
|
||||
public BlendStateStack() {
|
||||
public BlendStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new BlendState();
|
||||
}
|
||||
}
|
||||
|
||||
public BlendStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public BlendStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
}
|
||||
|
||||
final BlendState saved = stack[--pointer];
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
final BlendState saved = stack[s];
|
||||
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
setExceptFunc(saved);
|
||||
} else {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public void setFuncUnknownState() {
|
||||
funcUnknown = true;
|
||||
}
|
||||
|
||||
public boolean isFuncUnknown() {
|
||||
return funcUnknown;
|
||||
}
|
||||
|
||||
public void clearFuncUnknownState() {
|
||||
funcUnknown = false;
|
||||
}
|
||||
|
||||
public BlendState readEffective(BlendState out) {
|
||||
@@ -50,11 +66,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BooleanState;
|
||||
@@ -13,17 +12,12 @@ import lombok.Setter;
|
||||
* saved when actually modified, dramatically reducing overhead when most states
|
||||
* don't change (e.g., GL_ENABLE_BIT saves ~270 states but typically only 3-4 change).
|
||||
*/
|
||||
public class BooleanStateStack extends BooleanState implements IStateStack<BooleanStateStack> {
|
||||
public class BooleanStateStack extends BooleanState implements CowStateStack<BooleanStateStack> {
|
||||
|
||||
protected final boolean[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
@Setter private VanillaBooleanLayer vanillaLayer;
|
||||
|
||||
/**
|
||||
* The depth at which state has been saved. Compared against GLStateManager.getAttribDepth()
|
||||
* to determine if we need to save before modification or restore on pop.
|
||||
*/
|
||||
protected int savedDepth;
|
||||
|
||||
public BooleanStateStack(int glCap) {
|
||||
this(glCap, false, false);
|
||||
}
|
||||
@@ -52,24 +46,24 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
stack = new boolean[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
}
|
||||
|
||||
// ==================== Traditional Stack Operations ====================
|
||||
|
||||
@Override
|
||||
public BooleanStateStack push() {
|
||||
if (savedDepth >= stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (savedDepth + 1) + " reached");
|
||||
}
|
||||
stack[savedDepth++] = vanillaValue();
|
||||
return this;
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanStateStack pop() {
|
||||
if (savedDepth == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
restore(stack[--savedDepth]);
|
||||
return this;
|
||||
public void captureSlot(int slot) {
|
||||
stack[slot] = vanillaValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSlot(int slot) {
|
||||
restore(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
|
||||
public boolean isEffectivelyEnabled() {
|
||||
@@ -88,45 +82,6 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return savedDepth == 0;
|
||||
}
|
||||
|
||||
// ==================== Lazy Copy-on-Write Operations ====================
|
||||
|
||||
@Override
|
||||
public int pushDepth() {
|
||||
// No-op: global depth is managed by GLStateManager
|
||||
return GLStateManager.getAttribDepth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore state if it was modified at the current depth.
|
||||
* Only called by GLStateManager.popState() for states that registered as modified.
|
||||
*/
|
||||
@Override
|
||||
public BooleanStateStack popDepth() {
|
||||
// We're only called if we were modified, so savedDepth should match
|
||||
if (savedDepth > 0) {
|
||||
restore(stack[--savedDepth]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before modifying state. If we haven't saved at the current depth yet,
|
||||
* save the current value and register with GLStateManager for restoration.
|
||||
*/
|
||||
@Override
|
||||
public void beforeModify() {
|
||||
final int globalDepth = GLStateManager.getAttribDepth();
|
||||
if (savedDepth < globalDepth) {
|
||||
stack[savedDepth++] = vanillaValue();
|
||||
GLStateManager.registerModifiedState(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
return GLStateManager.getAttribDepth();
|
||||
@@ -134,7 +89,10 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
beforeModify();
|
||||
if (GLStateManager.getAttribDepth() > 0
|
||||
&& (stateUnknown || !GLStateManager.isCachingEnabled() || enabled != vanillaValue())) {
|
||||
beforeModify();
|
||||
}
|
||||
super.setEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
|
||||
public class Color4Stack extends Color4 implements IStateStack<Color4Stack> {
|
||||
public final class Color4Stack extends Color4 implements CowStateStack<Color4Stack> {
|
||||
|
||||
protected final Color4[] stack;
|
||||
private CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public Color4Stack() {
|
||||
public Color4Stack(int id) {
|
||||
cow.id = id;
|
||||
stack = new Color4[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new Color4();
|
||||
}
|
||||
}
|
||||
|
||||
public Color4Stack(Color4 color4) {
|
||||
this();
|
||||
public Color4Stack(int id, Color4 color4) {
|
||||
this(id);
|
||||
set(color4);
|
||||
}
|
||||
|
||||
public Color4Stack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
public Color4Stack restoreBit(int bit) {
|
||||
final int id = cow.id;
|
||||
cow = new CowDepths(bit);
|
||||
cow.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Color4Stack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,51 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskStack> {
|
||||
public final class ColorMaskStack extends ColorMask implements CowStateStack<ColorMaskStack> {
|
||||
|
||||
protected final ColorMask[] stack;
|
||||
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_COLOR_MASK);
|
||||
|
||||
@Setter private VanillaStateLayer<ColorMask> vanillaLayer;
|
||||
|
||||
public ColorMaskStack() {
|
||||
public ColorMaskStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new ColorMask();
|
||||
}
|
||||
}
|
||||
|
||||
public ColorMaskStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public ColorMaskStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
}
|
||||
|
||||
final ColorMask saved = stack[--pointer];
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
final ColorMask saved = stack[s];
|
||||
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
/**
|
||||
* __n__n__
|
||||
* .------`-\00/-'
|
||||
* / ## ## (oo)
|
||||
* / \## __ ./
|
||||
* |//YY \|/
|
||||
* ||| |||
|
||||
* Copy On Write Depths
|
||||
*/
|
||||
|
||||
public final class CowDepths {
|
||||
public int id = -1;
|
||||
public final int restoreBit;
|
||||
public final int restoreUnit;
|
||||
private final int[] slotDepth = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
private int pointer;
|
||||
|
||||
public CowDepths() {
|
||||
this(0, -1);
|
||||
}
|
||||
|
||||
public CowDepths(int restoreBit) {
|
||||
this(restoreBit, -1);
|
||||
}
|
||||
|
||||
public CowDepths(int restoreBit, int restoreUnit) {
|
||||
this.restoreBit = restoreBit;
|
||||
this.restoreUnit = restoreUnit;
|
||||
}
|
||||
|
||||
public int claim(int depth) {
|
||||
if (depth <= 0) return -1;
|
||||
if (pointer > 0 && slotDepth[pointer - 1] == depth) return -1;
|
||||
slotDepth[pointer] = depth;
|
||||
return pointer++;
|
||||
}
|
||||
|
||||
public int claimUnconditional() {
|
||||
slotDepth[pointer] = -1;
|
||||
return pointer++;
|
||||
}
|
||||
|
||||
public int pop() {
|
||||
return pointer > 0 ? --pointer : -1;
|
||||
}
|
||||
|
||||
public int discard(int poppedDepth) {
|
||||
if (pointer == 0) return -1;
|
||||
final int parent = poppedDepth - 1;
|
||||
if (parent > 0 && (pointer < 2 || slotDepth[pointer - 2] != parent)) {
|
||||
slotDepth[pointer - 1] = parent;
|
||||
return pointer - 1;
|
||||
}
|
||||
pointer--;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int top() {
|
||||
return pointer - 1;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ClipPlaneBooleanState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
|
||||
/**
|
||||
* __n__n__
|
||||
* .------`-\00/-'
|
||||
* / ## ## (oo) Casts look redundant, they aren't. Casting to the
|
||||
* / \## __ ./ final class avoids an interface lookup on every
|
||||
* |//YY \|/ call. 74-85% faster push/pops in JMH testing.
|
||||
* ||| |||
|
||||
* Copy On Write Dispatch
|
||||
*/
|
||||
@SuppressWarnings("RedundantCast")
|
||||
public final class CowDispatch {
|
||||
|
||||
private CowDispatch() {}
|
||||
|
||||
public static final byte BLEND = 0;
|
||||
public static final byte BOOLEAN = 1;
|
||||
public static final byte CLIP_PLANE_BOOLEAN = 2;
|
||||
public static final byte TEXTURE_UNIT_BOOLEAN = 3;
|
||||
public static final byte COLOR_MASK = 4;
|
||||
public static final byte LIGHT_STATE = 5;
|
||||
public static final byte MATERIAL = 6;
|
||||
public static final byte STENCIL = 7;
|
||||
public static final byte MATRIX_MODE = 8;
|
||||
public static final byte LIGHT_MODEL = 9;
|
||||
public static final byte ALPHA = 10;
|
||||
public static final byte POINT = 11;
|
||||
public static final byte FOG = 12;
|
||||
public static final byte VEC4F = 13;
|
||||
public static final byte VEC3F = 14;
|
||||
public static final byte INTEGER = 15;
|
||||
public static final byte DEPTH = 16;
|
||||
public static final byte VIEWPORT = 17;
|
||||
public static final byte POLYGON = 18;
|
||||
public static final byte COLOR4 = 19;
|
||||
public static final byte LINE = 20;
|
||||
public static final byte TEXTURE_BINDING = 21;
|
||||
public static final byte TEX_ENV = 22;
|
||||
|
||||
public static byte kindOf(CowStateStack<?> s) {
|
||||
final Class<?> c = s.getClass();
|
||||
if (c == TextureUnitBooleanStateStack.class) return TEXTURE_UNIT_BOOLEAN;
|
||||
if (c == ClipPlaneBooleanState.class) return CLIP_PLANE_BOOLEAN;
|
||||
if (c == BooleanStateStack.class) return BOOLEAN;
|
||||
if (c == BlendStateStack.class) return BLEND;
|
||||
if (c == ColorMaskStack.class) return COLOR_MASK;
|
||||
if (c == LightStateStack.class) return LIGHT_STATE;
|
||||
if (c == MaterialStateStack.class) return MATERIAL;
|
||||
if (c == StencilStateStack.class) return STENCIL;
|
||||
if (c == MatrixModeStack.class) return MATRIX_MODE;
|
||||
if (c == LightModelStateStack.class) return LIGHT_MODEL;
|
||||
if (c == AlphaStateStack.class) return ALPHA;
|
||||
if (c == PointStateStack.class) return POINT;
|
||||
if (c == FogStateStack.class) return FOG;
|
||||
if (c == Vec4fStack.class) return VEC4F;
|
||||
if (c == Vec3fStack.class) return VEC3F;
|
||||
if (c == IntegerStateStack.class) return INTEGER;
|
||||
if (c == DepthStateStack.class) return DEPTH;
|
||||
if (c == ViewPortStateStack.class) return VIEWPORT;
|
||||
if (c == PolygonStateStack.class) return POLYGON;
|
||||
if (c == Color4Stack.class) return COLOR4;
|
||||
if (c == LineStateStack.class) return LINE;
|
||||
if (c == TextureBindingStack.class) return TEXTURE_BINDING;
|
||||
if (c == TexEnvState.class) return TEX_ENV;
|
||||
throw new IllegalStateException("Unknown CowStateStack class: " + c.getName());
|
||||
}
|
||||
|
||||
public static boolean topSlotChanged(byte kind, CowStateStack<?> s) {
|
||||
return switch (kind) {
|
||||
case BLEND -> ((BlendStateStack) s).topSlotChanged();
|
||||
case BOOLEAN -> ((BooleanStateStack) s).topSlotChanged();
|
||||
case CLIP_PLANE_BOOLEAN -> ((ClipPlaneBooleanState) s).topSlotChanged();
|
||||
case TEXTURE_UNIT_BOOLEAN -> ((TextureUnitBooleanStateStack) s).topSlotChanged();
|
||||
case COLOR_MASK -> ((ColorMaskStack) s).topSlotChanged();
|
||||
case LIGHT_STATE -> ((LightStateStack) s).topSlotChanged();
|
||||
case MATERIAL -> ((MaterialStateStack) s).topSlotChanged();
|
||||
case STENCIL -> ((StencilStateStack) s).topSlotChanged();
|
||||
case MATRIX_MODE -> ((MatrixModeStack) s).topSlotChanged();
|
||||
case LIGHT_MODEL -> ((LightModelStateStack) s).topSlotChanged();
|
||||
case ALPHA -> ((AlphaStateStack) s).topSlotChanged();
|
||||
case POINT -> ((PointStateStack) s).topSlotChanged();
|
||||
case FOG -> ((FogStateStack) s).topSlotChanged();
|
||||
case VEC4F -> ((Vec4fStack) s).topSlotChanged();
|
||||
case VEC3F -> ((Vec3fStack) s).topSlotChanged();
|
||||
case INTEGER -> ((IntegerStateStack) s).topSlotChanged();
|
||||
case DEPTH -> ((DepthStateStack) s).topSlotChanged();
|
||||
case VIEWPORT -> ((ViewPortStateStack) s).topSlotChanged();
|
||||
case POLYGON -> ((PolygonStateStack) s).topSlotChanged();
|
||||
case COLOR4 -> ((Color4Stack) s).topSlotChanged();
|
||||
case LINE -> ((LineStateStack) s).topSlotChanged();
|
||||
case TEXTURE_BINDING -> ((TextureBindingStack) s).topSlotChanged();
|
||||
case TEX_ENV -> ((TexEnvState) s).topSlotChanged();
|
||||
default -> throw new IllegalStateException("Unknown kind: " + kind);
|
||||
};
|
||||
}
|
||||
|
||||
public static void restoreSlot(byte kind, CowStateStack<?> s, int slot) {
|
||||
switch (kind) {
|
||||
case BLEND -> ((BlendStateStack) s).restoreSlot(slot);
|
||||
case BOOLEAN -> ((BooleanStateStack) s).restoreSlot(slot);
|
||||
case CLIP_PLANE_BOOLEAN -> ((ClipPlaneBooleanState) s).restoreSlot(slot);
|
||||
case TEXTURE_UNIT_BOOLEAN -> ((TextureUnitBooleanStateStack) s).restoreSlot(slot);
|
||||
case COLOR_MASK -> ((ColorMaskStack) s).restoreSlot(slot);
|
||||
case LIGHT_STATE -> ((LightStateStack) s).restoreSlot(slot);
|
||||
case MATERIAL -> ((MaterialStateStack) s).restoreSlot(slot);
|
||||
case STENCIL -> ((StencilStateStack) s).restoreSlot(slot);
|
||||
case MATRIX_MODE -> ((MatrixModeStack) s).restoreSlot(slot);
|
||||
case LIGHT_MODEL -> ((LightModelStateStack) s).restoreSlot(slot);
|
||||
case ALPHA -> ((AlphaStateStack) s).restoreSlot(slot);
|
||||
case POINT -> ((PointStateStack) s).restoreSlot(slot);
|
||||
case FOG -> ((FogStateStack) s).restoreSlot(slot);
|
||||
case VEC4F -> ((Vec4fStack) s).restoreSlot(slot);
|
||||
case VEC3F -> ((Vec3fStack) s).restoreSlot(slot);
|
||||
case INTEGER -> ((IntegerStateStack) s).restoreSlot(slot);
|
||||
case DEPTH -> ((DepthStateStack) s).restoreSlot(slot);
|
||||
case VIEWPORT -> ((ViewPortStateStack) s).restoreSlot(slot);
|
||||
case POLYGON -> ((PolygonStateStack) s).restoreSlot(slot);
|
||||
case COLOR4 -> ((Color4Stack) s).restoreSlot(slot);
|
||||
case LINE -> ((LineStateStack) s).restoreSlot(slot);
|
||||
case TEXTURE_BINDING -> ((TextureBindingStack) s).restoreSlot(slot);
|
||||
case TEX_ENV -> ((TexEnvState) s).restoreSlot(slot);
|
||||
default -> throw new IllegalStateException("Unknown kind: " + kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
/**
|
||||
* __n__n__
|
||||
* .------`-\00/-'
|
||||
* / ## ## (oo)
|
||||
* / \## __ ./
|
||||
* |//YY \|/
|
||||
* ||| |||
|
||||
* Copy On Write State Stack
|
||||
*/
|
||||
public interface CowStateStack<T> extends IStateStack<T> {
|
||||
CowDepths cowDepths();
|
||||
|
||||
void captureSlot(int slot);
|
||||
|
||||
void restoreSlot(int slot);
|
||||
|
||||
default int stackId() {
|
||||
return cowDepths().id;
|
||||
}
|
||||
|
||||
default void setStackId(int id) {
|
||||
cowDepths().id = id;
|
||||
}
|
||||
|
||||
default int restoreBit() {
|
||||
return cowDepths().restoreBit;
|
||||
}
|
||||
|
||||
default int restoreUnit() {
|
||||
return cowDepths().restoreUnit;
|
||||
}
|
||||
|
||||
default boolean topSlotChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default int pushDepth() {
|
||||
return GLStateManager.getAttribDepth();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void beforeModify() {
|
||||
final int s = cowDepths().claim(GLStateManager.getAttribDepth());
|
||||
if (s >= 0) {
|
||||
captureSlot(s);
|
||||
GLStateManager.registerModifiedState(stackId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isEmpty() {
|
||||
return cowDepths().isEmpty();
|
||||
}
|
||||
|
||||
default void drain() {
|
||||
int s;
|
||||
while ((s = cowDepths().pop()) >= 0) restoreSlot(s);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
default T push() {
|
||||
captureSlot(cowDepths().claimUnconditional());
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
default T pop() {
|
||||
final int s = cowDepths().pop();
|
||||
if (s < 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
restoreSlot(s);
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,51 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.DepthState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class DepthStateStack extends DepthState implements IStateStack<DepthStateStack> {
|
||||
public final class DepthStateStack extends DepthState implements CowStateStack<DepthStateStack> {
|
||||
|
||||
protected final DepthState[] stack;
|
||||
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_DEPTH);
|
||||
|
||||
@Setter private VanillaStateLayer<DepthState> vanillaLayer;
|
||||
|
||||
private final DepthState effective = new DepthState();
|
||||
|
||||
public DepthStateStack() {
|
||||
public DepthStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new DepthState();
|
||||
}
|
||||
}
|
||||
|
||||
public DepthStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public DepthStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
}
|
||||
|
||||
final DepthState saved = stack[--pointer];
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
final DepthState saved = stack[s];
|
||||
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
setExceptMask(saved);
|
||||
} else {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public boolean isEffectiveMaskEnabled() {
|
||||
@@ -52,11 +54,8 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
|
||||
return effective.isEnabled();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
|
||||
public class FogStateStack extends FogState implements IStateStack<FogStateStack> {
|
||||
public final class FogStateStack extends FogState implements CowStateStack<FogStateStack> {
|
||||
|
||||
protected final FogState[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public FogStateStack() {
|
||||
public FogStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new FogState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new FogState();
|
||||
}
|
||||
}
|
||||
|
||||
public FogStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public FogStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.IntegerState;
|
||||
|
||||
public class IntegerStateStack extends IntegerState implements IStateStack<IntegerStateStack> {
|
||||
public final class IntegerStateStack extends IntegerState implements CowStateStack<IntegerStateStack> {
|
||||
|
||||
protected final IntegerState[] stack;
|
||||
private CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public IntegerStateStack(int val) {
|
||||
public IntegerStateStack(int val, int id) {
|
||||
cow.id = id;
|
||||
setValue(val);
|
||||
stack = new IntegerState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
@@ -19,34 +18,40 @@ public class IntegerStateStack extends IntegerState implements IStateStack<Integ
|
||||
}
|
||||
}
|
||||
|
||||
public IntegerStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
public IntegerStateStack restoreBit(int bit) {
|
||||
final int id = cow.id;
|
||||
cow = new CowDepths(bit);
|
||||
cow.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public IntegerStateStack push(int value) {
|
||||
push().setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IntegerStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
+19
-20
@@ -1,40 +1,39 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightModelState;
|
||||
|
||||
public class LightModelStateStack extends LightModelState implements IStateStack<LightModelStateStack> {
|
||||
public final class LightModelStateStack extends LightModelState implements CowStateStack<LightModelStateStack> {
|
||||
|
||||
protected final LightModelState[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public LightModelStateStack() {
|
||||
public LightModelStateStack(int id) {
|
||||
super();
|
||||
cow.id = id;
|
||||
stack = new LightModelState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new LightModelState();
|
||||
}
|
||||
}
|
||||
|
||||
public LightModelStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public LightModelStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() { return pointer == 0; }
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,39 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
|
||||
public class LightStateStack extends LightState implements IStateStack<LightStateStack> {
|
||||
public final class LightStateStack extends LightState implements CowStateStack<LightStateStack> {
|
||||
|
||||
protected final LightState[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public LightStateStack(int light) {
|
||||
public LightStateStack(int light, int id) {
|
||||
super(light);
|
||||
cow.id = id;
|
||||
stack = new LightState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new LightState(light);
|
||||
}
|
||||
}
|
||||
|
||||
public LightStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public LightStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() { return pointer == 0; }
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LineState;
|
||||
|
||||
/**
|
||||
* Stack for GL line state (width, stipple factor, stipple pattern).
|
||||
* Used by GL_LINE_BIT push/pop attrib.
|
||||
*/
|
||||
public class LineStateStack extends LineState implements IStateStack<LineStateStack> {
|
||||
public final class LineStateStack extends LineState implements CowStateStack<LineStateStack> {
|
||||
|
||||
protected final LineState[] stack;
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_LINE);
|
||||
|
||||
public LineStateStack() {
|
||||
public LineStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new LineState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new LineState();
|
||||
}
|
||||
}
|
||||
|
||||
public LineStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public LineStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,39 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
|
||||
public class MaterialStateStack extends MaterialState implements IStateStack<MaterialStateStack> {
|
||||
public final class MaterialStateStack extends MaterialState implements CowStateStack<MaterialStateStack> {
|
||||
|
||||
protected final MaterialState[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public MaterialStateStack(int face) {
|
||||
public MaterialStateStack(int face, int id) {
|
||||
super(face);
|
||||
cow.id = id;
|
||||
stack = new MaterialState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new MaterialState(face);
|
||||
}
|
||||
}
|
||||
|
||||
public MaterialStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public MaterialStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() { return pointer == 0; }
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MatrixMode;
|
||||
|
||||
public class MatrixModeStack extends MatrixMode implements IStateStack<MatrixModeStack> {
|
||||
public final class MatrixModeStack extends MatrixMode implements CowStateStack<MatrixModeStack> {
|
||||
|
||||
protected final MatrixMode[] stack;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public MatrixModeStack() {
|
||||
public MatrixModeStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new MatrixMode[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new MatrixMode();
|
||||
}
|
||||
}
|
||||
|
||||
public MatrixModeStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public MatrixModeStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PointState;
|
||||
|
||||
/**
|
||||
* Stack for GL point state (size).
|
||||
* Used by GL_POINT_BIT push/pop attrib.
|
||||
*/
|
||||
public class PointStateStack extends PointState implements IStateStack<PointStateStack> {
|
||||
public final class PointStateStack extends PointState implements CowStateStack<PointStateStack> {
|
||||
|
||||
protected final PointState[] stack;
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_POINT);
|
||||
|
||||
public PointStateStack() {
|
||||
public PointStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new PointState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new PointState();
|
||||
}
|
||||
}
|
||||
|
||||
public PointStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public PointStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
|
||||
/**
|
||||
* Stack for GL polygon state (mode, offset, cull face mode, front face).
|
||||
* Used by GL_POLYGON_BIT push/pop attrib.
|
||||
*/
|
||||
public class PolygonStateStack extends PolygonState implements IStateStack<PolygonStateStack> {
|
||||
public final class PolygonStateStack extends PolygonState implements CowStateStack<PolygonStateStack> {
|
||||
|
||||
protected final PolygonState[] stack;
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_POLYGON);
|
||||
|
||||
public PolygonStateStack() {
|
||||
public PolygonStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new PolygonState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new PolygonState();
|
||||
}
|
||||
}
|
||||
|
||||
public PolygonStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public PolygonStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
public final class StackIdAllocator {
|
||||
|
||||
private StackIdAllocator() {}
|
||||
|
||||
private static final int STATIC_BASE = 4096;
|
||||
private static final int STATIC_CAPACITY = 16;
|
||||
|
||||
private static int next = -1;
|
||||
private static int perContext = -1;
|
||||
private static int staticNext;
|
||||
|
||||
public static void beginContext() {
|
||||
next = 0;
|
||||
}
|
||||
|
||||
public static void endContext() {
|
||||
if (perContext == -1) {
|
||||
perContext = next;
|
||||
} else if (next != perContext) {
|
||||
throw new IllegalStateException("stack id count changed across contexts: " + perContext + " -> " + next);
|
||||
}
|
||||
next = -1;
|
||||
}
|
||||
|
||||
public static int nextId() {
|
||||
return next >= 0 ? next++ : -1;
|
||||
}
|
||||
|
||||
public static int nextStaticId() {
|
||||
if (staticNext >= STATIC_CAPACITY) {
|
||||
throw new IllegalStateException("static stack id capacity exceeded");
|
||||
}
|
||||
return STATIC_BASE + staticNext++;
|
||||
}
|
||||
|
||||
public static int capacity() {
|
||||
return STATIC_BASE + STATIC_CAPACITY;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.StencilState;
|
||||
|
||||
/**
|
||||
* Stack for GL stencil buffer state.
|
||||
* Used by GL_STENCIL_BUFFER_BIT push/pop attrib.
|
||||
*/
|
||||
public class StencilStateStack extends StencilState implements IStateStack<StencilStateStack> {
|
||||
public final class StencilStateStack extends StencilState implements CowStateStack<StencilStateStack> {
|
||||
|
||||
protected final StencilState[] stack;
|
||||
protected int pointer;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_STENCIL);
|
||||
|
||||
public StencilStateStack() {
|
||||
public StencilStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new StencilState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new StencilState();
|
||||
}
|
||||
}
|
||||
|
||||
public StencilStateStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public StencilStateStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
+21
-25
@@ -1,49 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
|
||||
|
||||
public class TextureBindingStack extends TextureBinding implements IStateStack<TextureBindingStack> {
|
||||
public final class TextureBindingStack extends TextureBinding implements CowStateStack<TextureBindingStack> {
|
||||
|
||||
protected final TextureBinding[] stack;
|
||||
private final CowDepths cow;
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public TextureBindingStack() {
|
||||
public TextureBindingStack(int id, int unit) {
|
||||
cow = new CowDepths(StateSet.R_TEXTURE, unit);
|
||||
cow.id = id;
|
||||
stack = new TextureBinding[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new TextureBinding();
|
||||
}
|
||||
}
|
||||
|
||||
public TextureBindingStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public TextureBindingStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public TextureBinding peek() {
|
||||
return stack[pointer];
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
+3
-7
@@ -9,7 +9,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* BooleanStateStack for per-texture-unit state.
|
||||
* Switches to correct unit (raw GL) before enable/disable calls.
|
||||
*/
|
||||
public class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
public final class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
private final int unitIndex;
|
||||
|
||||
public TextureUnitBooleanStateStack(int glCap, int unitIndex) {
|
||||
@@ -29,12 +29,8 @@ public class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureUnitBooleanStateStack popDepth() {
|
||||
if (savedDepth > 0) {
|
||||
final boolean oldValue = stack[--savedDepth];
|
||||
setEnabledWithUnitSwitch(oldValue);
|
||||
}
|
||||
return this;
|
||||
public void restoreSlot(int slot) {
|
||||
setEnabledWithUnitSwitch(stack[slot]);
|
||||
}
|
||||
|
||||
private void setEnabledWithUnitSwitch(boolean enabled) {
|
||||
|
||||
@@ -1,47 +1,42 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
/**
|
||||
* Stack for a Vector3f value (e.g. current normal).
|
||||
*/
|
||||
public class Vec3fStack implements IStateStack<Vec3fStack> {
|
||||
public final class Vec3fStack implements CowStateStack<Vec3fStack> {
|
||||
|
||||
private final Vector3f value;
|
||||
private final float[][] stack;
|
||||
private int pointer;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
public Vec3fStack(Vector3f value) {
|
||||
public Vec3fStack(Vector3f value, int id) {
|
||||
this.value = value;
|
||||
cow.id = id;
|
||||
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3fStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer][0] = value.x;
|
||||
stack[pointer][1] = value.y;
|
||||
stack[pointer][2] = value.z;
|
||||
pointer++;
|
||||
return this;
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3fStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
pointer--;
|
||||
value.set(stack[pointer][0], stack[pointer][1], stack[pointer][2]);
|
||||
return this;
|
||||
public void captureSlot(int s) {
|
||||
stack[s][0] = value.x;
|
||||
stack[s][1] = value.y;
|
||||
stack[s][2] = value.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
public void restoreSlot(int s) {
|
||||
value.set(stack[s][0], stack[s][1], stack[s][2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,43 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
/**
|
||||
* Stack for a Vector4f value (e.g. current texture coordinates).
|
||||
*/
|
||||
public class Vec4fStack implements IStateStack<Vec4fStack> {
|
||||
public final class Vec4fStack implements CowStateStack<Vec4fStack> {
|
||||
|
||||
private final Vector4f value;
|
||||
private final float[][] stack;
|
||||
private int pointer;
|
||||
private final CowDepths cow = new CowDepths();
|
||||
|
||||
public Vec4fStack(Vector4f value) {
|
||||
public Vec4fStack(Vector4f value, int id) {
|
||||
this.value = value;
|
||||
cow.id = id;
|
||||
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec4fStack push() {
|
||||
if (pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer][0] = value.x;
|
||||
stack[pointer][1] = value.y;
|
||||
stack[pointer][2] = value.z;
|
||||
stack[pointer][3] = value.w;
|
||||
pointer++;
|
||||
return this;
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec4fStack pop() {
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
pointer--;
|
||||
value.set(stack[pointer][0], stack[pointer][1], stack[pointer][2], stack[pointer][3]);
|
||||
return this;
|
||||
public void captureSlot(int s) {
|
||||
stack[s][0] = value.x;
|
||||
stack[s][1] = value.y;
|
||||
stack[s][2] = value.z;
|
||||
stack[s][3] = value.w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
public void restoreSlot(int s) {
|
||||
value.set(stack[s][0], stack[s][1], stack[s][2], stack[s][3]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
|
||||
public class ViewPortStateStack extends ViewportState implements IStateStack<ViewPortStateStack> {
|
||||
public final class ViewPortStateStack extends ViewportState implements CowStateStack<ViewPortStateStack> {
|
||||
|
||||
protected final ViewportState[] stack;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_VIEWPORT);
|
||||
|
||||
protected int pointer;
|
||||
|
||||
public ViewPortStateStack() {
|
||||
public ViewPortStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ViewportState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new ViewportState();
|
||||
}
|
||||
}
|
||||
|
||||
public ViewPortStateStack push() {
|
||||
if(pointer == stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
return this;
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
public ViewPortStateStack pop() {
|
||||
if(pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
return this;
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
public boolean topChanged() {
|
||||
return pointer > 0 && !sameAs(stack[pointer - 1]);
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.id;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public class BooleanState implements ISettableState<BooleanState> {
|
||||
if (stateUnknown || bypass || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.vendorIsAMD() && GLStateManager.isPoppingAttributes())) {
|
||||
stateUnknown = false;
|
||||
this.enabled = enabled;
|
||||
onEnabledChanged();
|
||||
if (!ffpStateOnly) {
|
||||
if (enabled) {
|
||||
RENDER_BACKEND.enable(this.glCap);
|
||||
@@ -58,6 +59,8 @@ public class BooleanState implements ISettableState<BooleanState> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void onEnabledChanged() {}
|
||||
|
||||
@Override
|
||||
public BooleanState set(BooleanState state) {
|
||||
this.enabled = state.enabled;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
|
||||
public class SamplerUnitArray {
|
||||
|
||||
private static final int MIN_UNITS = 16;
|
||||
|
||||
private int[] samplers;
|
||||
|
||||
public int get(int unit) {
|
||||
final int[] local = samplers;
|
||||
return local != null && unit >= 0 && unit < local.length ? local[unit] : 0;
|
||||
}
|
||||
|
||||
public boolean set(int unit, int sampler) {
|
||||
if (unit < 0) return false;
|
||||
int[] local = samplers;
|
||||
if (local == null) {
|
||||
local = new int[Math.max(MIN_UNITS, RenderSystem.getMaxCombinedTextureImageUnits())];
|
||||
samplers = local;
|
||||
}
|
||||
if (unit >= local.length) return false;
|
||||
local[unit] = sampler;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void unbindEverywhere(int sampler) {
|
||||
final int[] local = samplers;
|
||||
if (local == null) return;
|
||||
for (int i = 0; i < local.length; i++) {
|
||||
if (local[i] == sampler) local[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
final int[] local = samplers;
|
||||
return local == null ? 0 : local.length;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowDepths;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
/**
|
||||
* Per-texture-unit GL_TEXTURE_ENV state for FFP emulation. Tracks the simple texenv mode and all GL_COMBINE sub-parameters.
|
||||
*/
|
||||
public class TexEnvState implements IStateStack<TexEnvState> {
|
||||
public final class TexEnvState implements CowStateStack<TexEnvState> {
|
||||
|
||||
public int mode;
|
||||
|
||||
@@ -30,22 +31,25 @@ public class TexEnvState implements IStateStack<TexEnvState> {
|
||||
public float envColorA;
|
||||
|
||||
private final TexEnvState[] stack;
|
||||
private int pointer;
|
||||
private final CowDepths cow;
|
||||
private final boolean isStackEntry;
|
||||
|
||||
public TexEnvState() {
|
||||
public TexEnvState(int id) {
|
||||
this(false);
|
||||
cow.id = id;
|
||||
}
|
||||
|
||||
private TexEnvState(boolean isStackEntry) {
|
||||
this.isStackEntry = isStackEntry;
|
||||
if (isStackEntry) {
|
||||
stack = null;
|
||||
cow = null;
|
||||
} else {
|
||||
stack = new TexEnvState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new TexEnvState(true);
|
||||
}
|
||||
cow = new CowDepths();
|
||||
}
|
||||
reset();
|
||||
}
|
||||
@@ -80,31 +84,27 @@ public class TexEnvState implements IStateStack<TexEnvState> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TexEnvState push() {
|
||||
if (stack == null) throw new IllegalStateException("Cannot push stack entry");
|
||||
if (pointer >= stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
stack[pointer++].copyFrom(this);
|
||||
return this;
|
||||
public CowDepths cowDepths() {
|
||||
if (cow == null) throw new IllegalStateException("Cannot push/pop a stack entry");
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TexEnvState pop() {
|
||||
if (stack == null) throw new IllegalStateException("Cannot pop stack entry");
|
||||
if (pointer == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
copyFrom(stack[--pointer]);
|
||||
return this;
|
||||
public void captureSlot(int s) {
|
||||
stack[s].copyFrom(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
public void restoreSlot(int s) {
|
||||
copyFrom(stack[s]);
|
||||
}
|
||||
|
||||
public boolean isCombineMode() {
|
||||
return mode == GL13.GL_COMBINE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow == null ? -1 : cow.id;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.TextureUnitBooleanStateStack;
|
||||
import lombok.Getter;
|
||||
@@ -8,7 +10,11 @@ import org.joml.Matrix4fStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TextureUnitArray {
|
||||
private final List<IStateStack<?>> idStacks = new ArrayList<>();
|
||||
private final TextureBindingStack[] bindings;
|
||||
private final TextureUnitBooleanStateStack[] states;
|
||||
private final TextureUnitBooleanStateStack[] texture1DStates;
|
||||
@@ -22,6 +28,9 @@ public class TextureUnitArray {
|
||||
@Getter
|
||||
public final Matrix4fStack[] textureMatricies;
|
||||
|
||||
public int texMatIdentityMask;
|
||||
public int texMatIdentityGen;
|
||||
|
||||
public TextureUnitArray() {
|
||||
bindings = new TextureBindingStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
states = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
@@ -36,20 +45,35 @@ public class TextureUnitArray {
|
||||
textureMatricies = new Matrix4fStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
|
||||
for (int i = 0; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
bindings[i] = new TextureBindingStack();
|
||||
states[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_2D, i);
|
||||
texture1DStates[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_1D, i);
|
||||
texture3DStates[i] = new TextureUnitBooleanStateStack(GL12.GL_TEXTURE_3D, i);
|
||||
texGenSStates[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_S, i);
|
||||
texGenTStates[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_T, i);
|
||||
texGenRStates[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_R, i);
|
||||
texGenQStates[i] = new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_Q, i);
|
||||
bindings[i] = member(new TextureBindingStack(StackIdAllocator.nextId(), i));
|
||||
states[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_2D, i));
|
||||
texture1DStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_1D, i));
|
||||
texture3DStates[i] = track(new TextureUnitBooleanStateStack(GL12.GL_TEXTURE_3D, i));
|
||||
texGenSStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_S, i));
|
||||
texGenTStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_T, i));
|
||||
texGenRStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_R, i));
|
||||
texGenQStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_Q, i));
|
||||
texGenStates[i] = new TexGenState();
|
||||
texEnvStates[i] = new TexEnvState();
|
||||
texEnvStates[i] = member(new TexEnvState(StackIdAllocator.nextId()));
|
||||
textureMatricies[i] = new Matrix4fStack(GLStateManager.MAX_TEXTURE_STACK_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends TextureUnitBooleanStateStack> T track(T s) {
|
||||
s.setStackId(StackIdAllocator.nextId());
|
||||
idStacks.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private <T extends IStateStack<?>> T member(T s) {
|
||||
idStacks.add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public List<IStateStack<?>> idStacks() {
|
||||
return idStacks;
|
||||
}
|
||||
|
||||
public TextureBindingStack getTextureUnitBindings(int index) {
|
||||
return bindings[index];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
@@ -11,7 +12,6 @@ import org.lwjgl.opengl.GL44;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Persistent-mapped fence-reclaimed ring over any buffer target. Callers reserve a range and
|
||||
@@ -28,7 +28,8 @@ final class GlStreamingRing {
|
||||
private static final long SYNC_WAIT_SLICE_NANOS = 10_000_000L;
|
||||
private static final long SYNC_WAIT_TOTAL_NANOS = 5_000_000_000L;
|
||||
|
||||
private final ObjectArrayFIFOQueue<FencedRegion> fenceQueue = new ObjectArrayFIFOQueue<>();
|
||||
private final LongArrayFIFOQueue fenceIds = new LongArrayFIFOQueue();
|
||||
private final IntArrayFIFOQueue fenceBytes = new IntArrayFIFOQueue();
|
||||
private int writePos;
|
||||
private int remaining;
|
||||
private int pendingBytes;
|
||||
@@ -51,13 +52,13 @@ final class GlStreamingRing {
|
||||
|
||||
static GlStreamingRing create(int target, int capacity, boolean forceOrphan) {
|
||||
if (forceOrphan || !RenderSystem.supportsBufferStorage()) return null;
|
||||
final int id = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(target, id);
|
||||
final int id = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(target, id);
|
||||
final int storageFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT | GL44.GL_CLIENT_STORAGE_BIT;
|
||||
RenderSystem.bufferStorage(target, capacity, storageFlags);
|
||||
final int mapFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
|
||||
final ByteBuffer mapped = RENDER_BACKEND.mapBufferRange(target, 0, capacity, mapFlags);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
final ByteBuffer mapped = GLStateManager.glMapBufferRange(target, 0, capacity, mapFlags);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
if (mapped == null) {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
GLStateManager.LOGGER.warn("Persistent map failed for streaming ring (target=0x{}, {} bytes)", Integer.toHexString(target), capacity);
|
||||
@@ -92,7 +93,7 @@ final class GlStreamingRing {
|
||||
|
||||
private boolean ensureRemaining(int needed) {
|
||||
while (remaining < needed) {
|
||||
if (fenceQueue.isEmpty()) {
|
||||
if (fenceIds.isEmpty()) {
|
||||
if (pendingBytes == 0) return false;
|
||||
fence();
|
||||
}
|
||||
@@ -103,7 +104,8 @@ final class GlStreamingRing {
|
||||
|
||||
private void fence() {
|
||||
if (pendingBytes > 0) {
|
||||
fenceQueue.enqueue(new FencedRegion(RENDER_BACKEND.fenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0), pendingBytes));
|
||||
fenceIds.enqueue(GLStateManager.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
|
||||
fenceBytes.enqueue(pendingBytes);
|
||||
pendingBytes = 0;
|
||||
fencesIssued++;
|
||||
}
|
||||
@@ -114,30 +116,32 @@ final class GlStreamingRing {
|
||||
}
|
||||
|
||||
private void reclaim() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
if (!signaled(RENDER_BACKEND.clientWaitSync(region.fenceId, 0, 0L))) break;
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
while (!fenceIds.isEmpty()) {
|
||||
final long fenceId = fenceIds.firstLong();
|
||||
if (!signaled(GLStateManager.glClientWaitSync(fenceId, 0, 0L))) break;
|
||||
GLStateManager.glDeleteSync(fenceId);
|
||||
fenceIds.dequeueLong();
|
||||
remaining += fenceBytes.dequeueInt();
|
||||
}
|
||||
}
|
||||
|
||||
private void syncOldest() {
|
||||
final FencedRegion region = fenceQueue.dequeue();
|
||||
final long fenceId = fenceIds.dequeueLong();
|
||||
final int bytes = fenceBytes.dequeueInt();
|
||||
forcedReclaims++;
|
||||
final long deadline = System.nanoTime() + SYNC_WAIT_TOTAL_NANOS;
|
||||
long remainingNanos = SYNC_WAIT_TOTAL_NANOS;
|
||||
int status;
|
||||
do {
|
||||
status = RENDER_BACKEND.clientWaitSync(region.fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT, Math.min(SYNC_WAIT_SLICE_NANOS, remainingNanos));
|
||||
status = GLStateManager.glClientWaitSync(fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT,
|
||||
Math.min(SYNC_WAIT_SLICE_NANOS, remainingNanos));
|
||||
remainingNanos = deadline - System.nanoTime();
|
||||
} while (status == GL32.GL_TIMEOUT_EXPIRED && remainingNanos > 0);
|
||||
if (!signaled(status)) {
|
||||
GLStateManager.LOGGER.warn("Streaming ring fence wait did not signal (status=0x{}); reclaiming anyway", Integer.toHexString(status));
|
||||
}
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
remaining += region.bytes;
|
||||
GLStateManager.glDeleteSync(fenceId);
|
||||
remaining += bytes;
|
||||
reclaim();
|
||||
}
|
||||
|
||||
@@ -156,17 +160,16 @@ final class GlStreamingRing {
|
||||
int forcedReclaims() { return forcedReclaims; }
|
||||
|
||||
void destroy() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
GLStateManager.glDeleteSync(fenceQueue.dequeue().fenceId);
|
||||
while (!fenceIds.isEmpty()) {
|
||||
GLStateManager.glDeleteSync(fenceIds.dequeueLong());
|
||||
}
|
||||
fenceBytes.clear();
|
||||
if (bufferId != 0) {
|
||||
RENDER_BACKEND.bindBuffer(target, bufferId);
|
||||
RENDER_BACKEND.unmapBuffer(target);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
GLStateManager.glBindBuffer(target, bufferId);
|
||||
GLStateManager.glUnmapBuffer(target);
|
||||
GLStateManager.glBindBuffer(target, 0);
|
||||
GLStateManager.glDeleteBuffers(bufferId);
|
||||
bufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private record FencedRegion(long fenceId, int bytes) {}
|
||||
}
|
||||
+5
-7
@@ -5,8 +5,6 @@ import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Streaming buffer using the classic orphan pattern.
|
||||
*/
|
||||
@@ -16,19 +14,19 @@ public final class OrphanStreamingBuffer {
|
||||
private int capacity;
|
||||
|
||||
public OrphanStreamingBuffer() {
|
||||
this.bufferId = RENDER_BACKEND.genBuffers();
|
||||
this.bufferId = GLStateManager.glGenBuffers();
|
||||
}
|
||||
|
||||
public void upload(ByteBuffer data) {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
capacity = StreamingUploader.upload(data, capacity);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void upload(StreamingUploader.UploadStrategy strategy, ByteBuffer data) {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
capacity = StreamingUploader.upload(strategy, data, capacity);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
public int getCapacity() {
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.lwjgl.opengl.GL30;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
|
||||
|
||||
@@ -28,30 +27,30 @@ public final class StreamingUploader {
|
||||
public static int upload(UploadStrategy strategy, ByteBuffer data, int capacity) {
|
||||
switch (strategy) {
|
||||
case BUFFER_DATA -> {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
case BUFFER_SUB_DATA -> {
|
||||
if (data.remaining() > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, 0, data);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, data);
|
||||
return capacity;
|
||||
}
|
||||
case MAP_BUFFER_RANGE -> {
|
||||
if (data.remaining() > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
final int dataSize = data.remaining();
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
final long dst = GLStateManager.glMapBufferRangeAddress(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
if (dst == 0L) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
memCopy(memAddress0(data), dst, dataSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GLStateManager.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
return capacity;
|
||||
}
|
||||
default -> throw new UnsupportedOperationException();
|
||||
|
||||
+9
-10
@@ -10,7 +10,6 @@ import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class UniformRingBuffer {
|
||||
|
||||
@@ -35,10 +34,10 @@ public final class UniformRingBuffer {
|
||||
this.stride = (blockSize + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT;
|
||||
this.ring = GlStreamingRing.create(GL31.GL_UNIFORM_BUFFER, capacity, forceOrphan);
|
||||
if (ring == null) {
|
||||
orphanBufferId = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
orphanBufferId = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,20 +55,20 @@ public final class UniformRingBuffer {
|
||||
}
|
||||
|
||||
private int writeOrphan(long srcAddress) {
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
int offset = writePos;
|
||||
if (offset + stride > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
offset = 0;
|
||||
orphanWraps++;
|
||||
}
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL31.GL_UNIFORM_BUFFER, offset, blockSize, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
final long dst = GLStateManager.glMapBufferRangeAddress(GL31.GL_UNIFORM_BUFFER, offset, blockSize, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
if (dst == 0L) {
|
||||
throw new IllegalStateException("mapBufferRange failed on uniform ring (offset=" + offset + ")");
|
||||
}
|
||||
memCopy(srcAddress, dst, blockSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
GLStateManager.glUnmapBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
writePos = offset + stride;
|
||||
return offset;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -101,6 +104,22 @@ public class TextureInfoCache {
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isProxyTarget(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PROXY_TEXTURE_1D,
|
||||
GL11.GL_PROXY_TEXTURE_2D,
|
||||
GL12.GL_PROXY_TEXTURE_3D,
|
||||
GL13.GL_PROXY_TEXTURE_CUBE_MAP,
|
||||
GL30.GL_PROXY_TEXTURE_1D_ARRAY,
|
||||
GL30.GL_PROXY_TEXTURE_2D_ARRAY,
|
||||
GL31.GL_PROXY_TEXTURE_RECTANGLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isTrackedTarget(int target) {
|
||||
return target == GL11.GL_TEXTURE_2D
|
||||
|| target == GL11.GL_TEXTURE_1D
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
public final class TextureTargets {
|
||||
|
||||
private TextureTargets() {}
|
||||
|
||||
public static boolean isProxyTarget(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PROXY_TEXTURE_1D,
|
||||
GL11.GL_PROXY_TEXTURE_2D,
|
||||
GL12.GL_PROXY_TEXTURE_3D,
|
||||
GL13.GL_PROXY_TEXTURE_CUBE_MAP,
|
||||
GL30.GL_PROXY_TEXTURE_1D_ARRAY,
|
||||
GL30.GL_PROXY_TEXTURE_2D_ARRAY,
|
||||
GL31.GL_PROXY_TEXTURE_RECTANGLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE,
|
||||
GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
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.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_BlendFuncUnknownState_GLTest {
|
||||
|
||||
private static final int VANILLA_SRC_RGB = GL11.GL_SRC_ALPHA;
|
||||
private static final int VANILLA_DST_RGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
private static final int VANILLA_SRC_ALPHA = GL11.GL_ONE;
|
||||
private static final int VANILLA_DST_ALPHA = GL11.GL_ZERO;
|
||||
|
||||
@BeforeEach
|
||||
void requireBufferBlending() {
|
||||
assumeTrue(RenderSystem.supportsBufferBlending());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void restoreDefaultBlendFunc() {
|
||||
if (!RenderSystem.supportsBufferBlending()) return;
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void seedVanillaSeparate() {
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
}
|
||||
|
||||
private static void overrideBufferZero() {
|
||||
RenderSystem.blendFuncSeparatei(0, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
assertBufferBlend(0, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE);
|
||||
}
|
||||
|
||||
private static void assertBufferBlend(int buffer, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL30.glGetInteger(GL14.GL_BLEND_SRC_RGB, buffer), "buffer " + buffer + " src rgb");
|
||||
assertEquals(dstRgb, GL30.glGetInteger(GL14.GL_BLEND_DST_RGB, buffer), "buffer " + buffer + " dst rgb");
|
||||
assertEquals(srcAlpha, GL30.glGetInteger(GL14.GL_BLEND_SRC_ALPHA, buffer), "buffer " + buffer + " src alpha");
|
||||
assertEquals(dstAlpha, GL30.glGetInteger(GL14.GL_BLEND_DST_ALPHA, buffer), "buffer " + buffer + " dst alpha");
|
||||
}
|
||||
|
||||
private static void assertVanillaOnBothBuffers() {
|
||||
for (int buffer = 0; buffer < 2; buffer++) {
|
||||
assertBufferBlend(buffer, VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
}
|
||||
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tryBlendFuncSeparateForwardsAfterIndexedOverride() {
|
||||
seedVanillaSeparate();
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void glBlendFuncForwardsAfterIndexedOverride() {
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
for (int buffer = 0; buffer < 2; buffer++) {
|
||||
assertBufferBlend(buffer, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribForwardsAfterIndexedOverride() {
|
||||
seedVanillaSeparate();
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
try {
|
||||
overrideBufferZero();
|
||||
} finally {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void displayListReplayForwardsAfterIndexedOverride() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.tryBlendFuncSeparate(VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
seedVanillaSeparate();
|
||||
overrideBufferZero();
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
assertVanillaOnBothBuffers();
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_PopAttribMaskFidelity_GLTest {
|
||||
|
||||
private static int scanClipPlaneMask() {
|
||||
final BooleanStateStack[] planes = GLStateManager.getClipPlaneStates();
|
||||
int mask = 0;
|
||||
for (int i = 0; i < planes.length; i++) {
|
||||
if (planes[i].isEnabled()) mask |= (1 << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
private static void verifyClipPlaneMask(String message) {
|
||||
final int scanned = scanClipPlaneMask();
|
||||
assertEquals(scanned, GLStateManager.ctx().clipPlaneEnabledMask, message + " - mask mismatch");
|
||||
assertEquals(scanned != 0, GLStateManager.anyClipPlaneEnabled(), message + " - anyClipPlaneEnabled mismatch");
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorBufferBitDoesNotRestoreCull() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - Not a GL_COLOR_BUFFER_BIT member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableBitRestoresCull() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - After enable");
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - GL_ENABLE_BIT member, must restore");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerNonMemberPopKeepsChange_OuterMemberPopRestores() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - After enable inside inner bracket");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - Inner pop is not a member, must stay toggled");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Outer pop is a member, must restore to pre-inner value");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void modifyBeforeInnerPush_SurvivesInnerNonMemberPop() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - After enable at outer depth");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - Inner pop is not a member, must stay toggled");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Outer pop is a member, must restore");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoModifiesAtOneDepthRestoreOnce() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
verifyIsEnabled(GL11.GL_BLEND, true, "Blend - After multiple toggles at one depth");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend - GL_COLOR_BUFFER_BIT member, must restore original");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightingBitRestoresLightingAndLightEnables() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_LIGHTING, false, "Lighting - Initial");
|
||||
verifyIsEnabled(GL11.GL_LIGHT0, false, "Light0 - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHT0);
|
||||
verifyIsEnabled(GL11.GL_LIGHTING, true, "Lighting - After enable");
|
||||
verifyIsEnabled(GL11.GL_LIGHT0, true, "Light0 - After enable");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_LIGHTING, false, "Lighting - GL_LIGHTING_BIT member, must restore");
|
||||
verifyIsEnabled(GL11.GL_LIGHT0, false, "Light0 - GL_LIGHTING_BIT member, must restore");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableDisableTracksClipPlaneMask() {
|
||||
try {
|
||||
verifyClipPlaneMask("Initial");
|
||||
|
||||
GLStateManager.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE0, true, "Clip0 - After enable");
|
||||
verifyClipPlaneMask("After enable");
|
||||
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE0, false, "Clip0 - After disable");
|
||||
verifyClipPlaneMask("After disable");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformBitPopRestoresClipPlaneAndMask() {
|
||||
try {
|
||||
verifyClipPlaneMask("Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_TRANSFORM_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CLIP_PLANE1);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE1, true, "Clip1 - After enable inside GL_TRANSFORM_BIT");
|
||||
verifyClipPlaneMask("After enable inside GL_TRANSFORM_BIT");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE1, false, "Clip1 - GL_TRANSFORM_BIT member, must restore");
|
||||
verifyClipPlaneMask("After GL_TRANSFORM_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorBufferBitPopDoesNotRestoreClipPlaneButMaskStaysFaithful() {
|
||||
try {
|
||||
verifyClipPlaneMask("Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CLIP_PLANE2);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE2, true, "Clip2 - After enable inside GL_COLOR_BUFFER_BIT");
|
||||
verifyClipPlaneMask("After enable inside GL_COLOR_BUFFER_BIT");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE2, true, "Clip2 - Not a GL_COLOR_BUFFER_BIT member, must stay toggled");
|
||||
verifyClipPlaneMask("After GL_COLOR_BUFFER_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
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.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_SamplerBinding_GLTest {
|
||||
|
||||
private static final int UNITS = 4;
|
||||
|
||||
@BeforeEach
|
||||
void requireSamplerObjects() {
|
||||
assumeTrue(RenderSystem.supportsSamplerObjects());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void unbindTestUnits() {
|
||||
if (!RenderSystem.supportsSamplerObjects()) return;
|
||||
for (int unit = 0; unit < UNITS; unit++) {
|
||||
GLStateManager.glBindSampler(unit, 0);
|
||||
GL33.glBindSampler(unit, 0);
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static int driverBinding(int unit) {
|
||||
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
final int binding = GL11.glGetInteger(GL33.GL_SAMPLER_BINDING);
|
||||
GL13.glActiveTexture(saved);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static void assertUnits(String context, int... expected) {
|
||||
for (int unit = 0; unit < expected.length; unit++) {
|
||||
assertEquals(expected[unit], GLStateManager.getSamplerBinding(unit), context + ": cache unit " + unit);
|
||||
assertEquals(expected[unit], driverBinding(unit), context + ": driver unit " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
private static IntBuffer ints(int... values) {
|
||||
final IntBuffer buf = ByteBuffer.allocateDirect(values.length * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
|
||||
buf.put(values).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Test
|
||||
void renderSystemRebindAfterGlsmUnbindReachesDriver() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
GLStateManager.glBindSampler(0, 0);
|
||||
assertEquals(0, driverBinding(0), "setup: GLSM unbind must reach the driver");
|
||||
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
|
||||
assertEquals(sampler, driverBinding(0), "RenderSystem skipped a bind the driver no longer holds");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteClearsEveryUnitAndTheNextBindReachesDriver() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
GLStateManager.glBindSampler(3, sampler);
|
||||
|
||||
GLStateManager.glDeleteSamplers(sampler);
|
||||
|
||||
assertUnits("after delete", 0, 0, 0, 0);
|
||||
|
||||
final int next = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, next);
|
||||
assertEquals(next, driverBinding(1), "bind after delete never reached the driver");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(next);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerBindingQueryAnswersForTheActiveUnit() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(GL11.glGetInteger(GL33.GL_SAMPLER_BINDING),
|
||||
GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
assertEquals(sampler, GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertEquals(GL11.glGetInteger(GL33.GL_SAMPLER_BINDING),
|
||||
GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL33.GL_SAMPLER_BINDING));
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unbindAllSamplersClearsUnitsBoundThroughGlsm() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(1, sampler);
|
||||
GLStateManager.glBindSampler(3, sampler);
|
||||
|
||||
RenderSystem.unbindAllSamplers();
|
||||
|
||||
assertEquals(0, driverBinding(1), "unit 1 still bound on the driver");
|
||||
assertEquals(0, driverBinding(3), "unit 3 still bound on the driver");
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(1));
|
||||
assertEquals(0, GLStateManager.getSamplerBinding(3));
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayForwardsCachedSamplerBindings() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
try {
|
||||
GLStateManager.glBindSampler(2, sampler);
|
||||
GL33.glBindSampler(2, 0);
|
||||
assertEquals(0, driverBinding(2), "setup failed to diverge the driver");
|
||||
|
||||
GLStateManager.replayStateToBackend();
|
||||
|
||||
assertEquals(sampler, driverBinding(2), "replay dropped a cached sampler binding");
|
||||
} finally {
|
||||
RenderSystem.destroySampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intBufferGenFillsFromPositionWithBindableSamplers() {
|
||||
final IntBuffer buf = ints(-1, 0, 0);
|
||||
buf.position(1);
|
||||
GLStateManager.glGenSamplers(buf);
|
||||
final int a = buf.get(1);
|
||||
final int b = buf.get(2);
|
||||
try {
|
||||
assertEquals(-1, buf.get(0), "slot before position was overwritten");
|
||||
assertEquals(1, buf.position(), "gen must not move the buffer position");
|
||||
assertNotEquals(0, a);
|
||||
assertNotEquals(0, b);
|
||||
assertNotEquals(a, b);
|
||||
|
||||
GLStateManager.glBindSampler(0, a);
|
||||
GLStateManager.glBindSampler(1, b);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "generated name rejected by the driver");
|
||||
assertEquals(a, driverBinding(0));
|
||||
assertEquals(b, driverBinding(1));
|
||||
} finally {
|
||||
GLStateManager.glDeleteSamplers(a);
|
||||
GLStateManager.glDeleteSamplers(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersFormsBindFromPosition() {
|
||||
final int a = RenderSystem.genSampler();
|
||||
final int b = RenderSystem.genSampler();
|
||||
try {
|
||||
final IntBuffer buf = ints(-1, a, b);
|
||||
buf.position(1);
|
||||
|
||||
GLStateManager.glBindSamplers(1, 2, buf);
|
||||
assertEquals(1, buf.position(), "count form must not move the buffer position");
|
||||
assertUnits("count form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSampler(1, 0);
|
||||
GLStateManager.glBindSampler(2, 0);
|
||||
buf.position(1);
|
||||
|
||||
GLStateManager.glBindSamplers(1, buf);
|
||||
assertEquals(1, buf.position(), "remaining form must not move the buffer position");
|
||||
assertUnits("remaining form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSamplers(1, (IntBuffer) null);
|
||||
assertUnits("null remaining form", 0, a, b, 0);
|
||||
|
||||
GLStateManager.glBindSamplers(2, new int[]{a, b});
|
||||
assertUnits("int[] form", 0, a, a, b);
|
||||
} finally {
|
||||
RenderSystem.destroySampler(a);
|
||||
RenderSystem.destroySampler(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersNullUnbindsRangeAndShortBufferThrows() {
|
||||
final int prior = RenderSystem.genSampler();
|
||||
final int next = RenderSystem.genSampler();
|
||||
try {
|
||||
for (int unit = 0; unit < UNITS; unit++) GLStateManager.glBindSampler(unit, prior);
|
||||
|
||||
GLStateManager.glBindSamplers(1, 2, (IntBuffer) null);
|
||||
|
||||
assertUnits("null count form", prior, 0, 0, prior);
|
||||
|
||||
for (int unit = 0; unit < UNITS; unit++) GLStateManager.glBindSampler(unit, prior);
|
||||
final IntBuffer buf = ints(next, next);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> GLStateManager.glBindSamplers(0, UNITS, buf));
|
||||
|
||||
assertEquals(0, buf.position());
|
||||
assertUnits("short buffer", prior, prior, prior, prior);
|
||||
} finally {
|
||||
RenderSystem.destroySampler(prior);
|
||||
RenderSystem.destroySampler(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,725 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_StateSet_GLTest {
|
||||
|
||||
private static int driverTextureBinding(int unit) {
|
||||
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GL13.glActiveTexture(saved);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static boolean[] driverColorWriteMask() {
|
||||
final ByteBuffer buf = BufferUtils.createByteBuffer(16);
|
||||
GL11.glGetBoolean(GL11.GL_COLOR_WRITEMASK, buf);
|
||||
return new boolean[] { buf.get(0) != 0, buf.get(1) != 0, buf.get(2) != 0, buf.get(3) != 0 };
|
||||
}
|
||||
|
||||
private static int compileShader(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static final String TRIVIAL_VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
|
||||
private static final String TRIVIAL_FS = "#version 330 core\nout vec4 o;\nvoid main(){ o = vec4(1.0); }\n";
|
||||
|
||||
private static int linkTrivialProgram() {
|
||||
final int v = compileShader(GL20.GL_VERTEX_SHADER, TRIVIAL_VS);
|
||||
final int f = compileShader(GL20.GL_FRAGMENT_SHADER, TRIVIAL_FS);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, v);
|
||||
GL20.glAttachShader(p, f);
|
||||
GL20.glLinkProgram(p);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096));
|
||||
GL20.glDeleteShader(v);
|
||||
GL20.glDeleteShader(f);
|
||||
return p;
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchRestoresBlendAxisAndLightingBooleanButNotStencil() {
|
||||
try {
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.disableLighting();
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BATCH);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.enableLighting();
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BATCH pop");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), "driver blend dst rgb");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb");
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GLStateManager.getBlendState().getDstRgb(), "cache blend dst rgb");
|
||||
verifyIsEnabled(GL11.GL_LIGHTING, false, "Lighting enable - restored by BATCH pop");
|
||||
verifyIsEnabled(GL11.GL_STENCIL_TEST, true, "Stencil test - not a BATCH member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.disableLighting();
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fontRestoresDepthAxisAndShadeModelButNotCull() {
|
||||
try {
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
|
||||
GLStateManager.glShadeModel(GL11.GL_FLAT);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - restored by FONT pop");
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
|
||||
assertEquals(GL11.GL_SMOOTH, GLStateManager.getShadeModelState().getValue(), "cache shade model");
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a FONT member, must stay toggled");
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a FONT member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LESS);
|
||||
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fontPipelineRestoresBlendAndDepthFuncButNotProgram() {
|
||||
final int p0 = linkTrivialProgram();
|
||||
final int p1 = linkTrivialProgram();
|
||||
try {
|
||||
GLStateManager.glUseProgram(p0);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT_PIPELINE);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
|
||||
GLStateManager.glUseProgram(p1);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by FONT_PIPELINE pop");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - restored by FONT_PIPELINE pop");
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func - restored by FONT_PIPELINE pop");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func - restored by FONT_PIPELINE pop");
|
||||
assertEquals(p1, GLStateManager.getActiveProgram(), "program - not a FONT_PIPELINE member, must stay toggled");
|
||||
|
||||
GLStateManager.glUseProgram(p0);
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(p0);
|
||||
GLStateManager.glDeleteProgram(p1);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LESS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void handRestoresBlendAxisAndUnit1TexturingButNotColorMask() {
|
||||
try {
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.HAND);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.disableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by HAND pop");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by HAND pop");
|
||||
|
||||
final boolean[] driverMask = driverColorWriteMask();
|
||||
assertFalse(driverMask[0] || driverMask[1] || driverMask[2] || driverMask[3], "driver color mask - not a HAND member, must stay toggled");
|
||||
assertFalse(GLStateManager.getColorMask().red || GLStateManager.getColorMask().green
|
||||
|| GLStateManager.getColorMask().blue || GLStateManager.getColorMask().alpha, "cache color mask - not a HAND member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.disableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void masksRestoresDepthMaskAndColorMaskButNotDepthTestEnable() {
|
||||
try {
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glColorMask(true, false, true, false);
|
||||
GLStateManager.disableDepthTest();
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.MASKS);
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
|
||||
GLStateManager.glColorMask(false, true, false, true);
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth write mask - restored by MASKS pop");
|
||||
assertTrue(GLStateManager.getDepthState().isEnabled(), "cache depth write mask - restored by MASKS pop");
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
|
||||
|
||||
final boolean[] driverMask = driverColorWriteMask();
|
||||
assertTrue(driverMask[0] && !driverMask[1] && driverMask[2] && !driverMask[3], "driver color mask - restored by MASKS pop");
|
||||
assertTrue(GLStateManager.getColorMask().red && !GLStateManager.getColorMask().green
|
||||
&& GLStateManager.getColorMask().blue && !GLStateManager.getColorMask().alpha, "cache color mask - restored by MASKS pop");
|
||||
|
||||
verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - not a MASKS member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LESS);
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.disableDepthTest();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cullRestoresPolygonAxisAndCullEnableButNotBlendFunc() {
|
||||
try {
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.CULL);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by CULL pop");
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode");
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CULL member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CULL member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CULL member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void blendRestoresBlendAxisButNotCull() {
|
||||
try {
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BLEND pop");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull enable - not a BLEND member, must stay toggled");
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a BLEND member, must stay toggled");
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a BLEND member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void alphaRestoresAlphaAxisButNotBlend() {
|
||||
try {
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.ALPHA);
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by ALPHA pop");
|
||||
assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func");
|
||||
assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref");
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not an ALPHA member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not an ALPHA member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not an ALPHA member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cutoutRestoresAlphaAxisAndUnit1TexturingButNotBlend() {
|
||||
try {
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.CUTOUT);
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.disableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
|
||||
final int fragBefore = GLStateManager.ctx().fragmentGeneration;
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "CUTOUT pop must bump fragment generation once when the value changed inside the bracket");
|
||||
|
||||
verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by CUTOUT pop");
|
||||
assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func");
|
||||
assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref");
|
||||
assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by CUTOUT pop");
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CUTOUT member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CUTOUT member, must stay toggled");
|
||||
assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CUTOUT member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.disableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cullFaceModeRestoredThroughCullBracket() {
|
||||
try {
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
final int d = GLStateManager.pushState(StateSet.CULL);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode");
|
||||
} finally {
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cullFaceModeRestoredThroughBatchBracketRegression2117() {
|
||||
try {
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
final int d = GLStateManager.pushState(StateSet.BATCH);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode");
|
||||
} finally {
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchRestoresUnit0And1BindingsAndActiveUnitButNotUnit2() {
|
||||
if (GLStateManager.MAX_TEXTURE_UNITS < 3) return;
|
||||
final int tex0Base = GLStateManager.glGenTextures();
|
||||
final int tex1Base = GLStateManager.glGenTextures();
|
||||
final int tex2Base = GLStateManager.glGenTextures();
|
||||
final int tex0Dirty = GLStateManager.glGenTextures();
|
||||
final int tex1Dirty = GLStateManager.glGenTextures();
|
||||
final int tex2Dirty = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex0Base);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex1Base);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + 2);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex2Base);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BATCH);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex0Dirty);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex1Dirty);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + 2);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex2Dirty);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(2, GLStateManager.getActiveTextureUnit(), "cache active unit - restored by BATCH pop");
|
||||
assertEquals(GL13.GL_TEXTURE0 + 2, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), "driver active unit - restored by BATCH pop");
|
||||
|
||||
assertEquals(tex0Base, GLStateManager.getTextures().getTextureUnitBindings(0).getBinding(), "cache unit 0 binding - restored by BATCH pop");
|
||||
assertEquals(tex0Base, driverTextureBinding(0), "driver unit 0 binding - restored by BATCH pop");
|
||||
assertEquals(tex1Base, GLStateManager.getTextures().getTextureUnitBindings(1).getBinding(), "cache unit 1 binding - restored by BATCH pop");
|
||||
assertEquals(tex1Base, driverTextureBinding(1), "driver unit 1 binding - restored by BATCH pop");
|
||||
|
||||
assertEquals(tex2Dirty, GLStateManager.getTextures().getTextureUnitBindings(2).getBinding(), "cache unit 2 binding - not a BATCH member, must stay toggled");
|
||||
assertEquals(tex2Dirty, driverTextureBinding(2), "driver unit 2 binding - not a BATCH member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + 2);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(tex0Base);
|
||||
GLStateManager.glDeleteTextures(tex1Base);
|
||||
GLStateManager.glDeleteTextures(tex2Base);
|
||||
GLStateManager.glDeleteTextures(tex0Dirty);
|
||||
GLStateManager.glDeleteTextures(tex1Dirty);
|
||||
GLStateManager.glDeleteTextures(tex2Dirty);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popStateToRepairsUnbalancedInnerPush() {
|
||||
try {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.pushState(StateSet.CULL);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(d, GLStateManager.getAttribDepth());
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "popStateTo repairs the unbalanced non-member push and carries the change through to the BLEND member pop, restoring the pre-bracket baseline");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popStateToToleratesUnbalancedInnerPop() {
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.popState();
|
||||
assertEquals(d, GLStateManager.getAttribDepth());
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(d, GLStateManager.getAttribDepth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exceptionMidBracketLeavesDepthAndStateCorrectViaFinally() {
|
||||
try {
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int depthBefore = GLStateManager.getAttribDepth();
|
||||
|
||||
final RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
try {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
throw new RuntimeException("boom");
|
||||
} finally {
|
||||
GLStateManager.popStateTo(d);
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals("boom", thrown.getMessage());
|
||||
assertEquals(depthBefore, GLStateManager.getAttribDepth());
|
||||
verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored despite exception");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored despite exception");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void internalPushDuringDisplayListCompileThrowsAndLeavesDepthUnchanged() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
final int depthBefore = GLStateManager.getAttribDepth();
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
assertThrows(IllegalStateException.class, () -> GLStateManager.pushState(StateSet.BLEND));
|
||||
GLStateManager.pushState(StateSet.forMask(GL11.GL_COLOR_BUFFER_BIT));
|
||||
GLStateManager.popState();
|
||||
assertEquals(depthBefore, GLStateManager.getAttribDepth(), "mask-based push/pop is recorded, not executed, and leaves live depth unchanged");
|
||||
} finally {
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void allAttribBitsNestedInsideInternalBracketRestoresBothLevels() {
|
||||
try {
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by inner GL_ALL_ATTRIB_BITS pop");
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop");
|
||||
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by outer BLEND pop");
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by outer BLEND pop");
|
||||
} finally {
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void internalBracketNestedInsideAllAttribBitsRestoresBothLevels() {
|
||||
try {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
try {
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by inner BLEND pop");
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by inner BLEND pop");
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - change survives, not a BLEND member");
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - change survives, not a BLEND member");
|
||||
} finally {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop");
|
||||
} finally {
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rProgramFontRestoresRealProgramToDriverAndCache() {
|
||||
final int p0 = linkTrivialProgram();
|
||||
final int p1 = linkTrivialProgram();
|
||||
try {
|
||||
GLStateManager.glUseProgram(p0);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.glUseProgram(p1);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(p0, GLStateManager.getActiveProgram(), "cache active program restored");
|
||||
assertEquals(p0, GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM), "driver current program restored");
|
||||
assertFalse(ShaderManager.getInstance().isActive(), "FFP must stay inactive when a real program was bound before the bracket");
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(p0);
|
||||
GLStateManager.glDeleteProgram(p1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rProgramFontReactivatesFfpAfterBracket() {
|
||||
final int p1 = linkTrivialProgram();
|
||||
try {
|
||||
GLStateManager.glUseProgram(0);
|
||||
assertTrue(ShaderManager.getInstance().isActive(), "FFP must be active before the bracket");
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.glUseProgram(p1);
|
||||
assertFalse(ShaderManager.getInstance().isActive(), "FFP must deactivate once a real program binds");
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(0, GLStateManager.getActiveProgram(), "cache active program restored to the FFP sentinel");
|
||||
assertTrue(ShaderManager.getInstance().isActive(), "FFP must be active again after the pop");
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(p1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void blendPopDoesNotBumpFragmentOrLightingGeneration() {
|
||||
try {
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO);
|
||||
|
||||
final int fragBefore = GLStateManager.ctx().fragmentGeneration;
|
||||
final int lightBefore = GLStateManager.ctx().lightingGeneration;
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(fragBefore, GLStateManager.ctx().fragmentGeneration, "BLEND pop must not bump fragment generation");
|
||||
assertEquals(lightBefore, GLStateManager.ctx().lightingGeneration, "BLEND pop must not bump lighting generation");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void alphaPopBumpsFragmentGenerationWhenChanged() {
|
||||
try {
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.ALPHA);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f);
|
||||
|
||||
final int fragBefore = GLStateManager.ctx().fragmentGeneration;
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "ALPHA pop must bump fragment generation once when the value changed inside the bracket");
|
||||
} finally {
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textureBitBracketRestoresUnit1BindingAndActiveUnit() {
|
||||
final int texBase = GLStateManager.glGenTextures();
|
||||
final int texDirty = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texBase);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texDirty);
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(0, GLStateManager.getActiveTextureUnit(), "cache active unit restored by GL_TEXTURE_BIT pop");
|
||||
assertEquals(GL13.GL_TEXTURE0, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), "driver active unit restored by GL_TEXTURE_BIT pop");
|
||||
assertEquals(texBase, GLStateManager.getTextures().getTextureUnitBindings(1).getBinding(), "cache unit 1 binding restored by GL_TEXTURE_BIT pop");
|
||||
assertEquals(texBase, driverTextureBinding(1), "driver unit 1 binding restored by GL_TEXTURE_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(texBase);
|
||||
GLStateManager.glDeleteTextures(texDirty);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textureBitBracketRestoresTexEnvModeAndColor() {
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
final FloatBuffer zero = BufferUtils.createFloatBuffer(4);
|
||||
zero.put(0.0f).put(0.0f).put(0.0f).put(0.0f).flip();
|
||||
GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, zero);
|
||||
try {
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT));
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);
|
||||
final FloatBuffer dirty = BufferUtils.createFloatBuffer(4);
|
||||
dirty.put(0.1f).put(0.2f).put(0.3f).put(0.4f).flip();
|
||||
GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, dirty);
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(GL11.GL_MODULATE, GLStateManager.getTextures().getTexEnvState(0).mode, "cache tex env mode restored by GL_TEXTURE_BIT pop");
|
||||
assertEquals(0.0f, GLStateManager.getTextures().getTexEnvState(0).envColorR, 0.0001f, "cache tex env color restored by GL_TEXTURE_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightingBitBracketRestoresBakedFrontMaterialAfterColorMaterialToggle() {
|
||||
try {
|
||||
GLStateManager.glColor4f(0.9f, 0.9f, 0.9f, 0.9f);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_LIGHTING_BIT));
|
||||
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(0.9f, GLStateManager.getFrontMaterial().ambient.x, 0.0001f, "cache front material ambient restored by GL_LIGHTING_BIT pop");
|
||||
assertEquals(0.9f, GLStateManager.getFrontMaterial().diffuse.x, 0.0001f, "cache front material diffuse restored by GL_LIGHTING_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.disableColorMaterial();
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -42,6 +42,28 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyTargetBindIsNeitherForwardedNorCached() {
|
||||
drainErrors();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE5);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d);
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_PROXY_TEXTURE_2D, 0);
|
||||
GLStateManager.glBindTexture(GL12.GL_PROXY_TEXTURE_3D, t2d);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "a proxy target must never reach the driver");
|
||||
assertEquals(t2d, GLStateManager.getBoundTextureForServerState(5), "a proxy bind must not disturb the cache");
|
||||
assertEquals(t2d, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribRestoresRecordedTarget() {
|
||||
drainErrors();
|
||||
|
||||
+15
@@ -22,6 +22,21 @@ public class GLSM_TextureUploadGeneration_GLTest {
|
||||
return texId;
|
||||
}
|
||||
|
||||
@Test
|
||||
void aLevelBeyondTheShiftWidthClampsToOne() {
|
||||
final int texId = allocate(64);
|
||||
try {
|
||||
assertEquals(64, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 6, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 31, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 32, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 64, GL11.GL_TEXTURE_HEIGHT));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUploadLeavesANonZeroGeneration() {
|
||||
final int texId = allocate(4);
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAARB;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_ValueStackCow_GLTest {
|
||||
|
||||
private static int fixtureTexA = -1;
|
||||
private static int fixtureTexB = -1;
|
||||
|
||||
private static FloatBuffer buf4(float a, float b, float c, float d) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(4);
|
||||
buf.put(a).put(b).put(c).put(d).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static void ensureFixtureTextures() {
|
||||
if (fixtureTexA == -1) {
|
||||
fixtureTexA = GLStateManager.glGenTextures();
|
||||
fixtureTexB = GLStateManager.glGenTextures();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void deleteFixtureTextures() {
|
||||
if (fixtureTexA > 0) GLStateManager.glDeleteTextures(fixtureTexA);
|
||||
if (fixtureTexB > 0) GLStateManager.glDeleteTextures(fixtureTexB);
|
||||
fixtureTexA = fixtureTexB = -1;
|
||||
}
|
||||
|
||||
private static final String[] NAMES = {
|
||||
"glAlphaFunc", "glBlendFunc", "tryBlendFuncSeparate", "glBlendEquation", "glBlendEquationSeparate",
|
||||
"glBlendColor", "glColorMask", "glClearColor", "glDrawBuffer", "glLogicOp",
|
||||
"glDepthFunc", "glDepthMask", "glClearDepth", "glStencilFunc", "glStencilOp",
|
||||
"glStencilMask", "glClearStencil", "glViewport", "glDepthRange", "glLineWidth",
|
||||
"glLineStipple", "glPointSize", "glPolygonMode", "glCullFace", "glFrontFace",
|
||||
"glPolygonOffset", "fogColor", "glFogf", "glFogi", "glLightf",
|
||||
"glLighti", "glLight", "glLightModelf", "glLightModeli", "glLightModel",
|
||||
"glMaterialf", "glMateriali", "glMaterial", "glShadeModel", "glColorMaterial",
|
||||
"glMatrixMode", "glActiveTexture", "glBindTexture", "glTexEnvi", "glTexEnv",
|
||||
"glSecondaryColor3ub", "enableBlend/disableBlend", "enableAlphaTest/disableAlphaTest",
|
||||
"glEnable(GL_CULL_FACE)", "dsaBindTextureToUnit"
|
||||
};
|
||||
|
||||
private static final Runnable[] MUTATIONS = {
|
||||
() -> GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f),
|
||||
() -> GLStateManager.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO),
|
||||
() -> GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO),
|
||||
() -> GLStateManager.glBlendEquation(GL14.GL_FUNC_SUBTRACT),
|
||||
() -> GLStateManager.glBlendEquationSeparate(GL14.GL_FUNC_SUBTRACT, GL14.GL_FUNC_REVERSE_SUBTRACT),
|
||||
() -> GLStateManager.glBlendColor(0.5f, 0.6f, 0.7f, 0.8f),
|
||||
() -> GLStateManager.glColorMask(false, true, false, true),
|
||||
() -> GLStateManager.glClearColor(1.0f, 0.5f, 0.25f, 0.75f),
|
||||
() -> GLStateManager.glDrawBuffer(GL11.GL_NONE),
|
||||
() -> GLStateManager.glLogicOp(GL11.GL_XOR),
|
||||
() -> GLStateManager.glDepthFunc(GL11.GL_ALWAYS),
|
||||
() -> GLStateManager.glDepthMask(false),
|
||||
() -> GLStateManager.glClearDepth(0.25),
|
||||
() -> GLStateManager.glStencilFunc(GL11.GL_NOTEQUAL, 3, 0x0F),
|
||||
() -> GLStateManager.glStencilOp(GL11.GL_REPLACE, GL11.GL_INCR, GL11.GL_DECR),
|
||||
() -> GLStateManager.glStencilMask(0x0F),
|
||||
() -> GLStateManager.glClearStencil(9),
|
||||
() -> GLStateManager.glViewport(1, 2, 3, 4),
|
||||
() -> GLStateManager.glDepthRange(0.2, 0.8),
|
||||
() -> GLStateManager.glLineWidth(3.0f),
|
||||
() -> GLStateManager.glLineStipple(3, (short) 0x00FF),
|
||||
() -> GLStateManager.glPointSize(4.0f),
|
||||
() -> GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE),
|
||||
() -> GLStateManager.glCullFace(GL11.GL_FRONT),
|
||||
() -> GLStateManager.glFrontFace(GL11.GL_CW),
|
||||
() -> GLStateManager.glPolygonOffset(3.0f, 4.0f),
|
||||
() -> GLStateManager.fogColor(0.5f, 0.6f, 0.7f, 0.8f),
|
||||
() -> GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.5f),
|
||||
() -> GLStateManager.glFogi(GL11.GL_FOG_MODE, GL11.GL_LINEAR),
|
||||
() -> GLStateManager.glLightf(GL11.GL_LIGHT0, GL11.GL_SPOT_EXPONENT, 12.0f),
|
||||
() -> GLStateManager.glLighti(GL11.GL_LIGHT0, GL11.GL_SPOT_CUTOFF, 45),
|
||||
() -> GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, buf4(1.0f, 2.0f, 3.0f, 1.0f)),
|
||||
() -> GLStateManager.glLightModelf(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0f),
|
||||
() -> GLStateManager.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR),
|
||||
() -> GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, buf4(0.5f, 0.6f, 0.7f, 0.8f)),
|
||||
() -> GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 32.0f),
|
||||
() -> GLStateManager.glMateriali(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 32),
|
||||
() -> GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, buf4(0.1f, 0.2f, 0.3f, 0.4f)),
|
||||
() -> GLStateManager.glShadeModel(GL11.GL_FLAT),
|
||||
() -> GLStateManager.glColorMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR),
|
||||
() -> GLStateManager.glMatrixMode(GL11.GL_PROJECTION),
|
||||
() -> GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + 2),
|
||||
() -> {
|
||||
ensureFixtureTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, fixtureTexA);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
},
|
||||
() -> GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL),
|
||||
() -> GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, buf4(0.1f, 0.2f, 0.3f, 0.4f)),
|
||||
() -> GLStateManager.glSecondaryColor3ub((byte) 255, (byte) 128, (byte) 64),
|
||||
() -> {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.enableBlend();
|
||||
},
|
||||
() -> {
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.enableAlphaTest();
|
||||
},
|
||||
() -> {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
},
|
||||
() -> {
|
||||
assumeTrue(GLStateManager.capabilities.GL_ARB_direct_state_access, "test requires GL_ARB_direct_state_access");
|
||||
ensureFixtureTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, fixtureTexA);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
new DSAARB().bindTextureToUnit(1, fixtureTexB);
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
void everyMutationSiteReachesTheSaveHook() {
|
||||
final int entryDepth = GLStateManager.getAttribDepth();
|
||||
try {
|
||||
for (int i = 0; i < MUTATIONS.length; i++) {
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS));
|
||||
try {
|
||||
final long before = GLStateManager.attribSlotsSaved;
|
||||
MUTATIONS[i].run();
|
||||
assertTrue(GLStateManager.attribSlotsSaved - before >= 1, NAMES[i] + ": mutation site did not reach the save hook");
|
||||
} finally {
|
||||
GLStateManager.popStateTo(d);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.popStateTo(entryDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_WorkerContextStacks_GLTest {
|
||||
|
||||
@Test
|
||||
void enableBitBracketRestoresOnWorkerContext() {
|
||||
GLStateManager.enterWorkerContext();
|
||||
try {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_ENABLE_BIT));
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE), "cull face restored on the worker context");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.exitWorkerContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorBufferBitBracketRestoresOnWorkerContext() {
|
||||
GLStateManager.enterWorkerContext();
|
||||
try {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_COLOR_BUFFER_BIT));
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_BLEND), "blend enable restored on the worker context");
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "blend src rgb restored on the worker context");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.exitWorkerContext();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void workerResetLeavesPrimaryContextUntouched() {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
final int primaryDepthBefore = GLStateManager.getAttribDepth();
|
||||
|
||||
GLStateManager.enterWorkerContext();
|
||||
try {
|
||||
GLStateManager.pushState(StateSet.forMask(GL11.GL_ENABLE_BIT));
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
|
||||
GLStateManager.reset();
|
||||
|
||||
assertEquals(0, GLStateManager.getAttribDepth(), "worker attrib depth drained by its own reset");
|
||||
} finally {
|
||||
GLStateManager.exitWorkerContext();
|
||||
}
|
||||
|
||||
assertEquals(primaryDepthBefore, GLStateManager.getAttribDepth(), "primary context depth untouched by the worker reset");
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE), "primary context cull face untouched by the worker reset");
|
||||
}
|
||||
|
||||
@Test
|
||||
void stackIdsEqualAcrossContexts() {
|
||||
final GLContextState primary = GLStateManager.ctx();
|
||||
final GLContextState worker = GLStateManager.enterWorkerContext();
|
||||
try {
|
||||
final Object[] primaryStacks = {
|
||||
primary.blendState, primary.depthState, primary.colorMask, primary.blendMode,
|
||||
primary.cullState, primary.textures.getTextureUnitBindings(1), primary.polygonState
|
||||
};
|
||||
final Object[] workerStacks = {
|
||||
worker.blendState, worker.depthState, worker.colorMask, worker.blendMode,
|
||||
worker.cullState, worker.textures.getTextureUnitBindings(1), worker.polygonState
|
||||
};
|
||||
|
||||
final int[] primaryIds = new int[primaryStacks.length];
|
||||
final int[] workerIds = new int[workerStacks.length];
|
||||
for (int i = 0; i < primaryStacks.length; i++) {
|
||||
primaryIds[i] = Reflect.invoke(primaryStacks[i], "stackId", new Class[0]);
|
||||
workerIds[i] = Reflect.invoke(workerStacks[i], "stackId", new Class[0]);
|
||||
}
|
||||
|
||||
assertArrayEquals(primaryIds, workerIds, "stack ids must match between the primary and worker contexts for identically-constructed stacks");
|
||||
} finally {
|
||||
GLStateManager.exitWorkerContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,22 +44,48 @@ class BackendPolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshPrefersTheExactRational() {
|
||||
assertEquals(144, RenderBackend.refreshHzFrom(144, 1, 0.0f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(60000, 1001, 0.0f));
|
||||
void periodPrefersTheExactRational() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromRational(60000, 1001, 0.0f));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromRational(144, 1, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshRoundsUp() {
|
||||
assertEquals(144, RenderBackend.refreshHzFrom(0, 0, 143.98f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(0, 0, 59.94f));
|
||||
assertEquals(60, RenderBackend.refreshHzFrom(0, 0, 60.0f));
|
||||
void periodFallsBackToTheFloatRate() {
|
||||
assertEquals(16_683_350L, RenderBackend.periodFromRational(0, 0, 59.94f));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromRational(0, 0, 60.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshIsZeroWhenUnknown() {
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(0, 0, 0.0f));
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(-1, -1, -1.0f));
|
||||
assertEquals(0, RenderBackend.refreshHzFrom(144, 0, 0.0f));
|
||||
void periodIsZeroWhenUnknown() {
|
||||
assertEquals(0L, RenderBackend.periodFromRational(0, 0, 0.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(-1, -1, -1.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(144, 0, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void integerNtscRatesAreAdjusted() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromIntegerHz(59));
|
||||
assertEquals(6_951_389L, RenderBackend.periodFromIntegerHz(143));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromIntegerHz(60));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromIntegerHz(144));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(0));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(-60));
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(RenderBackend.periodFromIntegerHz(59)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void hzFromPeriodRoundsToNearest() {
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(16_666_667L));
|
||||
assertEquals(144, RenderBackend.hzFromPeriod(6_944_444L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(0L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(-1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void plausiblePeriodFilterKeeps20To1000Hz() {
|
||||
assertEquals(0L, RenderBackend.plausiblePeriodNanos(999_000L));
|
||||
assertEquals(1_000_000L, RenderBackend.plausiblePeriodNanos(1_000_000L));
|
||||
assertEquals(50_000_000L, RenderBackend.plausiblePeriodNanos(50_000_000L));
|
||||
assertEquals(0L, RenderBackend.plausiblePeriodNanos(51_000_000L));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class GateRecorderTest {
|
||||
|
||||
@Test
|
||||
void theLongestBlockWithinAFrameWinsRegardlessOfOrder() {
|
||||
final RenderBackend.GateRecorder longestFirst = new RenderBackend.GateRecorder();
|
||||
longestFirst.recordGate(5_000_000L, 100L);
|
||||
longestFirst.recordGate(1_000_000L, 200L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
longestFirst.readGateSample(out);
|
||||
assertEquals(5_000_000L, out.durationNanos);
|
||||
assertEquals(100L, out.endNanos);
|
||||
|
||||
final RenderBackend.GateRecorder shortestFirst = new RenderBackend.GateRecorder();
|
||||
shortestFirst.recordGate(1_000_000L, 10L);
|
||||
shortestFirst.recordGate(4_000_000L, 20L);
|
||||
|
||||
shortestFirst.readGateSample(out);
|
||||
assertEquals(4_000_000L, out.durationNanos);
|
||||
assertEquals(20L, out.endNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aReadConsumesTheSample() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGate(2_000_000L, 50L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.endNanos);
|
||||
assertEquals(0L, out.durationNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gpuWaitAccumulatesAndResetsOnRead() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGate(1_000_000L, 10L);
|
||||
recorder.recordGpuWait(300L);
|
||||
recorder.recordGpuWait(700L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(1_000L, out.gpuWaitNanos);
|
||||
|
||||
recorder.recordGate(1_000_000L, 11L);
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.gpuWaitNanos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gpuWaitIsDrainedOnASampleFreeRead() {
|
||||
final RenderBackend.GateRecorder recorder = new RenderBackend.GateRecorder();
|
||||
recorder.recordGpuWait(450L);
|
||||
|
||||
final RenderBackend.GateSample out = new RenderBackend.GateSample();
|
||||
recorder.readGateSample(out);
|
||||
|
||||
assertEquals(0L, out.endNanos);
|
||||
assertEquals(450L, out.gpuWaitNanos);
|
||||
|
||||
recorder.readGateSample(out);
|
||||
assertEquals(0L, out.gpuWaitNanos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Timeout(10)
|
||||
class MainThreadPumpTest {
|
||||
|
||||
private static final class DeferredExecutor implements Executor {
|
||||
final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>();
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
tasks.add(command);
|
||||
}
|
||||
|
||||
void runNext() {
|
||||
tasks.removeFirst().run();
|
||||
}
|
||||
|
||||
void awaitTask() {
|
||||
while (tasks.isEmpty()) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class InlineExecutor implements Executor {
|
||||
final List<Runnable> submitted = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
submitted.add(command);
|
||||
command.run();
|
||||
}
|
||||
}
|
||||
|
||||
private static void awaitWaiting(Thread t) {
|
||||
while (t.getState() != Thread.State.WAITING && t.getState() != Thread.State.TIMED_WAITING) {
|
||||
assertTrue(t.isAlive(), "thread died instead of blocking");
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
|
||||
private static MainThreadPump pump(Executor executor, Runnable pumpBody, Runnable pollBody) {
|
||||
return new MainThreadPump(executor, () -> false, pumpBody, pollBody);
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatedPumpsSubmitTheSameRunnable() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, pumps::incrementAndGet, polls::incrementAndGet);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
p.pumpMessages();
|
||||
}
|
||||
|
||||
assertEquals(16, executor.submitted.size());
|
||||
assertEquals(16, pumps.get());
|
||||
assertEquals(16, polls.get());
|
||||
final Runnable first = executor.submitted.get(0);
|
||||
for (Runnable r : executor.submitted) {
|
||||
assertSame(first, r, "pump must not allocate a new task per call");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pumpBlocksUntilTheMainThreadRunsTheTask() throws Exception {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, () -> {}, polls::incrementAndGet);
|
||||
|
||||
final Thread caller = new Thread(p::pumpMessages, "pump-caller");
|
||||
caller.start();
|
||||
awaitWaiting(caller);
|
||||
executor.awaitTask();
|
||||
assertEquals(1, executor.tasks.size());
|
||||
assertEquals(0, polls.get());
|
||||
|
||||
executor.runNext();
|
||||
caller.join();
|
||||
assertEquals(1, polls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void aThrowingBodyPropagatesToTheCallerAndDoesNotLeaveItParked() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final MainThreadPump p = pump(executor, () -> {
|
||||
if (pumps.incrementAndGet() == 1) throw new IllegalStateException("boom");
|
||||
}, polls::incrementAndGet);
|
||||
|
||||
final IllegalStateException thrown = assertThrows(IllegalStateException.class, p::pumpMessages);
|
||||
assertEquals("boom", thrown.getMessage());
|
||||
assertEquals(0, polls.get(), "poll must not run when the pump body failed");
|
||||
|
||||
p.pumpMessages();
|
||||
assertEquals(1, polls.get(), "a failed pump must not poison the next one");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mainThreadCallersRunInlineWithoutTheExecutor() {
|
||||
final InlineExecutor executor = new InlineExecutor();
|
||||
final AtomicInteger pumps = new AtomicInteger();
|
||||
final AtomicInteger polls = new AtomicInteger();
|
||||
final MainThreadPump p = new MainThreadPump(executor, () -> true, pumps::incrementAndGet,
|
||||
polls::incrementAndGet);
|
||||
|
||||
p.pumpMessages();
|
||||
|
||||
assertTrue(executor.submitted.isEmpty(), "the main thread must not hop through the executor");
|
||||
assertEquals(1, pumps.get());
|
||||
assertEquals(1, polls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void anInterruptedCallerCompletesAndKeepsItsInterruptFlag() throws Exception {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
final MainThreadPump p = pump(executor, () -> {}, () -> {});
|
||||
final AtomicInteger stillInterrupted = new AtomicInteger(-1);
|
||||
|
||||
final Thread caller = new Thread(() -> {
|
||||
p.pumpMessages();
|
||||
stillInterrupted.set(Thread.currentThread().isInterrupted() ? 1 : 0);
|
||||
}, "pump-interrupted-caller");
|
||||
caller.start();
|
||||
awaitWaiting(caller);
|
||||
executor.awaitTask();
|
||||
caller.interrupt();
|
||||
assertFalse(executor.tasks.isEmpty());
|
||||
|
||||
executor.runNext();
|
||||
caller.join();
|
||||
assertEquals(1, stillInterrupted.get());
|
||||
}
|
||||
}
|
||||
+46
@@ -1,8 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
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.PointerBuffer;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
@@ -12,6 +16,7 @@ import org.lwjgl.util.spvc.SpvcReflectedResource;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -33,6 +38,47 @@ class FFPUniformBlockSpirvLayoutTest {
|
||||
assertBlockLayout(FragmentShaderGenerator.generate(fk), Shaderc.shaderc_fragment_shader);
|
||||
}
|
||||
|
||||
static Stream<Arguments> instancedKeys() {
|
||||
return Stream.of(
|
||||
Arguments.of("cube-lightmap-texmat", cubeKey(true, true, false)),
|
||||
Arguments.of("cube-texgen", cubeKey(false, false, true)),
|
||||
Arguments.of("particle-fog-texmat", particleFogTexMatKey())
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("instancedKeys")
|
||||
void instancedVariantCompilesToSpirv(String label, VertexKey key) {
|
||||
final String glsl = VertexShaderGenerator.generate(key);
|
||||
final SpirvCompiler.Result result = SpirvCompiler.compile(glsl, Shaderc.shaderc_vertex_shader, label, SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(result.spirv(), () -> "SPIR-V compile failed: " + result.error() + "\n" + glsl);
|
||||
MemoryUtil.memFree(result.spirv());
|
||||
}
|
||||
|
||||
private static VertexKey cubeKey(boolean lightmapUnit, boolean unit0TextureMatrix, boolean texGen) {
|
||||
long bits = bit("BIT_HAS_VERTEX_COLOR") | bit("BIT_HAS_VERTEX_NORMAL") | bit("BIT_HAS_VERTEX_TEX") | bit("BIT_UNIT_TEX_BASE");
|
||||
if (lightmapUnit) bits |= 1L << (position("BIT_UNIT_TEX_BASE") + 1);
|
||||
if (unit0TextureMatrix || texGen) bits |= bit("BIT_UNIT_TEXMAT_BASE");
|
||||
if (texGen) {
|
||||
bits |= (long) VertexKey.TG_OBJ_LINEAR << position("BIT_TEXGEN_S");
|
||||
bits |= (long) VertexKey.TG_OBJ_LINEAR << position("BIT_TEXGEN_T");
|
||||
}
|
||||
return VertexKey.fromPacked(VertexKey.withInstancing(bits, Instancing.CUBE));
|
||||
}
|
||||
|
||||
private static VertexKey particleFogTexMatKey() {
|
||||
final long bits = bit("BIT_UNIT_TEX_BASE") | 1L << (position("BIT_UNIT_TEX_BASE") + 1) | bit("BIT_UNIT_TEXMAT_BASE") | bit("BIT_FOG");
|
||||
return VertexKey.fromPacked(VertexKey.withInstancing(bits, Instancing.PARTICLE));
|
||||
}
|
||||
|
||||
private static long bit(String name) {
|
||||
return 1L << position(name);
|
||||
}
|
||||
|
||||
private static int position(String name) {
|
||||
return Reflect.getStatic(VertexKey.class, name);
|
||||
}
|
||||
|
||||
private static void assertBlockLayout(String glsl, int shaderKind) {
|
||||
final SpirvCompiler.Result result = SpirvCompiler.compile(glsl, shaderKind, "ffp-layout-test", SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(result.spirv(), () -> "SPIR-V compile failed: " + result.error() + "\n" + glsl);
|
||||
|
||||
@@ -46,7 +46,7 @@ class FFPUniformValuesTest {
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
try {
|
||||
GL20.glUseProgram(program.getProgramId());
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
|
||||
final ByteBuffer block = uniforms.getStaging();
|
||||
|
||||
|
||||
+10
-10
@@ -68,18 +68,18 @@ class FfpUniformBlockDedupTest {
|
||||
resetState();
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
try {
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(1, uniforms.blockWrites, "first upload always writes");
|
||||
final int firstOffset = boundRangeStart();
|
||||
|
||||
GLStateManager.glLoadIdentity();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(1, uniforms.blockWrites, "a dirty generation with unchanged bytes must not write");
|
||||
assertEquals(1, uniforms.blockSkips, "and must be counted as a skip");
|
||||
assertEquals(firstOffset, boundRangeStart(), "the skipped write must not advance the ring or rebind");
|
||||
|
||||
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f);
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(2, uniforms.blockWrites, "a changed matrix must write");
|
||||
assertEquals(1, uniforms.blockSkips, "a write is not a skip");
|
||||
assertNotEquals(firstOffset, boundRangeStart(), "a write takes a fresh ring range and rebinds");
|
||||
@@ -95,11 +95,11 @@ class FfpUniformBlockDedupTest {
|
||||
final Uniforms first = new Uniforms();
|
||||
final Uniforms second = new Uniforms();
|
||||
try {
|
||||
first.upload();
|
||||
first.upload();
|
||||
first.upload(GLStateManager.ctx());
|
||||
first.upload(GLStateManager.ctx());
|
||||
assertEquals(1, first.blockWrites);
|
||||
|
||||
second.upload();
|
||||
second.upload(GLStateManager.ctx());
|
||||
assertEquals(1, second.blockWrites, "a never-bound instance must write regardless of content");
|
||||
} finally {
|
||||
first.destroy();
|
||||
@@ -146,19 +146,19 @@ class FfpUniformBlockDedupTest {
|
||||
resetState();
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
try {
|
||||
uniforms.upload();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
final int writesBefore = uniforms.blockWrites;
|
||||
final int settledOffset = boundRangeStart();
|
||||
|
||||
mutate.run();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(writesBefore + 1, uniforms.blockWrites, name + ": a changed member must write");
|
||||
assertNotEquals(settledOffset, boundRangeStart(), name + ": a write must rebind to a fresh range");
|
||||
|
||||
final int skipsBefore = uniforms.blockSkips;
|
||||
final int writtenOffset = boundRangeStart();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(writesBefore + 1, uniforms.blockWrites, name + ": re-uploading unchanged bytes must not write");
|
||||
assertEquals(writtenOffset, boundRangeStart(), name + ": and must not rebind");
|
||||
if (uniforms.blockSkips != skipsBefore) {
|
||||
|
||||
+251
-85
@@ -2,14 +2,23 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
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.PendingProgramSelection;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.NORMAL_BIT;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -22,41 +31,203 @@ class InstancedFFPDrawGLTest {
|
||||
private static final int TEMPLATE_STRIDE = 32;
|
||||
private static final int INSTANCE_STRIDE = InstancedAttribs.STRIDE;
|
||||
|
||||
private static final int PARITY_SIZE = 256;
|
||||
private static final int INTEGER_WIDTH = 300;
|
||||
private static final int INTEGER_HEIGHT = 100;
|
||||
|
||||
private static final List<String> log = new ArrayList<>();
|
||||
|
||||
private static final PendingProgramSelection LOGGING_SELECTION = () -> log.add("resolve");
|
||||
|
||||
private static final class LoggingBlendHandler implements DeferredBlendHandler {
|
||||
@Override
|
||||
public boolean isBlendLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deferBlendModeToggle(boolean enabled) {}
|
||||
|
||||
@Override
|
||||
public void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {}
|
||||
|
||||
@Override
|
||||
public void flushDeferredBlend() {
|
||||
log.add("flush");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean capturing;
|
||||
|
||||
private int vao, templateVbo, instanceVbo;
|
||||
|
||||
@BeforeAll
|
||||
static void registerForeignDrawEndListener() {
|
||||
capturing = true;
|
||||
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
|
||||
if (capturing) log.add("listener");
|
||||
});
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void stopCapturing() {
|
||||
capturing = false;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
sm.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLSMHooks.pendingProgramSelection = null;
|
||||
GLSMHooks.blendHandler = null;
|
||||
log.clear();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
FfpFixture.resetFfpState();
|
||||
CubeParityFixture.disableDirectionalLight();
|
||||
CubeParityFixture.deleteResources();
|
||||
ParticleParityFixture.deleteResources();
|
||||
if (templateVbo != 0) { GLStateManager.glDeleteBuffers(templateVbo); templateVbo = 0; }
|
||||
if (instanceVbo != 0) { GLStateManager.glDeleteBuffers(instanceVbo); instanceVbo = 0; }
|
||||
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexKeyCarriesInstancedBit() {
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
final long plain = VertexKey.packFromState(true, true, false, false, 0);
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
final long instanced = VertexKey.packFromState(true, true, false, false, 0);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
void drawArraysResolvesBeforeFlushDeferredBlend() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
GLSMHooks.blendHandler = new LoggingBlendHandler();
|
||||
|
||||
assertFalse(VertexKey.fromPacked(plain).instancedDraw());
|
||||
assertTrue(VertexKey.fromPacked(instanced).instancedDraw());
|
||||
assertEquals(1, Long.bitCount(plain ^ instanced), "only the instanced bit may differ");
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
|
||||
assertEquals(List.of("resolve", "flush"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveIsSuppressedWhileDisplayListIsRecording() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
assertTrue(log.isEmpty());
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
assertEquals(List.of("resolve"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedForeignDrawsResolveOnceAtOutermostEndBeforeListeners() {
|
||||
GLSMHooks.pendingProgramSelection = LOGGING_SELECTION;
|
||||
|
||||
GLStateManager.beginForeignDraw();
|
||||
GLStateManager.beginForeignDraw();
|
||||
GLStateManager.endForeignDraw();
|
||||
assertTrue(log.isEmpty());
|
||||
|
||||
GLStateManager.endForeignDraw();
|
||||
assertEquals(List.of("resolve", "listener"), log);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unitCubeMatchesBakedQuadsWithLightmap() {
|
||||
assertCubeParity(true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unitCubeMatchesBakedQuadsWhenLit() {
|
||||
assertCubeParity(false, true);
|
||||
}
|
||||
|
||||
private void assertCubeParity(boolean lightmap, boolean lit) {
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
CubeParityFixture.setupScene(lightmap, lit);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
final CubeParityFixture.Spec[] specs = CubeParityFixture.specs();
|
||||
|
||||
FfpFixture.clear();
|
||||
CubeParityFixture.drawReference(specs, lightmap);
|
||||
final int[] reference = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
FfpFixture.clear();
|
||||
CubeParityFixture.drawInstanced(specs);
|
||||
final int[] instanced = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "parity draws must not raise a GL error");
|
||||
|
||||
CubeParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
|
||||
}
|
||||
|
||||
@Test
|
||||
void particleUnit0TextureMatrixMatchesReference() {
|
||||
final ParticleParityFixture.Rotation rot = ParticleParityFixture.Rotation.of(35f, -12f, false);
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
ParticleParityFixture.setupScene(true);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
final ParticleParityFixture.Particle[] particles = ParticleParityFixture.particles();
|
||||
|
||||
FfpFixture.clear();
|
||||
ParticleParityFixture.drawReference(particles, rot.x(), rot.xz(), rot.z(), rot.yz(), rot.xy());
|
||||
final int[] reference = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
FfpFixture.clear();
|
||||
ParticleParityFixture.drawInstanced(particles, rot.x(), rot.xz(), rot.z(), rot.yz(), rot.xy());
|
||||
final int[] instanced = FfpFixture.readRegion(PARITY_SIZE);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "parity draws must not raise a GL error");
|
||||
ParticleParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
|
||||
}
|
||||
|
||||
@Test
|
||||
void entityAttribSelectsColorPerInstance() {
|
||||
GLStateManager.glViewport(0, 0, INTEGER_WIDTH, INTEGER_HEIGHT);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
FfpFixture.IntegerInstances.build(0.66f);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
FfpFixture.IntegerInstances.draw();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced ivec4 draw must not raise a GL error");
|
||||
|
||||
assertPixel(51, 50, 255, 0, 0, "entity -1 must render red");
|
||||
assertPixel(150, 50, 0, 255, 0, "entity 7 must render green");
|
||||
assertPixel(249, 50, 0, 0, 255, "entity 42 must render blue");
|
||||
}
|
||||
|
||||
private static void assertPixel(int x, int y, int r, int g, int b, String label) {
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
final int gotR = pixel.get(0) & 0xFF;
|
||||
final int gotG = pixel.get(1) & 0xFF;
|
||||
final int gotB = pixel.get(2) & 0xFF;
|
||||
assertEquals(r, gotR, () -> label + ": red channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
assertEquals(g, gotG, () -> label + ": green channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
assertEquals(b, gotB, () -> label + ": blue channel, got (" + gotR + "," + gotG + "," + gotB + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void instancedSourceUsesInstanceAttribs() {
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
final VertexKey key = VertexKey.fromState(true, true, false, false, 0b10);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
|
||||
final String source = VertexShaderGenerator.generate(key);
|
||||
assertTrue(source.contains("a_InstCol0"), "instance matrix attribs declared");
|
||||
assertTrue(source.contains("a_InstRow0"), "instance matrix attribs declared");
|
||||
assertTrue(source.contains("mat4 instMV"), "instance matrix used as modelview");
|
||||
assertTrue(source.contains("a_InstColor"), "instance color multiplier used");
|
||||
assertTrue(source.contains("a_InstLightmap"), "instance lightmap coord used");
|
||||
@@ -74,7 +245,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstances();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -83,14 +254,12 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
drawInstances();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced draw must not raise a GL error");
|
||||
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] right = readPixel(600, 300);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200 && left[1] < 50, "left instance red, got " + left[0] + "," + left[1] + "," + left[2]);
|
||||
assertTrue(right[1] > 200 && right[0] < 50, "right instance green, got " + right[0] + "," + right[1] + "," + right[2]);
|
||||
}
|
||||
@@ -106,7 +275,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstancesWithAlpha();
|
||||
uploadInstances(0xFF0000FF, 0, 0x0D0000FF, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -117,14 +286,12 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
drawInstances();
|
||||
GLStateManager.disableAlphaTest();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] right = readPixel(600, 300);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200, "opaque instance drawn, got " + left[0]);
|
||||
assertTrue(right[0] < 20 && right[1] < 20, "alpha 0.05 instance discarded, got " + right[0] + "," + right[1]);
|
||||
}
|
||||
@@ -141,7 +308,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
buildInstances();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
@@ -150,48 +317,71 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
drawInstances();
|
||||
|
||||
GLStateManager.glDepthFunc(GL11.GL_EQUAL);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLStateManager.glColor4f(1f, 1f, 1f, 1f);
|
||||
rewriteInstanceColors(0x800000FF, 0x8000FF00);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.instancedFfpDrawActive = false;
|
||||
uploadInstances(0x800000FF, 0, 0x8000FF00, 0);
|
||||
drawInstances();
|
||||
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.disableDepthTest();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
final int[] left = readPixel(200, 300);
|
||||
final int[] background = readPixel(400, 550);
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] background = FfpFixture.readPixel(400, 550);
|
||||
assertTrue(left[0] > 200, "overlay blended over base, got " + left[0]);
|
||||
assertTrue(background[0] < 20 && background[1] < 20, "overlay must not draw off-base, got " + background[0]);
|
||||
}
|
||||
|
||||
private void rewriteInstanceColors(int leftABGR, int rightABGR) {
|
||||
final ByteBuffer patch = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(patch, -0.5f, leftABGR);
|
||||
putInstance(patch, 0.5f, rightABGR);
|
||||
patch.flip();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, patch, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
@Test
|
||||
void perInstanceOverlayMixesOnlyTintedInstances() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
uploadInstances(0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFF0000FF);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLStateManager.setOverlayColor(0f, 1f, 0f, 1f);
|
||||
drawInstances();
|
||||
GLStateManager.setOverlayColor(0f, 0f, 0f, 0f);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced overlay draw must not raise a GL error");
|
||||
|
||||
final int[] left = FfpFixture.readPixel(200, 300);
|
||||
final int[] right = FfpFixture.readPixel(600, 300);
|
||||
assertTrue(left[0] > 200 && left[1] > 200 && left[2] > 200, "left instance overlay alpha 0 keeps its base color, got " + left[0] + "," + left[1] + "," + left[2]);
|
||||
assertTrue(right[0] > 200 && right[1] < 50 && right[2] < 50, "right instance is mixed to its own opaque red overlay, got " + right[0] + "," + right[1] + "," + right[2]);
|
||||
}
|
||||
|
||||
private void buildInstancesWithAlpha() {
|
||||
private void uploadInstances(int leftColor, int leftOverlay, int rightColor, int rightOverlay) {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(instances, -0.5f, 0xFF0000FF);
|
||||
putInstance(instances, 0.5f, 0x0D0000FF);
|
||||
putInstance(instances, -0.5f, leftColor, leftOverlay);
|
||||
putInstance(instances, 0.5f, rightColor, rightOverlay);
|
||||
instances.flip();
|
||||
|
||||
instanceVbo = GLStateManager.glGenBuffers();
|
||||
final boolean first = instanceVbo == 0;
|
||||
if (first) instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, instances, GL15.GL_STATIC_DRAW);
|
||||
pointAndEnableInstanceAttribs();
|
||||
if (first) {
|
||||
pointAndEnableInstanceAttribs();
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
@@ -225,47 +415,23 @@ class InstancedFFPDrawGLTest {
|
||||
buf.position(base + TEMPLATE_STRIDE);
|
||||
}
|
||||
|
||||
private void buildInstances() {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(instances, -0.5f, 0xFF0000FF);
|
||||
putInstance(instances, 0.5f, 0xFF00FF00);
|
||||
instances.flip();
|
||||
|
||||
instanceVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, instances, GL15.GL_STATIC_DRAW);
|
||||
pointAndEnableInstanceAttribs();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
private static void pointAndEnableInstanceAttribs() {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
final int loc = InstancedAttribs.LOC_MATRIX_COL0 + c;
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribPointer(loc, 4, GL11.GL_FLOAT, false, INSTANCE_STRIDE, c * 16L);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
}
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(InstancedAttribs.LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, INSTANCE_STRIDE, InstancedAttribs.OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribDivisor(InstancedAttribs.LOC_COLOR, 1);
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_LIGHTMAP);
|
||||
GLStateManager.glVertexAttribPointer(InstancedAttribs.LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, INSTANCE_STRIDE, InstancedAttribs.OFFSET_LIGHTMAP);
|
||||
GLStateManager.glVertexAttribDivisor(InstancedAttribs.LOC_LIGHTMAP, 1);
|
||||
InstancedAttribs.enableHeadArrays();
|
||||
InstancedAttribs.pointTemplate(0L);
|
||||
}
|
||||
|
||||
private void putInstance(ByteBuffer buf, float translateX, int colorABGR) {
|
||||
private static void putInstance(ByteBuffer buf, float translateX, int colorABGR, int overlayABGR) {
|
||||
final int base = buf.position();
|
||||
buf.putFloat(base, 1f).putFloat(base + 20, 1f).putFloat(base + 40, 1f).putFloat(base + 60, 1f);
|
||||
buf.putFloat(base + 48, translateX);
|
||||
buf.putInt(base + InstancedAttribs.OFFSET_COLOR, colorABGR);
|
||||
buf.putFloat(base + InstancedAttribs.OFFSET_LIGHTMAP, 240f);
|
||||
buf.putFloat(base + InstancedAttribs.OFFSET_LIGHTMAP + 4, 240f);
|
||||
final long ptr = memAddress0(buf) + base;
|
||||
InstancedAttribs.writeHead(ptr, FfpFixture.translationMatrix(translateX), 0, colorABGR, overlayABGR, 0L);
|
||||
memPutFloat(ptr + InstancedAttribs.OFFSET_LIGHTMAP, 240f);
|
||||
memPutFloat(ptr + InstancedAttribs.OFFSET_LIGHTMAP + 4, 240f);
|
||||
buf.position(base + INSTANCE_STRIDE);
|
||||
}
|
||||
|
||||
private static int[] readPixel(int x, int y) {
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
return new int[] { pixel.get(0) & 0xFF, pixel.get(1) & 0xFF, pixel.get(2) & 0xFF };
|
||||
private static void drawInstances() {
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -31,20 +31,20 @@ class ProgramUniformTrackingTest {
|
||||
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
|
||||
|
||||
GL20.glUseProgram(a.getProgramId());
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
final int afterFirst = uniforms.blockWrites;
|
||||
|
||||
GL20.glUseProgram(b.getProgramId());
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
GL20.glUseProgram(a.getProgramId());
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(afterFirst, uniforms.blockWrites, "program switches with unchanged state write nothing");
|
||||
|
||||
GLStateManager.glTranslatef(4.0F, 0.0F, 0.0F);
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(afterFirst + 1, uniforms.blockWrites, "a gen bump writes exactly one block");
|
||||
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(afterFirst + 1, uniforms.blockWrites, "clean draw writes nothing");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
@@ -66,18 +66,18 @@ class ProgramUniformTrackingTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
GL20.glUseProgram(a.getProgramId());
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
final int base = uniforms.blockWrites;
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_TRANSFORM_BIT);
|
||||
GLStateManager.glPopAttrib();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(base, uniforms.blockWrites, "push/pop with no change inside writes nothing");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_TRANSFORM_BIT);
|
||||
GLStateManager.glTranslatef(7.0F, 0.0F, 0.0F);
|
||||
GLStateManager.glPopAttrib();
|
||||
uniforms.upload();
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(base + 1, uniforms.blockWrites, "pop after change stages once");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
Loaded 100 of 527 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user