Compare commits

...
Author SHA1 Message Date
Jason Mitchell b17bb42c49 vsync adjustments: on/mailbox/off + windows input lag fix + glsl parsing optimizations (#2009) 2026-08-12 21:20:10 -07:00
Jason Mitchell 3659b59db9 Adjust vsync and fps limiters, and fix WSR with player models (#2002) 2026-08-09 20:06:53 -07:00
Jason Mitchell 8c31eec525 SDL followup fixes (#2001)
* Deploy fixes
* Narrow transformer exclusions for Legends mods, and mixin priority for CustomNPC
* Journeymap 6 stencil/depth fix
* OpenLTW swizzle workaround
2026-08-09 13:43:46 -07:00
Jason Mitchell 166116d692 More fixes (#1995) 2026-08-08 23:53:06 -07:00
Jason Mitchell 4b21bc77b6 A few fixes (#1994) 2026-08-08 18:29:26 -07:00
Jason Mitchell 8da561d3b6 SDL GPU backend, TESR Batching/caching API, Tracy Integration (#1993) 2026-08-08 15:53:58 -07:00
ah-OOG-ah 413df76ad4 Fix compat for the original battlegear 2 (#1992)
* Fix compat for the original battlegear 2

* oops
2026-08-07 15:54:59 -04:00
boubou19 18fe05eda8 Do not concat strings from glCallList when debug is off (#1988) 2026-08-04 13:37:17 -07:00
ah-OOG-ah 4fa8cfe3e4 Bump Gradle and 3ify dep (#1986) 2026-08-04 05:05:23 +00:00
Eclipse-Sol b5dd1c04ce Generalize non-terrain block's material ID setting (#1981) 2026-08-02 13:10:38 -07:00
Algent 6c5eedb7c1 Optimize item frame rendering (#1964) 2026-08-01 20:16:16 -07:00
Eldrinn-Elantey 9d5eb01091 Remove dead getCharWidth ModifyConstant (#1983) 2026-08-01 16:01:10 -07:00
XLin0mu 0d54a65026 Fix Mekanism crash when it cast chunkTileEntityMap to HashMap (#1982) 2026-08-01 21:41:28 +00:00
Midnight145 8148d8f927 RenderChunkEvent for fluxloading to avoid mixin (#1974) 2026-08-01 14:27:25 -07:00
Jason Mitchell 5c441e4418 Stupid splash thread (#1973) 2026-07-28 18:39:27 -07:00
Jason Mitchell 78562f12d9 Do less work (#1971) 2026-07-28 15:24:36 -07:00
CRIMSTAL 031456f224 fix(DisplayListVBO): guard against null VAO and zero-count draw during display list replay (#1969) 2026-07-28 13:28:59 -07:00
Jason Mitchell ff35c684ca Cache a few more things on glsm (#1970) 2026-07-28 13:28:25 -07:00
Eclipse-Sol 5c4ef022d5 Make mipmap attribution hook not suck and thread-safe (#1967) 2026-07-27 19:14:19 -07:00
Jason Mitchell 4eb837a2c0 Chunk -> world coords (#1966) 2026-07-27 10:54:26 -07:00
Jason Mitchell 6570b6a0a5 Properly handle flags removed in core (#1965) 2026-07-26 21:46:30 -07:00
Jason Mitchell 493b641d56 Switch to an offset based cloud adjustment for World Providers that adjust it (#1962) 2026-07-25 15:35:40 -07:00
Eclipse-Sol 3f4995466c Fix scrollbar not following mouse (#1961)
* Fix scrollbar not following mouse

* add /*deobfName*/
2026-07-24 21:50:46 -04:00
Eclipse-Sol 56d7568fd3 Attempt at making mipmaps better (#1952) 2026-07-24 17:54:45 -07:00
Jason Mitchell 4910cfd6d3 Fix some CMEs and black screens seen during boot/splash. Should be minimal overhead after splash. (#1959) 2026-07-23 19:33:01 -07:00
Jason Mitchell d1c6677caf Fix plascrete/cleanroom trophy rendering (#1957) 2026-07-23 13:42:46 -07:00
Jason Mitchell 8cf0296284 Properly gate end flash (#1954) 2026-07-23 09:34:41 -07:00
Jason Mitchell 4fd98e5026 Add some missing buffer redirects (#1953) 2026-07-23 08:34:11 -07:00
SpacEagle17 8e293f13fb Fix at_midBlock at high coordinates (#1949) 2026-07-22 18:02:55 -07:00
Jason Mitchell 2db20ad866 Handle some Intel driver shenanigans (#1951) 2026-07-21 08:05:46 -07:00
Eclipse-Sol 10a5b550f2 Add Uniforms for End Flashes (#1892) 2026-07-18 06:42:57 -07:00
Eldrinn-Elantey c1403dcf13 Reload unicode glyph widths on resource pack change (#1944) 2026-07-18 05:58:45 -07:00
tiffit 5d1fabfca9 Add API for allowing other mods to have celeritas support (#1936) 2026-07-16 09:54:55 -05:00
tiffit 555b2b7dd2 Fix typo in PR template (#1937) 2026-07-14 00:10:41 -05:00
SuperSouprandJason Mitchell 7017786a7c CTM integration improvements (#1931)
Co-authored-by: Jason Mitchell <[email protected]>
2026-07-12 10:45:16 -05:00
Eclipse-Sol 1c49649685 Fix Streams not rendering correctly (#1932) 2026-07-12 10:41:36 -05:00
Caedis 2bae79c4ee Up default zoom speed to 20 (#1910) 2026-07-09 21:34:03 -04:00
Jason Mitchell c22935660a Reenable compute shaders (#1918) 2026-07-05 20:38:13 -07:00
tiffit 79c488102b CTM: Fix "flipped" value not set to false in setFace() (#1913) 2026-07-05 18:24:28 -07:00
Jason Mitchell cc1353b73a Fix infinite farplane causing incorrect culling in pdim with no clouds (#1917) 2026-07-05 18:18:17 -07:00
Eclipse-Sol d1e6dc8c94 Fix/bug fixes (#1905) 2026-07-04 13:06:36 -07:00
GribanovIvan ecbf8fea2f font: load bundled .ttf/.otf fonts from fontfiles/ and config/angelia/fonts/ (#1904) 2026-07-04 12:16:48 -07:00
tiffit 610209605f Compact CTM: Decouple block face and rendered face (#1907) 2026-07-04 03:22:04 +03:00
mist475andJason Mitchell 112aae3735 Fix lotr biome colours again (essentially copy of https://github.com/GTNewHorizons/Angelica/pull/25) (#1902)
Co-authored-by: Jason Mitchell <[email protected]>
2026-07-03 14:21:05 -07:00
RecursivePineapple 5844b1b5c1 Cubic Chunks Compatibility (#1808) 2026-07-03 12:41:04 -07:00
Jason Mitchell 05f01c17e6 No point continuously building clouds if we're not rendering them... (#1900) 2026-06-29 20:17:06 -07:00
SpacEagle17 f07d96316a Add shader setting search functionality and center selection (#1897) 2026-06-29 19:36:55 -07:00
Jason Mitchell 5b704d1bc2 Fix space elevator cable (#1896) 2026-06-28 16:33:58 -07:00
Jason Mitchell 26135124f4 Support more glDrawElement overloads in display lists (#1895) 2026-06-28 15:07:24 -07:00
Jason Mitchell 3abc89b7b2 Betweenlands fix (#1894) 2026-06-28 13:46:10 -07:00
Jason Mitchell 03d908d658 Update readme and version detectors (#1893) 2026-06-27 15:47:32 -07:00
Eclipse-Sol 76bf19c8f6 Majority shader fixes (#1868) 2026-06-27 14:48:51 -07:00
Eclipse-Sol b27834cbd9 Narrow and make damage overlay a config option (#1886) 2026-06-27 10:12:39 -07:00
sisyphussyandJason Mitchell eb0ecb0483 Fix deps (#1888)
Co-authored-by: Jason Mitchell <[email protected]>
2026-06-27 10:11:30 -07:00
Caedis 9acb0e67a7 feat: add smooth zoom option (#1890) 2026-06-27 09:57:21 -07:00
Jason Mitchell f4f9b56b17 End portal fix (#1891) 2026-06-27 09:56:21 -07:00
Midnight145 2196439e6c ClientEvent framework (primarily written for LiteloaderLoader) (#1843) 2026-06-25 20:13:18 -07:00
866 changed files with 74787 additions and 6668 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
# Description or your *PR* and other info
# Description of your *PR* and other info
<!-- Please include a summary of the change -->
<!-- Any details that you think are important to review this PR? -->
<!-- Are there any Issues / PRs related to this one? -->
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# Builds the libTracyClient shared library for the tracy-client module.
set -euo pipefail
TRACY_VERSION="${TRACY_VERSION:-v0.13.1}"
OUT_DIR="${OUT_DIR:-dist/tracy-natives}"
TRACY_REPO="https://github.com/wolfpld/tracy"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
case "$(uname -s)" in
Darwin) PLAT_OS="macos" ;;
Linux) PLAT_OS="linux" ;;
*) PLAT_OS="windows" ;;
esac
case "$(uname -m)" in
arm64|aarch64) PLAT_ARCH="arm64" ;;
*) PLAT_ARCH="x64" ;;
esac
RES_DIR="${RES_DIR:-$REPO_ROOT/tracy-client/src/main/resources/natives/tracy/$PLAT_OS-$PLAT_ARCH}"
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT
if [[ -n "${TRACY_SRC:-}" ]]; then
SRC_DIR="$WORK_DIR/tracy"
git clone --shared --no-checkout "$TRACY_SRC" "$SRC_DIR"
git -C "$SRC_DIR" checkout --detach "$TRACY_VERSION"
else
SRC_DIR="$WORK_DIR/tracy"
git clone --depth 1 --branch "$TRACY_VERSION" "$TRACY_REPO" "$SRC_DIR"
fi
BUILD_DIR="$WORK_DIR/build"
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-11.0}"
cmake -S "$SRC_DIR" -B "$BUILD_DIR" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DTRACY_STATIC=OFF \
-DTRACY_ENABLE=ON \
-DTRACY_ON_DEMAND=ON \
-DTRACY_DELAYED_INIT=ON \
-DTRACY_MANUAL_LIFETIME=ON \
-DTRACY_NO_SAMPLING=ON \
-DTRACY_NO_SYSTEM_TRACING=ON \
-DTRACY_NO_CRASH_HANDLER=ON \
-DTRACY_LTO=OFF
cmake --build "$BUILD_DIR" --config Release --parallel
mkdir -p "$OUT_DIR"
for NAME in libTracyClient.so libTracyClient.dylib TracyClient.dll; do
LIB="$(find "$BUILD_DIR" -name "$NAME" | head -1)"
if [[ -n "$LIB" ]]; then
cp -L "$LIB" "$OUT_DIR/$NAME"
cp "$SRC_DIR/LICENSE" "$OUT_DIR/LICENSE"
mkdir -p "$RES_DIR"
cp -L "$LIB" "$RES_DIR/$NAME"
cp "$SRC_DIR/LICENSE" "$RES_DIR/LICENSE"
echo "built: $OUT_DIR/$NAME ($TRACY_VERSION), staged: $RES_DIR/$NAME"
exit 0
fi
done
echo "error: built library not found under $BUILD_DIR" >&2
exit 1
+92
View File
@@ -0,0 +1,92 @@
name: Build Tracy natives
on:
push:
tags:
- "tracy-v*"
workflow_dispatch:
inputs:
tracy-version:
description: Tracy git tag to build
required: false
default: v0.13.1
publish:
description: Publish angelica-tracy.jar to the GTNH maven
type: boolean
required: false
default: false
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-22.04
platform: linux-x64
- os: windows-2025
platform: windows-x64
- os: macos-15-intel
platform: macos-x64
- os: macos-15
platform: macos-arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Build libTracyClient
shell: bash
env:
RAW_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tracy-version }}
OUT_DIR: dist/tracy-natives
run: |
export TRACY_VERSION="${RAW_VERSION#tracy-}"
bash .github/scripts/build-tracy-natives.sh
- uses: actions/upload-artifact@v4
with:
name: tracy-natives-${{ matrix.platform }}
path: dist/tracy-natives/*
if-no-files-found: error
assemble:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v4
- name: Check tracy version matches the version catalog
shell: bash
env:
RAW_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tracy-version }}
run: |
ver="${RAW_VERSION#tracy-}"
grep -q "^tracy = \"${ver#v}\"$" gradle/libs.versions.toml \
|| { echo "libs.versions.toml tracy does not match $ver" >&2; exit 1; }
- uses: actions/download-artifact@v5
with:
pattern: tracy-natives-*
path: staging
- name: Stage natives into tracy-client resources
shell: bash
run: |
for d in staging/tracy-natives-*; do
dest="tracy-client/src/main/resources/natives/tracy/${d#staging/tracy-natives-}"
mkdir -p "$dest"
cp "$d"/* "$dest/"
done
find tracy-client/src/main/resources/natives -type f
- name: Build angelica-tracy.jar
run: ./gradlew :tracy-client:distJar
- uses: actions/upload-artifact@v4
with:
name: angelica-tracy
path: tracy-client/build/libs/angelica-tracy-*.jar
if-no-files-found: error
- name: Publish to GTNH maven
if: github.ref_type == 'tag' || inputs.publish
env:
MAVEN_USER: ${{ secrets.MAVEN_USER }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
run: ./gradlew -PpublishTracy=true :tracy-client:publish
+1 -1
View File
@@ -3,7 +3,7 @@ name: Release tagged build
on:
push:
tags: [ '*' ]
tags: [ '*', '!tracy-v*' ]
permissions:
contents: write
+6
View File
@@ -1,4 +1,5 @@
.gradle
.kotlin/
.settings
/.idea/
/.vs/
@@ -12,6 +13,7 @@ build/
/config/
/crash-reports/
/logs/
tmp/
options.txt
/saves/
usernamecache.json
@@ -37,3 +39,7 @@ addon.local.gradle.kts
addon.late.local.gradle
addon.late.local.gradle.kts
local/
tracy-client/src/main/resources/natives/
tracy-client/angelica/
dist/tracy-natives/
*.tracy
+17
View File
@@ -0,0 +1,17 @@
# Angelica dev commands
# Build with headless display
build:
xvfb-run --server-args="-screen 0 1366x768x24" ./gradlew build
# Run tests with headless display
test:
xvfb-run --server-args="-screen 0 1366x768x24" ./gradlew test
# Build jar only (no tests)
jar:
./gradlew jar
# Clean build directory
clean:
./gradlew clean
+1 -1
View File
@@ -23,7 +23,7 @@ Targets GL 3.3+ core profile
# Required Dependencies
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.9.0
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
# Known (temporary) Incompatibilities
+190 -28
View File
@@ -1,3 +1,5 @@
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
@@ -8,33 +10,120 @@ plugins {
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
minecraft {
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
// extraRunJvmArguments.add("-Dangelica.useVulkan=true")
// extraRunJvmArguments.add("-Dangelica.vulkanDebug=true")
// extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=true")
minecraft {
extraRunJvmArguments.add("-Dangelica.debug.testBlocks=true")
extraRunJvmArguments.add("-Dangelica.tracy=true")
extraRunJvmArguments.add("-Dangelica.sdlgpu.enable=true")
extraRunJvmArguments.add("-Dangelica.unmappedGL=fail")
extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=$lwjglDebug")
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.redirectorLogspam=true")
//extraRunJvmArguments.add("-Dangelica.debug.redirectorLogspam=true")
}
tasks.test {
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
}
// On macOS ARM64, use an ARM64-compatible JVM for tests
val isMacOsArm64 = System.getProperty("os.name").lowercase().contains("mac") && System.getProperty("os.arch") == "aarch64"
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
if (isMacOsArm64) {
vendor = JvmVendorSpec.AZUL
// 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")
}
}
}
if (gpuHud) {
tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configureEach {
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX) {
environment("MTL_HUD_ENABLED", "1")
} else {
environment("MANGOHUD", "1")
environment("LD_PRELOAD", "/usr/\$LIB/mangohud/libMangoHud_shim.so")
}
}
}
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")) {
environment("WAYLAND_DISPLAY", "")
environment("XDG_SESSION_TYPE", "x11")
val rgpFrame = project.findProperty("rgpFrame") as String?
val rgpCapture = rgp || project.hasProperty("rgp") || rgpFrame != null
if (rgpCapture) {
environment("MESA_VK_TRACE", "rgp")
if (rgpFrame != null) {
environment("MESA_VK_TRACE_FRAME", rgpFrame)
doFirst { logger.lifecycle("RGP: capturing frame $rgpFrame -> /tmp/java_*.rgp") }
} else {
environment("MESA_VK_TRACE_TRIGGER", "/tmp/angelica_rgp.trigger")
doFirst { logger.lifecycle("RGP: touch /tmp/angelica_rgp.trigger (or press F1) to capture -> /tmp/java_*.rgp") }
}
} else if (renderdoc || project.hasProperty("renderdoc")) {
val renderdocLib = file(project.findProperty("renderdocLib") as String? ?: "${System.getProperty("user.home")}/Downloads/renderdoc_1.44/lib/librenderdoc.so")
if (renderdocLib.isFile) {
environment("LD_PRELOAD", renderdocLib.absolutePath)
doFirst { logger.lifecycle("RENDERDOC: LD_PRELOAD set, press F12 to capture") }
} else {
doFirst { logger.lifecycle("RENDERDOC: enabled but ${renderdocLib.absolutePath} not found; set -PrenderdocLib=<path>") }
}
}
}
dependsOn(tasks.extractNatives2)
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
}
tasks.processResources {
@@ -57,6 +146,7 @@ val shadowImplementation: Configuration by configurations
val downgradeEmbedOnlyForTest by tasks.registering(DowngradeFiles::class) {
inputCollection = embedOnly
outputs.dir(temporaryDir)
}
val stripModuleInfoFromShadow by tasks.registering(Jar::class) {
@@ -64,7 +154,7 @@ val stripModuleInfoFromShadow by tasks.registering(Jar::class) {
from(shadowImplementation.map(::zipTree))
exclude("module-info.class")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier = "shadow-stripped"
archiveFileName = "shadow-stripped.jar"
}
val downgradeShadowImplForTest by tasks.registering(DowngradeFiles::class) {
@@ -72,15 +162,74 @@ val downgradeShadowImplForTest by tasks.registering(DowngradeFiles::class) {
outputs.dir(temporaryDir)
}
tasks.test {
dependsOn(downgradeEmbedOnlyForTest, downgradeShadowImplForTest)
classpath = classpath
.minus(embedOnly)
.plus(files(downgradeEmbedOnlyForTest.map { it.outputCollection }))
.minus(shadowImplementation)
.plus(files({ fileTree(downgradeShadowImplForTest.get().temporaryDir) }))
val glsmTestFixtures: Configuration by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
val downgradeGlsmTestFixturesForTest by tasks.registering(DowngradeFiles::class) {
inputCollection = glsmTestFixtures
outputs.dir(temporaryDir)
}
fun downgraded(task: TaskProvider<DowngradeFiles>): FileCollection = files(task.map { it.outputMap.values })
fun Test.configureAngelicaJava8() {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
if (isMacOsArm64) {
vendor = JvmVendorSpec.AZUL
}
}
dependsOn(tasks.extractNatives2)
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
testLogging { events("passed", "skipped", "failed") }
val swaps = mapOf(
embedOnly to downgradeEmbedOnlyForTest,
shadowImplementation to downgradeShadowImplForTest,
glsmTestFixtures to downgradeGlsmTestFixturesForTest,
)
dependsOn(swaps.values)
for ((original, downgrade) in swaps) {
classpath = classpath.minus(original).plus(downgraded(downgrade))
}
}
tasks.test {
useJUnitPlatform { excludeTags = setOf("gl-core") }
configureAngelicaJava8()
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
}
val downgradeRootMainClasses = tasks.named<DowngradeFiles>("downgradeMainClasses")
val downgradeRootTestClasses = tasks.named<DowngradeFiles>("downgradeTestClasses")
val glCoreTest by tasks.registering(Test::class) {
description = "Runs the gl-core tests in their own JVM with a real GL context."
group = "verification"
useJUnitPlatform { includeTags = setOf("gl-core") }
dependsOn(downgradeRootMainClasses, downgradeRootTestClasses)
val rawMain = sourceSets["main"].output.classesDirs
val rawTest = sourceSets["test"].output.classesDirs
val downgradedTest = downgraded(downgradeRootTestClasses)
setTestClassesDirs(downgradedTest)
classpath = downgradedTest
.plus(downgraded(downgradeRootMainClasses))
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
configureAngelicaJava8()
mustRunAfter(tasks.test)
}
tasks.test { finalizedBy(glCoreTest) }
tasks.shadowJar {
dependsOn(embedOnly)
from(embedOnly.map(::zipTree))
@@ -89,13 +238,26 @@ tasks.shadowJar {
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")
}
tasks.withType<TaskPublishCurseForge>().configureEach {
uploadArtifacts.forEach { it.addGameVersion("Client") }
}
tasks.withType<TaskModrinthUpload>().configureEach {
setDependsOn(dependsOn.filterNot { it == "build" } + "assemble")
}
+14
View File
@@ -0,0 +1,14 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
}
// Re-export Gradle's generated typed catalog accessors so applied scripts (dependencies.gradle.kts) can use them.
dependencies {
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
implementation(libs.asm.build)
}
+5
View File
@@ -0,0 +1,5 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") { from(files("../gradle/libs.versions.toml")) }
}
}
@@ -0,0 +1,49 @@
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import java.io.File
private const val LWJGL3_AWARE_DESC = "Lme/eigenraven/lwjgl3ify/api/Lwjgl3Aware;"
/**
* Stamps @Lwjgl3Aware onto every class under [classesDir] - classes that already carry the annotation are left untouched.
*/
fun injectLwjgl3Aware(classesDir: File) {
if (!classesDir.isDirectory) return
classesDir.walkTopDown()
.filter { it.isFile && it.extension == "class" && it.name != "module-info.class" }
.forEach { classFile ->
val reader = ClassReader(classFile.readBytes())
if (hasLwjgl3Aware(reader)) return@forEach
val writer = ClassWriter(reader, 0)
reader.accept(object : ClassVisitor(Opcodes.ASM9, writer) {
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, access, name, signature, superName, interfaces)
super.visitAnnotation(LWJGL3_AWARE_DESC, true)?.visitEnd()
}
}, 0)
classFile.writeBytes(writer.toByteArray())
}
}
private fun hasLwjgl3Aware(reader: ClassReader): Boolean {
var found = false
reader.accept(object : ClassVisitor(Opcodes.ASM9) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
if (descriptor == LWJGL3_AWARE_DESC) found = true
return null
}
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
return found
}
-180
View File
@@ -1,180 +0,0 @@
/*
* Add your dependencies here. Supported configurations:
* - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod
* Available at runtime and compiletime for mods depending on this mod
* - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API
* Available at runtime but not compiletime for mods depending on this mod
* - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods
* Not available at all for mods depending on this mod, only visible at compiletime for this mod
* - compileOnlyApi("g:n:v:c"): like compileOnly, but also visible at compiletime for mods depending on this mod
* Available at compiletime but not runtime for mods depending on this mod
* - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency
* Not available at all for mods depending on this mod, only visible at runtime for this mod
* - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly for dependencies present at both compiletime and runtime,
* but not published as Maven dependencies - useful for RFG-deobfuscated dependencies or local testing
* - runtimeOnly("g:n:v:c"): if you don't need this at compile time, but want it to be present at runtime
* Available at runtime for mods depending on this mod
* - annotationProcessor("g:n:v:c"): mostly for java compiler plugins, if you know you need this, use it, otherwise don't worry
* - testCONFIG("g:n:v:c") - replace CONFIG by one of the above (except api), same as above but for the test sources instead of main
*
* - shadowImplementation("g:n:v:c"): effectively the same as API, but the dependency is included in your jar under a renamed package name
* Requires you to enable usesShadowedDependencies in gradle.properties
*
* - compile("g:n:v:c"): deprecated, replace with "api" (works like the old "compile") or "implementation" (can be more efficient)
*
* You can exclude transitive dependencies (dependencies of the chosen dependency) by appending { transitive = false } if needed,
* but use this sparingly as it can break using your mod as another mod's dependency if you're not careful.
*
* To depend on obfuscated jars you can use `devOnlyNonPublishable(rfg.deobf("dep:spec:1.2.3"))` to fetch an obfuscated jar from maven,
* or `devOnlyNonPublishable(rfg.deobf(project.files("libs/my-mod-jar.jar")))` to use a file.
*
* Gradle names for some of the configuration can be misleading, compileOnlyApi and runtimeOnly both get published as dependencies in Maven, but compileOnly does not.
* The buildscript adds runtimeOnlyNonPublishable to also have a runtime dependency that's not published.
*
* For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph
*/
configurations {
transformedMod // Mods that can be transformed - used for compiling angelica, but not necessary at runtime
transformedMod.canBeConsumed = false
transformedModCompileOnly
transformedModCompileOnly.canBeConsumed = false
// Add the transformed mod dependencies to the compilation classpaths, but don't publish them in the Maven metadata
compileClasspath.extendsFrom(transformedMod, transformedModCompileOnly)
testCompileClasspath.extendsFrom(transformedMod, transformedModCompileOnly)
// Can be enabled for testing if desired
// runtimeClasspath.extendsFrom(transformedMod)
// testRuntimeClasspath.extendsFrom(transformedMod)
testRuntimeClasspath.exclude group: "com.github.GTNewHorizons", module: "lwjgl3ify"
// Embed in JAR without relocation (compile only, shadowed into JAR)
embedOnly
embedOnly.canBeConsumed = false
embedOnly.canBeResolved = true
compileClasspath.extendsFrom(embedOnly)
testCompileClasspath.extendsFrom(embedOnly)
testRuntimeClasspath.extendsFrom(embedOnly)
}
// Exclude self-reference from all configurations - prevents transitive Angelica deps from resolving against this project
configurations.all {
exclude group: "com.github.GTNewHorizons", module: "Angelica"
}
dependencies {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.158:dev")
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.1.0") { transitive = false }
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:lwjgl3ify:3.0.99")
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.42")
// Iris Shaders
compileOnly('org.jetbrains:annotations:26.0.2')
api("com.github.GTNewHorizons:GTNHLib:0.11.4:dev")
shadowImplementation(project(":glsm")) { transitive = false }
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
shadowImplementation("net.minecraftforge:eventbus:7.0-beta.15")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-33.g3ec381e") {
exclude module: "antlr4" // we only want to shadow the runtime module
}
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
compileOnly "org.apache.ant:ant:1.8.2"
// Celeritas (transitive=false: deps provided by GTNHLib/runtime)
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
// Because who doesn't want NEI
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
// Display List Testing
/*
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-749-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.14-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.54:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.0:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.4-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.12-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.12-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.13:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.6:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.7:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.15-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.10-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.18-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.8-GTNH:dev")
*/
//devOnlyNonPublishable('com.github.GTNewHorizons:worldedit-gtnh:v0.0.9')
// Notfine Deps
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.13:dev") { transitive = false }
compileOnly(rfg.deobf('curse.maven:witchery-69673:2234410'))
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.6-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.8.9:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.4:dev")
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { transitive = false }
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { transitive = false }
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { transitive = false }
compileOnly(rfg.deobf("curse.maven:extrautils-225561:2264383"))
compileOnly(rfg.deobf("curse.maven:dynamiclights-227874:2337326"))
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev")
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5663_H261"))
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
// For testing alternative splash screens — https://github.com/MalTeeez/ModernSplash/releases
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
// Hodgepodge
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
// Better Crashes
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.1-GTNH:dev")
// Distant Horizons
compileOnly("maven.modrinth:DistantHorizonsApi:5.1.0")
afterEvaluate {
dependencies {
testImplementation(platform('org.junit:junit-bom:5.14.1'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation("org.mockito:mockito-core:3.+")
testImplementation("org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209")
testImplementation('org.junit.platform:junit-platform-engine')
testImplementation('org.junit.platform:junit-platform-reporting')
testImplementation('org.junit.platform:junit-platform-launcher')
testImplementation('com.tngtech.archunit:archunit-junit5:1.4.1')
}
}
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
}
+209
View File
@@ -0,0 +1,209 @@
/*
* Angelica's dependency declarations. Supported configurations:
* - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod
* Available at runtime and compiletime for mods depending on this mod
* - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API
* Available at runtime but not compiletime for mods depending on this mod
* - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods
* Not available at all for mods depending on this mod, only visible at compiletime for this mod
* - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency
* - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly, not published as a Maven dependency
* - shadowImplementation("g:n:v:c"): like api, but included in your jar under a renamed package name
* - embedOnly("g:n:v:c"): embedded in the jar without relocation
*
* Versions come from gradle/libs.versions.toml. Mod coordinates stay literal: nearly all carry a
* :dev classifier, which a version catalog cannot express, and curse/modrinth "versions" are file ids.
*
* To depend on obfuscated jars use `rfg.deobf("dep:spec:1.2.3")`.
*/
import com.gtnewhorizons.retrofuturagradle.modutils.ModUtils
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.kotlin.dsl.DependencyHandlerScope
val libs = the<LibrariesForLibs>()
val rfg = (dependencies as ExtensionAware).extensions.getByType<ModUtils.RfgDependencyExtension>()
val api = configurations["api"]
val compileOnly = configurations["compileOnly"]
val annotationProcessor = configurations["annotationProcessor"]
val shadowImplementation = configurations["shadowImplementation"]
val runtimeOnlyNonPublishable = configurations["runtimeOnlyNonPublishable"]
val devOnlyNonPublishable = configurations["devOnlyNonPublishable"]
// Mods that can be transformed - used for compiling angelica, but not necessary at runtime
val transformedMod by configurations.creating { isCanBeConsumed = false }
val transformedModCompileOnly by configurations.creating { isCanBeConsumed = false }
// Embed in JAR without relocation (compile only, shadowed into JAR)
val embedOnly by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
// Add the transformed mod dependencies to the compilation classpaths, but don't publish them in the Maven metadata
configurations["compileClasspath"].extendsFrom(transformedMod, transformedModCompileOnly, embedOnly)
configurations["testCompileClasspath"].extendsFrom(transformedMod, transformedModCompileOnly, embedOnly)
configurations["testRuntimeClasspath"].extendsFrom(embedOnly)
configurations["testRuntimeClasspath"].exclude(group = "com.github.GTNewHorizons", module = "lwjgl3ify")
// Prevents transitive Angelica deps from resolving against this project
configurations.configureEach {
exclude(group = "com.github.GTNewHorizons", module = "Angelica")
}
DependencyHandlerScope.of(dependencies).apply {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.166:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.166:dev")
runtimeOnlyNonPublishable(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-994-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BetterLoadingScreen:1.7.9-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.72-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.63:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.32-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:LogisticsPipes:1.5.32-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.55-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.18-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.10:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.18-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Schematica:1.12.6-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.4.1:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.13:dev")
runtimeOnlyNonPublishable("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.19.30:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.30-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.15-GTNH:dev")
runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
compileOnly(libs.retrofuturabootstrap) { isTransitive = false }
// runtimeOnlyNonPublishable(libs.lwjgl3ify)
compileOnly(libs.lombok) { isTransitive = false }
annotationProcessor(libs.lombok)
// Iris Shaders
compileOnly(libs.annotations)
api(libs.gtnhlib) { artifact { classifier = "dev" } }
shadowImplementation(project(":glsm")) { isTransitive = false }
shadowImplementation(project(":lwjgl3-backend")) { isTransitive = false }
shadowImplementation(project(":sdl-gpu")) { isTransitive = false }
runtimeOnlyNonPublishable(project(":tracy-client")) { isTransitive = false }
shadowImplementation(libs.eventbus)
shadowImplementation(libs.jcpp) // Apache 2.0
shadowImplementation(libs.glsl.transformation.lib) {
exclude(module = "antlr4") // we only want to shadow the runtime module
}
shadowImplementation(libs.antlr4.runtime)
compileOnly(libs.ant)
// 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")
devOnlyNonPublishable("com.github.GTNewHorizons:CodeChickenCore:1.4.16:dev")
// BQ Testing
// devOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.74-GTNH:dev")
// Display List Testing
/*
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-994-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.61:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.10:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.18-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.47-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.4.1:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.13:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.19.30:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.25-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.18-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.27-GTNH:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.15-GTNH:dev")
*/
//devOnlyNonPublishable("com.github.GTNewHorizons:worldedit-gtnh:v0.0.9")
// Notfine Deps
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.21-GTNH:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.36:dev") { isTransitive = false }
compileOnly(rfg.deobf("curse.maven:witchery-69673:2234410"))
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.93-GTNH:dev") { isTransitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.8.19:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.8.20:dev")
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { isTransitive = false }
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { isTransitive = false }
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { isTransitive = false }
compileOnly(rfg.deobf("curse.maven:extrautils-225561:2264383"))
compileOnly(rfg.deobf("curse.maven:dynamiclights-227874:2337326"))
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.11:dev")
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5663_H261"))
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.6-GTNH:dev")
// For testing alternative splash screens - https://github.com/MalTeeez/ModernSplash/releases
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
// Hodgepodge
transformedMod("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev")
// Better Crashes
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.5-GTNH:dev")
// Distant Horizons
compileOnly("maven.modrinth:DistantHorizonsApi:5.1.0")
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { isTransitive = false }
}
afterEvaluate {
val glsmTestFixtures = configurations["glsmTestFixtures"]
val testImplementation = configurations["testImplementation"]
val testCompileOnly = configurations["testCompileOnly"]
val testRuntimeOnly = configurations["testRuntimeOnly"]
DependencyHandlerScope.of(dependencies).apply {
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testImplementation(libs.mockito.core)
testImplementation(libs.lwjgl2)
testImplementation(libs.junit.platform.engine)
testImplementation(libs.junit.platform.reporting)
testImplementation(libs.junit.platform.launcher)
testImplementation(libs.archunit.junit5)
glsmTestFixtures(testFixtures(project(":glsm")) as ModuleDependency) { isTransitive = false }
testCompileOnly(testFixtures(project(":glsm")))
testRuntimeOnly(libs.fastutil)
testRuntimeOnly(libs.joml)
}
}
+158 -40
View File
@@ -2,6 +2,7 @@ import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
plugins {
`java-library`
`java-test-fixtures`
`maven-publish`
}
@@ -9,6 +10,8 @@ apply(plugin = "xyz.wagyourtail.jvmdowngrader")
version = rootProject.version
val lwjglNatives: String by extra
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
withSourcesJar()
@@ -20,7 +23,7 @@ val rfgObfAttr = Attribute.of("com.gtnewhorizons.retrofuturagradle.obfuscation",
val rfgTransformedAttr = Attribute.of("rfgDeobfuscatorTransformed", Boolean::class.javaObjectType)
configurations {
listOf("apiElements", "runtimeElements").forEach { name ->
listOf("apiElements", "runtimeElements", "testFixturesApiElements", "testFixturesRuntimeElements").forEach { name ->
named(name) {
attributes {
attribute(rfgObfAttr, "mcp")
@@ -31,7 +34,7 @@ configurations {
}
// Compile stubs: MC class signatures needed to resolve GTNHLib's type hierarchy.
// Not included in the published jar — real MC classes are on the classpath at runtime.
// Not included in the published jar - real MC classes are on the classpath at runtime.
sourceSets {
create("stubs") {
java.srcDir("src/stubs/java")
@@ -65,6 +68,16 @@ repositories {
url = uri("https://maven.wagyourtail.xyz/releases")
content { includeGroup("xyz.wagyourtail.jvmdowngrader") }
}
maven {
name = "Sonatype Snapshots"
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
}
maven {
name = "Maven Central Snapshots"
url = uri("https://central.sonatype.com/repository/maven-snapshots/")
}
mavenLocal()
mavenCentral()
mavenLocal()
}
@@ -74,40 +87,62 @@ dependencies {
compileOnly(sourceSets["stubs"].output)
// LWJGL
compileOnly("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
compileOnly("org.lwjgl.lwjgl:lwjgl_util:${property("lwjglVersion")}")
compileOnly(libs.lwjgl2)
compileOnly(libs.lwjgl2.util)
compileOnly("org.ow2.asm:asm-tree:5.0.3")
compileOnly(libs.asm.tree.compile)
// Our Deps
api("com.github.GTNewHorizons:GTNHLib:${property("gtnhlibVersion")}:dev")
api("net.minecraftforge:eventbus:${property("eventbusVersion")}")
api(libs.gtnhlib) { artifact { classifier = "dev" } }
api(libs.eventbus)
compileOnly("org.embeddedt.celeritas:celeritas-common:${property("celeritasVersion")}") { isTransitive = false }
implementation("org.taumc:glsl-transformation-lib:${property("glslTransformLibVersion")}") { exclude(module = "antlr4") }
implementation("org.antlr:antlr4-runtime:${property("antlr4RuntimeVersion")}")
implementation("org.anarres:jcpp:${property("jcppVersion")}")
compileOnly(libs.celeritas.common) { isTransitive = false }
api(libs.glsl.transformation.lib) { exclude(module = "antlr4") }
api(libs.antlr4.runtime)
implementation(libs.jcpp)
compileOnly("org.projectlombok:lombok:${property("lombokVersion")}") { isTransitive = false }
annotationProcessor("org.projectlombok:lombok:${property("lombokVersion")}")
compileOnly("org.jetbrains:annotations:${property("jetbrainsAnnotationsVersion")}")
compileOnly("org.apache.logging.log4j:log4j-api:${property("log4jVersion")}")
// shaderc + SPIRV-Cross for the SpirvShaderTranslator (GLSL -> SPIR-V -> GLSL ES).
// TODO: Remove
compileOnly(libs.lwjgl3.shaderc)
compileOnly(libs.lwjgl3.spvc)
testImplementation(libs.lwjgl3.shaderc)
testImplementation(libs.lwjgl3.spvc)
// Host natives so the JNI wrapper can find libshaderc / libspirv-cross / liblwjgl.
testRuntimeOnly(libs.lwjgl3.shaderc) { artifact { classifier = lwjglNatives } }
testRuntimeOnly(libs.lwjgl3.spvc) { artifact { classifier = lwjglNatives } }
testRuntimeOnly(libs.lwjgl3)
testRuntimeOnly(libs.lwjgl3) { artifact { classifier = lwjglNatives } }
// @Lwjgl3Aware annotation
compileOnly(libs.lwjgl3ify) { artifact { classifier = "dev" }; isTransitive = false }
compileOnly(libs.lombok) { isTransitive = false }
annotationProcessor(libs.lombok)
compileOnly(libs.annotations)
compileOnly(libs.log4j.api)
// Deps (normally from MC) - compile only, but needed at test runtime
compileOnly("com.google.guava:guava:${property("guavaVersion")}")
testRuntimeOnly("com.google.guava:guava:${property("guavaVersion")}")
compileOnly(libs.guava)
testRuntimeOnly(libs.guava)
testRuntimeOnly(libs.fastutil)
testRuntimeOnly(libs.joml) { isTransitive = false }
testFixturesCompileOnly(sourceSets["stubs"].output)
testFixturesApi(platform(libs.junit.bom))
testFixturesApi(libs.junit.jupiter.api)
testFixturesApi(libs.lwjgl2)
// Test
testImplementation(sourceSets["stubs"].output)
testImplementation(platform("org.junit:junit-bom:${property("junitBomVersion")}"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.ow2.asm:asm-tree:9.7")
testImplementation("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.apache.logging.log4j:log4j-core:${property("log4jVersion")}")
testRuntimeOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:${property("celeritasVersion")}") { isTransitive = false }
testRuntimeOnly("xyz.wagyourtail.jvmdowngrader:jvmdowngrader-java-api:${property("jvmDowngraderVersion")}:downgraded-8")
testRuntimeOnly("org.apache.commons:commons-lang3:${property("commonsLang3Version")}")
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testImplementation(libs.asm.tree.test)
testImplementation(libs.lwjgl2)
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)
}
val depsToDowngrade by configurations.creating {
@@ -116,8 +151,8 @@ val depsToDowngrade by configurations.creating {
}
dependencies {
depsToDowngrade("net.minecraftforge:eventbus:${property("eventbusVersion")}")
depsToDowngrade("org.embeddedt.celeritas:celeritas-common:${property("celeritasVersion")}") { isTransitive = false }
depsToDowngrade(libs.eventbus)
depsToDowngrade(libs.celeritas.common) { isTransitive = false }
}
tasks.withType<DowngradeFiles>().configureEach {
@@ -134,9 +169,18 @@ val downgradeDepsForTest by tasks.registering(DowngradeFiles::class) {
val downgradeMainClasses by tasks.registering(DowngradeFiles::class) {
inputCollection = sourceSets["main"].output.classesDirs.plus(sourceSets["stubs"].output.classesDirs)
classpath = sourceSets["main"].compileClasspath
(sourceSets["main"].output.classesDirs.files + sourceSets["stubs"].output.classesDirs.files)
.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
dependsOn(tasks.named("classes"), tasks.named("stubsClasses"))
}
val downgradeTestFixturesClasses by tasks.registering(DowngradeFiles::class) {
inputCollection = sourceSets["testFixtures"].output.classesDirs
classpath = sourceSets["testFixtures"].compileClasspath
sourceSets["testFixtures"].output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
dependsOn(tasks.named("testFixturesClasses"))
}
val downgradeTestClasses by tasks.registering(DowngradeFiles::class) {
inputCollection = sourceSets["test"].output.classesDirs
classpath = sourceSets["test"].compileClasspath
@@ -144,9 +188,13 @@ val downgradeTestClasses by tasks.registering(DowngradeFiles::class) {
dependsOn(tasks.named("testClasses"))
}
tasks.test {
useJUnitPlatform()
val GL_TASK_TAGS = mapOf(
"glCompatTest" to "gl-compat",
"glCoreTest" to "gl-core",
"glSharedTest" to "gl-shared",
)
fun Test.configureGlsmJava8() {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
if (System.getProperty("os.name").lowercase().contains("mac")
@@ -155,7 +203,7 @@ tasks.test {
}
}
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeDepsForTest)
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeTestFixturesClasses, downgradeDepsForTest)
val mainClassesDirs = sourceSets["main"].output.classesDirs
.plus(sourceSets["stubs"].output.classesDirs)
@@ -169,26 +217,96 @@ tasks.test {
classpath = downgradedTest
.plus(downgradedMain)
.plus(downgradedDeps)
.plus(classpath.minus(mainClassesDirs).minus(testClassesDirs).minus(depsToDowngrade))
.plus(files(downgradeTestFixturesClasses.map { it.outputCollection }))
.plus(sourceSets["test"].runtimeClasspath.minus(mainClassesDirs).minus(testClassesDirs)
.minus(sourceSets["testFixtures"].output.classesDirs).minus(depsToDowngrade))
val extractNatives = rootProject.tasks.named("extractNatives2")
dependsOn(extractNatives)
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
}
val verifyTestsRan by tasks.registering {
val resultsDir = layout.buildDirectory.dir("test-results/test")
dependsOn(tasks.test)
doLast {
val dir = resultsDir.get().asFile
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
check(xmls.isNotEmpty()) {
":glsm:test produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
tasks.test {
useJUnitPlatform {
excludeTags = GL_TASK_TAGS.values.toSet() + "lwjgl3"
}
filter {
excludeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
}
configureGlsmJava8()
}
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
tasks.register<Test>(taskName) {
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
group = "verification"
useJUnitPlatform {
includeTags = setOf(tag)
}
configureGlsmJava8()
}
}
(listOf(tasks.test) + glTestTasks).zipWithNext { earlier, later ->
later.configure { mustRunAfter(earlier) }
}
tasks.test { finalizedBy(glTestTasks) }
val verifyTestsRan by tasks.registering {
dependsOn(tasks.test, glTestTasks)
}
(listOf(tasks.test) + glTestTasks).forEach { testTask ->
val name = testTask.name
val verify = tasks.register("verify${name.replaceFirstChar { it.uppercase() }}Ran") {
val resultsDir = layout.buildDirectory.dir("test-results/$name")
dependsOn(testTask)
doLast {
val dir = resultsDir.get().asFile
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
check(xmls.isNotEmpty()) {
":glsm:$name produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
}
}
}
verifyTestsRan.configure { dependsOn(verify) }
}
tasks.check { dependsOn(verifyTestsRan) }
val spirvTest by tasks.registering(Test::class) {
description = "Runs SpirvShaderTranslator* tests against native shaderc + spvc on Java 21."
group = "verification"
useJUnitPlatform()
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(21)
}
testClassesDirs = sourceSets["test"].output.classesDirs
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-")
}
filter {
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
includeTestsMatching("com.gtnewhorizons.angelica.glsm.ffp.FFPUniformBlockSpirvLayoutTest")
}
// libshaderc.so / libspirv-cross.so are extracted by extractNatives3.
val extractNatives3 = rootProject.tasks.named("extractNatives3")
dependsOn(extractNatives3)
jvmArgs(
"-Djava.library.path=${extractNatives3.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}",
"--enable-native-access=ALL-UNNAMED"
)
testLogging {
showStandardStreams = true
}
}
tasks.named("check") { dependsOn(spirvTest) }
publishing {
publications {
create<MavenPublication>("mavenJava") {
-14
View File
@@ -1,14 +0,0 @@
lwjglVersion = 2.9.4-nightly-20150209
eventbusVersion = 7.0-beta.15
celeritasVersion = 2.5.3-GTNH
lombokVersion = 1.18.42
log4jVersion = 2.0-beta9
guavaVersion = 18.0
gtnhlibVersion = 0.10.0
glslTransformLibVersion = 0.2.0-26.g5ee7d96-GTNH
antlr4RuntimeVersion = 4.13.2
jcppVersion = 1.4.14
jetbrainsAnnotationsVersion = 26.0.2
junitBomVersion = 5.14.1
jvmDowngraderVersion = 1.3.5
commonsLang3Version = 3.17.0
@@ -0,0 +1,126 @@
package com.gtnewhorizons.angelica.config;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Locale;
public final class SystemProperties {
private static final Logger LOGGER = LogManager.getLogger("SystemProperties");
// Backend selection
public static final String KEY_USE_SDL_GPU = "angelica.sdlgpu.enable";
public static final boolean USE_SDL_GPU = Boolean.getBoolean(KEY_USE_SDL_GPU);
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
public static final boolean DISABLE_LTW_WORKAROUND = Boolean.getBoolean("angelica.disableLtwWorkaround");
// Redirector
public static final UnmappedGLMode UNMAPPED_GL = parseEnum("angelica.unmappedGL", UnmappedGLMode.WARN, UnmappedGLMode.WARN, UnmappedGLMode.class);
public static final boolean FFP_WARN_ON_UNSUPPORTED = Boolean.getBoolean("angelica.ffp.warnOnUnsupported");
// SDL-GPU
public static final String KEY_SDL_GPU_DRIVER = "angelica.sdlgpu.driver";
public static final String SDL_GPU_DRIVER = System.getProperty(KEY_SDL_GPU_DRIVER, "");
public static final boolean SDL_GPU_DEBUG = Boolean.getBoolean("angelica.sdlgpu.debug");
private static final SdlAssertionMode ENCODER_ASSERTIONS = parseEnum("angelica.sdlgpu.encoderAssertions", SdlAssertionMode.OFF, SdlAssertionMode.WARN, SdlAssertionMode.class);
public static final boolean SDL_ENCODER_ASSERTIONS = ENCODER_ASSERTIONS != SdlAssertionMode.OFF;
public static final boolean SDL_ENCODER_ASSERTIONS_FATAL = ENCODER_ASSERTIONS == SdlAssertionMode.FATAL;
public static final boolean SDL_VERIFY_STATE_SYNC = Boolean.getBoolean("angelica.sdlgpu.verifyStateSync");
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");
// Tracy
public static final boolean TRACY = Boolean.getBoolean("angelica.tracy");
public static final boolean TRACY_FINE_ZONES = Boolean.getBoolean("angelica.tracy.fineZones");
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));
// 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");
public static final int FLYBY_WARMUP_TICKS = Integer.getInteger("angelica.flyby.warmupTicks", 400);
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");
// Debug
public static final boolean LWJGL_DEBUG = Boolean.getBoolean("org.lwjgl.util.Debug");
public static final boolean DEBUG_MARKERS = Boolean.getBoolean("angelica.debug.markers");
public static final boolean DEBUG_F3_DETAIL = Boolean.getBoolean("angelica.debug.f3Detail");
public static final boolean ENABLE_TEST_BLOCKS = Boolean.getBoolean("angelica.debug.testBlocks");
public static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.debug.dumpClass");
public static final boolean DUMP_SHADERS = Boolean.getBoolean("angelica.debug.dumpShaders") || isDeobf();
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 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 Path shaderDumpDir(String phase) {
return DUMP_SHADERS ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
}
public enum UnmappedGLMode {
OFF,
WARN,
FAIL,
STRICT;
public boolean detects() {
return this != OFF;
}
public boolean failsUnmapped() {
return this == FAIL || this == STRICT;
}
public boolean failsAwareUnroutable() {
return this == STRICT;
}
}
private enum SdlAssertionMode {
OFF,
WARN,
FATAL
}
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;
try {
return Enum.valueOf(type, raw.trim().toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
LOGGER.warn("Unrecognized value '{}' for -D{}; using {}. Valid values: {}", raw, key, whenInvalid, Arrays.toString(type.getEnumConstants()));
return whenInvalid;
}
}
private static double parseDouble(String key) {
final String raw = System.getProperty(key);
if (raw == null || raw.isEmpty()) return 0.0D;
try {
return Double.parseDouble(raw);
} catch (NumberFormatException e) {
return 0.0D;
}
}
private static boolean isDeobf() {
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("fml.deobfuscatedEnvironment"));
}
private SystemProperties() {}
}
@@ -0,0 +1,76 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.function.UnaryOperator;
/** Decides whether instrumentation is worth emitting this frame. */
public final class CaptureGate {
private static final Logger LOGGER = LogManager.getLogger("Angelica");
private static final boolean IS_LINUX = System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("linux");
private static final Path PROC_SELF_MAPS = Paths.get("/proc/self/maps");
private static final String[] CAPTURE_ENV = { "RENDERDOC_CAPTURE_KEYPRESS", "ENABLE_VULKAN_RENDERDOC_CAPTURE", "RENDERDOC_HOOK_EGL" };
private static final String[] CAPTURE_LIBS = { "renderdoc", "glxtrace", "egltrace", "apitrace" };
public static final boolean FORCED = SystemProperties.DEBUG_MARKERS || SystemProperties.LWJGL_DEBUG;
public static final boolean TOOL_ATTACHED = detectCaptureTool();
public static boolean markersThisFrame = true;
private CaptureGate() {}
public static void refresh() {
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, Tracy.isConnected());
}
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
return forced || toolAttached || tracyConnected;
}
public static boolean enabledAtStartup() {
return FORCED || TOOL_ATTACHED;
}
private static boolean detectCaptureTool() {
if (detectFromEnv(System::getenv)) {
LOGGER.info("Capture tool detected via environment; instrumentation enabled");
return true;
}
if (!IS_LINUX) return false;
try {
if (detectFromMaps(Files.readAllLines(PROC_SELF_MAPS, StandardCharsets.ISO_8859_1))) {
LOGGER.info("Capture tool library detected in process map; instrumentation enabled");
return true;
}
} catch (IOException e) {
LOGGER.warn("Could not read {} for capture tool detection: {}", PROC_SELF_MAPS, e.toString());
}
return false;
}
static boolean detectFromEnv(UnaryOperator<String> env) {
for (String key : CAPTURE_ENV) {
if (env.apply(key) != null) return true;
}
return false;
}
static boolean detectFromMaps(List<String> mapLines) {
for (int i = 0; i < mapLines.size(); i++) {
final String line = mapLines.get(i).toLowerCase(Locale.ROOT);
for (String lib : CAPTURE_LIBS) {
if (line.contains(lib)) return true;
}
}
return false;
}
}
@@ -1,5 +1,14 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
import com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.lwjgl.opengl.GL20;
import org.taumc.glsl.grammar.GLSLLexer;
import org.taumc.glsl.grammar.GLSLParser;
import org.taumc.glsl.ShaderParser;
import org.taumc.glsl.Transformer;
@@ -8,9 +17,12 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -26,12 +38,12 @@ import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACK
* <ul>
* <li>Matrix builtin replacement (gl_ModelViewMatrix, gl_ProjectionMatrix, etc.)</li>
* <li>Vertex attribute replacement (gl_Vertex, gl_Color, gl_MultiTexCoord0/1, gl_Normal)</li>
* <li>gl_TexCoord[N] varying array → per-index in/out declarations</li>
* <li>Texture function renames (texture2D → texture, etc.)</li>
* <li>Fragment output handling (gl_FragColor → layout-qualified out declarations)</li>
* <li>gl_TexCoord[N] varying array -> per-index in/out declarations</li>
* <li>Texture function renames (texture2D -> texture, etc.)</li>
* <li>Fragment output handling (gl_FragColor -> layout-qualified out declarations)</li>
* <li>Fog builtins (gl_Fog, gl_FogFragCoord)</li>
* <li>gl_FrontColor → local variable in vertex shaders</li>
* <li>shadow2D/shadow2DLod → texture/textureLod with vec4 wrapping</li>
* <li>gl_FrontColor -> local variable in vertex shaders</li>
* <li>shadow2D/shadow2DLod -> texture/textureLod with vec4 wrapping</li>
* <li>Version upgrade to 330 core minimum</li>
* <li>Reserved word pre-parse renaming (texture-as-variable, sample, etc.)</li>
* </ul>
@@ -64,30 +76,59 @@ public class CompatShaderTransformer {
"gl_NormalMatrix", "angelica_NormalMatrix"
);
private static final Path DUMP_DIR;
private static final AtomicInteger dumpCounter = new AtomicInteger(0);
static {
DUMP_DIR = Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false")) ? Paths.get("compat_shaders") : null;
}
private static final int CACHE_SIZE = 32;
private static final int CACHE_SIZE = 256;
private record CacheKey(String source, boolean isFragment) {}
private static final Map<CacheKey, String> cache = Collections.synchronizedMap(
new LinkedHashMap<>(32, 0.75f, true) {
new LinkedHashMap<>(CACHE_SIZE, 0.75f, true) {
@Override protected boolean removeEldestEntry(Map.Entry<CacheKey, String> eldest) {
return size() > CACHE_SIZE;
}
return size() > CACHE_SIZE;
}
});
private record EsCacheKey(String source, int glShaderType, boolean isFragment) {}
private static final Map<EsCacheKey, String> esCache = Collections.synchronizedMap(
new LinkedHashMap<>(CACHE_SIZE, 0.75f, true) {
@Override protected boolean removeEldestEntry(Map.Entry<EsCacheKey, String> eldest) {
return size() > CACHE_SIZE;
}
});
public static void clearCache() {
cache.clear();
esCache.clear();
}
/**
* Transform a mod shader source for core profile compatibility.
*/
public static void prewarm(String source, int glShaderType, boolean isFragment) {
if (!RenderSystem.isGLES()) return;
final EsCacheKey key = new EsCacheKey(source, glShaderType, isFragment);
if (esCache.containsKey(key)) return;
transform(source, glShaderType, isFragment);
}
/** Transform a mod shader for core profile; under GLES, also rewrites to ES 320. */
public static String transform(String source, boolean isFragment) {
return transform(source, isFragment ? GL20.GL_FRAGMENT_SHADER : GL20.GL_VERTEX_SHADER, isFragment);
}
/** Variant carrying the exact GL shader type so the GLES re-emit dispatches to the right shaderc kind (geometry/tess/compute). */
public static String transform(String source, int glShaderType, boolean isFragment) {
if (!source.isEmpty() && source.charAt(source.length() - 1) == '\0') {
source = source.substring(0, source.length() - 1);
}
final boolean gles = RenderSystem.isGLES();
if (gles) {
final EsCacheKey esKey = new EsCacheKey(source, glShaderType, isFragment);
final String esCached = esCache.get(esKey);
if (esCached != null) {
dumpShader(source, esCached, isFragment, needsTransformation(source));
return esCached;
}
}
final boolean needsTransform = needsTransformation(source);
String result;
if (!needsTransform) {
@@ -96,17 +137,27 @@ public class CompatShaderTransformer {
final CacheKey key = new CacheKey(source, isFragment);
final String cached = cache.get(key);
if (cached != null) {
dumpShader(source, cached, isFragment, needsTransform);
return cached;
result = cached;
} else {
try {
result = transformInternal(source, isFragment);
cache.put(key, result);
} catch (Exception e) {
GLStateManager.LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
result = fixupVersion(source);
}
}
}
if (gles) {
final String preGles = result;
try {
result = transformInternal(source, isFragment);
cache.put(key, result);
} catch (Exception e) {
GLStateManager.LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
result = fixupVersion(source);
result = toGLES(result, glShaderType, isFragment);
} catch (RuntimeException e) {
dumpShader(source, preGles, isFragment, needsTransform);
throw e;
}
esCache.put(new EsCacheKey(source, glShaderType, isFragment), result);
}
dumpShader(source, result, isFragment, needsTransform);
@@ -137,24 +188,24 @@ public class CompatShaderTransformer {
final int targetVersion = Math.max(declaredVersion, RENDER_BACKEND != null ? RENDER_BACKEND.getMinGLSLVersion() : 330);
// Pre-parse reserved word renaming — prevents ANTLR parse failures
// Pre-parse reserved word renaming - prevents ANTLR parse failures
source = GlslTransformUtils.replaceTexture(source);
source = GlslTransformUtils.renameReservedWords(source, targetVersion);
final ShaderParser.ParsedShader parsedShader = ShaderParser.parseShader(source);
final Transformer transformer = new Transformer(parsedShader.full());
final GlslTransformUtils.QuietParse parsed = GlslTransformUtils.parseBothQuiet(source);
final Transformer transformer = new Transformer(parsed.full());
injectMatrixUniforms(transformer);
transformFog(transformer, isFragment, source);
// gl_FrontLightModelProduct.sceneColor → angelica_SceneColor uniform
// gl_FrontLightModelProduct.sceneColor -> angelica_SceneColor uniform
if (source.contains("gl_FrontLightModelProduct")) {
transformer.injectVariable("uniform vec4 angelica_SceneColor;");
transformer.replaceExpression("gl_FrontLightModelProduct.sceneColor", "angelica_SceneColor");
}
// gl_LightSource[i] → struct + uniform array (both via injectFunction to keep struct before uniform)
// gl_LightSource[i] -> struct + uniform array (both via injectFunction to keep struct before uniform)
if (source.contains("gl_LightSource")) {
transformer.injectFunction("struct angelica_LightSourceParameters {"
+ "vec4 ambient;vec4 diffuse;vec4 specular;vec4 position;vec4 halfVector;"
@@ -165,7 +216,7 @@ public class CompatShaderTransformer {
transformer.rename("gl_LightSource", "angelica_LightSource");
}
// gl_FrontMaterial → struct + uniform (both via injectFunction to keep struct before uniform)
// gl_FrontMaterial -> struct + uniform (both via injectFunction to keep struct before uniform)
if (source.contains("gl_FrontMaterial")) {
transformer.injectFunction("struct angelica_MaterialParameters {"
+ "vec4 emission;vec4 ambient;vec4 diffuse;vec4 specular;float shininess;"
@@ -174,7 +225,7 @@ public class CompatShaderTransformer {
transformer.rename("gl_FrontMaterial", "angelica_FrontMaterial");
}
// gl_FrontColor (vertex) → gl_Color (fragment) varying chain
// gl_FrontColor (vertex) -> gl_Color (fragment) varying chain
// Vertex side is unconditional: fragment may read gl_Color without vertex writing gl_FrontColor.
// Uses angelica_ prefix to avoid colliding with user-declared varyings (e.g. "v_Color").
if (!isFragment) {
@@ -182,8 +233,9 @@ public class CompatShaderTransformer {
transformer.rename("gl_FrontColor", "angelica_FrontColor");
transformer.prependMain("angelica_FrontColor = vec4(1.0);");
// Vertex attributes — replaces removed FFP vertex inputs with explicit in declarations
transformVertexAttributes(transformer, source);
final FFPInputs ffp = FFPInputs.detect(source);
assignVertexInputLocations(transformer, ffp);
transformVertexAttributes(transformer, source, ffp);
} else {
if (source.contains("gl_Color")) {
transformer.injectVariable("in vec4 angelica_FrontColor;");
@@ -191,7 +243,7 @@ public class CompatShaderTransformer {
}
}
// gl_TexCoord[N] varying array → per-index in/out declarations
// gl_TexCoord[N] varying array -> per-index in/out declarations
final Set<Integer> texCoordIndices = new HashSet<>();
transformer.renameArray("gl_TexCoord", "angelica_TexCoord", texCoordIndices);
for (Integer i : texCoordIndices) {
@@ -218,7 +270,7 @@ public class CompatShaderTransformer {
transformer.renameAndWrapShadow("shadow2DLod", "textureLod");
final String versionDirective = "#version " + targetVersion + " core\n";
final String extensions = VERSION_PATTERN.matcher(GlslTransformUtils.getFormattedShader(parsedShader.pre(), "")).replaceFirst("").trim();
final String extensions = VERSION_PATTERN.matcher(GlslTransformUtils.getFormattedShader(parsed.pre(), "")).replaceFirst("").trim();
// Preserve #define directives
final StringBuilder defines = new StringBuilder();
@@ -234,7 +286,7 @@ public class CompatShaderTransformer {
// Restore pre-parse renames
String output = GlslTransformUtils.restoreReservedWords(result.toString());
// Core profile: attribute → in, varying → out (vertex) / in (fragment)
// Core profile: attribute -> in, varying -> out (vertex) / in (fragment)
output = fixupQualifiers(output, isFragment);
return output;
@@ -302,7 +354,8 @@ public class CompatShaderTransformer {
transformer.injectVariable("uniform float angelica_FogEnd;");
transformer.injectVariable("uniform vec4 angelica_FogColor;");
transformer.injectFunction("struct angelica_FogParameters {vec4 color;float density;float start;float end;float scale;};");
transformer.injectFunction("angelica_FogParameters angelica_Fog = angelica_FogParameters("
transformer.injectFunction("angelica_FogParameters angelica_Fog;");
transformer.prependMain("angelica_Fog = angelica_FogParameters("
+ "angelica_FogColor, angelica_FogDensity, angelica_FogStart, angelica_FogEnd, "
+ "1.0 / (angelica_FogEnd - angelica_FogStart));");
}
@@ -310,25 +363,25 @@ public class CompatShaderTransformer {
/**
* Replace removed FFP vertex attributes with explicit {@code in} declarations at core profile attribute locations.
*/
private static void transformVertexAttributes(Transformer transformer, String source) {
if (source.contains("gl_Vertex") || source.contains("ftransform")) {
private static void transformVertexAttributes(Transformer transformer, String source, FFPInputs ffp) {
if (ffp.position()) {
transformer.injectVariable("layout(location = 0) in vec4 angelica_Vertex;");
transformer.rename("gl_Vertex", "angelica_Vertex");
}
// gl_Color in vertex shaders is the per-vertex color attribute, distinct from the fragment gl_Color (interpolated gl_FrontColor) handled above
if (source.contains("gl_Color")) {
if (ffp.color()) {
transformer.injectVariable("layout(location = 1) in vec4 angelica_Color;");
transformer.rename("gl_Color", "angelica_Color");
}
if (source.contains("gl_MultiTexCoord0")) {
if (ffp.uv0()) {
transformer.injectVariable("layout(location = 2) in vec4 angelica_MultiTexCoord0;");
transformer.rename("gl_MultiTexCoord0", "angelica_MultiTexCoord0");
}
if (source.contains("gl_MultiTexCoord1")) {
if (ffp.uv1()) {
transformer.injectVariable("layout(location = 3) in vec4 angelica_MultiTexCoord1;");
transformer.rename("gl_MultiTexCoord1", "angelica_MultiTexCoord1");
}
if (source.contains("gl_Normal")) {
if (ffp.normal()) {
transformer.injectVariable("layout(location = 4) in vec3 angelica_Normal;");
transformer.rename("gl_Normal", "angelica_Normal");
}
@@ -337,19 +390,159 @@ public class CompatShaderTransformer {
}
}
private static final int MAX_VERTEX_ATTRIBS = 16;
private record FFPInputs(boolean position, boolean color, boolean uv0, boolean uv1, boolean normal) {
static FFPInputs detect(String source) {
return new FFPInputs(
source.contains("gl_Vertex") || source.contains("ftransform"),
source.contains("gl_Color"),
source.contains("gl_MultiTexCoord0"),
source.contains("gl_MultiTexCoord1"),
source.contains("gl_Normal"));
}
}
/** Adaptation of Mesa's assign_attribute_or_color_locations */
private static void assignVertexInputLocations(Transformer transformer, FFPInputs ffp) {
int used = 0;
if (ffp.position()) used |= 1 << Usage.POSITION.getAttributeLocation();
if (ffp.color()) used |= 1 << Usage.COLOR.getAttributeLocation();
if (ffp.uv0()) used |= 1 << Usage.PRIMARY_UV.getAttributeLocation();
if (ffp.uv1()) used |= 1 << Usage.SECONDARY_UV.getAttributeLocation();
if (ffp.normal()) used |= 1 << Usage.NORMAL.getAttributeLocation();
record Input(String name, String baseType, String arraySuffix, int slots, int index) {}
final List<Input> toAssign = new ArrayList<>();
int index = 0;
for (TerminalNode storage : transformer.collectStorage()) {
final int tokenType = storage.getSymbol().getType();
if (tokenType != GLSLLexer.ATTRIBUTE && tokenType != GLSLLexer.IN) continue;
ParseTree node = storage.getParent();
while (node != null && !(node instanceof GLSLParser.Single_declarationContext)) {
node = node.getParent();
}
if (!(node instanceof GLSLParser.Single_declarationContext decl)) continue;
if (decl.typeless_declaration() == null) continue;
final GLSLParser.Type_specifierContext typeSpec = decl.fully_specified_type().type_specifier();
final String baseType = GlslTransformUtils.getFormattedShader(typeSpec.type_specifier_nonarray(), "").trim();
final int typeArraySlots = arraySlots(typeSpec.array_specifier());
final String typeArray = arraySuffix(typeSpec.array_specifier());
if (hasLayoutQualifier(decl)) {
final int loc = explicitLocation(decl);
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(decl.typeless_declaration().array_specifier());
if (loc >= 0) used |= slotMask(slots) << loc;
continue;
}
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
declarators.add(decl.typeless_declaration());
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
declarators.addAll(list.typeless_declaration());
}
for (GLSLParser.Typeless_declarationContext d : declarators) {
if (d.IDENTIFIER() == null) continue;
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
toAssign.add(new Input(d.IDENTIFIER().getText(), baseType, arraySuffix, slots, index++));
}
}
toAssign.sort((a, b) -> a.slots() != b.slots() ? b.slots() - a.slots() : a.index() - b.index());
for (Input in : toAssign) {
final int loc = findAvailableSlots(used, in.slots());
if (loc < 0) continue;
used |= slotMask(in.slots()) << loc;
transformer.removeVariable(in.name());
transformer.variable = null;
transformer.injectVariable("layout(location = " + loc + ") in " + in.baseType() + " " + in.name() + in.arraySuffix() + ";");
}
}
private static String arraySuffix(GLSLParser.Array_specifierContext arraySpec) {
return arraySpec != null ? GlslTransformUtils.getFormattedShader(arraySpec, "").trim() : "";
}
private static int arraySlots(GLSLParser.Array_specifierContext arraySpec) {
if (arraySpec == null) return 1;
int product = 1;
for (GLSLParser.DimensionContext dim : arraySpec.dimension()) {
if (dim.constant_expression() == null) continue;
try {
product *= Integer.decode(GlslTransformUtils.getFormattedShader(dim.constant_expression(), "").trim());
} catch (NumberFormatException ignored) {}
}
return product;
}
private static int baseSlots(String type) {
final String dims = type.startsWith("dmat") ? type.substring(4) : type.startsWith("mat") ? type.substring(3) : null;
if (dims != null && !dims.isEmpty() && Character.isDigit(dims.charAt(0))) {
return dims.charAt(0) - '0';
}
return 1;
}
private static int slotMask(int slots) {
return (slots >= 32) ? -1 : (1 << slots) - 1;
}
private static int findAvailableSlots(int used, int needed) {
if (needed <= 0) return -1;
final int mask = slotMask(needed);
for (int i = 0; i <= MAX_VERTEX_ATTRIBS - needed; i++) {
if (((mask << i) & ~used) == (mask << i)) return i;
}
return -1;
}
private static int explicitLocation(GLSLParser.Single_declarationContext decl) {
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
if (qualifier == null) return -1;
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
final GLSLParser.Layout_qualifierContext layout = single.layout_qualifier();
if (layout == null) continue;
for (GLSLParser.Layout_qualifier_idContext id : layout.layout_qualifier_id_list().layout_qualifier_id()) {
if (id.IDENTIFIER() != null && "location".equals(id.IDENTIFIER().getText()) && id.constant_expression() != null) {
try {
return Integer.decode(GlslTransformUtils.getFormattedShader(id.constant_expression(), "").trim());
} catch (NumberFormatException ignored) {
return -1;
}
}
}
}
return -1;
}
private static boolean hasLayoutQualifier(GLSLParser.Single_declarationContext decl) {
final GLSLParser.Type_qualifierContext qualifier = decl.fully_specified_type().type_qualifier();
if (qualifier == null) return false;
for (GLSLParser.Single_type_qualifierContext single : qualifier.single_type_qualifier()) {
if (single.layout_qualifier() != null) return true;
}
return false;
}
private static void dumpShader(String original, String transformed, boolean isFragment, boolean wasTransformed) {
if (DUMP_DIR == null) return;
final Path dir = SystemProperties.shaderDumpDir("compat");
if (dir == null) return;
final int id = dumpCounter.getAndIncrement();
final String suffix = isFragment ? ".frag.glsl" : ".vert.glsl";
try {
Files.createDirectories(DUMP_DIR);
Files.createDirectories(dir);
// Capture caller info for identification
final String caller = identifyCaller();
final String header = "// Compat shader dump #" + id + " (" + (isFragment ? "fragment" : "vertex") + ")"
+ "\n// Transformed: " + wasTransformed
+ "\n// Caller: " + caller + "\n\n";
Files.writeString(DUMP_DIR.resolve(id + "_original" + suffix), header + original, StandardCharsets.UTF_8);
Files.writeString(DUMP_DIR.resolve(id + "_transformed" + suffix), header + transformed, StandardCharsets.UTF_8);
Files.writeString(dir.resolve(id + "_original" + suffix), header + original, StandardCharsets.UTF_8);
Files.writeString(dir.resolve(id + "_transformed" + suffix), header + transformed, StandardCharsets.UTF_8);
} catch (IOException e) {
GLStateManager.LOGGER.warn("Failed to dump compat shader: {}", e.getMessage());
}
@@ -377,10 +570,10 @@ public class CompatShaderTransformer {
return source;
}
// Patterns for parsing fragment shader in declarations to generate passthrough vertex shaders
private static final Pattern FRAG_IN_COLOR = Pattern.compile("\\bin\\s+vec4\\s+angelica_FrontColor\\b");
private static final Pattern FRAG_IN_TEXCOORD = Pattern.compile("\\bin\\s+vec4\\s+angelica_TexCoord(\\d+)\\b");
private static final Pattern FRAG_IN_FOGCOORD = Pattern.compile("\\bin\\s+float\\s+angelica_FogFragCoord\\b");
private static final String PRECISION_QUALIFIER = "(?:highp\\s+|mediump\\s+|lowp\\s+)?";
private static final Pattern FRAG_IN_COLOR = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "(vec[234])\\s+angelica_FrontColor\\b");
private static final Pattern FRAG_IN_TEXCOORD = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "(vec[234])\\s+angelica_TexCoord(\\d+)\\b");
private static final Pattern FRAG_IN_FOGCOORD = Pattern.compile("\\bin\\s+" + PRECISION_QUALIFIER + "float\\s+angelica_FogFragCoord\\b");
/**
* Generate a passthrough vertex shader for a fragment-only program.
@@ -401,22 +594,26 @@ public class CompatShaderTransformer {
final StringBuilder varyings = new StringBuilder();
final StringBuilder assignments = new StringBuilder();
if (FRAG_IN_COLOR.matcher(fragmentSource).find()) {
sb.append("layout(location = 1) in vec4 a_Color;\n");
varyings.append("out vec4 angelica_FrontColor;\n");
final Matcher colorMatcher = FRAG_IN_COLOR.matcher(fragmentSource);
if (colorMatcher.find()) {
final String colorType = colorMatcher.group(1);
sb.append("layout(location = 1) in ").append(colorType).append(" a_Color;\n");
varyings.append("out ").append(colorType).append(" angelica_FrontColor;\n");
assignments.append(" angelica_FrontColor = a_Color;\n");
}
final Matcher texCoordMatcher = FRAG_IN_TEXCOORD.matcher(fragmentSource);
final Set<Integer> texCoordIndices = new HashSet<>();
final Map<Integer, String> texCoordTypes = new HashMap<>();
while (texCoordMatcher.find()) {
texCoordIndices.add(Integer.parseInt(texCoordMatcher.group(1)));
texCoordTypes.putIfAbsent(Integer.parseInt(texCoordMatcher.group(2)), texCoordMatcher.group(1));
}
for (int i : texCoordIndices) {
for (Map.Entry<Integer, String> e : texCoordTypes.entrySet()) {
final int i = e.getKey();
final String type = e.getValue();
// PRIMARY_UV=2, SECONDARY_UV=3. Only indices 0 and 1 are supported; higher indices collide at location 3.
final int location = i == 0 ? 2 : 3;
sb.append("layout(location = ").append(location).append(") in vec4 a_TexCoord").append(i).append(";\n");
varyings.append("out vec4 angelica_TexCoord").append(i).append(";\n");
sb.append("layout(location = ").append(location).append(") in ").append(type).append(" a_TexCoord").append(i).append(";\n");
varyings.append("out ").append(type).append(" angelica_TexCoord").append(i).append(";\n");
assignments.append(" angelica_TexCoord").append(i).append(" = a_TexCoord").append(i).append(";\n");
}
@@ -434,6 +631,23 @@ public class CompatShaderTransformer {
return sb.toString();
}
/** 330-core -> GLSL ES 320 via shaderc + SPIRV-Cross. Throws on shaderc failure. */
public static String toGLES(String source, int glShaderType, boolean isFragment) {
final Matcher m = VERSION_PATTERN.matcher(source);
if (m.find() && "es".equalsIgnoreCase(m.group(2))) return source;
if (glShaderType == 0) throw new IllegalArgumentException("toGLES requires a valid GL shader type");
final String translated = SpirvShaderTranslator.glslToGlslEs(source, glShaderType, "compat");
if (translated == null) {
throw new IllegalStateException("SpirvShaderTranslator failed for shader kind 0x"
+ Integer.toHexString(glShaderType) + "; see preceding shaderc error + run/client/angelica_dumps/compat/");
}
return translated;
}
public static String toGLES(String source, boolean isFragment) {
return toGLES(source, isFragment ? GL20.GL_FRAGMENT_SHADER : GL20.GL_VERTEX_SHADER, isFragment);
}
/** Ensure #version is at least 330 core, strip 'compatibility' profile. */
private static String fixupVersion(String source) {
final Matcher m = VERSION_PATTERN.matcher(source);
@@ -7,6 +7,10 @@ import com.gtnewhorizons.angelica.glsm.states.LightModelState;
import com.gtnewhorizons.angelica.glsm.states.LightState;
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;
@@ -173,10 +177,10 @@ public class CompatUniformManager {
final int[] locs = pu.locs;
// Matrix uniforms — skip if this program's storage already holds the current generation
final int mvGen = GLStateManager.mvGeneration;
final int mvLinearGen = GLStateManager.mvLinearGeneration;
final int projGen = GLStateManager.projGeneration;
final int texMatGen = GLStateManager.texMatrixGeneration;
final int mvGen = GLStateManager.getMvGeneration();
final int mvLinearGen = GLStateManager.getMvLinearGeneration();
final int projGen = GLStateManager.getProjGeneration();
final int texMatGen = GLStateManager.getTexMatrixGeneration();
final boolean mvChanged = mvGen != pu.lastMvGen;
final boolean mvLinearChanged = mvLinearGen != pu.lastMvLinearGen;
final boolean projChanged = projGen != pu.lastProjGen;
@@ -190,14 +194,14 @@ public class CompatUniformManager {
}
// Fragment-category uniforms (fog, alpha) — skip if generation unchanged
final int fragGen = GLStateManager.fragmentGeneration;
final int fragGen = GLStateManager.getFragmentGeneration();
if (fragGen != pu.lastFragmentGen) {
pu.lastFragmentGen = fragGen;
uploadFragmentUniforms(locs);
}
// Lighting-derived uniforms (scene color, light sources, material)
final int litGen = GLStateManager.lightingGeneration;
final int litGen = GLStateManager.getLightingGeneration();
if (litGen != pu.lastLightingGen) {
pu.lastLightingGen = litGen;
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
@@ -207,7 +211,7 @@ public class CompatUniformManager {
// Clip plane equations + enabled bool — uploaded when enable state or equations change
if (locs[LOC_CLIP_PLANES] != -1 || locs[LOC_CLIP_PLANES_ENABLED] != -1) {
final int cpGen = GLStateManager.clipPlaneGeneration;
final int cpGen = GLStateManager.getClipPlaneGeneration();
if (cpGen != pu.lastClipPlaneGen) {
pu.lastClipPlaneGen = cpGen;
uploadClipPlanes(locs);
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizon.gtnhlib.client.model.NormalHelper;
import com.gtnewhorizon.gtnhlib.client.renderer.CallbackTessellator;
@@ -10,6 +12,8 @@ import com.gtnewhorizon.gtnhlib.client.renderer.tessellator.VertexTransformCallb
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.VBOManager;
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.VertexBuffer;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.recording.AccumulatedDraw;
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
@@ -52,18 +56,15 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
*/
@UtilityClass
public class DisplayListManager {
// -Dangelica.debugDisplayLists: disable transform collapsing and draw merging
private static final boolean DEBUG_DISPLAY_LISTS;
private static final Tracy.ZoneId Z_GL_LIST_COMPILE = Tracy.zoneId("glListCompile", Tracy.COLOR_FFP);
private static final Tracy.ZoneId Z_GL_LIST_PLAYBACK = Tracy.zoneId("glListPlayback", Tracy.COLOR_FFP);
// -Dangelica.logDisplayListCompilation: log compiled display list commands
private static final boolean LOG_DISPLAY_LIST_COMPILATION;
// Tracy profiling counter
public static long listPlaybacks;
static {
DEBUG_DISPLAY_LISTS = Boolean.getBoolean("angelica.debugDisplayLists");
LOG_DISPLAY_LIST_COMPILATION = Boolean.getBoolean("angelica.logDisplayListCompilation");
if (LOG_DISPLAY_LIST_COMPILATION) {
GLStateManager.LOGGER.warn("Display list compilation logging ENABLED (-Dangelica.logDisplayListCompilation=true)");
if (SystemProperties.LOG_DISPLAY_LIST_COMPILATION) {
GLStateManager.LOGGER.warn("Display list compilation logging ENABLED (-Dangelica.debug.displayLists.compilation=true)");
}
}
@@ -133,7 +134,10 @@ public class DisplayListManager {
}
public static UnsupportedOperationException unsupportedInList(String what) {
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
final UnsupportedOperationException e = new UnsupportedOperationException( what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
GLStateManager.warnOnce("unsupported:" + what, "Aborting display list {} compilation: {} unsupported during recording", glListId, what);
abortCompilation();
return e;
}
public static String describeCurrentCompilation() {
@@ -457,7 +461,7 @@ public class DisplayListManager {
currentRecorder.writeHint(target, mode);
}
public static void recordFog(int pname, java.nio.FloatBuffer params) {
public static void recordFog(int pname, FloatBuffer params) {
drawBarrier();
currentRecorder.writeFog(pname, params);
}
@@ -472,7 +476,7 @@ public class DisplayListManager {
currentRecorder.writeLighti(light, pname, param);
}
public static void recordLight(int light, int pname, java.nio.FloatBuffer params) {
public static void recordLight(int light, int pname, FloatBuffer params) {
drawBarrier();
currentRecorder.writeLight(light, pname, params);
}
@@ -487,7 +491,7 @@ public class DisplayListManager {
currentRecorder.writeLightModeli(pname, param);
}
public static void recordLightModel(int pname, java.nio.FloatBuffer params) {
public static void recordLightModel(int pname, FloatBuffer params) {
drawBarrier();
currentRecorder.writeLightModel(pname, params);
}
@@ -497,7 +501,7 @@ public class DisplayListManager {
currentRecorder.writeMaterialf(face, pname, val);
}
public static void recordMaterial(int face, int pname, java.nio.FloatBuffer params) {
public static void recordMaterial(int face, int pname, FloatBuffer params) {
drawBarrier();
currentRecorder.writeMaterial(face, pname, params);
}
@@ -553,7 +557,7 @@ public class DisplayListManager {
currentRecorder.writeDrawBuffers(count, buf);
}
public static void recordDrawBuffers(int count, java.nio.IntBuffer bufs) {
public static void recordDrawBuffers(int count, IntBuffer bufs) {
drawBarrier();
currentRecorder.writeDrawBuffers(count, bufs);
}
@@ -609,6 +613,7 @@ public class DisplayListManager {
// Get relative transform (changes since glNewList, not absolute matrix state)
addAccumulatedDraw(tessellator, tessellator.getVertexFormat() != DefaultVertexFormat.POSITION);
}
transformCallback.clearBaked(tessellator);
tessellator.reset();
}
@@ -620,7 +625,7 @@ public class DisplayListManager {
pendingTransformOps.add(String.format("glTranslatef(%.4f, %.4f, %.4f)", x, y, z));
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -633,7 +638,7 @@ public class DisplayListManager {
pendingTransformOps.add(String.format("glScalef(%.4f, %.4f, %.4f)", x, y, z));
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -651,7 +656,7 @@ public class DisplayListManager {
pendingTransformOps.add(String.format("glRotatef(%.4f, %.4f, %.4f, %.4f)", Math.toDegrees(rad), x, y, z));
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -673,7 +678,7 @@ public class DisplayListManager {
pendingTransformOps.add("glMultMatrixf(...)");
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -691,7 +696,7 @@ public class DisplayListManager {
pendingTransformOps.add(String.format("glOrtho(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -706,7 +711,7 @@ public class DisplayListManager {
pendingTransformOps.add(String.format("glFrustum(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
}
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
flushMatrix();
}
}
@@ -777,10 +782,10 @@ public class DisplayListManager {
currentRecorder = new CommandRecorder(); // Create command recorder
accumulatedDraws = new ArrayList<>(8); // Fewer draws than commands typically
transformCallback = new DisplayListCallback();
compilationStackTrace = LOG_DISPLAY_LIST_COMPILATION ? Thread.currentThread().getStackTrace() : null;
compilationStackTrace = SystemProperties.LOG_DISPLAY_LIST_COMPILATION ? Thread.currentThread().getStackTrace() : null;
// Initialize debug logging fields (only when logging enabled)
if (LOG_DISPLAY_LIST_COMPILATION) {
if (SystemProperties.LOG_DISPLAY_LIST_COMPILATION) {
pendingTransformOps = new ArrayList<>();
multMatrixSources = new ArrayList<>();
drawRangeSources = new ArrayList<>();
@@ -798,11 +803,14 @@ public class DisplayListManager {
*/
public static void glEndList() {
if (glListMode == 0) {
throw new RuntimeException("glEndList called outside of a display list!");
GLStateManager.warnOnce("endlist-outside", "glEndList called outside of a display list!");
return;
}
if (Tracy.ENABLED) Tracy.beginZone(Z_GL_LIST_COMPILE);
try {
finishCurrentList();
} finally {
if (Tracy.ENABLED) Tracy.endZone();
// Non-null here means finishCurrentList threw before transferring or releasing
// ownership of the recorder.
if (currentRecorder != null) {
@@ -873,13 +881,18 @@ public class DisplayListManager {
// Empty display list - per OpenGL spec, still valid after glNewList/glEndList
compiled = CompiledDisplayList.EMPTY;
}
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
previous.delete();
final boolean locked = GLStateManager.acquireDrawLock();
try {
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
previous.delete();
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
// Log compilation details if enabled (before context restoration changes glListId)
if (LOG_DISPLAY_LIST_COMPILATION) {
if (SystemProperties.LOG_DISPLAY_LIST_COMPILATION) {
logCompiledDisplayList(glListId, compiled, compilationStackTrace);
}
}
@@ -906,17 +919,56 @@ public class DisplayListManager {
// Parent's COMPILING callback is now active again
} else {
// Not nested - clear all compilation state
currentRecorder = null;
recordingThread = null;
accumulatedDraws = null;
pendingDraw = null;
transformCallback = null;
compilationStackTrace = null;
pendingTransformOps = null;
multMatrixSources = null;
drawRangeSources = null;
glListId = -1;
glListMode = 0;
resetCompilationState();
}
}
/** Clear all root-level compilation state back to "not recording". */
private static void resetCompilationState() {
currentRecorder = null;
recordingThread = null;
accumulatedDraws = null;
pendingDraw = null;
transformCallback = null;
compilationStackTrace = null;
pendingTransformOps = null;
multMatrixSources = null;
drawRangeSources = null;
glListId = -1;
glListMode = 0;
}
public static void abortCompilation() {
if (glListMode == 0 && compilationStack.isEmpty()) {
return;
}
final int levels = compilationStack.size() + (glListMode != 0 ? 1 : 0);
for (int i = 0; i < levels; i++) {
try {
TessellatorManager.stopCapturingDirect();
} catch (IllegalStateException ignored) {
break;
}
}
if (currentRecorder != null) {
currentRecorder.delete();
}
for (CompilationContext ctx : compilationStack) {
if (ctx.recorder() != null) {
ctx.recorder().delete();
}
}
compilationStack.clear();
resetCompilationState();
}
public static void abortIfLeaked() {
if (glListMode != 0 || !compilationStack.isEmpty()) {
GLStateManager.warnOnce("frame-leak", "Display list {} left open across a frame boundary - aborting to recover", glListId);
abortCompilation();
}
}
@@ -941,7 +993,7 @@ public class DisplayListManager {
tessellator, copyLast
);
accumulatedDraws.add(pendingDraw);
if (DEBUG_DISPLAY_LISTS) {
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
drawBarrier();
}
}
@@ -969,6 +1021,20 @@ public class DisplayListManager {
}
private static void executeDisplayList(int list) {
if (Tracy.ENABLED) {
listPlaybacks++;
Tracy.beginZone(Z_GL_LIST_PLAYBACK);
}
final boolean locked = GLStateManager.acquireDrawLock();
try {
executeDisplayListInner(list);
} finally {
if (locked) GLStateManager.releaseDrawLock();
if (Tracy.ENABLED) Tracy.endZone();
}
}
private static void executeDisplayListInner(int list) {
final CompiledDisplayList compiled = displayListCache.get(list);
if (compiled != null) {
compiled.render(list);
@@ -991,11 +1057,16 @@ public class DisplayListManager {
* Delete display lists and free their VBO resources.
*/
public static void glDeleteLists(int list, int range) {
for (int i = list; i < list + range; i++) {
CompiledDisplayList compiled = displayListCache.remove(i);
if (compiled != null) {
compiled.delete();
final boolean locked = GLStateManager.acquireDrawLock();
try {
for (int i = list; i < list + range; i++) {
CompiledDisplayList compiled = displayListCache.remove(i);
if (compiled != null) {
compiled.delete();
}
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
}
@@ -1252,6 +1323,16 @@ public class DisplayListManager {
private final Matrix4f transformMatrix = new Matrix4f();
private DirectTessellator bakedOwner;
private int bakedVertices;
void clearBaked(DirectTessellator tessellator) {
if (bakedOwner == tessellator) {
bakedOwner = null;
bakedVertices = 0;
}
}
public void scale(float x, float y, float z) {
transformMatrix.scale(x, y, z);
markDirty();
@@ -1314,10 +1395,26 @@ public class DisplayListManager {
return MatrixHelper.isIdentity(transformMatrix);
}
@Override
public boolean onStartDrawing(CallbackTessellator tessellator, int drawMode) {
bakedOwner = tessellator;
bakedVertices = 0;
return super.onStartDrawing(tessellator, drawMode);
}
@Override
public boolean onDraw(CallbackTessellator tessellator) {
if (!tessellator.isEmpty()) {
if (!isIdentity()) {
final int baked = bakedOwner == tessellator ? bakedVertices : 0;
if (bakedVertices == 0) {
flushMatrix();
} else if (baked != tessellator.vertexCount) {
GLStateManager.warnOnce("dl-mixed-baked-raw", "Display list {}: draw of {} vertices carries {} transformed ones - the rest lose the pending transform", glListId, tessellator.vertexCount, baked);
}
}
addAccumulatedDraw(tessellator, false);
clearBaked(tessellator);
}
return true;
}
@@ -1326,6 +1423,11 @@ public class DisplayListManager {
public void onVertex(CallbackTessellator tessellator, double x, double y, double z) {
tessellator.syncVanillaTessellator();
super.onVertex(tessellator, x, y, z);
if (bakedOwner != tessellator) {
bakedOwner = tessellator;
bakedVertices = 0;
}
bakedVertices++;
}
@Override
@@ -76,29 +76,29 @@ public class Feature {
*/
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
collection.add(GLStateManager.textures.getTexGenSStates(i));
collection.add(GLStateManager.textures.getTexGenTStates(i));
collection.add(GLStateManager.textures.getTexGenRStates(i));
collection.add(GLStateManager.textures.getTexGenQStates(i));
collection.add(GLStateManager.getTextures().getTexGenSStates(i));
collection.add(GLStateManager.getTextures().getTexGenTStates(i));
collection.add(GLStateManager.getTextures().getTexGenRStates(i));
collection.add(GLStateManager.getTextures().getTexGenQStates(i));
}
}
static {
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST enable bit
, GLStateManager.alphaState // Alpha test function and reference value
, GLStateManager.blendMode // GL_BLEND enable bit
, GLStateManager.blendState // Blending source/destination functions, equation, blend color
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP enable bit
, GLStateManager.ditherState // GL_DITHER enable bit
, GLStateManager.drawBuffer // GL_DRAW_BUFFER setting
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP enable bit
, GLStateManager.logicOpMode // Logic op function
, GLStateManager.colorMask // Color-mode and index-mode writemasks
, GLStateManager.clearColor // Color-mode and index-mode clear values
GLStateManager.getAlphaTest()
, GLStateManager.getAlphaState()
, GLStateManager.getBlendMode()
, GLStateManager.getBlendState()
, GLStateManager.getColorLogicOpState()
, GLStateManager.getDitherState()
, GLStateManager.getDrawBuffer()
, GLStateManager.getIndexLogicOpState()
, GLStateManager.getLogicOpMode()
, GLStateManager.getColorMask()
, GLStateManager.getClearColor()
));
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
GLStateManager.color // Current RGBA color
GLStateManager.getColor()
, ShaderManager.getNormalStack() // Current normal vector
, ShaderManager.getTexCoordStack() // Current texture coordinates
// Current color index
@@ -110,78 +110,78 @@ public class Feature {
// GL_EDGE_FLAG flag
));
attribToFeatures.put(GL11.GL_DEPTH_BUFFER_BIT, ImmutableSet.of(
GLStateManager.depthTest // GL_DEPTH_TEST enable bit
, GLStateManager.depthState // Depth buffer test function
GLStateManager.getDepthTest()
, GLStateManager.getDepthState()
// Depth buffer clear value
// GL_DEPTH_WRITEMASK enable bit
));
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST flag
, GLStateManager.autoNormalState // GL_AUTO_NORMAL flag
, GLStateManager.blendMode // GL_BLEND flag
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP flag
, GLStateManager.colorMaterial // GL_COLOR_MATERIAL
, GLStateManager.cullState // GL_CULL_FACE flag
, GLStateManager.depthTest // GL_DEPTH_TEST flag
, GLStateManager.ditherState // GL_DITHER flag
, GLStateManager.fogMode // GL_FOG flag
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP flag
, GLStateManager.lightStates[0] // GL_LIGHT0
, GLStateManager.lightStates[1] // GL_LIGHT1
, GLStateManager.lightStates[2] // GL_LIGHT2
, GLStateManager.lightStates[3] // GL_LIGHT3
, GLStateManager.lightStates[4] // GL_LIGHT4
, GLStateManager.lightStates[5] // GL_LIGHT5
, GLStateManager.lightStates[6] // GL_LIGHT6
, GLStateManager.lightStates[7] // GL_LIGHT7
, GLStateManager.lightingState // GL_LIGHTING flag
, GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
, GLStateManager.lineStippleState // GL_LINE_STIPPLE flag
, GLStateManager.map1Color4State // GL_MAP1_COLOR_4
, GLStateManager.map1IndexState // GL_MAP1_INDEX
, GLStateManager.map1NormalState // GL_MAP1_NORMAL
, GLStateManager.map1TextureCoord1State // GL_MAP1_TEXTURE_COORD_1
, GLStateManager.map1TextureCoord2State // GL_MAP1_TEXTURE_COORD_2
, GLStateManager.map1TextureCoord3State // GL_MAP1_TEXTURE_COORD_3
, GLStateManager.map1TextureCoord4State // GL_MAP1_TEXTURE_COORD_4
, GLStateManager.map1Vertex3State // GL_MAP1_VERTEX_3
, GLStateManager.map1Vertex4State // GL_MAP1_VERTEX_4
, GLStateManager.map2Color4State // GL_MAP2_COLOR_4
, GLStateManager.map2IndexState // GL_MAP2_INDEX
, GLStateManager.map2NormalState // GL_MAP2_NORMAL
, GLStateManager.map2TextureCoord1State // GL_MAP2_TEXTURE_COORD_1
, GLStateManager.map2TextureCoord2State // GL_MAP2_TEXTURE_COORD_2
, GLStateManager.map2TextureCoord3State // GL_MAP2_TEXTURE_COORD_3
, GLStateManager.map2TextureCoord4State // GL_MAP2_TEXTURE_COORD_4
, GLStateManager.map2Vertex3State // GL_MAP2_VERTEX_3
, GLStateManager.map2Vertex4State // GL_MAP2_VERTEX_4
, GLStateManager.multisampleState // GL_MULTISAMPLE flag
, GLStateManager.normalizeState // GL_NORMALIZE flag
, GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
, GLStateManager.sampleAlphaToCoverageState // GL_SAMPLE_ALPHA_TO_COVERAGE flag
, GLStateManager.sampleAlphaToOneState // GL_SAMPLE_ALPHA_TO_ONE flag
, GLStateManager.sampleCoverageState // GL_SAMPLE_COVERAGE flag
, GLStateManager.scissorTest // GL_SCISSOR_TEST flag
, GLStateManager.stencilTest // GL_STENCIL_TEST flag
GLStateManager.getAlphaTest()
, GLStateManager.getAutoNormalState()
, GLStateManager.getBlendMode()
, GLStateManager.getColorLogicOpState()
, GLStateManager.getColorMaterial()
, GLStateManager.getCullState()
, GLStateManager.getDepthTest()
, GLStateManager.getDitherState()
, GLStateManager.getFogMode()
, GLStateManager.getIndexLogicOpState()
, GLStateManager.getLightStates()[0] // GL_LIGHT0
, GLStateManager.getLightStates()[1] // GL_LIGHT1
, GLStateManager.getLightStates()[2] // GL_LIGHT2
, GLStateManager.getLightStates()[3] // GL_LIGHT3
, GLStateManager.getLightStates()[4] // GL_LIGHT4
, GLStateManager.getLightStates()[5] // GL_LIGHT5
, GLStateManager.getLightStates()[6] // GL_LIGHT6
, GLStateManager.getLightStates()[7] // GL_LIGHT7
, GLStateManager.getLightingState()
, GLStateManager.getLineSmoothState()
, GLStateManager.getLineStippleState()
, 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()
, GLStateManager.getMultisampleState()
, GLStateManager.getNormalizeState()
, GLStateManager.getPointSmoothState()
, GLStateManager.getPolygonOffsetPointState()
, GLStateManager.getPolygonOffsetLineState()
, GLStateManager.getPolygonOffsetFillState()
, GLStateManager.getPolygonSmoothState()
, GLStateManager.getPolygonStippleState()
, GLStateManager.getRescaleNormalState()
, GLStateManager.getSampleAlphaToCoverageState()
, GLStateManager.getSampleAlphaToOneState()
, GLStateManager.getSampleCoverageState()
, GLStateManager.getScissorTest()
, GLStateManager.getStencilTest()
));
// Enable bits for the user-definable clipping planes
for(int i = 0; i < GLStateManager.clipPlaneStates.length; i++) {
enableBits.add(GLStateManager.clipPlaneStates[i]);
for(int i = 0; i < GLStateManager.getClipPlaneStates().length; i++) {
enableBits.add(GLStateManager.getClipPlaneStates()[i]);
}
// GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D flags
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
enableBits.add(GLStateManager.textures.getTexture1DStates(i));
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
enableBits.add(GLStateManager.textures.getTexture3DStates(i));
enableBits.add(GLStateManager.getTextures().getTexture1DStates(i));
enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i));
enableBits.add(GLStateManager.getTextures().getTexture3DStates(i));
}
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
@@ -196,8 +196,8 @@ public class Feature {
// GL_AUTO_NORMAL enable bit
));
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
GLStateManager.fogMode // GL_FOG enable bit
, GLStateManager.fogState // Fog color
GLStateManager.getFogMode()
, GLStateManager.getFogState()
// ^^ Fog density
// ^^ Linear fog start
// ^^ Linear fog end
@@ -214,42 +214,42 @@ public class Feature {
// GL_TEXTURE_COMPRESSION_HINT setting
));
attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of(
GLStateManager.colorMaterial // GL_COLOR_MATERIAL enable bit
, GLStateManager.colorMaterialFace // GL_COLOR_MATERIAL_FACE value
, GLStateManager.colorMaterialParameter // Color material parameters that are tracking the current color
, GLStateManager.lightModel // Ambient scene color, GL_LIGHT_MODEL_LOCAL_VIEWER, GL_LIGHT_MODEL_TWO_SIDE
, GLStateManager.lightingState // GL_LIGHTING enable bit
GLStateManager.getColorMaterial()
, GLStateManager.getColorMaterialFace()
, GLStateManager.getColorMaterialParameter()
, GLStateManager.getLightModel()
, GLStateManager.getLightingState()
// Enable bit for each light
, GLStateManager.lightStates[0] // GL_LIGHT0
, GLStateManager.lightStates[1] // GL_LIGHT1
, GLStateManager.lightStates[2] // GL_LIGHT2
, GLStateManager.lightStates[3] // GL_LIGHT3
, GLStateManager.lightStates[4] // GL_LIGHT4
, GLStateManager.lightStates[5] // GL_LIGHT5
, GLStateManager.lightStates[6] // GL_LIGHT6
, GLStateManager.lightStates[7] // GL_LIGHT7
, GLStateManager.getLightStates()[0] // GL_LIGHT0
, GLStateManager.getLightStates()[1] // GL_LIGHT1
, GLStateManager.getLightStates()[2] // GL_LIGHT2
, GLStateManager.getLightStates()[3] // GL_LIGHT3
, GLStateManager.getLightStates()[4] // GL_LIGHT4
, GLStateManager.getLightStates()[5] // GL_LIGHT5
, GLStateManager.getLightStates()[6] // GL_LIGHT6
, GLStateManager.getLightStates()[7] // GL_LIGHT7
// Ambient, diffuse, and specular intensity for each light
// Direction, position, exponent, and cutoff angle for each light
// Constant, linear, and quadratic attenuation factors for each light
, GLStateManager.lightDataStates[0]
, GLStateManager.lightDataStates[1]
, GLStateManager.lightDataStates[2]
, GLStateManager.lightDataStates[3]
, GLStateManager.lightDataStates[4]
, GLStateManager.lightDataStates[5]
, GLStateManager.lightDataStates[6]
, GLStateManager.lightDataStates[7]
, GLStateManager.getLightDataStates()[0]
, GLStateManager.getLightDataStates()[1]
, GLStateManager.getLightDataStates()[2]
, GLStateManager.getLightDataStates()[3]
, GLStateManager.getLightDataStates()[4]
, GLStateManager.getLightDataStates()[5]
, GLStateManager.getLightDataStates()[6]
, GLStateManager.getLightDataStates()[7]
// Ambient, diffuse, specular, and emissive color for each material
// Ambient, diffuse, and specular color indices for each material
// Specular exponent for each material
, GLStateManager.frontMaterial
, GLStateManager.backMaterial
, GLStateManager.shadeModelState // GL_SHADE_MODEL setting
, GLStateManager.getFrontMaterial()
, GLStateManager.getBackMaterial()
, GLStateManager.getShadeModelState()
));
attribToFeatures.put(GL11.GL_LINE_BIT, ImmutableSet.of(
GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
, GLStateManager.lineStippleState // GL_LINE_STIPPLE enable bit
, GLStateManager.lineState // Line stipple pattern, repeat counter, and width
GLStateManager.getLineSmoothState()
, GLStateManager.getLineStippleState()
, GLStateManager.getLineState()
));
attribToFeatures.put(GL11.GL_LIST_BIT, ImmutableSet.of(
// GL_LIST_BASE setting
@@ -274,31 +274,31 @@ public class Feature {
// GL_READ_BUFFER setting
));
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
, GLStateManager.pointState // Point size
GLStateManager.getPointSmoothState()
, GLStateManager.getPointState()
));
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
GLStateManager.cullState // GL_CULL_FACE enable bit
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE enable bit
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
, GLStateManager.polygonState // GL_CULL_FACE_MODE, GL_FRONT_FACE, GL_POLYGON_MODE, GL_POLYGON_OFFSET_FACTOR/UNITS
GLStateManager.getCullState()
, GLStateManager.getPolygonSmoothState()
, GLStateManager.getPolygonStippleState()
, GLStateManager.getPolygonOffsetFillState()
, GLStateManager.getPolygonOffsetLineState()
, GLStateManager.getPolygonOffsetPointState()
, GLStateManager.getPolygonState()
));
attribToFeatures.put(GL11.GL_POLYGON_STIPPLE_BIT, ImmutableSet.of(
// Polygon stipple pattern
));
attribToFeatures.put(GL11.GL_SCISSOR_BIT, ImmutableSet.of(
GLStateManager.scissorTest // GL_SCISSOR_TEST enable bit
GLStateManager.getScissorTest()
// Scissor box
));
attribToFeatures.put(GL11.GL_STENCIL_BUFFER_BIT, ImmutableSet.of(
GLStateManager.stencilTest // GL_STENCIL_TEST enable bit
, GLStateManager.stencilState // Stencil function, ref, mask, ops, writemask, clear value
GLStateManager.getStencilTest()
, GLStateManager.getStencilState()
));
final Set<IStateStack<?>> textureAttribs = new HashSet<>(ImmutableSet.of(
GLStateManager.activeTextureUnit // Active texture unit
GLStateManager.getActiveTextureUnitStack()
// GL_TEXTURE_ENV_MODE + GL_TEXTURE_ENV_COLOR — now per-unit in TexEnvState (added below)
// Enable bits for the four texture coordinates
@@ -315,8 +315,8 @@ public class Feature {
// Current Texture Bindings - GL_TEXTURE_BINDING_2D + per-unit TexEnvState
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
textureAttribs.add(GLStateManager.textures.getTextureUnitBindings(i));
textureAttribs.add(GLStateManager.textures.getTexEnvState(i));
textureAttribs.add(GLStateManager.getTextures().getTextureUnitBindings(i));
textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i));
}
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
@@ -326,14 +326,14 @@ public class Feature {
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
// Coefficients of the six clipping planes
// Enable bits for the user-definable clipping planes
GLStateManager.matrixMode
, GLStateManager.normalizeState // GL_NORMALIZE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
GLStateManager.getMatrixMode()
, GLStateManager.getNormalizeState()
, GLStateManager.getRescaleNormalState()
));
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
// Depth range (near and far)
GLStateManager.viewportState
GLStateManager.getViewportState()
));
}
@@ -151,7 +151,7 @@ public class FeedbackManager {
computeMVP();
final int stride = positionStride();
final int elementSize = VAOManager.Attrib.glTypeSizeBytes(type);
final int elementSize = GLTypes.sizeBytes(type);
final int indexByteCount = indexCount * elementSize;
ensureIndexReadbackCapacity(indexByteCount);
indexReadbackBuf.clear().limit(indexByteCount);
@@ -0,0 +1,203 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
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.DepthStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.LightModelStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.LightStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.LineStateStack;
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.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
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.TextureUnitArray;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntStack;
import org.joml.Matrix4fStack;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
class GLContextState {
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
private BooleanStateStack track(BooleanStateStack s) {
allBooleanStates.add(s);
return s;
}
public int programGeneration;
public int drawFramebufferGeneration;
public boolean poppingAttributes;
// modelview matrix changes (any: translation, rotation, scale)
public int mvGeneration;
public int mvLinearGeneration;
// projection matrix changes
public int projGeneration;
// texture matrix changes
public int texMatrixGeneration;
public int lightingGeneration;
// fog + alpha ref + overlay color
public int fragmentGeneration;
// current vertex color
public int colorGeneration;
// clip plane equation changes
public int clipPlaneGeneration;
public float overlayR = 0.0f;
public float overlayG = 0.0f;
public float overlayB = 0.0f;
public float overlayA = 0.0f;
public float shaderColorR = 1.0f;
public float shaderColorG = 1.0f;
public float shaderColorB = 1.0f;
public float shaderColorA = 1.0f;
// Deferred vertex attribute upload flags - set when state changes, flushed before draw
public boolean dirtyColorAttrib = true;
public boolean dirtyNormalAttrib;
public boolean dirtyTexCoordAttrib;
public boolean dirtyLightmapAttrib = true;
public boolean unit23TexCoordSetDuringDraw = false;
public int maxBoundTextureUnit = 0;
public int maxBoundImageUnit = 0;
public final IntStack attribs = new IntArrayList(GLStateManager.MAX_ATTRIB_STACK_DEPTH);
public int attribDepth = 0;
@SuppressWarnings("unchecked")
public final List<IStateStack<?>>[] modifiedAtDepth = new List[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
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];
public final int[] savedTexMatGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
public final int[] savedLightingGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
public final int[] savedFragmentGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
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 int clientActiveTextureUnit = 0;
public final IntegerStateStack shadeModelState = new IntegerStateStack(GL11.GL_SMOOTH);
public final TextureUnitArray textures = new TextureUnitArray();
public final ImageUnitArray imageUnits = new ImageUnitArray();
public final BlendStateStack blendState = new BlendStateStack();
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 BooleanStateStack depthTest = track(new BooleanStateStack(GL11.GL_DEPTH_TEST));
public final FogStateStack fogState = new FogStateStack();
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
public final Color4Stack color = new Color4Stack();
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 BooleanStateStack cullState = track(new BooleanStateStack(GL11.GL_CULL_FACE));
public final AlphaStateStack alphaState = new AlphaStateStack();
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));
public final BooleanStateStack normalizeState = track(new BooleanStateStack(GL11.GL_NORMALIZE, false, true));
public final BooleanStateStack ditherState = track(new BooleanStateStack(GL11.GL_DITHER, true));
public final BooleanStateStack stencilTest = track(new BooleanStateStack(GL11.GL_STENCIL_TEST));
public final BooleanStateStack lineSmoothState = track(new BooleanStateStack(GL11.GL_LINE_SMOOTH));
public final BooleanStateStack lineStippleState = track(new BooleanStateStack(GL11.GL_LINE_STIPPLE, false, true));
public final BooleanStateStack pointSmoothState = track(new BooleanStateStack(GL11.GL_POINT_SMOOTH, false, true));
public final BooleanStateStack polygonSmoothState = track(new BooleanStateStack(GL11.GL_POLYGON_SMOOTH));
public final BooleanStateStack polygonStippleState = track(new BooleanStateStack(GL11.GL_POLYGON_STIPPLE, false, true));
public final BooleanStateStack multisampleState = track(new BooleanStateStack(GL13.GL_MULTISAMPLE, true));
public final BooleanStateStack sampleAlphaToCoverageState = track(new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE));
public final BooleanStateStack sampleAlphaToOneState = track(new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_ONE));
public final BooleanStateStack sampleCoverageState = track(new BooleanStateStack(GL13.GL_SAMPLE_COVERAGE));
public final BooleanStateStack colorLogicOpState = track(new BooleanStateStack(GL11.GL_COLOR_LOGIC_OP));
public final BooleanStateStack indexLogicOpState = track(new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP, false, true));
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 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));
public final BooleanStateStack map1IndexState = track(new BooleanStateStack(GL11.GL_MAP1_INDEX, false, true));
public final BooleanStateStack map1NormalState = track(new BooleanStateStack(GL11.GL_MAP1_NORMAL, false, true));
public final BooleanStateStack map1TextureCoord1State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1, false, true));
public final BooleanStateStack map1TextureCoord2State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2, false, true));
public final BooleanStateStack map1TextureCoord3State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3, false, true));
public final BooleanStateStack map1TextureCoord4State = track(new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4, false, true));
public final BooleanStateStack map1Vertex3State = track(new BooleanStateStack(GL11.GL_MAP1_VERTEX_3, false, true));
public final BooleanStateStack map1Vertex4State = track(new BooleanStateStack(GL11.GL_MAP1_VERTEX_4, false, true));
public final BooleanStateStack map2Color4State = track(new BooleanStateStack(GL11.GL_MAP2_COLOR_4, false, true));
public final BooleanStateStack map2IndexState = track(new BooleanStateStack(GL11.GL_MAP2_INDEX, false, true));
public final BooleanStateStack map2NormalState = track(new BooleanStateStack(GL11.GL_MAP2_NORMAL, false, true));
public final BooleanStateStack map2TextureCoord1State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1, false, true));
public final BooleanStateStack map2TextureCoord2State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2, false, true));
public final BooleanStateStack map2TextureCoord3State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3, false, true));
public final BooleanStateStack map2TextureCoord4State = track(new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4, false, true));
public final BooleanStateStack map2Vertex3State = track(new BooleanStateStack(GL11.GL_MAP2_VERTEX_3, false, true));
public final BooleanStateStack map2Vertex4State = track(new BooleanStateStack(GL11.GL_MAP2_VERTEX_4, false, true));
public final BooleanStateStack[] clipPlaneStates = new BooleanStateStack[GLStateManager.MAX_CLIP_PLANES];
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 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 int activeProgram = 0;
public int listBase = 0;
public int boundVBO;
public int boundVAO;
public int boundPixelUnpackBuffer;
public int boundPixelPackBuffer;
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
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;
public int drawFramebuffer = 0;
public int readFramebuffer = 0;
public int texGenGeneration;
GLContextState() {
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) modifiedAtDepth[i] = new ArrayList<>();
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));
}
}
void init() {
for (int i = 0; i < lightDataStates.length; i++) {
lightDataStates[i] = new LightStateStack(GL11.GL_LIGHT0 + i);
}
}
}
@@ -6,6 +6,8 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import org.lwjgl.opengl.EXTBlendColor;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
@@ -14,6 +16,10 @@ import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL43;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public final class GLDebug {
/**
@@ -252,25 +258,6 @@ public final class GLDebug {
};
}
public static String getDataTypeName(int type) {
return switch (type) {
case GL11.GL_UNSIGNED_BYTE -> "UNSIGNED_BYTE";
case GL11.GL_BYTE -> "BYTE";
case GL11.GL_UNSIGNED_SHORT -> "UNSIGNED_SHORT";
case GL11.GL_SHORT -> "SHORT";
case GL11.GL_UNSIGNED_INT -> "UNSIGNED_INT";
case GL11.GL_INT -> "INT";
case GL11.GL_FLOAT -> "FLOAT";
case GL11.GL_DOUBLE -> "DOUBLE";
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> "UNSIGNED_BYTE_3_3_2";
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> "UNSIGNED_SHORT_4_4_4_4";
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> "UNSIGNED_SHORT_5_5_5_1";
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> "UNSIGNED_INT_8_8_8_8";
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> "UNSIGNED_INT_10_10_10_2";
default -> String.format("0x%X", type);
};
}
public static String getLightName(int light) {
return switch (light) {
case GL11.GL_LIGHT0 -> "LIGHT0";
@@ -559,7 +546,9 @@ public final class GLDebug {
}
public static void initDebugState() {
if (BackendManager.RENDER_BACKEND.supportsDebugOutput()) {
final GLSMInitConfig cfg = GLStateManager.getInitConfig();
final boolean userRequested = cfg != null && (cfg.isLwjglDebug() || CaptureGate.enabledAtStartup());
if (userRequested && BackendManager.RENDER_BACKEND.supportsDebugOutput()) {
debugState = new KHRDebugState();
} else {
debugState = new UnsupportedDebugState();
@@ -572,6 +561,19 @@ public final class GLDebug {
}
}
/**
* Overload that lets you gate the String concat behind the debugState
*/
public static void pushGroup(String prefix, int value) {
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
debugState.pushGroup(prefix + value);
}
}
public static boolean isActive() {
return debugState instanceof KHRDebugState && CaptureGate.markersThisFrame && Thread.currentThread() == GLStateManager.getMainThread();
}
public static void pushGroup(String group) {
if (debugState != null && Thread.currentThread() == GLStateManager.getMainThread()) {
debugState.pushGroup(group);
@@ -597,4 +599,49 @@ public final class GLDebug {
return "";
}
private static final Scope NOOP_SCOPE = () -> {};
private static final Scope ACTIVE_SCOPE = GLDebug::popGroup;
public static Scope scope(String name) {
if (isActive()) {
debugState.pushGroup(name);
return ACTIVE_SCOPE;
}
return NOOP_SCOPE;
}
@FunctionalInterface
public interface Scope extends AutoCloseable {
@Override void close();
}
private static MethodHandle khrCallbackInvoke;
private static boolean khrCallbackInvokeResolved;
/// Use reflection because of lwjgl3ify
static GLDebugMessageListener adaptDebugCallback(Object callback) {
if (callback == null) return null;
final MethodHandle invoke = resolveKhrCallbackInvoke(callback.getClass());
if (invoke == null) return null;
return (source, type, id, severity, length, message, userParam) -> {
try {
invoke.invoke(callback, source, type, id, severity, length, message, userParam);
} catch (Throwable t) {
GLStateManager.LOGGER.warn("Debug message callback threw", t);
}
};
}
private static synchronized MethodHandle resolveKhrCallbackInvoke(Class<?> callbackClass) {
if (!khrCallbackInvokeResolved) {
khrCallbackInvokeResolved = true;
try {
khrCallbackInvoke = MethodHandles.publicLookup().findVirtual(callbackClass, "invoke", MethodType.methodType(void.class, int.class, int.class, int.class, int.class, int.class, long.class, long.class));
} catch (ReflectiveOperationException e) {
GLStateManager.LOGGER.warn("{} has no public invoke(int,int,int,int,int,long,long); debug messages registered through it will not be delivered.", callbackClass.getName());
}
}
return khrCallbackInvoke;
}
}
@@ -0,0 +1,34 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL32;
public final class GLESCaps {
private GLESCaps() {}
public static boolean isCapAllowed(int cap, boolean isGLES, boolean hasClipCullDistance) {
if (!isGLES) return true;
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + GLStateManager.MAX_CLIP_PLANES) {
return hasClipCullDistance;
}
return switch (cap) {
case GL11.GL_LINE_SMOOTH,
GL11.GL_POLYGON_SMOOTH,
GL11.GL_COLOR_LOGIC_OP,
GL11.GL_POLYGON_OFFSET_POINT,
GL11.GL_POLYGON_OFFSET_LINE,
GL13.GL_MULTISAMPLE,
GL13.GL_SAMPLE_ALPHA_TO_ONE,
GL20.GL_VERTEX_PROGRAM_POINT_SIZE,
GL30.GL_FRAMEBUFFER_SRGB,
GL31.GL_PRIMITIVE_RESTART,
GL32.GL_DEPTH_CLAMP -> false;
default -> true;
};
}
}
@@ -0,0 +1,86 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
public final class GLESFormatRemap {
private GLESFormatRemap() {}
public record Result(int internalFormat, int format, int type) {}
public static Result apply(int internalformat, int format, int type, boolean isGLES) {
internalformat = promoteAlphaFormat(internalformat);
if (isGLES) {
internalformat = remapInternalFormat(internalformat);
if (type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV && (format == GL12.GL_BGRA || format == GL11.GL_RGBA)) {
type = GL11.GL_UNSIGNED_BYTE;
}
if (isGenericPixelType(type)) {
type = typeForInternalFormatES32(internalformat, type);
}
}
return new Result(internalformat, format, type);
}
public static int promoteAlphaFormat(int internalformat) {
return switch (internalformat) {
case GL11.GL_ALPHA4 -> GL11.GL_RGBA4;
case GL11.GL_ALPHA8 -> GL11.GL_RGBA8;
case GL11.GL_ALPHA12 -> GL11.GL_RGBA12;
case GL11.GL_ALPHA16 -> GL11.GL_RGBA16;
default -> internalformat;
};
}
public static int remapInternalFormat(int internalformat) {
return switch (internalformat) {
case GL11.GL_RGB16, GL11.GL_RGB12 -> GL30.GL_RGB16F;
case GL11.GL_RGBA16, GL11.GL_RGBA12 -> GL30.GL_RGBA16F;
case GL30.GL_R16 -> GL30.GL_R16F;
case GL30.GL_RG16 -> GL30.GL_RG16F;
case GL11.GL_RGB10, GL11.GL_R3_G3_B2 -> GL11.GL_RGB8;
default -> internalformat;
};
}
public static int remapPixelType(int format, int type) {
if (type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV && (format == GL12.GL_BGRA || format == GL11.GL_RGBA)) {
return GL11.GL_UNSIGNED_BYTE;
}
return type;
}
public static int typeForInternalFormatES32(int internalformat, int type) {
return switch (internalformat) {
case GL30.GL_R16F, GL30.GL_RG16F, GL30.GL_RGB16F, GL30.GL_RGBA16F -> GL30.GL_HALF_FLOAT;
case GL30.GL_R32F, GL30.GL_RG32F, GL30.GL_RGB32F, GL30.GL_RGBA32F -> GL11.GL_FLOAT;
case GL31.GL_R8_SNORM, GL31.GL_RG8_SNORM, GL31.GL_RGB8_SNORM, GL31.GL_RGBA8_SNORM -> GL11.GL_BYTE;
case GL30.GL_R11F_G11F_B10F, GL30.GL_RGB9_E5 -> GL30.GL_HALF_FLOAT;
case GL11.GL_RGB10_A2 -> GL12.GL_UNSIGNED_INT_2_10_10_10_REV;
case GL30.GL_R8I, GL30.GL_RG8I, GL30.GL_RGB8I, GL30.GL_RGBA8I,
GL30.GL_R16I, GL30.GL_RG16I, GL30.GL_RGB16I, GL30.GL_RGBA16I,
GL30.GL_R32I, GL30.GL_RG32I, GL30.GL_RGB32I, GL30.GL_RGBA32I -> GL11.GL_INT;
case GL30.GL_R8UI, GL30.GL_RG8UI, GL30.GL_RGB8UI, GL30.GL_RGBA8UI,
GL30.GL_R16UI, GL30.GL_RG16UI, GL30.GL_RGB16UI, GL30.GL_RGBA16UI,
GL30.GL_R32UI, GL30.GL_RG32UI, GL30.GL_RGB32UI, GL30.GL_RGBA32UI -> GL11.GL_UNSIGNED_INT;
case GL14.GL_DEPTH_COMPONENT24 -> GL11.GL_UNSIGNED_INT;
case GL30.GL_DEPTH_COMPONENT32F -> GL11.GL_FLOAT;
case GL30.GL_DEPTH24_STENCIL8 -> GL30.GL_UNSIGNED_INT_24_8;
case GL30.GL_DEPTH32F_STENCIL8 -> GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
default -> type;
};
}
public static boolean isGenericPixelType(int type) {
return type == GL11.GL_UNSIGNED_BYTE
|| type == GL11.GL_UNSIGNED_SHORT
|| type == GL11.GL_UNSIGNED_INT
|| type == GL11.GL_BYTE
|| type == GL11.GL_SHORT
|| type == GL11.GL_INT;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,57 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;
public final class GLTypes {
private GLTypes() {}
public static int sizeBytes(int glType) {
return switch (glType) {
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT, GL30.GL_HALF_FLOAT -> 2;
case GL11.GL_INT, GL11.GL_UNSIGNED_INT, GL11.GL_FLOAT -> 4;
case GL11.GL_DOUBLE -> 8;
default -> throw new IllegalArgumentException("Unknown GL type: 0x" + Integer.toHexString(glType));
};
}
public static int packedTexelBytes(int glType) {
return switch (glType) {
case GL12.GL_UNSIGNED_BYTE_3_3_2, GL12.GL_UNSIGNED_BYTE_2_3_3_REV -> 1;
case GL12.GL_UNSIGNED_SHORT_5_6_5, GL12.GL_UNSIGNED_SHORT_5_6_5_REV,
GL12.GL_UNSIGNED_SHORT_4_4_4_4, GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV,
GL12.GL_UNSIGNED_SHORT_5_5_5_1, GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV -> 2;
case GL12.GL_UNSIGNED_INT_8_8_8_8, GL12.GL_UNSIGNED_INT_8_8_8_8_REV,
GL12.GL_UNSIGNED_INT_10_10_10_2, GL12.GL_UNSIGNED_INT_2_10_10_10_REV,
GL30.GL_UNSIGNED_INT_24_8, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV,
GL30.GL_UNSIGNED_INT_5_9_9_9_REV -> 4;
case GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV -> 8;
default -> 0;
};
}
public static String name(int glType) {
return switch (glType) {
case GL11.GL_BYTE -> "BYTE";
case GL11.GL_UNSIGNED_BYTE -> "UNSIGNED_BYTE";
case GL11.GL_SHORT -> "SHORT";
case GL11.GL_UNSIGNED_SHORT -> "UNSIGNED_SHORT";
case GL11.GL_INT -> "INT";
case GL11.GL_UNSIGNED_INT -> "UNSIGNED_INT";
case GL11.GL_FLOAT -> "FLOAT";
case GL11.GL_DOUBLE -> "DOUBLE";
case GL30.GL_HALF_FLOAT -> "HALF_FLOAT";
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> "UNSIGNED_BYTE_3_3_2";
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> "UNSIGNED_SHORT_4_4_4_4";
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> "UNSIGNED_SHORT_5_5_5_1";
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> "UNSIGNED_INT_8_8_8_8";
case GL12.GL_UNSIGNED_INT_8_8_8_8_REV -> "UNSIGNED_INT_8_8_8_8_REV";
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> "UNSIGNED_INT_10_10_10_2";
case GL12.GL_UNSIGNED_INT_2_10_10_10_REV -> "UNSIGNED_INT_2_10_10_10_REV";
default -> String.format("0x%X", glType);
};
}
}
@@ -1,10 +1,21 @@
package com.gtnewhorizons.angelica.glsm;
import org.antlr.v4.runtime.BufferedTokenStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.WritableToken;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.taumc.glsl.grammar.GLSLLexer;
import org.taumc.glsl.grammar.GLSLParser;
import org.taumc.glsl.grammar.GLSLPreParser;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -79,24 +90,123 @@ public class GlslTransformUtils {
return source;
}
public static void restoreReservedWordsInTree(ParseTree tree) {
if (tree instanceof TerminalNode terminal) {
final Token token = terminal.getSymbol();
final String text = token.getText();
if (text != null && text.startsWith(RENAMED_PREFIX) && token instanceof WritableToken writable) {
final String rest = text.substring(RENAMED_PREFIX.length());
if (rest.startsWith("texture") || rest.startsWith("sample")) {
writable.setText(rest);
}
}
return;
}
for (int i = 0; i < tree.getChildCount(); i++) {
restoreReservedWordsInTree(tree.getChild(i));
}
}
public record QuietParse(GLSLParser.Translation_unitContext full, GLSLPreParser.Translation_unitContext pre) {}
public static GLSLParser.Translation_unitContext parseFullQuiet(String source) {
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
lexer.removeErrorListeners();
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.setBuildParseTree(true);
return parser.translation_unit();
}
public static GLSLPreParser.Translation_unitContext parsePreQuiet(String source) {
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
lexer.removeErrorListeners();
final GLSLPreParser preParser = new GLSLPreParser(new CommonTokenStream(lexer, GLSLLexer.DIRECTIVES));
preParser.removeErrorListeners();
preParser.setBuildParseTree(true);
return preParser.translation_unit();
}
public static QuietParse parseBothQuiet(String source) {
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
lexer.removeErrorListeners();
final GLSLPreParser preParser = new GLSLPreParser(new BufferedTokenStream(lexer));
preParser.removeErrorListeners();
preParser.setBuildParseTree(true);
final GLSLPreParser.Translation_unitContext pre = preParser.translation_unit();
lexer.reset();
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.setBuildParseTree(true);
return new QuietParse(parser.translation_unit(), pre);
}
public static Set<String> identifiersInDirectiveText(String source) {
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
lexer.removeErrorListeners();
final BufferedTokenStream tokens = new BufferedTokenStream(lexer);
tokens.fill();
final Set<String> names = new HashSet<>();
for (Token token : tokens.getTokens()) {
switch (token.getType()) {
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
default -> { }
}
}
return names;
}
private static void collectIdentifiers(String text, Set<String> out) {
if (text == null) return;
final int len = text.length();
int i = 0;
while (i < len) {
final char c = text.charAt(i);
if (c != '_' && !Character.isLetter(c)) {
i++;
continue;
}
int end = i + 1;
while (end < len) {
final char n = text.charAt(end);
if (n != '_' && !Character.isLetterOrDigit(n)) break;
end++;
}
out.add(text.substring(i, end));
i = end;
}
}
public static String getFormattedShader(ParseTree tree, String header) {
final StringBuilder sb = new StringBuilder(header + "\n");
final String[] tabHolder = {""};
getFormattedShader(tree, sb, tabHolder);
getFormattedShader(tree, sb, tabHolder, false);
return sb.toString();
}
private static void getFormattedShader(ParseTree tree, StringBuilder stringBuilder, String[] tabHolder) {
if (tree instanceof TerminalNode) {
public static String getFormattedShaderRebased(ParseTree tree, String header) {
final StringBuilder sb = new StringBuilder(header + "\n");
final String[] tabHolder = {""};
getFormattedShader(tree, sb, tabHolder, true);
return sb.toString();
}
private static void getFormattedShader(ParseTree tree, StringBuilder stringBuilder, String[] tabHolder, boolean rebaseOffsets) {
if (tree instanceof TerminalNode terminal) {
final String text = tree.getText();
if (text.equals("<EOF>")) {
return;
}
if (text.equals("#")) {
stringBuilder.append("\n#");
rebaseAt(terminal, stringBuilder.length() - 1, text, rebaseOffsets);
return;
}
stringBuilder.append(text);
if (!text.equals("}")) {
rebaseAt(terminal, stringBuilder.length() - text.length(), text, rebaseOffsets);
}
if (text.equals("{")) {
stringBuilder.append(" \n\t");
tabHolder[0] = "\t";
@@ -106,6 +216,7 @@ public class GlslTransformUtils {
if (stringBuilder.length() >= 2) {
stringBuilder.deleteCharAt(stringBuilder.length() - 2);
}
rebaseAt(terminal, stringBuilder.length() - 1, text, rebaseOffsets);
tabHolder[0] = "";
stringBuilder.append(" \n");
} else {
@@ -113,8 +224,17 @@ public class GlslTransformUtils {
}
} else {
for (int i = 0; i < tree.getChildCount(); ++i) {
getFormattedShader(tree.getChild(i), stringBuilder, tabHolder);
getFormattedShader(tree.getChild(i), stringBuilder, tabHolder, rebaseOffsets);
}
}
}
private static void rebaseAt(TerminalNode terminal, int startIndex, String text, boolean rebaseOffsets) {
if (!rebaseOffsets) return;
if (terminal.getSymbol() instanceof CommonToken common) {
common.setText(text);
common.setStartIndex(startIndex);
common.setStopIndex(startIndex + text.length() - 1);
}
}
}
@@ -0,0 +1,34 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
public final class LTWWorkaround {
private LTWWorkaround() {}
private static ByteBuffer oneTexel;
public static boolean isLtwVersionString(String glVersion) {
return glVersion != null && glVersion.contains("LTW");
}
public static void onTexImage2D(int target, int level, int format, boolean hasPixels) {
if (target != GL11.GL_TEXTURE_2D || level != 0 || format != GL12.GL_BGRA || hasPixels) return;
if (oneTexel == null) {
oneTexel = BufferUtils.createByteBuffer(4);
}
oneTexel.clear();
GLStateManager.forcePixelUnpackState(PixelUnpackState.DEFAULT);
GLStateManager.suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage2D(target, level, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, oneTexel);
GLStateManager.restorePixelUnpackBuffer();
GLStateManager.restorePixelUnpackState(PixelUnpackState.DEFAULT);
}
}
@@ -90,22 +90,47 @@ public final class QuadConverter {
drawSharedQuadEbo(first, vertexCount, primcount, true);
}
private static int misalignedDraws;
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
GLStateManager.preDraw();
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
final int quadCount = vertexCount / 4;
final int prevEbo = GLStateManager.getBoundEBO();
ensureCapacity(first / 4 + quadCount);
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
final long indexOffset = (long) (first / 4) * 6 * 4;
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
if ((first & 3) != 0) {
misalignedDraws++;
if ((misalignedDraws & (misalignedDraws - 1)) == 0) {
GLStateManager.LOGGER.warn("QuadConverter: quad draw with unaligned first={} count={} (#{}) -- using scratch indices", first, vertexCount, misalignedDraws);
}
final ByteBuffer dst = memAlloc(quadCount * 6 * 4);
long ptr = memAddress0(dst);
for (int i = 0; i < quadCount; i++) {
final int base = first + i * 4;
memPutInt(ptr, base);
memPutInt(ptr + 4, base + 1);
memPutInt(ptr + 8, base + 3);
memPutInt(ptr + 12, base + 1);
memPutInt(ptr + 16, base + 2);
memPutInt(ptr + 20, base + 3);
ptr += 24;
}
uploadAndDraw(dst, quadCount * 6, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
return;
}
final boolean locked = GLStateManager.acquireDrawLock();
try {
GLStateManager.preDraw();
final int prevEbo = GLStateManager.getBoundEBO();
ensureCapacity(first / 4 + quadCount);
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
final long indexOffset = (long) (first / 4) * 6 * 4;
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
}
/**
@@ -123,32 +148,37 @@ public final class QuadConverter {
}
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
final boolean locked = GLStateManager.acquireDrawLock();
try {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
if (scratchEboId == 0) {
scratchEboId = RENDER_BACKEND.genBuffers();
if (scratchEboId == 0) {
scratchEboId = RENDER_BACKEND.genBuffers();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
if (needed > scratchEboCapacity) {
// Power-of-2 growth -- allocate full capacity, upload actual data
int newCap = Math.max(4096, scratchEboCapacity);
while (newCap < needed) newCap *= 2;
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
scratchEboCapacity = newCap;
}
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
if (needed > scratchEboCapacity) {
// Power-of-2 growth — allocate full capacity, upload actual data
int newCap = Math.max(4096, scratchEboCapacity);
while (newCap < needed) newCap *= 2;
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
scratchEboCapacity = newCap;
}
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
memFree(dst);
}
@@ -174,7 +204,7 @@ public final class QuadConverter {
final int quadCount = indexCount / 4;
final int triIndexCount = quadCount * 6;
final int bytesPerIndex = VAOManager.Attrib.glTypeSizeBytes(type);
final int bytesPerIndex = GLTypes.sizeBytes(type);
final ByteBuffer src = memAlloc(indexCount * bytesPerIndex);
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, src);
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.dsa.DSAARB;
import com.gtnewhorizons.angelica.glsm.dsa.DSAAccess;
@@ -8,6 +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 lombok.Getter;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
@@ -18,6 +20,7 @@ import org.lwjgl.opengl.ARBShaderStorageBufferObject;
import org.lwjgl.opengl.EXTShaderImageLoadStore;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
@@ -40,6 +43,7 @@ public class RenderSystem {
private static boolean supportsCompute;
private static boolean supportsImageLoadStore;
private static boolean supportsSSBO;
private static boolean supportsMultiDrawIndirect;
private static boolean supportsBufferStorage;
private static boolean supportsClearTexture;
private static boolean supportsTesselation;
@@ -52,6 +56,14 @@ public class RenderSystem {
@Getter private static int glMinor;
@Getter private static boolean coreProfile;
private static volatile boolean isGLES;
private static volatile int glesVersion;
private static volatile boolean glesDetected;
private static volatile boolean hasClipCullDistance;
private static volatile boolean isLTW;
private static volatile boolean ltwDetected;
// Sampler object state tracking (null if unsupported)
private static int[] samplers;
@@ -64,34 +76,49 @@ public class RenderSystem {
public static void initRenderer() {
if (rendererInitialized) return;
rendererInitialized = true;
try {
if (GLStateManager.vendorIsIntel() && GLStateManager.isWindows()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("Detected Intel drivers on Windows, disabling DSA.");
} else if (!GLStateManager.getInitConfig().isDSAEnabled()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("enableDSA is set to false, disabling DSA.");
} else if (GLStateManager.capabilities.OpenGL45) {
dsaState = (Runtime.version().feature() > 8 && GLStateManager.capabilities.GL_EXT_direct_state_access) ? new DSAEXT() : new DSACore();
GLStateManager.LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
}
} catch (NoSuchFieldError ignored) {
final boolean wasDetected = glesDetected;
if (isGLES() && !wasDetected) {
GLStateManager.LOGGER.info("OpenGL ES {}.{} detected", glesVersion / 100, (glesVersion / 10) % 10);
}
try {
if (dsaState == null && GLStateManager.capabilities.GL_ARB_direct_state_access) {
dsaState = new DSAARB();
GLStateManager.LOGGER.info("ARB_direct_state_access detected, enabling DSA.");
if (!isGLES) {
try {
if (GLStateManager.vendorIsIntel() && GLStateManager.isWindows()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("Detected Intel drivers on Windows, disabling DSA.");
} else if (!GLStateManager.getInitConfig().isDSAEnabled()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("enableDSA is set to false, disabling DSA.");
} else if (GLStateManager.capabilities.OpenGL45) {
dsaState = (Runtime.version().feature() > 8 && GLStateManager.capabilities.GL_EXT_direct_state_access) ? new DSAEXT() : new DSACore();
GLStateManager.LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
}
} catch (NoSuchFieldError ignored) {
}
try {
if (dsaState == null && GLStateManager.capabilities.GL_ARB_direct_state_access) {
dsaState = new DSAARB();
GLStateManager.LOGGER.info("ARB_direct_state_access detected, enabling DSA.");
}
} catch (NoSuchFieldError ignored) {
}
} catch (NoSuchFieldError ignored) {
}
if (dsaState == null) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("No DSA support detected, falling back to legacy OpenGL.");
GLStateManager.LOGGER.info("{}", isGLES ? "GLES context, using DSAUnsupported (bind-based) fallback." : "No DSA support detected, falling back to legacy OpenGL.");
}
BackendManager.init();
try {
// Force init on client thread
GLStateManager.LOGGER.info("Celeritas LWJGL service: {}", LWJGLServiceProvider.LWJGL.getClass().getName());
} catch (Throwable t) {
GLStateManager.LOGGER.debug("Could not resolve the celeritas LWJGL service: {}", t.toString());
}
supportsCompute = supportsCompute();
supportsTesselation = GLStateManager.capabilities.GL_ARB_tessellation_shader || GLStateManager.capabilities.OpenGL40;
@@ -99,8 +126,10 @@ public class RenderSystem {
|| GLStateManager.capabilities.GL_ARB_shader_image_load_store
|| GLStateManager.capabilities.GL_EXT_shader_image_load_store;
supportsSSBO = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_shader_storage_buffer_object;
supportsBufferStorage = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage;
supportsMultiDrawIndirect = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_multi_draw_indirect;
supportsBufferStorage = !isGLES && (GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage);
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
hasClipCullDistance = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
// Cache maximum image units
if (supportsImageLoadStore) {
@@ -140,12 +169,18 @@ public class RenderSystem {
GLStateManager.LOGGER.info("SSBO: {}, Max SSBO Bindings: {}", supportsSSBO, maxSSBOBindings);
GLStateManager.LOGGER.info("Buffer Storage: {}, Clear Texture: {}, Sampler Objects: {}", supportsBufferStorage, supportsClearTexture, supportsSamplerObjects);
if (GLStateManager.capabilities.OpenGL32) {
if (isGLES) {
GLStateManager.LOGGER.info("GL ES context detected, enabling shader transformer.");
ShaderManager.getInstance().enable();
} else if (GLStateManager.capabilities.OpenGL32) {
final int profileMask = RENDER_BACKEND.getInteger(GL32.GL_CONTEXT_PROFILE_MASK);
if ((profileMask & GL32.GL_CONTEXT_CORE_PROFILE_BIT) != 0) {
coreProfile = true;
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
ShaderManager.getInstance().enable();
} else {
throw new IllegalStateException("Non-core GL context (profile mask 0x" + Integer.toHexString(profileMask)
+ "); FFP emulation requires a core profile and nothing would render. Context creation should have rejected this.");
}
}
@@ -165,6 +200,10 @@ public class RenderSystem {
}
public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
final GLStateManager.GLESTexImageRemap remap = GLStateManager.remapTexImageForGLES(internalformat, format, type);
internalformat = remap.internalFormat();
format = remap.format();
type = remap.type();
GLStateManager.glBindTexture(target, texture);
GLStateManager.suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
@@ -301,7 +340,7 @@ public class RenderSystem {
}
public static void bufferData(int target, FloatBuffer data, int usage) {
RENDER_BACKEND.bufferData(target, data, usage);
GLStateManager.glBufferData(target, data, usage);
}
public static int bufferStorage(int target, FloatBuffer data, int usage) {
@@ -324,12 +363,16 @@ public class RenderSystem {
return dsaState.getTexParameteri(texture, target, pname);
}
public static float getTexParameterf(int texture, int target, int pname) {
return dsaState.getTexParameterf(texture, target, pname);
}
public static int getTexLevelParameteri(int texture, int level, int pname) {
return dsaState.getTexLevelParameteri(texture, level, pname);
}
public static void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) {
RENDER_BACKEND.bindImageTexture(unit, texture, level, layered, layer, access, format);
GLStateManager.glBindImageTexture(unit, texture, level, layered, layer, access, format);
}
public static int getMaxImageUnits() {
@@ -337,13 +380,69 @@ public class RenderSystem {
}
public static boolean supportsImageLoadStore() {
if (RENDER_BACKEND.isSDLGPU()) return true;
return supportsImageLoadStore;
}
public static boolean hasClipCullDistance() {
return hasClipCullDistance;
}
private static boolean hasExtension(String name) {
final int count = GLStateManager.glGetInteger(GL30.GL_NUM_EXTENSIONS);
for (int i = 0; i < count; i++) {
if (name.equals(GLStateManager.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
}
return false;
}
public static boolean isGLES() {
if (!glesDetected) detectGLES();
return isGLES;
}
private static synchronized void detectGLES() {
if (glesDetected) return;
try {
final String v = RENDER_BACKEND.getString(GL11.GL_VERSION);
if (v != null && v.startsWith("OpenGL ES ")) {
isGLES = true;
glesVersion = Integer.parseInt(parseGlVersionString(v));
}
glesDetected = true;
} catch (Throwable ignored) {
// No GL context on this thread yet (splash); retry on next call.
}
}
public static boolean isLTW() {
if (!ltwDetected) detectLTW();
return isLTW;
}
private static synchronized void detectLTW() {
if (ltwDetected) return;
if (SystemProperties.DISABLE_LTW_WORKAROUND) {
ltwDetected = true;
return;
}
try {
final String v = RENDER_BACKEND.getString(GL11.GL_VERSION);
if (v == null) return;
isLTW = LTWWorkaround.isLtwVersionString(v);
ltwDetected = true;
} catch (Throwable ignored) {
}
}
public static boolean supportsSSBO() {
return supportsSSBO;
}
public static boolean supportsMultiDrawIndirect() {
return supportsMultiDrawIndirect;
}
public static boolean supportsBufferStorage() {
return supportsBufferStorage;
}
@@ -424,9 +523,14 @@ public class RenderSystem {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
public static void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2,
int height2, int bufferChoice, int filter) {
dsaState.blitFramebuffer(source, dest, offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
public static void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2, int bufferChoice, int filter) {
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
try {
dsaState.blitFramebuffer(source, dest, offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
} finally {
if (scissor) GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
}
}
public static int createFramebuffer() {
@@ -440,14 +544,18 @@ public class RenderSystem {
// TODO: Proper notification of compute support
public static boolean supportsCompute() {
try {
return GLStateManager.capabilities.GL_ARB_compute_shader;
return GLStateManager.capabilities.GL_ARB_compute_shader || GLStateManager.capabilities.OpenGL43;
} catch (Exception ignored) {
return false;
}
}
public static int createBuffers() {
return RENDER_BACKEND.genBuffers();
return GLStateManager.glGenBuffers();
}
public static int getIndexedBufferBinding(int target, int index) {
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
}
public static void bindBufferBase(int target, int index, int buffer) {
@@ -455,13 +563,13 @@ public class RenderSystem {
}
public static void bufferStorage(int target, long size, int flags) {
RENDER_BACKEND.bufferStorage(target, size, flags);
GLStateManager.glBufferStorage(target, size, flags);
}
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
final ByteBuffer buf = BufferUtils.createByteBuffer(data.length * 4);
buf.asIntBuffer().put(data);
RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, buf);
GLStateManager.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
}
public static void deleteBuffers(int buffer) {
@@ -502,6 +610,7 @@ public class RenderSystem {
}
public static boolean supportsSnormFormats() {
if (isGLES()) return false;
return GLStateManager.capabilities.OpenGL31;
}
@@ -549,9 +658,17 @@ public class RenderSystem {
}
}
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Inlined from StandardMacros.getGlVersion(). */
static String parseGlVersionString(String info) {
final Matcher matcher = SEMVER_PATTERN.matcher(Objects.requireNonNull(info));
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Strips
* "OpenGL ES " / "OpenGL ES GLSL ES " prefixes so ES contexts parse too. */
public static String parseGlVersionString(String info) {
Objects.requireNonNull(info);
String stripped = info;
if (stripped.startsWith("OpenGL ES GLSL ES ")) {
stripped = stripped.substring("OpenGL ES GLSL ES ".length());
} else if (stripped.startsWith("OpenGL ES ")) {
stripped = stripped.substring("OpenGL ES ".length());
}
final Matcher matcher = SEMVER_PATTERN.matcher(stripped);
if (!matcher.matches()) {
throw new IllegalStateException("Could not parse GL version from \"" + info + "\"");
}
@@ -561,4 +678,18 @@ public class RenderSystem {
if (bugfix == null || bugfix.isEmpty()) bugfix = "0";
return major + minor + bugfix;
}
/** Parse a GL version string into packed major*10+minor (e.g. "4.3.0 - Build ..." -> 43), or -1 if unparseable. */
public static int parseMajorMinor(String glVersion) {
if (glVersion == null) return -1;
final Matcher matcher = SEMVER_PATTERN.matcher(glVersion.trim());
if (!matcher.matches()) return -1;
return Integer.parseInt(matcher.group("major")) * 10 + Integer.parseInt(matcher.group("minor"));
}
public static boolean isContextValid(int requestedMajor, int requestedMinor, int actualVersion, boolean coreBitSet) {
if (!coreBitSet) return false;
if (actualVersion < 0) return true;
return actualVersion >= requestedMajor * 10 + requestedMinor;
}
}
@@ -4,7 +4,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
public final class BackendManager {
@@ -39,8 +38,8 @@ public final class BackendManager {
if (backend.isAvailable() && (best == null || backend.getPriority() > best.getPriority())) {
best = backend;
}
} catch (ServiceConfigurationError | LinkageError e) {
LOGGER.debug("Skipping unavailable backend: {}", e.getMessage());
} catch (Throwable t) {
LOGGER.debug("Skipping unavailable backend: {}", t.getMessage());
}
}
@@ -0,0 +1,5 @@
package com.gtnewhorizons.angelica.glsm.backend;
public interface GLDebugMessageListener {
void onMessage(int source, int type, int id, int severity, int length, long message, long userParam);
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.AMDDebugOutput;
import org.lwjgl.opengl.AMDDebugOutputCallback;
@@ -7,8 +8,11 @@ import org.lwjgl.opengl.ARBClearTexture;
import org.lwjgl.opengl.ARBDebugOutput;
import org.lwjgl.opengl.ARBDebugOutputCallback;
import org.lwjgl.opengl.ARBDirectStateAccess;
import org.lwjgl.opengl.ARBTimerQuery;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.EXTDirectStateAccess;
import org.lwjgl.opengl.EXTShaderImageLoadStore;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
@@ -27,6 +31,8 @@ import org.lwjgl.opengl.GL44;
import org.lwjgl.opengl.GL45;
import org.lwjgl.opengl.KHRDebug;
import org.lwjgl.opengl.KHRDebugCallback;
import org.lwjgl.opengl.Display;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
@@ -96,9 +102,38 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
}
}
@Override
public boolean isCurrent() {
try {
return Display.isCurrent();
} catch (Exception e) {
return false;
}
}
@Override
public int getMinGLSLVersion() {return 330;}
@Override
protected VSyncMode applyVSyncMode(VSyncMode preferred) {
final boolean enabled = preferred.tearFree();
Display.setVSyncEnabled(enabled);
return enabled ? VSyncMode.ON : VSyncMode.OFF;
}
@Override
protected int queryDisplayRefreshRateHz() {
final DisplayMode mode = Display.getDisplayMode();
if (mode != null && mode.getFrequency() > 0) return mode.getFrequency();
final DisplayMode desktop = Display.getDesktopDisplayMode();
return desktop == null ? 0 : desktop.getFrequency();
}
@Override
public boolean isAnisotropicSupported() {
return caps != null && caps.GL_EXT_texture_filter_anisotropic;
}
@Override
public void flush() {GL11.glFlush();}
@@ -322,18 +357,17 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
@Override
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
org.lwjgl.opengl.GL43.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
GL43.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
}
@Override
public void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) {
throw new UnsupportedOperationException("GLRenderBackend.copyBufferSubData should not be called — LWJGL service handles this");
GL31.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
}
@Override
public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
// On GL path, LWJGL3 service dispatches directly. This is only called via VulkanRenderBackend.
throw new UnsupportedOperationException("GLRenderBackend.drawElementsBaseVertex should not be called — LWJGL service handles this");
GL32.glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
}
@Override
@@ -622,6 +656,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glGetTexImage(target, level, format, type, pixels);
}
@Override
public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
GL11.glGetTexImage(target, level, format, type, pixelBufferOffset);
}
@Override
public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
return GL30.glGetFramebufferAttachmentParameteri(target, attachment, pname);
@@ -922,6 +961,32 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL30.glBindBufferBase(target, index, buffer);
}
@Override
public void bindBufferRange(int target, int index, int buffer, long offset, long size) {
GL30.glBindBufferRange(target, index, buffer, offset, size);
}
@Override
public int getUniformBlockIndex(int program, CharSequence name) {
return GL31.glGetUniformBlockIndex(program, name);
}
@Override
public void uniformBlockBinding(int program, int blockIndex, int binding) {
GL31.glUniformBlockBinding(program, blockIndex, binding);
}
@Override
public int getIndexedBufferBinding(int target, int index) {
final int pname = (target == GL43.GL_SHADER_STORAGE_BUFFER) ? GL43.GL_SHADER_STORAGE_BUFFER_BINDING : GL31.GL_UNIFORM_BUFFER_BINDING;
return GL30.glGetInteger(pname, index);
}
@Override
public int getIntegerIndexed(int pname, int index) {
return GL30.glGetInteger(pname, index);
}
@Override
public void bufferData(int target, long size, int usage) {
GL15.glBufferData(target, size, usage);
@@ -1053,11 +1118,26 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return GL15.glIsBuffer(buffer);
}
@Override
public boolean isTexture(int texture) {
return GL11.glIsTexture(texture);
}
@Override public boolean isFramebuffer(int framebuffer) { return GL30.glIsFramebuffer(framebuffer); }
@Override public boolean isRenderbuffer(int renderbuffer) { return GL30.glIsRenderbuffer(renderbuffer); }
@Override public boolean isSampler(int sampler) { return GL33.glIsSampler(sampler); }
@Override public boolean isQuery(int query) { return GL15.glIsQuery(query); }
@Override
public ByteBuffer mapBufferRange(int target, long offset, long length, int access) {
return GL30.glMapBufferRange(target, offset, length, access, null);
}
@Override
public void flushMappedBufferRange(int target, long offset, long length) {
GL30.glFlushMappedBufferRange(target, offset, length);
}
@Override
public int genVertexArrays() {
return GL30.glGenVertexArrays();
@@ -1249,6 +1329,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return GL45.glCreateBuffers();
}
@Override
public void namedBufferData(int buffer, long size, int usage) {
GL45.glNamedBufferData(buffer, size, usage);
}
@Override
public void namedBufferData(int buffer, ByteBuffer data, int usage) {
GL45.glNamedBufferData(buffer, data, usage);
@@ -1269,6 +1354,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return ARBDirectStateAccess.glGetTextureParameteri(texture, pname);
}
@Override
public float getTextureParameterf(int texture, int target, int pname) {
return ARBDirectStateAccess.glGetTextureParameterf(texture, pname);
}
@Override
public int getTextureLevelParameteri(int texture, int level, int pname) {
return ARBDirectStateAccess.glGetTextureLevelParameteri(texture, level, pname);
@@ -1294,6 +1384,16 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glGetFloat(pname, params);
}
@Override
public double getDouble(int pname) {
return GL11.glGetDouble(pname);
}
@Override
public void getDouble(int pname, DoubleBuffer params) {
GL11.glGetDouble(pname, params);
}
@Override
public boolean getBoolean(int pname) {
return GL11.glGetBoolean(pname);
@@ -1335,6 +1435,66 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
LWJGL.glDeleteSync(sync);
}
@Override
public boolean supportsGpuProfiling() {
if (!caps.OpenGL33 && !caps.GL_ARB_timer_query) return false;
return GL15.glGetQuery(GL33.GL_TIMESTAMP, GL15.GL_QUERY_COUNTER_BITS) > 0;
}
@Override
public int genQuery() {
return GL15.glGenQueries();
}
@Override
public void deleteQuery(int query) {
GL15.glDeleteQueries(query);
}
@Override
public void queryCounter(int query) {
if (caps.OpenGL33) {
GL33.glQueryCounter(query, GL33.GL_TIMESTAMP);
} else {
ARBTimerQuery.glQueryCounter(query, GL33.GL_TIMESTAMP);
}
}
@Override
public boolean isQueryResultAvailable(int query) {
return GL15.glGetQueryObjecti(query, GL15.GL_QUERY_RESULT_AVAILABLE) != 0;
}
@Override
public long getQueryResult64(int query) {
return caps.OpenGL33 ? GL33.glGetQueryObjectui64(query, GL15.GL_QUERY_RESULT) : ARBTimerQuery.glGetQueryObjectui64(query, GL15.GL_QUERY_RESULT);
}
@Override
public long getGpuTimestamp() {
return GL32.glGetInteger64(GL33.GL_TIMESTAMP);
}
@Override public void genQueries(IntBuffer ids) { GL15.glGenQueries(ids); }
@Override public int genQueries() { return GL15.glGenQueries(); }
@Override public void deleteQueries(int id) { GL15.glDeleteQueries(id); }
@Override public void beginQuery(int target, int id) { GL15.glBeginQuery(target, id); }
@Override public void endQuery(int target) { GL15.glEndQuery(target); }
@Override public void getQueryObjectui(int id, int pname, IntBuffer params) { GL15.glGetQueryObjectu(id, pname, params); }
@Override public int getQueryObjecti(int id, int pname) { return GL15.glGetQueryObjecti(id, pname); }
@Override public void waitSync(long sync, int flags, long timeout) { LWJGL.glWaitSync(sync, flags, timeout); }
@Override public int getSynci(long sync, int pname, IntBuffer length) { return LWJGL.glGetSynci(sync, pname, length); }
@Override public void queryCounter(int id, int target) {
if (caps.OpenGL33) GL33.glQueryCounter(id, target); else ARBTimerQuery.glQueryCounter(id, target);
}
@Override public long getQueryObjectui64(int id, int pname) {
return caps.OpenGL33 ? GL33.glGetQueryObjectui64(id, pname) : ARBTimerQuery.glGetQueryObjectui64(id, pname);
}
@Override public void uniform3fv(int location, float[] values) { GL20.glUniform3f(location, values[0], values[1], values[2]); }
@Override public void uniform4fv(int location, float[] values) { GL20.glUniform4f(location, values[0], values[1], values[2], values[3]); }
@Override public void bindFragDataLocation(int program, int colorNumber, CharSequence name) { GL30.glBindFragDataLocation(program, colorNumber, name); }
@Override
public void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) {
GL43.glClearBufferSubData(target, internalFormat, offset, size, format, type, data);
@@ -1351,7 +1511,13 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
@Override
public void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) {
GL42.glBindImageTexture(unit, texture, level, layered, layer, access, format);
if (GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_shader_image_load_store) {
GL42.glBindImageTexture(unit, texture, level, layered, layer, access, format);
} else if (GLStateManager.capabilities.GL_EXT_shader_image_load_store) {
EXTShaderImageLoadStore.glBindImageTextureEXT(unit, texture, level, layered, layer, access, format);
} else {
GLStateManager.warnOnce("bindImageTexture", "glBindImageTexture ignored: no image load/store support");
}
}
@Override
@@ -1376,7 +1542,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
@Override
public int setupDebugOutput(DebugMessageHandler handler) {
if (caps.OpenGL43 || caps.GL_KHR_debug) {
khrCallback = new KHRDebugCallback((source, type, id, severity, message) -> handler.handleMessage(source, type, id, severity, message));
khrCallback = new KHRDebugCallback(handler::handleMessage);
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
GL43.glDebugMessageControl(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
@@ -1391,7 +1557,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
}
return 1;
} else if (caps.GL_ARB_debug_output) {
arbCallback = new ARBDebugOutputCallback((source, type, id, severity, message) -> handler.handleMessage(source, type, id, severity, message));
arbCallback = new ARBDebugOutputCallback(handler::handleMessage);
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_HIGH, null, true);
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_MEDIUM, null, false);
ARBDebugOutput.glDebugMessageControlARB(GL11.GL_DONT_CARE, GL11.GL_DONT_CARE, GL43.GL_DEBUG_SEVERITY_LOW, null, false);
@@ -1505,4 +1671,76 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
}
}
@Override
public void debugMessageControl(int source, int type, int severity, IntBuffer ids, boolean enabled) {
if (activeDebugExtension == 1) {
GL43.glDebugMessageControl(source, type, severity, ids, enabled);
}
}
@Override
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
GL14.glMultiDrawArrays(mode, firsts, counts);
}
@Override
public void primitiveRestartIndex(int index) {
GL31.glPrimitiveRestartIndex(index);
}
@Override
public void pointParameterf(int pname, float param) {
GL14.glPointParameterf(pname, param);
}
@Override
public void pointParameteri(int pname, int param) {
GL14.glPointParameteri(pname, param);
}
@Override
public void getActiveAttrib(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name) {
GL20.glGetActiveAttrib(program, index, length, size, type, name);
}
@Override
public String getActiveAttrib(int program, int index, int maxLength, IntBuffer sizeType) {
return GL20.glGetActiveAttrib(program, index, maxLength, sizeType);
}
@Override
public int genRenderbuffers() {
return GL30.glGenRenderbuffers();
}
@Override
public void deleteRenderbuffers(int renderbuffer) {
GL30.glDeleteRenderbuffers(renderbuffer);
}
@Override
public void bindRenderbuffer(int target, int renderbuffer) {
GL30.glBindRenderbuffer(target, renderbuffer);
}
@Override
public void renderbufferStorage(int target, int internalformat, int width, int height) {
GL30.glRenderbufferStorage(target, internalformat, width, height);
}
@Override
public void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) {
GL30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
}
@Override
public void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) {
GL30.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
}
@Override
public void clearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) {
GL43.glClearBufferData(target, internalformat, format, type, data);
}
}
@@ -1,6 +1,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.opengl.GL20;
import java.nio.ByteBuffer;
@@ -10,20 +12,26 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
/**
* Abstract rendering backend.
*/
public abstract class RenderBackend {
public void onPostWindowCreate(long window) {}
public abstract void init();
public abstract void shutdown();
public abstract boolean isAvailable();
public abstract String getName();
/** Returns true if the current thread has a valid render context. Safe to call from any thread. */
/** Returns true if the current thread has a valid render context. */
public abstract boolean hasContext();
/** Returns true if the calling thread owns the "GL Context" */
public abstract boolean isCurrent();
/** Higher priority backends are preferred. */
public int getPriority() { return 0; }
@@ -35,14 +43,121 @@ public abstract class RenderBackend {
public List<String> pollDroppedFiles() { return Collections.emptyList(); }
public boolean isIndirectRequired() { return false; }
public boolean supportsGeometryShaders() { return true; }
public boolean isSDLGPU() { return false; }
public boolean framebufferCompletenessIsMeaningful() { return true; }
public void onPersistentBufferWrite(int glId, long offset, long size) {}
public boolean isAnisotropicSupported() { return true; }
public boolean supportsGpuDrivenCulling() { return RenderSystem.supportsCompute(); }
public void beginComputeDispatchBatch() {}
public void endComputeDispatchBatch() {}
public abstract int getMinGLSLVersion();
public abstract void flush();
public abstract void finish();
public void onFrameBegin() {}
private VSyncMode effectiveVSyncMode = VSyncMode.ON;
private VSyncMode preferredTearFreeMode;
public final void setVSyncEnabled(boolean enabled) {
setVSyncMode(enabled ? preferredTearFreeMode() : VSyncMode.OFF);
}
public final void setVSyncMode(VSyncMode preferred) {
setVSyncPreference(preferred);
publishVSyncMode(applyVSyncMode(preferred));
}
public final void setVSyncPreference(VSyncMode preferred) {
if (preferred.tearFree()) preferredTearFreeMode = preferred;
}
public final VSyncMode preferredTearFreeMode() {
return resolveTearFreeMode(preferredTearFreeMode, this::supportsVSyncMode);
}
static VSyncMode resolveTearFreeMode(VSyncMode preferred, Predicate<VSyncMode> supported) {
if (preferred != null && preferred.tearFree() && supported.test(preferred)) return preferred;
return supported.test(VSyncMode.MAILBOX) ? VSyncMode.MAILBOX : VSyncMode.ON;
}
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
public boolean supportsVSyncMode(VSyncMode mode) { return mode != VSyncMode.MAILBOX; }
protected final void publishVSyncMode(VSyncMode mode) { effectiveVSyncMode = mode; }
public final VSyncMode getEffectiveVSyncMode() { return effectiveVSyncMode; }
private static final long REFRESH_POLL_NANOS = 1_000_000_000L;
private long refreshPollNanos;
private int cachedRefreshHz;
public final int getDisplayRefreshRateHz() {
final long now = System.nanoTime();
if (refreshPollNanos == 0L || now - refreshPollNanos >= REFRESH_POLL_NANOS) {
refreshPollNanos = now;
cachedRefreshHz = queryDisplayRefreshRateHz();
}
return cachedRefreshHz;
}
protected int queryDisplayRefreshRateHz() { return 0; }
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;
}
public boolean hasSwapchainBackpressure() { return false; }
private long frameGateNanos;
private long frameGateEndNanos;
public boolean gateAnchorsNextFrameStart() { return false; }
public boolean wantsDisplayUpdateGateTiming() { return true; }
public void recordFrameGate(long durationNanos, long endNanos) {
frameGateNanos = durationNanos;
frameGateEndNanos = endNanos;
}
public long lastFrameGateNanos() { return frameGateNanos; }
public long lastFrameGateEndNanos() { return frameGateEndNanos; }
public void onFrameBegin() {
if (GLStateManager.takeStateSeedPending()) {
GLStateManager.replayStateToBackend();
}
}
public void onFrameEnd() {}
/** Fired by lwjgl3ify Display before a mutation that recreates the swapchain. */
public void onPreSwapchainInvalidatingChange(Object change) {}
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 String getTransferDebugInfo() { return null; }
public boolean bindVoxelizationRegion(int ssboBinding, int vertexBufferGlId, 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) {}
public abstract void enable(int cap);
public abstract void enablei(int cap, int index);
public abstract void disable(int cap);
@@ -60,6 +175,7 @@ public abstract class RenderBackend {
public abstract void frontFace(int mode);
public abstract void polygonMode(int face, int mode);
public abstract void polygonOffset(float factor, float units);
public void polygonOffsetClamp(float factor, float units, float clamp) { throw new UnsupportedOperationException(getName() + ": polygon offset clamp unsupported"); }
public abstract void stencilFunc(int func, int ref, int mask);
public abstract void stencilOp(int sfail, int dpfail, int dppass);
public abstract void stencilMask(int mask);
@@ -85,12 +201,24 @@ public abstract class RenderBackend {
public void drawElements(int mode, ShortBuffer indices) {}
public void drawElements(int mode, int count, int type, ByteBuffer indices) {}
public abstract void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
public void multiDrawArraysIndirectCount(int mode, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
public void multiDrawElementsIndirectCount(int mode, int type, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
public abstract void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size);
public abstract void drawElementsInstanced(int mode, int count, int type, long indices, int primcount);
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
public void provokingVertex(int provokeMode) {}
public abstract void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
public abstract void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
public void drawRangeElements(int mode, int start, int end, int count, int type, long indices) {
drawElements(mode, count, type, indices);
}
public void drawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
drawElementsBaseVertex(mode, count, type, indices, baseVertex);
}
public abstract void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
public abstract void primitiveRestartIndex(int index);
public abstract void pointParameterf(int pname, float param);
public abstract void pointParameteri(int pname, int param);
public abstract void drawBuffer(int mode);
public abstract void dispatchCompute(int numGroupsX, int numGroupsY, int numGroupsZ);
public abstract void dispatchComputeIndirect(long offset);
@@ -141,6 +269,12 @@ public abstract class RenderBackend {
public abstract void bindFramebuffer(int target, int framebuffer);
public abstract void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
public abstract void framebufferTexture(int target, int attachment, int texture, int level);
public abstract int genRenderbuffers();
public abstract void deleteRenderbuffers(int renderbuffer);
public abstract void bindRenderbuffer(int target, int renderbuffer);
public abstract void renderbufferStorage(int target, int internalformat, int width, int height);
public abstract void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height);
public abstract void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer);
public abstract int checkFramebufferStatus(int target);
public abstract void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
public abstract void drawBuffers(int buffer);
@@ -151,12 +285,14 @@ public abstract class RenderBackend {
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
public abstract int getFramebufferAttachmentParameteri(int target, int attachment, int pname);
public abstract int createShader(int type);
public abstract void deleteShader(int shader);
public abstract void shaderSource(int shader, CharSequence source);
public abstract void compileShader(int shader);
public void specializeShader(int shader, CharSequence entryPoint, IntBuffer constantIndex, IntBuffer constantValue) { throw new UnsupportedOperationException(getName() + ": externally supplied SPIR-V binaries unsupported"); }
public abstract int createProgram();
public abstract void deleteProgram(int program);
public abstract void attachShader(int program, int shader);
@@ -178,7 +314,10 @@ public abstract class RenderBackend {
public abstract void getProgramiv(int program, int pname, IntBuffer params);
public abstract String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType);
public abstract void getActiveUniform(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name);
public abstract void getActiveAttrib(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name);
public abstract String getActiveAttrib(int program, int index, int maxLength, IntBuffer sizeType);
public abstract void bindAttribLocation(int program, int index, CharSequence name);
public void bindFragDataLocation(int program, int colorNumber, CharSequence name) {}
public abstract int getAttribLocation(int program, CharSequence name);
public abstract int getAttribLocation(int program, ByteBuffer name);
public abstract int getUniformLocation(int program, CharSequence name);
@@ -195,6 +334,8 @@ public abstract class RenderBackend {
public abstract void uniform1i(int location, int v0);
public abstract void uniform1f(int location, float v0);
public void uniform1fv(int location, FloatBuffer values) {}
public void uniform3fv(int location, float[] values) {}
public void uniform4fv(int location, float[] values) {}
public abstract void uniform2f(int location, float v0, float v1);
public abstract void uniform2i(int location, int v0, int v1);
public abstract void uniform3f(int location, float v0, float v1, float v2);
@@ -223,6 +364,11 @@ public abstract class RenderBackend {
public abstract void deleteBuffers(IntBuffer buffers);
public abstract void bindBuffer(int target, int buffer);
public abstract void bindBufferBase(int target, int index, int buffer);
public abstract void bindBufferRange(int target, int index, int buffer, long offset, long size);
public abstract int getUniformBlockIndex(int program, CharSequence name);
public abstract void uniformBlockBinding(int program, int blockIndex, int binding);
public abstract int getIndexedBufferBinding(int target, int index);
public abstract int getIntegerIndexed(int pname, int index);
public abstract void bufferData(int target, long size, int usage);
public abstract void bufferData(int target, ByteBuffer data, int usage);
public abstract void bufferData(int target, FloatBuffer data, int usage);
@@ -249,6 +395,17 @@ public abstract class RenderBackend {
public abstract int getBufferParameteri(int target, int pname);
public abstract boolean isBuffer(int buffer);
public abstract ByteBuffer mapBufferRange(int target, long offset, long length, int access);
public abstract void flushMappedBufferRange(int target, long offset, long length);
public long mapBufferRangeAddress(int target, long offset, long length, int access) {
final ByteBuffer buf = mapBufferRange(target, offset, length, access);
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
}
public abstract boolean isTexture(int texture);
public abstract boolean isFramebuffer(int framebuffer);
public abstract boolean isRenderbuffer(int renderbuffer);
public abstract boolean isSampler(int sampler);
public abstract boolean isQuery(int query);
public abstract int genVertexArrays();
public abstract void deleteVertexArrays(int array);
@@ -292,17 +449,22 @@ public abstract class RenderBackend {
public abstract void namedFramebufferDrawBuffers(int framebuffer, IntBuffer bufs);
public abstract void blitNamedFramebuffer(int readFramebuffer, int drawFramebuffer, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
public abstract int createBuffers();
public abstract void namedBufferData(int buffer, long size, int usage);
public abstract void namedBufferData(int buffer, ByteBuffer data, int usage);
public abstract void namedBufferData(int buffer, FloatBuffer data, int usage);
public abstract void namedBufferSubData(int buffer, long offset, ByteBuffer data);
public abstract void copyTextureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int x, int y, int width, int height);
public abstract int getTextureParameteri(int texture, int target, int pname);
public abstract float getTextureParameterf(int texture, int target, int pname);
public abstract int getTextureLevelParameteri(int texture, int level, int pname);
public abstract int getInteger(int pname);
public abstract void getInteger(int pname, IntBuffer params);
public void getInteger(int pname, int[] params) { if (params.length > 0) params[0] = getInteger(pname); }
public abstract float getFloat(int pname);
public abstract void getFloat(int pname, FloatBuffer params);
public double getDouble(int pname) { return getFloat(pname); }
public void getDouble(int pname, DoubleBuffer params) { if (params.remaining() > 0) params.put(params.position(), getDouble(pname)); }
public abstract boolean getBoolean(int pname);
public abstract void getBoolean(int pname, ByteBuffer params);
public abstract String getString(int pname);
@@ -312,8 +474,30 @@ public abstract class RenderBackend {
public abstract long fenceSync(int condition, int flags);
public abstract int clientWaitSync(long sync, int flags, long timeout);
public abstract void deleteSync(long sync);
public void waitSync(long sync, int flags, long timeout) { throw new UnsupportedOperationException("sync objects unsupported"); }
public int getSynci(long sync, int pname, IntBuffer length) { throw new UnsupportedOperationException("sync objects unsupported"); }
public void genQueries(IntBuffer ids) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public int genQueries() { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public void deleteQueries(int id) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public void beginQuery(int target, int id) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public void endQuery(int target) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public void getQueryObjectui(int id, int pname, IntBuffer params) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public int getQueryObjecti(int id, int pname) { throw new UnsupportedOperationException("occlusion queries unsupported"); }
public void queryCounter(int id, int target) { throw new UnsupportedOperationException("timer queries unsupported"); }
public long getQueryObjectui64(int id, int pname) { throw new UnsupportedOperationException("timer queries unsupported"); }
public boolean supportsGpuProfiling() { return false; }
public int genQuery() { throw new UnsupportedOperationException("timer queries unsupported"); }
public void deleteQuery(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
public void queryCounter(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
public boolean isQueryResultAvailable(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
public long getQueryResult64(int query) { throw new UnsupportedOperationException("timer queries unsupported"); }
public long getGpuTimestamp() { throw new UnsupportedOperationException("timer queries unsupported"); }
public abstract void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data);
public abstract void clearBufferData(int target, int internalformat, int format, int type, ByteBuffer data);
public abstract void clearTexImage(int texture, int level, int format, int type);
public abstract void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format);
@@ -339,4 +523,10 @@ public abstract class RenderBackend {
// Debug message insertion
public void debugMessageInsert(int source, int type, int id, int severity, CharSequence message) {}
public void debugMessageControl(int source, int type, int severity, IntBuffer ids, boolean enabled) {}
public void debugMessageCallback(GLDebugMessageListener listener, long userParam) {}
public int getDebugMessageLog(int count, IntBuffer sources, IntBuffer types, IntBuffer ids, IntBuffer severities, IntBuffer lengths, ByteBuffer messageLog) { return 0; }
}
@@ -0,0 +1,11 @@
package com.gtnewhorizons.angelica.glsm.backend;
public enum VSyncMode {
OFF,
ON,
MAILBOX;
public boolean tearFree() {
return this != OFF;
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.dsa;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
@@ -75,6 +76,11 @@ public class DSAARB extends DSAUnsupported {
return GLStateManager.getTexParameterOrDefault(texture, pname, () -> RENDER_BACKEND.getTextureParameteri(texture, target, pname));
}
@Override
public float getTexParameterf(int texture, int target, int pname) {
return RENDER_BACKEND.getTextureParameterf(texture, target, pname);
}
@Override
public int getTexLevelParameteri(int texture, int level, int pname) {
return RENDER_BACKEND.getTextureLevelParameteri(texture, level, pname);
@@ -87,25 +93,26 @@ public class DSAARB extends DSAUnsupported {
@Override
public void bindTextureToUnit(int unit, int texture) {
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
if (texture == 0) {
super.bindTextureToUnit(unit, texture);
} else {
RENDER_BACKEND.bindTextureUnit(unit, texture);
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture);
}
bindTextureToUnit(0, unit, texture);
}
@Override
public void bindTextureToUnit(int target, int unit, int texture) {
bindTextureToUnit(unit, texture);
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
if (texture == 0) {
super.bindTextureToUnit(target != 0 ? target : GL11.GL_TEXTURE_2D, unit, texture);
} else {
RENDER_BACKEND.bindTextureUnit(unit, texture);
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture, target);
GLStateManager.trackMaxBoundTextureUnit(unit);
}
}
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = RENDER_BACKEND.createBuffers();
RENDER_BACKEND.namedBufferData(buffer, data, usage);
final int buffer = GLStateManager.glCreateBuffers();
GLStateManager.glNamedBufferData(buffer, data, usage);
return buffer;
}
@@ -23,6 +23,8 @@ public interface DSAAccess {
int getTexParameteri(int texture, int target, int pname);
float getTexParameterf(int texture, int target, int pname);
int getTexLevelParameteri(int texture, int level, int pname);
void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height);
@@ -90,6 +90,12 @@ public class DSAUnsupported implements DSAAccess {
return GLStateManager.glGetTexParameteri(target, pname);
}
@Override
public float getTexParameterf(int texture, int target, int pname) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
return GLStateManager.glGetTexParameterf(target, pname);
}
@Override
public int getTexLevelParameteri(int texture, int level, int pname) {
final int previous = GLStateManager.getBoundTextureForServerState();
@@ -121,9 +127,9 @@ public class DSAUnsupported implements DSAAccess {
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = RENDER_BACKEND.genBuffers();
final int buffer = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(target, buffer);
RENDER_BACKEND.bufferData(target, data, usage);
GLStateManager.glBufferData(target, data, usage);
GLStateManager.glBindBuffer(target, 0);
return buffer;
@@ -0,0 +1,119 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
public final class FFPUniformBlock {
public static final String BLOCK_NAME = "FFPUniformBlock";
public static final int BINDING_POINT = 0;
private static final StringBuilder DECL = new StringBuilder(2048);
private static int cursor = 0;
static final Object2IntMap<String> MEMBER_OFFSETS = new Object2IntLinkedOpenHashMap<>();
private static int align(int alignment) {
cursor = (cursor + alignment - 1) & -alignment;
return cursor;
}
private static int member(String type, String name, int alignment, int size) {
DECL.append(" ").append(type).append(' ').append(name).append(";\n");
final int offset = align(alignment);
cursor += size;
MEMBER_OFFSETS.put(name, offset);
return offset;
}
private static int mat4(String name) { return member("mat4", name, 16, 64); }
private static int mat3(String name) { return member("mat3", name, 16, 48); }
private static int vec4(String name) { return member("vec4", name, 16, 16); }
private static int vec3(String name) { return member("vec3", name, 16, 12); }
private static int vec2(String name) { return member("vec2", name, 8, 8); }
private static int flt(String name) { return member("float", name, 4, 4); }
private static int integer(String name) { return member("int", name, 4, 4); }
private static int vec4Array(String name, int count) {
DECL.append(" vec4 ").append(name).append('[').append(count).append("];\n");
final int offset = align(16);
cursor += 16 * count;
MEMBER_OFFSETS.put(name + "[0]", offset);
return offset;
}
static { DECL.append("layout(std140) uniform ").append(BLOCK_NAME).append(" {\n"); }
public static final int MODEL_VIEW_MATRIX = mat4("u_ModelViewMatrix");
public static final int PROJECTION_MATRIX = mat4("u_ProjectionMatrix");
public static final int MVP_MATRIX = mat4("u_MVPMatrix");
public static final int NORMAL_MATRIX = mat3("u_NormalMatrix");
public static final int TEXTURE_MATRIX_0 = mat4("u_TextureMatrix0");
public static final int TEXTURE_MATRIX_2 = mat4("u_TextureMatrix2");
public static final int TEXTURE_MATRIX_3 = mat4("u_TextureMatrix3");
public static final int LIGHTMAP_TEXTURE_MATRIX = mat4("u_LightmapTextureMatrix");
public static final int CURRENT_COLOR = vec4("u_CurrentColor");
public static final int CURRENT_TEX_COORD_0 = vec4("u_CurrentTexCoord0");
public static final int CURRENT_TEX_COORD_2 = vec4("u_CurrentTexCoord2");
public static final int CURRENT_TEX_COORD_3 = vec4("u_CurrentTexCoord3");
public static final int VIEWPORT = vec4("u_Viewport");
public static final int CURRENT_NORMAL = vec3("u_CurrentNormal");
public static final int CURRENT_LIGHTMAP_COORD = vec2("u_CurrentLightmapCoord");
public static final int VIEWPORT_SIZE = vec2("u_ViewportSize");
public static final int NORMAL_SCALE = flt("u_NormalScale");
public static final int MATERIAL_SHININESS = flt("u_MaterialShininess");
public static final int ALPHA_REF = flt("u_AlphaRef");
public static final int LINE_WIDTH = flt("u_LineWidth");
public static final int LINE_STIPPLE = integer("u_LineStipple");
public static final int TEX_GEN_OBJ_PLANE_S = vec4("u_TexGenObjPlaneS");
public static final int TEX_GEN_OBJ_PLANE_T = vec4("u_TexGenObjPlaneT");
public static final int TEX_GEN_OBJ_PLANE_R = vec4("u_TexGenObjPlaneR");
public static final int TEX_GEN_OBJ_PLANE_Q = vec4("u_TexGenObjPlaneQ");
public static final int TEX_GEN_EYE_PLANE_S = vec4("u_TexGenEyePlaneS");
public static final int TEX_GEN_EYE_PLANE_T = vec4("u_TexGenEyePlaneT");
public static final int TEX_GEN_EYE_PLANE_R = vec4("u_TexGenEyePlaneR");
public static final int TEX_GEN_EYE_PLANE_Q = vec4("u_TexGenEyePlaneQ");
public static final int CLIP_PLANES = vec4Array("u_ClipPlane", 8);
public static final int LIGHT_MODEL_AMBIENT = vec4("u_LightModelAmbient");
public static final int MATERIAL_EMISSION = vec4("u_MaterialEmission");
public static final int MATERIAL_AMBIENT = vec4("u_MaterialAmbient");
public static final int MATERIAL_DIFFUSE = vec4("u_MaterialDiffuse");
public static final int MATERIAL_SPECULAR = vec4("u_MaterialSpecular");
public static final int LIGHT0_AMBIENT = vec4("u_Light0Ambient");
public static final int LIGHT0_DIFFUSE = vec4("u_Light0Diffuse");
public static final int LIGHT0_SPECULAR = vec4("u_Light0Specular");
public static final int LIGHT0_POSITION = vec4("u_Light0Position");
public static final int LIGHT1_AMBIENT = vec4("u_Light1Ambient");
public static final int LIGHT1_DIFFUSE = vec4("u_Light1Diffuse");
public static final int LIGHT1_SPECULAR = vec4("u_Light1Specular");
public static final int LIGHT1_POSITION = vec4("u_Light1Position");
public static final int SCENE_COLOR = vec4("u_SceneColor");
public static final int LIGHT_PROD0_AMBIENT = vec3("u_LightProd0Ambient");
public static final int LIGHT_PROD0_DIFFUSE = vec3("u_LightProd0Diffuse");
public static final int LIGHT_PROD0_SPECULAR = vec3("u_LightProd0Specular");
public static final int LIGHT_PROD1_AMBIENT = vec3("u_LightProd1Ambient");
public static final int LIGHT_PROD1_DIFFUSE = vec3("u_LightProd1Diffuse");
public static final int LIGHT_PROD1_SPECULAR = vec3("u_LightProd1Specular");
public static final int TEX_ENV_COLOR_0 = vec4("u_TexEnvColor0");
public static final int TEX_ENV_COLOR_1 = vec4("u_TexEnvColor1");
public static final int TEX_ENV_COLOR_2 = vec4("u_TexEnvColor2");
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
public static final int FOG_PARAMS = vec4("u_FogParams");
public static final int FOG_COLOR = vec4("u_FogColor");
public static final int SIZE;
public static final String GLSL_DECL;
static {
SIZE = (cursor + 15) & -16;
DECL.append("};\n");
GLSL_DECL = DECL.toString();
}
private FFPUniformBlock() {}
}
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
* Packed-long fragment shader permutation key for FFP emulation.
*
* Layout:
* long[0]: global bits (11) + unit 0 (47 bits at offset 11)
* long[0]: global bits (12) + unit 0 (47 bits at offset 12)
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
* Only max(1, nrEnabledUnits) longs are significant.
*
@@ -63,13 +63,14 @@ public final class FragmentKey {
public static final int FOG_EXP = 2;
public static final int FOG_EXP2 = 3;
private static final int GLOBAL_BITS = 11;
private static final int GLOBAL_BITS = 12;
private static final int BIT_FOG_MODE = 0; // 2 bits
private static final int BIT_ALPHA_TEST = 2; // 1 bit
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
private static final int U_ENABLED = 0;
private static final int U_MODE = 1; // 3 bits
@@ -125,6 +126,10 @@ public final class FragmentKey {
global |= (1L << BIT_OVERLAY_ENABLED);
}
if (GLStateManager.lineStippleActive) {
global |= (1L << BIT_LINE_STIPPLE);
}
// Separate specular
if (GLStateManager.getLightingState().isEnabled()
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
@@ -135,7 +140,8 @@ public final class FragmentKey {
int highestEnabled = -1;
long unit0Bits = 0;
for (int i = 0; i < MAX_UNITS; i++) {
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled();
// Per Mesa - an enabled unit with no complete texture bound counts as disabled.
final boolean texEnabled = GLStateManager.getTextures().getTextureUnitStates(i).isEnabled() && GLStateManager.getTextures().getTextureUnitBindings(i).getBinding() != 0;
if (texEnabled) highestEnabled = i;
final long unitBits = packUnit(i, texEnabled);
@@ -232,6 +238,7 @@ public final class FragmentKey {
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
@@ -20,6 +20,7 @@ public final class FragmentShaderGenerator {
sb.append("out vec4 fragColor;\n\n");
sb.append("void main() {\n");
emitLineStipple(sb, key);
emitTextureSampling(sb, key);
emitTexEnvChain(sb, key);
emitSpecularAdd(sb, key);
@@ -46,39 +47,33 @@ public final class FragmentShaderGenerator {
if (key.fogMode() != FragmentKey.FOG_NONE) {
sb.append("in float v_FogCoord;\n");
}
if (key.lineStipple()) {
sb.append("flat in vec2 v_LineStart;\n");
}
sb.append('\n');
}
private static void emitLineStipple(StringBuilder sb, FragmentKey key) {
if (!key.lineStipple()) return;
sb.append(" vec2 stippleDelta = abs(gl_FragCoord.xy - v_LineStart);\n");
sb.append(" int stippleBit = int(floor(max(stippleDelta.x, stippleDelta.y) / float(u_LineStipple >> 16))) & 15;\n");
sb.append(" if (((u_LineStipple >> stippleBit) & 1) == 0) discard;\n\n");
}
private static void emitUniforms(StringBuilder sb, FragmentKey key) {
// Samplers for each enabled unit
for (int i = 0; i < key.nrEnabledUnits(); i++) {
if (key.unitEnabled(i)) {
sb.append("uniform sampler2D u_Sampler").append(i).append(";\n");
}
}
if (key.alphaTestEnabled()) {
sb.append("uniform float u_AlphaRef;\n");
}
// Per-unit texEnvColor uniforms
for (int i = 0; i < key.nrEnabledUnits(); i++) {
if (key.unitEnabled(i) && key.unitNeedsEnvColor(i)) {
sb.append("uniform vec4 u_TexEnvColor").append(i).append(";\n");
}
}
if (key.overlayEnabled()) {
sb.append("uniform vec4 u_OverlayColor;\n");
}
if (key.fogMode() != FragmentKey.FOG_NONE) {
sb.append("uniform vec4 u_FogParams;\n");
sb.append("uniform vec4 u_FogColor;\n");
}
sb.append(FFPUniformBlock.GLSL_DECL);
sb.append('\n');
}
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
for (int i = 0; i < key.nrEnabledUnits(); i++) {
if (!key.unitEnabled(i)) continue;
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", v_TexCoord").append(i).append(".st);\n");
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
}
}
@@ -14,8 +14,8 @@ public final class GeometryShaderGenerator {
sb.append("layout(lines) in;\n");
sb.append("layout(triangle_strip, max_vertices = 4) out;\n\n");
sb.append("uniform vec2 u_ViewportSize;\n");
sb.append("uniform float u_LineWidth;\n\n");
sb.append(FFPUniformBlock.GLSL_DECL);
sb.append('\n');
sb.append("// Pass-through varyings\n");
sb.append("in vec4 v_Color_gs[];\n");
@@ -29,6 +29,9 @@ public final class GeometryShaderGenerator {
if (key.fogEnabled()) {
sb.append("in float v_FogCoord_gs[];\n");
}
if (key.lineStipple()) {
sb.append("flat in vec2 v_LineStart_gs[];\n");
}
sb.append('\n');
sb.append("out vec4 v_Color;\n");
@@ -42,6 +45,9 @@ public final class GeometryShaderGenerator {
if (key.fogEnabled()) {
sb.append("out float v_FogCoord;\n");
}
if (key.lineStipple()) {
sb.append("flat out vec2 v_LineStart;\n");
}
sb.append('\n');
sb.append("void main() {\n");
@@ -85,6 +91,9 @@ public final class GeometryShaderGenerator {
if (key.fogEnabled()) {
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
}
if (key.lineStipple()) {
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
}
if (key.clipPlanesEnabled()) {
for (int i = 0; i < 8; i++) {
@@ -0,0 +1,18 @@
package com.gtnewhorizons.angelica.glsm.ffp;
public final class InstancedAttribs {
public static final int LOC_MATRIX_COL0 = 7;
public static final int LOC_MATRIX_COL1 = 8;
public static final int LOC_MATRIX_COL2 = 9;
public static final int LOC_MATRIX_COL3 = 10;
public static final int LOC_COLOR = 11;
public static final int LOC_LIGHTMAP = 12;
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;
private InstancedAttribs() {}
}
@@ -1,16 +1,23 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.CompatShaderTransformer;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
import lombok.Getter;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.KHRDebug;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicInteger;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -26,169 +33,15 @@ public class Program {
@Getter private final VertexKey vertexKey;
@Getter private final FragmentKey fragmentKey;
// Per-program uniform dirty-tracking
static final class UploadState {
int mvGen = -1, mvLinearGen = -1, projGen = -1, texMatGen = -1;
int lightingGen = -1, fragmentGen = -1, colorGen = -1, normalGen = -1, texCoordGen = -1;
int texGenGen = -1, clipPlaneGen = -1;
float lightmapX = Float.NaN, lightmapY = Float.NaN, lineWidth = Float.NaN;
int viewportWidth = -1, viewportHeight = -1;
}
final UploadState uploadState = new UploadState();
// Uniform locations (-1 = not present in this variant)
// Matrices
public int locModelViewMatrix = -1;
public int locProjectionMatrix = -1;
public int locMVPMatrix = -1;
public int locNormalMatrix = -1;
public final int[] locTextureMatrix = { -1, -1, -1, -1 };
// Current state defaults
public int locCurrentNormal = -1;
public int locCurrentColor = -1;
public final int[] locCurrentTexCoord = { -1, -1, -1, -1 };
public int locCurrentLightmapCoord = -1;
public int locLightmapTextureMatrix = -1;
// Lighting - color material path (raw values)
public int locLightModelAmbient = -1;
public int locMaterialEmission = -1;
public int locMaterialAmbient = -1;
public int locMaterialDiffuse = -1;
public int locMaterialSpecular = -1;
public int locMaterialShininess = -1;
public int locLight0Ambient = -1;
public int locLight0Diffuse = -1;
public int locLight0Specular = -1;
public int locLight1Ambient = -1;
public int locLight1Diffuse = -1;
public int locLight1Specular = -1;
// Lighting - pre-computed path
public int locSceneColor = -1;
public int locLightProd0Ambient = -1;
public int locLightProd0Diffuse = -1;
public int locLightProd0Specular = -1;
public int locLightProd1Ambient = -1;
public int locLightProd1Diffuse = -1;
public int locLightProd1Specular = -1;
// Light positions
public int locLight0Position = -1;
public int locLight1Position = -1;
// Normal scale (for rescale normals)
public int locNormalScale = -1;
// TexGen plane uniforms
public int locTexGenObjPlaneS = -1;
public int locTexGenObjPlaneT = -1;
public int locTexGenObjPlaneR = -1;
public int locTexGenObjPlaneQ = -1;
public int locTexGenEyePlaneS = -1;
public int locTexGenEyePlaneT = -1;
public int locTexGenEyePlaneR = -1;
public int locTexGenEyePlaneQ = -1;
// Clip planes
public int locClipPlanes = -1;
// Wide line emulation (geometry shader)
public int locViewportSize = -1;
public int locLineWidth = -1;
// Fragment uniforms
public final int[] locSampler = { -1, -1, -1, -1 };
public int locAlphaRef = -1;
public final int[] locTexEnvColor = { -1, -1, -1, -1 };
public int locOverlayColor = -1;
public int locFogParams = -1;
public int locFogColor = -1;
Program(int programId, VertexKey vertexKey, FragmentKey fragmentKey) {
this.programId = programId;
this.vertexKey = vertexKey;
this.fragmentKey = fragmentKey;
resolveLocations();
}
private void resolveLocations() {
locModelViewMatrix = loc("u_ModelViewMatrix");
locProjectionMatrix = loc("u_ProjectionMatrix");
locMVPMatrix = loc("u_MVPMatrix");
locNormalMatrix = loc("u_NormalMatrix");
for (int i = 0; i < 4; i++) {
locTextureMatrix[i] = loc("u_TextureMatrix" + i);
locCurrentTexCoord[i] = loc("u_CurrentTexCoord" + i);
locSampler[i] = RENDER_BACKEND.getUniformLocation(programId, "u_Sampler" + i);
}
locCurrentNormal = loc("u_CurrentNormal");
locCurrentColor = loc("u_CurrentColor");
locCurrentLightmapCoord = loc("u_CurrentLightmapCoord");
locLightmapTextureMatrix = loc("u_LightmapTextureMatrix");
// Color material path
locLightModelAmbient = loc("u_LightModelAmbient");
locMaterialEmission = loc("u_MaterialEmission");
locMaterialAmbient = loc("u_MaterialAmbient");
locMaterialDiffuse = loc("u_MaterialDiffuse");
locMaterialSpecular = loc("u_MaterialSpecular");
locMaterialShininess = loc("u_MaterialShininess");
locLight0Ambient = loc("u_Light0Ambient");
locLight0Diffuse = loc("u_Light0Diffuse");
locLight0Specular = loc("u_Light0Specular");
locLight1Ambient = loc("u_Light1Ambient");
locLight1Diffuse = loc("u_Light1Diffuse");
locLight1Specular = loc("u_Light1Specular");
// Pre-computed path
locSceneColor = loc("u_SceneColor");
locLightProd0Ambient = loc("u_LightProd0Ambient");
locLightProd0Diffuse = loc("u_LightProd0Diffuse");
locLightProd0Specular = loc("u_LightProd0Specular");
locLightProd1Ambient = loc("u_LightProd1Ambient");
locLightProd1Diffuse = loc("u_LightProd1Diffuse");
locLightProd1Specular = loc("u_LightProd1Specular");
// Light positions
locLight0Position = loc("u_Light0Position");
locLight1Position = loc("u_Light1Position");
locNormalScale = loc("u_NormalScale");
// TexGen
locTexGenObjPlaneS = loc("u_TexGenObjPlaneS");
locTexGenObjPlaneT = loc("u_TexGenObjPlaneT");
locTexGenObjPlaneR = loc("u_TexGenObjPlaneR");
locTexGenObjPlaneQ = loc("u_TexGenObjPlaneQ");
locTexGenEyePlaneS = loc("u_TexGenEyePlaneS");
locTexGenEyePlaneT = loc("u_TexGenEyePlaneT");
locTexGenEyePlaneR = loc("u_TexGenEyePlaneR");
locTexGenEyePlaneQ = loc("u_TexGenEyePlaneQ");
// Clip planes
locClipPlanes = loc("u_ClipPlane[0]");
// Wide line emulation
locViewportSize = loc("u_ViewportSize");
locLineWidth = loc("u_LineWidth");
// Fragment
for (int i = 0; i < 4; i++) {
locSampler[i] = loc("u_Sampler" + i);
}
locAlphaRef = loc("u_AlphaRef");
for (int i = 0; i < 4; i++) {
locTexEnvColor[i] = loc("u_TexEnvColor" + i);
}
locOverlayColor = loc("u_OverlayColor");
locFogParams = loc("u_FogParams");
locFogColor = loc("u_FogColor");
}
private int loc(String name) {
return RENDER_BACKEND.getUniformLocation(programId, name);
}
public void destroy() {
@@ -230,7 +83,7 @@ public class Program {
final String debugName = "FFP(v=0x" + vkHex + ",f=" + id + (hasGeom ? ",g" : "") + ")";
GLDebug.nameObject(KHRDebug.GL_PROGRAM, program, debugName);
// Detach and delete individual shaders — they're linked into the program
// Detach and delete individual shaders - they're linked into the program
for (int i = 0; i < shaderCount; i++) {
backend.detachShader(program, shaders[i]);
backend.deleteShader(shaders[i]);
@@ -239,6 +92,11 @@ public class Program {
final Program ffpProgram = new Program(program, vk, fk);
final int blockIndex = backend.getUniformBlockIndex(program, FFPUniformBlock.BLOCK_NAME);
if (blockIndex != GL31.GL_INVALID_INDEX) {
backend.uniformBlockBinding(program, blockIndex, FFPUniformBlock.BINDING_POINT);
}
final int previousProgram = GLStateManager.getActiveProgram();
backend.useProgram(program);
for (int i = 0; i < 4; i++) {
@@ -257,6 +115,10 @@ public class Program {
private static int compileShader(int type, String src, String name) {
final RenderBackend backend = RENDER_BACKEND;
final int shader = backend.createShader(type);
if (RenderSystem.isGLES()) {
src = CompatShaderTransformer.toGLES(src, type, type == GL20.GL_FRAGMENT_SHADER);
dumpTranslatedFFPShader(type, src, name);
}
backend.shaderSource(shader, src);
backend.compileShader(shader);
@@ -285,4 +147,22 @@ public class Program {
return shader;
}
// Post-translation GLSL ES dump, paired with ShaderCache's pre-translation 330 core dump.
private static void dumpTranslatedFFPShader(int type, String translatedSrc, String name) {
final Path dir = SystemProperties.shaderDumpDir("ffp");
if (dir == null) return;
final String suffix = switch (type) {
case GL20.GL_VERTEX_SHADER -> ".vert.glsl";
case GL20.GL_FRAGMENT_SHADER -> ".frag.glsl";
case GL32.GL_GEOMETRY_SHADER -> ".geom.glsl";
default -> ".glsl";
};
try {
Files.createDirectories(dir);
Files.writeString(dir.resolve(name + ".gles" + suffix), translatedSrc);
} catch (IOException e) {
GLStateManager.LOGGER.warn("Failed to dump translated FFP shader: {}", e.getMessage());
}
}
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Setter;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -25,6 +26,8 @@ import java.util.IdentityHashMap;
* </pre>
*/
public class ShaderCache {
private static final Tracy.ZoneId Z_FFP_SHADER_GEN = Tracy.zoneId("ffpShaderGen", Tracy.COLOR_FFP);
private static final int SLOT_STRIDE = 6;
private static final int INITIAL_CAPACITY = 64; // power of 2
private static final long EMPTY_MARKER = 0L; // slot [1] == 0 means empty (fkLen >= 1 guarantees nonzero)
@@ -98,18 +101,25 @@ public class ShaderCache {
}
final VertexKey vk = VertexKey.fromPacked(vkPacked);
final String vertSrc = getOrGenerateVertex(vk);
final FragmentKey fk = FragmentKey.fromPacked(fkPacked, fkLen);
String fragSrc = findFragSource(fkPacked, fkLen);
if (fragSrc == null) {
fragSrc = FragmentShaderGenerator.generate(fk);
uniqueFragCount++;
final String vertSrc;
String fragSrc;
final String geomSrc;
final Program program;
if (Tracy.ENABLED) Tracy.beginZone(Z_FFP_SHADER_GEN);
try {
vertSrc = getOrGenerateVertex(vk);
fragSrc = findFragSource(fkPacked, fkLen);
if (fragSrc == null) {
fragSrc = FragmentShaderGenerator.generate(fk);
uniqueFragCount++;
}
geomSrc = vk.wideLineEmulation() ? getOrGenerateGeometry(vk) : null;
program = Program.create(vk, fk, vertSrc, fragSrc, geomSrc);
} finally {
if (Tracy.ENABLED) Tracy.endZone();
}
final String geomSrc = vk.wideLineEmulation() ? getOrGenerateGeometry(vk) : null;
final Program program = Program.create(vk, fk, vertSrc, fragSrc, geomSrc);
final int insertIdx = findEmptySlot(hash);
final int base = insertIdx * SLOT_STRIDE;
keys[base] = vkPacked;
@@ -1,18 +1,22 @@
package com.gtnewhorizons.angelica.glsm.ffp;
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.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.Vec3fStack;
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import lombok.Getter;
import org.joml.Vector3f;
import org.joml.Vector4f;
import java.nio.file.Paths;
import java.util.Arrays;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -51,14 +55,21 @@ public final class ShaderManager {
@Getter private static int normalGeneration;
@Getter private static int texCoordGeneration;
private int preDrawCalls;
private int lastFramePreDrawCalls;
public static long variantSwitches;
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
@Getter private boolean enabled = false;
private ShaderManager() {
if (Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false"))) {
cache.setDumpDir(Paths.get("ffp_shaders"));
}
cache.setDumpDir(SystemProperties.shaderDumpDir("ffp"));
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
VAOManager.setCurrentVertexFlags(format.getVertexFlags());
return false;
});
}
public static ShaderManager getInstance() {
@@ -66,11 +77,33 @@ public final class ShaderManager {
}
public void enable() {
warmUp();
enabled = true;
GLStateManager.LOGGER.info("FFP shader emulation enabled");
}
// Force loading of classes before the SplashThread kicks off
// Ensure it's GL free
private static void warmUp() {
try {
final long[] fkScratch = new long[FragmentKey.MAX_UNITS];
final int fkLen = FragmentKey.packFromState(fkScratch);
final int fragMask = FragmentKey.unitMaskFromPacked(fkScratch, fkLen);
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
final VertexKey vk = VertexKey.fromPacked(vkPacked);
VertexShaderGenerator.generate(vk);
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
GeometryShaderGenerator.generate(vk);
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
for (Class<?> c : touched) {
c.getName();
}
} catch (Throwable t) {
GLStateManager.LOGGER.warn("FFP warmup failed; draw-path classes will resolve lazily", t);
}
}
public void disable() {
enabled = false;
}
@@ -86,6 +119,7 @@ public final class ShaderManager {
lastBoundProgramId = -1;
currentVertexKeyPacked = Long.MIN_VALUE;
currentFKLen = 0;
GLStateManager.forceAttribDefaultsDirty();
}
public void preDraw() {
@@ -109,7 +143,7 @@ public final class ShaderManager {
if (!active) return;
preDrawCalls++;
final int fkLen = FragmentKey.packFromState(currentFKScratch);
final int fragMask = FragmentKey.unitMaskFromPacked(currentFKScratch, fkLen);
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask);
@@ -128,6 +162,7 @@ public final class ShaderManager {
currentProgram = cache.getOrCreate(vkPacked, currentFKPacked, currentFKLen);
final int programId = currentProgram.getProgramId();
if (programId != lastBoundProgramId) {
if (Tracy.ENABLED) variantSwitches++;
RENDER_BACKEND.useProgram(programId);
lastBoundProgramId = programId;
}
@@ -135,10 +170,17 @@ public final class ShaderManager {
private void uploadUniforms() {
if (currentProgram != null) {
uniforms.upload(currentProgram);
uniforms.upload();
}
}
public static void endFrame() {
final ShaderManager sm = Holder.INSTANCE;
sm.lastFramePreDrawCalls = sm.preDrawCalls;
sm.preDrawCalls = 0;
sm.uniforms.endFrame();
}
public static void setCurrentNormal(float x, float y, float z) {
currentNormal.set(x, y, z);
normalGeneration++;
@@ -162,13 +204,31 @@ public final class ShaderManager {
uniforms.destroy();
final GLSMInitConfig config = GLStateManager.getInitConfig();
if (config != null && config.getStreamingDrawerDestroy() != null) config.getStreamingDrawerDestroy().run();
com.gtnewhorizons.angelica.glsm.QuadConverter.destroy();
QuadConverter.destroy();
active = false;
currentProgram = null;
}
public String getDebugInfo() {
return String.format("FFP: %d programs (%d vert, %d frag variants)",
cache.getProgramCount(), cache.getVertexVariantCount(), cache.getFragmentVariantCount());
return String.format("FFP: %d programs (%d vert, %d frag variants) | UBO: %d/%d draws wrote (mat %d, light %d, frag %d, misc %d)",
cache.getProgramCount(), cache.getVertexVariantCount(), cache.getFragmentVariantCount(),
uniforms.lastFrameBlockWrites, lastFramePreDrawCalls,
uniforms.lastFrameStagedMatrices, uniforms.lastFrameStagedLighting,
uniforms.lastFrameStagedFragment, uniforms.lastFrameStagedMisc);
}
public int statLastFramePreDrawCalls() { return lastFramePreDrawCalls; }
public int statLastFrameBlockWrites() { return uniforms.lastFrameBlockWrites; }
public int statLastFrameBlockSkips() { return uniforms.lastFrameBlockSkips; }
public int statLastFrameStagedMatrices() { return uniforms.lastFrameStagedMatrices; }
public int statLastFrameStagedLighting() { return uniforms.lastFrameStagedLighting; }
public int statLastFrameStagedFragment() { return uniforms.lastFrameStagedFragment; }
public int statLastFrameStagedColor() { return uniforms.lastFrameStagedColor; }
public int statLastFrameStagedNormal() { return uniforms.lastFrameStagedNormal; }
public int statLastFrameStagedTexCoord() { return uniforms.lastFrameStagedTexCoord; }
public int statLastFrameStagedLightmap() { return uniforms.lastFrameStagedLightmap; }
public int statLastFrameStagedTexGen() { return uniforms.lastFrameStagedTexGen; }
public int statLastFrameStagedClipPlanes() { return uniforms.lastFrameStagedClipPlanes; }
public int statLastFrameStagedMisc() { return uniforms.lastFrameStagedMisc; }
public int statProgramCount() { return cache.getProgramCount(); }
}
@@ -4,480 +4,438 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.FogState;
import com.gtnewhorizons.angelica.glsm.states.LightState;
import com.gtnewhorizons.angelica.glsm.states.LineState;
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import org.joml.Math;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL31;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAllocFloat;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Uploads GLSM cached state to the active FFP program's uniforms.
*
* Follows Mesa's prog_statevars.c patterns:
* - Pre-computed derived state (light products, scene color) when material is static
* - Raw light/material values when color material is active
* - Optimized fog params (pre-divided for single MAD in shader)
* Maintains the shared {@link FFPUniformBlock} image from GLSM cached state and streams it to the UBO ring.
*/
public class Uniforms {
// Reusable temp buffers for uniform upload
private final FloatBuffer mat4Buf = memAllocFloat(16);
private final FloatBuffer mat3Buf = memAllocFloat(9);
private final FloatBuffer vec4Buf = memAllocFloat(4);
private final FloatBuffer vec3Buf = memAllocFloat(3);
private final FloatBuffer clipPlaneBuf = memAllocFloat(32); // 8 planes * vec4
// Derived matrices (computed on CPU)
private static final int RING_CAPACITY = 16 * 1024 * 1024;
private final ByteBuffer staging = memCalloc(FFPUniformBlock.SIZE);
private final long stagingAddress = memAddress0(staging);
private UniformRingBuffer ring;
private boolean bound;
private boolean contentChanged;
private int mvGen = -1, mvLinearGen = -1, projGen = -1, texMatGen = -1;
private int lightingGen = -1, fragmentGen = -1, colorGen = -1, normalGen = -1, texCoordGen = -1;
private int texGenGen = -1, clipPlaneGen = -1;
private float lightmapX = Float.NaN, lightmapY = Float.NaN, lineWidth = Float.NaN;
private int viewportX = Integer.MIN_VALUE, viewportY = Integer.MIN_VALUE, viewportWidth = -1, viewportHeight = -1;
private int lineStipple = -1;
int blockWrites;
int blockSkips;
int stagedMatrices;
int stagedLighting;
int stagedFragment;
int stagedColor;
int stagedNormal;
int stagedTexCoord;
int stagedLightmap;
int stagedTexGen;
int stagedClipPlanes;
int stagedMisc;
int lastFrameBlockWrites;
int lastFrameBlockSkips;
int lastFrameStagedMatrices;
int lastFrameStagedLighting;
int lastFrameStagedFragment;
int lastFrameStagedColor;
int lastFrameStagedNormal;
int lastFrameStagedTexCoord;
int lastFrameStagedLightmap;
int lastFrameStagedTexGen;
int lastFrameStagedClipPlanes;
int lastFrameStagedMisc;
private final Matrix4f mvpMatrix = new Matrix4f();
private final Matrix3f normalMatrix = new Matrix3f();
private final Vector3f tempVec3 = new Vector3f();
private final FloatBuffer clipPlaneBuf = memAllocFloat(32); // 8 planes * vec4
// Temp vectors for light product and normal scale computation
private final Vector4f tempVec4 = new Vector4f();
private final org.joml.Vector3f tempVec3 = new org.joml.Vector3f();
// Pre-computed constants for fog params (Mesa STATE_FOG_PARAMS_OPTIMIZED)
private static final double LN2 = java.lang.Math.log(2.0);
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
/**
* Upload all relevant uniforms to the given FFP program based on current GLSM state.
* Uses generation counters to skip categories that haven't changed since last upload.
*/
public void upload(Program program) {
final Program.UploadState st = program.uploadState;
public void upload() {
boolean dirty = false;
contentChanged = false;
final int mvGen = GLStateManager.mvGeneration;
final int mvLinearGen = GLStateManager.mvLinearGeneration;
final int projGen = GLStateManager.projGeneration;
final int texMatGen = GLStateManager.texMatrixGeneration;
final boolean mvChanged = mvGen != st.mvGen;
final boolean mvLinearChanged = mvLinearGen != st.mvLinearGen;
final boolean projChanged = projGen != st.projGen;
final boolean texMatChanged = texMatGen != st.texMatGen;
if (mvChanged || projChanged || texMatChanged) {
uploadMatrices(program, mvChanged, mvLinearChanged, projChanged, texMatChanged);
st.mvGen = mvGen;
st.mvLinearGen = mvLinearGen;
st.projGen = projGen;
st.texMatGen = texMatGen;
final int mvG = GLStateManager.getMvGeneration();
final int projG = GLStateManager.getProjGeneration();
final boolean mvChanged = mvG != mvGen;
final boolean projChanged = projG != projGen;
if (mvChanged || projChanged) {
stageMatrices(mvChanged, projChanged);
mvGen = mvG;
projGen = projG;
stagedMatrices++;
dirty = true;
}
final int mvLinG = GLStateManager.getMvLinearGeneration();
if (mvLinG != mvLinearGen) {
stageNormalMatrix();
mvLinearGen = mvLinG;
dirty = true;
}
final int texMatG = GLStateManager.getTexMatrixGeneration();
if (texMatG != texMatGen) {
stageTextureMatrices();
texMatGen = texMatG;
dirty = true;
}
final int litG = GLStateManager.getLightingGeneration();
if (litG != lightingGen) {
stageLighting();
lightingGen = litG;
stagedLighting++;
dirty = true;
}
final int colG = GLStateManager.getColorGeneration();
if (colG != colorGen) {
stageCurrentColor();
colorGen = colG;
stagedColor++;
dirty = true;
}
final int normG = ShaderManager.getNormalGeneration();
if (normG != normalGen) {
final Vector3f normal = ShaderManager.getCurrentNormal();
putVec3(FFPUniformBlock.CURRENT_NORMAL, normal.x, normal.y, normal.z);
normalGen = normG;
stagedNormal++;
dirty = true;
}
final int tcG = ShaderManager.getTexCoordGeneration();
if (tcG != texCoordGen) {
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_0, ShaderManager.getCurrentTexCoord(0));
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_2, ShaderManager.getCurrentTexCoord(2));
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_3, ShaderManager.getCurrentTexCoord(3));
texCoordGen = tcG;
stagedTexCoord++;
dirty = true;
}
final float brightX = GLSMConfig.lastBrightnessX;
final float brightY = GLSMConfig.lastBrightnessY;
if (brightX != lightmapX || brightY != lightmapY) {
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD, brightX);
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD + 4, brightY);
lightmapX = brightX;
lightmapY = brightY;
stagedLightmap++;
dirty = true;
}
final int tgG = GLStateManager.getTexGenGeneration();
if (tgG != texGenGen) {
stageTexGen();
texGenGen = tgG;
stagedTexGen++;
dirty = true;
}
final int cpG = GLStateManager.getClipPlaneGeneration();
if (cpG != clipPlaneGen) {
stageClipPlanes();
clipPlaneGen = cpG;
stagedClipPlanes++;
dirty = true;
}
final int fragG = GLStateManager.getFragmentGeneration();
if (fragG != fragmentGen) {
stageFragment();
fragmentGen = fragG;
stagedFragment++;
dirty = true;
}
final float lw = GLStateManager.getLineState().getWidth();
if (lw != lineWidth) {
putFloat(FFPUniformBlock.LINE_WIDTH, lw);
lineWidth = lw;
stagedMisc++;
dirty = true;
}
final ViewportState vp = GLStateManager.getViewportState();
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);
putVec4(FFPUniformBlock.VIEWPORT, vp.x, vp.y, vp.width, vp.height);
viewportX = vp.x;
viewportY = vp.y;
viewportWidth = vp.width;
viewportHeight = vp.height;
stagedMisc++;
dirty = true;
}
final LineState line = GLStateManager.getLineState();
final int stippleFactor = line.getStippleFactor() < 1 ? 1 : line.getStippleFactor();
final int stipplePacked = (line.getStipplePattern() & 0xFFFF) | (stippleFactor << 16);
if (stipplePacked != lineStipple) {
putInt(FFPUniformBlock.LINE_STIPPLE, stipplePacked);
lineStipple = stipplePacked;
stagedMisc++;
dirty = true;
}
if (program.getVertexKey().lightingEnabled()) {
final int litGen = GLStateManager.lightingGeneration;
if (litGen != st.lightingGen) {
uploadLighting(program);
st.lightingGen = litGen;
if (dirty || !bound) {
if (!contentChanged && bound) {
blockSkips++;
return;
}
}
// Current color/normal/texcoord — skip if generation unchanged
if (!program.getVertexKey().hasVertexColor()) {
final int colorGen = GLStateManager.colorGeneration;
if (colorGen != st.colorGen) {
uploadCurrentColor(program);
st.colorGen = colorGen;
if (ring == null) {
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
}
}
if (!program.getVertexKey().hasVertexNormal() && program.getVertexKey().lightingEnabled()) {
final int normalGen = ShaderManager.getNormalGeneration();
if (normalGen != st.normalGen) {
uploadCurrentNormal(program);
st.normalGen = normalGen;
}
}
if (program.getVertexKey().anyUnitTexCoordEnabled()) {
final int texGen = ShaderManager.getTexCoordGeneration();
if (texGen != st.texCoordGen) {
uploadCurrentTexCoords(program);
st.texCoordGen = texGen;
}
}
if (program.getVertexKey().lightmapEnabled() && !program.getVertexKey().hasVertexLightmap()) {
if (GLSMConfig.lastBrightnessX != st.lightmapX || GLSMConfig.lastBrightnessY != st.lightmapY) {
uploadCurrentLightmapCoord(program);
st.lightmapX = GLSMConfig.lastBrightnessX;
st.lightmapY = GLSMConfig.lastBrightnessY;
}
}
if (program.getVertexKey().texGenEnabled()) {
final int tgGen = GLStateManager.texGenGeneration;
if (tgGen != st.texGenGen) {
uploadTexGen(program);
st.texGenGen = tgGen;
}
}
if (program.getVertexKey().clipPlanesEnabled()) {
final int cpGen = GLStateManager.clipPlaneGeneration;
if (cpGen != st.clipPlaneGen) {
uploadClipPlanes(program);
st.clipPlaneGen = cpGen;
}
}
final int fragGen = GLStateManager.fragmentGeneration;
if (fragGen != st.fragmentGen) {
uploadFragmentUniforms(program);
st.fragmentGen = fragGen;
}
// Wide line emulation uniforms
if (program.locLineWidth != -1 && program.locViewportSize != -1) {
uploadWideLineUniforms(program);
final int offset = ring.writeBlock(staging);
RENDER_BACKEND.bindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
ring.getBufferId(), offset, FFPUniformBlock.SIZE);
bound = true;
blockWrites++;
}
}
private void uploadMatrices(Program program, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
private void stageMatrices(boolean mvChanged, boolean projChanged) {
final Matrix4f mv = GLStateManager.getModelViewMatrix();
final Matrix4f proj = GLStateManager.getProjectionMatrix();
// ModelView + derived (normal matrix, normal scale)
if (mvChanged) {
if (program.locModelViewMatrix != -1) {
mv.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(program.locModelViewMatrix, false, mat4Buf);
}
putMat4(FFPUniformBlock.MODEL_VIEW_MATRIX, mv);
}
if (mvLinearChanged) {
// Normal matrix = inverse transpose of upper-left 3x3 of ModelView
if (program.locNormalMatrix != -1 || program.locNormalScale != -1) {
mv.normal(normalMatrix);
}
if (program.locNormalMatrix != -1) {
normalMatrix.get(mat3Buf);
RENDER_BACKEND.uniformMatrix3(program.locNormalMatrix, false, mat3Buf);
}
// Normal scale (for GL_RESCALE_NORMAL without GL_NORMALIZE)
if (program.locNormalScale != -1) {
// Scale factor = 1/length of first column of normal matrix
final float scale = 1.0f / normalMatrix.getColumn(0, tempVec3).length();
RENDER_BACKEND.uniform1f(program.locNormalScale, scale);
}
}
// Projection
if (projChanged) {
if (program.locProjectionMatrix != -1) {
proj.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(program.locProjectionMatrix, false, mat4Buf);
}
}
// MVP = Projection * ModelView — needs recompute if either input changed
if ((mvChanged || projChanged) && program.locMVPMatrix != -1) {
proj.mul(mv, mvpMatrix);
mvpMatrix.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(program.locMVPMatrix, false, mat4Buf);
}
// Texture matrices (per-unit).
if (texMatChanged) {
for (int i = 0; i < 4; i++) {
if (program.locTextureMatrix[i] != -1) {
final Matrix4f texMat = GLStateManager.getTextures().getTextureUnitMatrix(i);
texMat.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(program.locTextureMatrix[i], false, mat4Buf);
}
}
if (program.locLightmapTextureMatrix != -1) {
final Matrix4f lmTexMat = GLStateManager.getTextures().getTextureUnitMatrix(1);
lmTexMat.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(program.locLightmapTextureMatrix, false, mat4Buf);
}
putMat4(FFPUniformBlock.PROJECTION_MATRIX, proj);
}
proj.mul(mv, mvpMatrix);
putMat4(FFPUniformBlock.MVP_MATRIX, mvpMatrix);
}
private void uploadLighting(Program program) {
final VertexKey vk = program.getVertexKey();
if (vk.colorMaterialEnabled()) {
uploadLightingColorMaterial(program, vk);
} else {
uploadLightingPreComputed(program, vk);
}
private void stageNormalMatrix() {
GLStateManager.getModelViewMatrix().normal(normalMatrix);
putMat3(FFPUniformBlock.NORMAL_MATRIX, normalMatrix);
final float scale = 1.0f / normalMatrix.getColumn(0, tempVec3).length();
putFloat(FFPUniformBlock.NORMAL_SCALE, scale);
}
/**
* Color material active: upload raw light and material values.
* The shader multiplies them with vertex color at runtime.
*/
private void uploadLightingColorMaterial(Program program, VertexKey vk) {
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 stageLighting() {
final MaterialState mat = GLStateManager.getFrontMaterial();
final Vector4f lmAmbient = GLStateManager.getLightModel().ambient;
final LightState light0 = GLStateManager.getLightDataStates()[0];
final LightState light1 = GLStateManager.getLightDataStates()[1];
uploadVec4(program.locLightModelAmbient, GLStateManager.getLightModel().ambient);
uploadVec4(program.locMaterialEmission, mat.emission);
uploadVec4(program.locMaterialAmbient, mat.ambient);
uploadVec4(program.locMaterialDiffuse, mat.diffuse);
uploadVec4(program.locMaterialSpecular, mat.specular);
if (program.locMaterialShininess != -1) {
RENDER_BACKEND.uniform1f(program.locMaterialShininess, mat.shininess);
}
putVec4(FFPUniformBlock.LIGHT_MODEL_AMBIENT, lmAmbient);
putVec4(FFPUniformBlock.MATERIAL_EMISSION, mat.emission);
putVec4(FFPUniformBlock.MATERIAL_AMBIENT, mat.ambient);
putVec4(FFPUniformBlock.MATERIAL_DIFFUSE, mat.diffuse);
putVec4(FFPUniformBlock.MATERIAL_SPECULAR, mat.specular);
putFloat(FFPUniformBlock.MATERIAL_SHININESS, mat.shininess);
putVec4(FFPUniformBlock.LIGHT0_AMBIENT, light0.ambient);
putVec4(FFPUniformBlock.LIGHT0_DIFFUSE, light0.diffuse);
putVec4(FFPUniformBlock.LIGHT0_SPECULAR, light0.specular);
putVec4(FFPUniformBlock.LIGHT0_POSITION, light0.position);
putVec4(FFPUniformBlock.LIGHT1_AMBIENT, light1.ambient);
putVec4(FFPUniformBlock.LIGHT1_DIFFUSE, light1.diffuse);
putVec4(FFPUniformBlock.LIGHT1_SPECULAR, light1.specular);
putVec4(FFPUniformBlock.LIGHT1_POSITION, light1.position);
if (vk.light0Enabled()) {
final LightState light0 = GLStateManager.getLightDataStates()[0];
uploadVec4(program.locLight0Ambient, light0.ambient);
uploadVec4(program.locLight0Diffuse, light0.diffuse);
uploadVec4(program.locLight0Specular, light0.specular);
uploadVec4(program.locLight0Position, light0.position);
}
if (vk.light1Enabled()) {
final LightState light1 = GLStateManager.getLightDataStates()[1];
uploadVec4(program.locLight1Ambient, light1.ambient);
uploadVec4(program.locLight1Diffuse, light1.diffuse);
uploadVec4(program.locLight1Specular, light1.specular);
uploadVec4(program.locLight1Position, light1.position);
}
putVec4(FFPUniformBlock.SCENE_COLOR,
mat.emission.x + mat.ambient.x * lmAmbient.x,
mat.emission.y + mat.ambient.y * lmAmbient.y,
mat.emission.z + mat.ambient.z * lmAmbient.z,
mat.diffuse.w);
putLightProduct(FFPUniformBlock.LIGHT_PROD0_AMBIENT, light0.ambient, mat.ambient);
putLightProduct(FFPUniformBlock.LIGHT_PROD0_DIFFUSE, light0.diffuse, mat.diffuse);
putLightProduct(FFPUniformBlock.LIGHT_PROD0_SPECULAR, light0.specular, mat.specular);
putLightProduct(FFPUniformBlock.LIGHT_PROD1_AMBIENT, light1.ambient, mat.ambient);
putLightProduct(FFPUniformBlock.LIGHT_PROD1_DIFFUSE, light1.diffuse, mat.diffuse);
putLightProduct(FFPUniformBlock.LIGHT_PROD1_SPECULAR, light1.specular, mat.specular);
}
/**
* No color material: upload pre-computed light products and scene color.
* Fewer shader multiplies since material is static.
*/
private void uploadLightingPreComputed(Program program, VertexKey vk) {
final MaterialState mat = GLStateManager.getFrontMaterial();
// Scene color = emission + ambient * lightModel.ambient
// Alpha = material.diffuse.a
if (program.locSceneColor != -1) {
tempVec4.set(
mat.emission.x + mat.ambient.x * GLStateManager.getLightModel().ambient.x,
mat.emission.y + mat.ambient.y * GLStateManager.getLightModel().ambient.y,
mat.emission.z + mat.ambient.z * GLStateManager.getLightModel().ambient.z,
mat.diffuse.w // alpha from diffuse
);
uploadVec4(program.locSceneColor, tempVec4);
}
if (program.locMaterialShininess != -1) {
RENDER_BACKEND.uniform1f(program.locMaterialShininess, mat.shininess);
}
// Per-light products
if (vk.light0Enabled()) {
final LightState light0 = GLStateManager.getLightDataStates()[0];
uploadVec4(program.locLight0Position, light0.position);
uploadLightProduct(program.locLightProd0Ambient, light0.ambient, mat.ambient);
uploadLightProduct(program.locLightProd0Diffuse, light0.diffuse, mat.diffuse);
uploadLightProduct(program.locLightProd0Specular, light0.specular, mat.specular);
}
if (vk.light1Enabled()) {
final LightState light1 = GLStateManager.getLightDataStates()[1];
uploadVec4(program.locLight1Position, light1.position);
uploadLightProduct(program.locLightProd1Ambient, light1.ambient, mat.ambient);
uploadLightProduct(program.locLightProd1Diffuse, light1.diffuse, mat.diffuse);
uploadLightProduct(program.locLightProd1Specular, light1.specular, mat.specular);
}
private void putLightProduct(int offset, Vector4f lightVal, Vector4f materialVal) {
putVec3(offset, lightVal.x * materialVal.x, lightVal.y * materialVal.y, lightVal.z * materialVal.z);
}
/**
* Upload pre-multiplied light product: light.X * material.X (RGB only).
*/
private void uploadLightProduct(int loc, Vector4f lightVal, Vector4f materialVal) {
if (loc == -1) return;
vec3Buf.clear();
vec3Buf.put(lightVal.x * materialVal.x);
vec3Buf.put(lightVal.y * materialVal.y);
vec3Buf.put(lightVal.z * materialVal.z);
vec3Buf.flip();
RENDER_BACKEND.uniform3(loc, vec3Buf);
}
private void uploadCurrentColor(Program program) {
if (program.locCurrentColor == -1) return;
// Upload the current color from GLSM
private void stageCurrentColor() {
final var color = GLStateManager.getColor();
tempVec4.set(
putVec4(FFPUniformBlock.CURRENT_COLOR,
Math.clamp(0f, 1f, color.getRed()),
Math.clamp(0f, 1f, color.getGreen()),
Math.clamp(0f, 1f, color.getBlue()),
Math.clamp(0f, 1f, color.getAlpha()));
vec4Buf.clear();
tempVec4.get(vec4Buf);
RENDER_BACKEND.uniform4(program.locCurrentColor, vec4Buf);
}
private void uploadCurrentTexCoords(Program program) {
for (int i = 0; i < 4; i++) {
if (program.locCurrentTexCoord[i] != -1) {
uploadVec4(program.locCurrentTexCoord[i], ShaderManager.getCurrentTexCoord(i));
}
}
}
private void uploadCurrentLightmapCoord(Program program) {
if (program.locCurrentLightmapCoord == -1) return;
RENDER_BACKEND.uniform2f(program.locCurrentLightmapCoord,
GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY);
}
private void uploadCurrentNormal(Program program) {
if (program.locCurrentNormal == -1) return;
// Upload current normal from FFPShaderManager's tracked normal
final var normal = ShaderManager.getCurrentNormal();
vec3Buf.clear();
normal.get(vec3Buf);
RENDER_BACKEND.uniform3(program.locCurrentNormal, vec3Buf);
}
private void uploadTexGen(Program program) {
final VertexKey vk = program.getVertexKey();
private void stageTexGen() {
final TexGenState tg = GLStateManager.getTextures().getTexGenState(0);
if (vk.texGenModeS() == VertexKey.TG_OBJ_LINEAR) {
uploadPlane(program.locTexGenObjPlaneS, tg.getObjectPlane(org.lwjgl.opengl.GL11.GL_S));
} else if (vk.texGenModeS() == VertexKey.TG_EYE_LINEAR) {
uploadPlane(program.locTexGenEyePlaneS, tg.getEyePlane(org.lwjgl.opengl.GL11.GL_S));
}
if (vk.texGenModeT() == VertexKey.TG_OBJ_LINEAR) {
uploadPlane(program.locTexGenObjPlaneT, tg.getObjectPlane(org.lwjgl.opengl.GL11.GL_T));
} else if (vk.texGenModeT() == VertexKey.TG_EYE_LINEAR) {
uploadPlane(program.locTexGenEyePlaneT, tg.getEyePlane(org.lwjgl.opengl.GL11.GL_T));
}
if (vk.texGenModeR() == VertexKey.TG_OBJ_LINEAR) {
uploadPlane(program.locTexGenObjPlaneR, tg.getObjectPlane(org.lwjgl.opengl.GL11.GL_R));
} else if (vk.texGenModeR() == VertexKey.TG_EYE_LINEAR) {
uploadPlane(program.locTexGenEyePlaneR, tg.getEyePlane(org.lwjgl.opengl.GL11.GL_R));
}
if (vk.texGenModeQ() == VertexKey.TG_OBJ_LINEAR) {
uploadPlane(program.locTexGenObjPlaneQ, tg.getObjectPlane(org.lwjgl.opengl.GL11.GL_Q));
} else if (vk.texGenModeQ() == VertexKey.TG_EYE_LINEAR) {
uploadPlane(program.locTexGenEyePlaneQ, tg.getEyePlane(org.lwjgl.opengl.GL11.GL_Q));
}
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));
putPlane(FFPUniformBlock.TEX_GEN_OBJ_PLANE_Q, tg.getObjectPlane(GL11.GL_Q));
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_S, tg.getEyePlane(GL11.GL_S));
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_T, tg.getEyePlane(GL11.GL_T));
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_R, tg.getEyePlane(GL11.GL_R));
putPlane(FFPUniformBlock.TEX_GEN_EYE_PLANE_Q, tg.getEyePlane(GL11.GL_Q));
}
private void uploadPlane(int loc, float[] plane) {
if (loc == -1) return;
vec4Buf.clear();
vec4Buf.put(plane, 0, 4);
vec4Buf.flip();
RENDER_BACKEND.uniform4(loc, vec4Buf);
}
private void uploadClipPlanes(Program program) {
if (program.locClipPlanes == -1) return;
private void stageClipPlanes() {
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
clipPlaneBuf.clear();
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
cps.putEyePlane(i, clipPlaneBuf);
}
clipPlaneBuf.flip();
RENDER_BACKEND.uniform4(program.locClipPlanes, clipPlaneBuf);
final long src = memAddress0(clipPlaneBuf);
for (int i = 0; i < 32 * 4; i += 4) {
putInt(FFPUniformBlock.CLIP_PLANES + i, memGetInt(src + i));
}
}
private void uploadFragmentUniforms(Program program) {
final FragmentKey fk = program.getFragmentKey();
private void stageFragment() {
putFloat(FFPUniformBlock.ALPHA_REF, GLStateManager.getAlphaState().getReference());
// Alpha test reference
if (fk.alphaTestEnabled() && program.locAlphaRef != -1) {
RENDER_BACKEND.uniform1f(program.locAlphaRef, GLStateManager.getAlphaState().getReference());
}
// Per-unit tex env color
for (int i = 0; i < 4; i++) {
if (program.locTexEnvColor[i] != -1) {
final var envState = GLStateManager.getTextures().getTexEnvState(i);
vec4Buf.clear();
vec4Buf.put(envState.envColorR);
vec4Buf.put(envState.envColorG);
vec4Buf.put(envState.envColorB);
vec4Buf.put(envState.envColorA);
vec4Buf.flip();
RENDER_BACKEND.uniform4(program.locTexEnvColor[i], vec4Buf);
}
final var envState = GLStateManager.getTextures().getTexEnvState(i);
putVec4(FFPUniformBlock.TEX_ENV_COLOR_0 + i * 16,
envState.envColorR, envState.envColorG, envState.envColorB, envState.envColorA);
}
if (fk.overlayEnabled() && program.locOverlayColor != -1) {
vec4Buf.clear();
vec4Buf.put(GLStateManager.getOverlayR());
vec4Buf.put(GLStateManager.getOverlayG());
vec4Buf.put(GLStateManager.getOverlayB());
vec4Buf.put(GLStateManager.getOverlayA());
vec4Buf.flip();
RENDER_BACKEND.uniform4(program.locOverlayColor, vec4Buf);
}
putVec4(FFPUniformBlock.OVERLAY_COLOR,
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
// Fog
if (fk.fogMode() != FragmentKey.FOG_NONE) {
uploadFog(program);
}
}
/**
* Upload optimized fog params (following Mesa's STATE_FOG_PARAMS_OPTIMIZED):
*/
private void uploadFog(Program program) {
// Mesa STATE_FOG_PARAMS_OPTIMIZED
final FogState fog = GLStateManager.getFogState();
if (program.locFogParams != -1) {
final float start = fog.getStart();
final float end = fog.getEnd();
final float density = fog.getDensity();
final float range = end - start;
vec4Buf.clear();
vec4Buf.put(range != 0.0f ? -1.0f / range : 0.0f); // [0]: -1/(end-start)
vec4Buf.put(range != 0.0f ? end / range : 1.0f); // [1]: end/(end-start)
vec4Buf.put((float)(density / LN2)); // [2]: density/ln(2)
vec4Buf.put((float)(density / SQRT_LN2)); // [3]: density/sqrt(ln(2))
vec4Buf.flip();
RENDER_BACKEND.uniform4(program.locFogParams, vec4Buf);
}
if (program.locFogColor != -1) {
vec4Buf.clear();
vec4Buf.put((float) fog.getFogColor().x);
vec4Buf.put((float) fog.getFogColor().y);
vec4Buf.put((float) fog.getFogColor().z);
vec4Buf.put(fog.getFogAlpha());
vec4Buf.flip();
RENDER_BACKEND.uniform4(program.locFogColor, vec4Buf);
}
final float start = fog.getStart();
final float end = fog.getEnd();
final float density = fog.getDensity();
final float range = end - start;
putVec4(FFPUniformBlock.FOG_PARAMS,
range != 0.0f ? -1.0f / range : 0.0f,
range != 0.0f ? end / range : 1.0f,
(float) (density / LN2),
(float) (density / SQRT_LN2));
putVec4(FFPUniformBlock.FOG_COLOR,
(float) fog.getFogColor().x, (float) fog.getFogColor().y, (float) fog.getFogColor().z,
fog.getFogAlpha());
}
private void uploadWideLineUniforms(Program program) {
final Program.UploadState st = program.uploadState;
final float lineWidth = GLStateManager.getLineState().getWidth();
if (lineWidth != st.lineWidth) {
RENDER_BACKEND.uniform1f(program.locLineWidth, lineWidth);
st.lineWidth = lineWidth;
}
final int vw = GLStateManager.getViewportState().width;
final int vh = GLStateManager.getViewportState().height;
if (vw != st.viewportWidth || vh != st.viewportHeight) {
RENDER_BACKEND.uniform2f(program.locViewportSize, vw, vh);
st.viewportWidth = vw;
st.viewportHeight = vh;
}
private void putFloat(int offset, float v) {
final long a = stagingAddress + offset;
final int bits = Float.floatToRawIntBits(v);
if (memGetInt(a) != bits) contentChanged = true;
memPutInt(a, bits);
}
private void uploadVec4(int loc, Vector4f v) {
if (loc == -1) return;
vec4Buf.clear();
v.get(vec4Buf);
RENDER_BACKEND.uniform4(loc, vec4Buf);
private void putInt(int offset, int v) {
final long a = stagingAddress + offset;
if (memGetInt(a) != v) contentChanged = true;
memPutInt(a, v);
}
private void putMat4(int offset, Matrix4f m) {
putVec4(offset, m.m00(), m.m01(), m.m02(), m.m03());
putVec4(offset + 16, m.m10(), m.m11(), m.m12(), m.m13());
putVec4(offset + 32, m.m20(), m.m21(), m.m22(), m.m23());
putVec4(offset + 48, m.m30(), m.m31(), m.m32(), m.m33());
}
/** std140 mat3: three vec4-aligned columns. */
private void putMat3(int offset, Matrix3f m) {
putVec3(offset, m.m00(), m.m01(), m.m02());
putVec3(offset + 16, m.m10(), m.m11(), m.m12());
putVec3(offset + 32, m.m20(), m.m21(), m.m22());
}
private void putVec4(int offset, Vector4f v) {
putVec4(offset, v.x, v.y, v.z, v.w);
}
private void putVec4(int offset, float x, float y, float z, float w) {
putFloat(offset, x);
putFloat(offset + 4, y);
putFloat(offset + 8, z);
putFloat(offset + 12, w);
}
private void putVec3(int offset, float x, float y, float z) {
putFloat(offset, x);
putFloat(offset + 4, y);
putFloat(offset + 8, z);
}
private void putPlane(int offset, float[] plane) {
putVec4(offset, plane[0], plane[1], plane[2], plane[3]);
}
public void endFrame() {
if (ring != null) {
ring.endFrame();
}
lastFrameBlockWrites = blockWrites;
lastFrameBlockSkips = blockSkips;
lastFrameStagedMatrices = stagedMatrices;
lastFrameStagedLighting = stagedLighting;
lastFrameStagedFragment = stagedFragment;
lastFrameStagedColor = stagedColor;
lastFrameStagedNormal = stagedNormal;
lastFrameStagedTexCoord = stagedTexCoord;
lastFrameStagedLightmap = stagedLightmap;
lastFrameStagedTexGen = stagedTexGen;
lastFrameStagedClipPlanes = stagedClipPlanes;
lastFrameStagedMisc = stagedMisc;
blockWrites = 0;
blockSkips = 0;
stagedMatrices = 0;
stagedLighting = 0;
stagedFragment = 0;
stagedColor = 0;
stagedNormal = 0;
stagedTexCoord = 0;
stagedLightmap = 0;
stagedTexGen = 0;
stagedClipPlanes = 0;
stagedMisc = 0;
}
UniformRingBuffer getRing() {
return ring;
}
ByteBuffer getStaging() {
return staging;
}
public void destroy() {
memFree(mat4Buf);
memFree(mat3Buf);
memFree(vec4Buf);
memFree(vec3Buf);
memFree(staging);
memFree(clipPlaneBuf);
if (ring != null) {
ring.destroy();
ring = null;
}
bound = false;
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -15,6 +16,8 @@ public final class VAOManager {
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
static final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
private static VAOData current = null;
private static int currentVertexFlags = 0;
private static Attrib[] currentAttribs = null;
@@ -38,8 +41,13 @@ public final class VAOManager {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.ebo = boundEBO;
if (current.ebo != boundEBO) {
removeEboOwner(current.ebo, current);
current.ebo = boundEBO;
addEboOwner(current.ebo, current);
}
current.attribs = currentAttribs;
current.clientSideEnabledCount = clientSideEnabledCount;
}
VAOData data = vaoMap.get(vaoId);
@@ -51,12 +59,50 @@ public final class VAOManager {
currentVertexFlags = data.vertexFlags;
currentAttribs = data.attribs;
boundEBO = data.ebo;
recomputeClientSideCount();
clientSideEnabledCount = data.clientSideEnabledCount;
}
public static void onDeleteVertexArray(int vaoId) {
vaoMap.remove(vaoId);
final VAOData data = vaoMap.remove(vaoId);
if (data == null) return;
removeEboOwner(data.ebo, data);
if (data == current) current = null;
}
public static void onDeleteBuffer(int buffer) {
if (buffer == 0) return;
VAOData owner = eboOwners.remove(buffer);
while (owner != null) {
final VAOData next = owner.nextEboOwner;
owner.nextEboOwner = null;
owner.ebo = 0;
owner = next;
}
}
private static void addEboOwner(int ebo, VAOData data) {
if (ebo == 0) return;
data.nextEboOwner = eboOwners.put(ebo, data);
}
private static void removeEboOwner(int ebo, VAOData data) {
if (ebo == 0) return;
VAOData node = eboOwners.get(ebo);
if (node == data) {
final VAOData next = data.nextEboOwner;
if (next == null) eboOwners.remove(ebo); else eboOwners.put(ebo, next);
data.nextEboOwner = null;
return;
}
while (node != null) {
final VAOData next = node.nextEboOwner;
if (next == data) {
node.nextEboOwner = data.nextEboOwner;
data.nextEboOwner = null;
return;
}
node = next;
}
}
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
@@ -68,6 +114,8 @@ public final class VAOManager {
public Attrib[] attribs;
public int vertexFlags;
public int ebo;
public int clientSideEnabledCount;
private VAOData nextEboOwner;
public VAOData() {
attribs = new Attrib[MAX_ATTRIBS];
@@ -85,6 +133,7 @@ public final class VAOManager {
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
a.genericPointer = true;
if (wasClient && a.enabled) {
clientSideEnabledCount--;
}
@@ -101,11 +150,24 @@ public final class VAOManager {
a.offset = 0;
a.vboId = 0;
a.clientPointer = pointer;
a.genericPointer = true;
if (!wasClient && a.enabled) {
clientSideEnabledCount++;
}
}
public static boolean isGenericPointerEnabled(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
final Attrib a = currentAttribs[index];
return a != null && a.genericPointer && a.enabled;
}
public static void markConventional(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = currentAttribs[index];
if (a != null) a.genericPointer = false;
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
@@ -169,14 +231,6 @@ public final class VAOManager {
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (isClientSideAttrib(i)) count++;
}
clientSideEnabledCount = count;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
@@ -239,6 +293,7 @@ public final class VAOManager {
public long offset;
public int vboId;
public ByteBuffer clientPointer;
public boolean genericPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
@@ -253,6 +308,7 @@ public final class VAOManager {
offset = 0;
vboId = 0;
clientPointer = null;
genericPointer = false;
}
public int effectiveStride() {
@@ -260,17 +316,7 @@ public final class VAOManager {
}
public int typeSizeBytes() {
return glTypeSizeBytes(type);
}
public static int glTypeSizeBytes(int glType) {
return switch (glType) {
case GL11.GL_FLOAT, GL11.GL_INT, GL11.GL_UNSIGNED_INT -> 4;
case GL11.GL_DOUBLE -> 8;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT -> 2;
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
default -> 4;
};
return GLTypes.sizeBytes(type);
}
public float readComponent(ByteBuffer buf, int base, int component) {
@@ -50,6 +50,8 @@ public final class VertexKey {
// Per-unit texmat enable: bits 37-40 (unit 0..3). Unit 1 is the lightmap matrix.
private static final int BIT_UNIT_TEXMAT_BASE = 37;
private static final int BIT_UNIT23_UV_FROM_UNIT0 = 41;
private static final int BIT_LINE_STIPPLE = 42;
private static final int BIT_INSTANCED = 44;
public static final int TG_NONE = 0;
public static final int TG_OBJ_LINEAR = 1;
@@ -98,6 +100,8 @@ public final class VertexKey {
public boolean texGenEnabled() { return texGenModeS() != TG_NONE || texGenModeT() != TG_NONE || texGenModeR() != TG_NONE || texGenModeQ() != TG_NONE; }
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
public boolean instancedDraw() { return bit(BIT_INSTANCED); }
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
public boolean cmReplacesDiffuse() { final int m = colorMaterialMode(); return m == CM_DIFFUSE || m == CM_AMBIENT_AND_DIFFUSE; }
@@ -227,6 +231,14 @@ public final class VertexKey {
bits |= (1L << BIT_WIDE_LINE);
}
if (GLStateManager.lineStippleActive) {
bits |= (1L << BIT_LINE_STIPPLE);
}
if (GLStateManager.instancedFfpDrawActive) {
bits |= (1L << BIT_INSTANCED);
}
return bits;
}
@@ -252,12 +264,12 @@ 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]",
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(), light0Enabled(), light1Enabled(),
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());
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancedDraw());
}
}
@@ -29,6 +29,9 @@ public final class VertexShaderGenerator {
emitColorPassthrough(sb, key);
}
emitTexCoordPassthrough(sb, key);
if (key.lineStipple()) {
emitLineStart(sb);
}
if (key.fogEnabled()) {
emitFogDistance(sb, key);
}
@@ -45,6 +48,7 @@ public final class VertexShaderGenerator {
.replace("v_TexCoord2", "v_TexCoord2_gs")
.replace("v_TexCoord3", "v_TexCoord3_gs")
.replace("v_FogCoord", "v_FogCoord_gs")
.replace("v_LineStart", "v_LineStart_gs")
.replace("v_Color", "v_Color_gs");
}
return source;
@@ -65,123 +69,33 @@ public final class VertexShaderGenerator {
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");
}
sb.append('\n');
}
private static void emitLineStart(StringBuilder sb) {
sb.append(" vec2 ndc = gl_Position.xy / gl_Position.w;\n");
sb.append(" v_LineStart = u_Viewport.xy + (ndc * 0.5 + 0.5) * u_Viewport.zw;\n\n");
}
private static void emitUniforms(StringBuilder sb, VertexKey key) {
sb.append("// Matrices\n");
sb.append("uniform mat4 u_ModelViewMatrix;\n");
sb.append("uniform mat4 u_ProjectionMatrix;\n");
sb.append("uniform mat4 u_MVPMatrix;\n");
if (key.lightingEnabled()) {
sb.append("uniform mat3 u_NormalMatrix;\n");
}
for (int i = 0; i < VertexKey.MAX_UNITS; i++) {
if (i == 1) continue;
if (key.unitTexMatEnabled(i)) {
sb.append("uniform mat4 u_TextureMatrix").append(i).append(";\n");
}
}
// TexGen plane uniforms (unit 0 only - TODO: per-unit)
if (key.texGenModeS() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneS;\n");
if (key.texGenModeS() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneS;\n");
if (key.texGenModeT() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneT;\n");
if (key.texGenModeT() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneT;\n");
if (key.texGenModeR() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneR;\n");
if (key.texGenModeR() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneR;\n");
if (key.texGenModeQ() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneQ;\n");
if (key.texGenModeQ() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneQ;\n");
if (!key.hasVertexNormal() && key.lightingEnabled()) {
sb.append("uniform vec3 u_CurrentNormal;\n");
}
if (!key.hasVertexColor()) {
sb.append("uniform vec4 u_CurrentColor;\n");
}
if (key.unitTexCoordEnabled(0) && !key.hasVertexTexCoord()) {
sb.append("uniform vec4 u_CurrentTexCoord0;\n");
}
if (key.unitTexCoordEnabled(2) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
if (key.unitTexCoordEnabled(3) && !key.unit23UvFromUnit0()) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
if (key.lightmapEnabled() && !key.hasVertexLightmap()) {
sb.append("uniform vec2 u_CurrentLightmapCoord;\n");
}
if (key.lightmapEnabled()) {
sb.append("uniform mat4 u_LightmapTextureMatrix;\n");
}
if (key.lightingEnabled()) {
emitLightingUniforms(sb, key);
}
if (key.lightingEnabled() && (key.normalizeEnabled() || key.rescaleNormalsEnabled())) {
// normalize is done in shader; rescale uses normalScale
if (key.rescaleNormalsEnabled() && !key.normalizeEnabled()) {
sb.append("uniform float u_NormalScale;\n");
}
}
if (key.clipPlanesEnabled()) {
sb.append("uniform vec4 u_ClipPlane[8];\n");
}
sb.append(FFPUniformBlock.GLSL_DECL);
sb.append('\n');
}
private static void emitLightingUniforms(StringBuilder sb, VertexKey key) {
sb.append("\n// Lighting\n");
if (key.colorMaterialEnabled()) {
// When color material is active, we can't pre-compute light products so we upload raw light and material values
sb.append("uniform vec4 u_LightModelAmbient;\n");
sb.append("uniform vec4 u_MaterialEmission;\n");
sb.append("uniform vec4 u_MaterialAmbient;\n");
sb.append("uniform vec4 u_MaterialDiffuse;\n");
sb.append("uniform vec4 u_MaterialSpecular;\n");
sb.append("uniform float u_MaterialShininess;\n");
if (key.light0Enabled()) {
sb.append("uniform vec4 u_Light0Ambient;\n");
sb.append("uniform vec4 u_Light0Diffuse;\n");
sb.append("uniform vec4 u_Light0Specular;\n");
sb.append("uniform vec4 u_Light0Position;\n");
}
if (key.light1Enabled()) {
sb.append("uniform vec4 u_Light1Ambient;\n");
sb.append("uniform vec4 u_Light1Diffuse;\n");
sb.append("uniform vec4 u_Light1Specular;\n");
sb.append("uniform vec4 u_Light1Position;\n");
}
} else {
// Pre-computed: sceneColor + lightProducts
sb.append("uniform vec4 u_SceneColor;\n");
if (key.light0Enabled()) {
sb.append("uniform vec4 u_Light0Position;\n");
sb.append("uniform vec3 u_LightProd0Ambient;\n");
sb.append("uniform vec3 u_LightProd0Diffuse;\n");
sb.append("uniform vec3 u_LightProd0Specular;\n");
}
if (key.light1Enabled()) {
sb.append("uniform vec4 u_Light1Position;\n");
sb.append("uniform vec3 u_LightProd1Ambient;\n");
sb.append("uniform vec3 u_LightProd1Diffuse;\n");
sb.append("uniform vec3 u_LightProd1Specular;\n");
}
sb.append("uniform float u_MaterialShininess;\n");
}
}
private static void emitOutputs(StringBuilder sb, VertexKey key) {
sb.append("// Outputs\n");
sb.append("out vec4 v_Color;\n");
if (key.lineStipple()) {
sb.append("flat out vec2 v_LineStart;\n");
}
if (key.separateSpecular()) {
sb.append("out vec3 v_SpecularColor;\n");
}
@@ -203,22 +117,30 @@ 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");
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
if (key.instancedDraw()) {
sb.append(" mat4 instMV = mat4(a_InstCol0, a_InstCol1, a_InstCol2, a_InstCol3);\n");
sb.append(" vec4 eyePos = instMV * pos4;\n");
sb.append(" gl_Position = u_ProjectionMatrix * eyePos;\n");
} else {
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
// Eye position needed for lighting, fog, EYE_LINEAR texgen, and clip planes
if (key.lightingEnabled() || key.fogEnabled() || texGenNeedsEyePos(key) || key.clipPlanesEnabled()) {
sb.append(" vec4 eyePos = u_ModelViewMatrix * pos4;\n");
// Eye position needed for lighting, fog, EYE_LINEAR texgen, and clip planes
if (key.lightingEnabled() || key.fogEnabled() || texGenNeedsEyePos(key) || key.clipPlanesEnabled()) {
sb.append(" vec4 eyePos = u_ModelViewMatrix * pos4;\n");
}
}
sb.append('\n');
}
private static void emitNormalTransform(StringBuilder sb, VertexKey key) {
sb.append(" // Normal transform\n");
if (key.hasVertexNormal()) {
sb.append(" vec3 normal = u_NormalMatrix * a_Normal;\n");
} else {
sb.append(" vec3 normal = u_NormalMatrix * u_CurrentNormal;\n");
final String srcNormal = key.hasVertexNormal() ? "a_Normal" : "u_CurrentNormal";
if (key.instancedDraw()) {
sb.append(" vec3 normal = normalize(mat3(instMV) * ").append(srcNormal).append(");\n");
sb.append('\n');
return;
}
sb.append(" vec3 normal = u_NormalMatrix * ").append(srcNormal).append(";\n");
if (key.normalizeEnabled()) {
sb.append(" normal = normalize(normal);\n");
@@ -235,6 +157,9 @@ public final class VertexShaderGenerator {
// 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");
}
sb.append(";\n");
// Emission source: vertex color for CM_EMISSION, else material uniform
@@ -330,10 +255,11 @@ public final class VertexShaderGenerator {
private static void emitColorPassthrough(StringBuilder sb, VertexKey key) {
sb.append(" // Color passthrough (no lighting)\n");
if (key.hasVertexColor()) {
sb.append(" v_Color = a_Color;\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 = u_CurrentColor;\n");
sb.append(" v_Color = ").append(src).append(";\n");
}
sb.append('\n');
}
@@ -354,7 +280,9 @@ public final class VertexShaderGenerator {
}
if (key.lightmapEnabled()) {
if (key.hasVertexLightmap()) {
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");
@@ -385,7 +313,7 @@ public final class VertexShaderGenerator {
emitTexGenComponent(sb, key.texGenModeS(), "s", "S", key);
emitTexGenComponent(sb, key.texGenModeT(), "t", "T", key);
emitTexGenComponent(sb, key.texGenModeR(), "r", "R", key);
emitTexGenComponent(sb, key.texGenModeR(), "p", "R", key);
emitTexGenComponent(sb, key.texGenModeQ(), "q", "Q", key);
// Always apply texture matrix when texgen is active (forced on in VertexKey)
@@ -8,5 +8,7 @@ public final class GLSMConfig {
public static boolean hudCacheOverride;
public static volatile boolean expandVertexFormats;
private GLSMConfig() {}
}
@@ -8,28 +8,37 @@ public final class GLSMHooks {
public static DeferredBlendHandler blendHandler;
public static DeferredAlphaHandler alphaHandler;
public static DeferredDepthColorHandler depthColorHandler;
public static GlintColorHandler glintColorHandler;
public static ShaderWorkSubmitter shaderWorkSubmitter;
public static ShaderTransformPostProcessor postTransformProcessor;
public static PerFrameUniformBlock perFrameUniformBlock;
public static PerFrameUniformBlock perPassUniformBlock;
// EventBus instances
public static final EventBus<TextureBindEvent> TEXTURE_BIND = EventBus.create(TextureBindEvent.class);
public static final EventBus<TextureDeleteEvent> TEXTURE_DELETE = EventBus.create(TextureDeleteEvent.class);
public static final EventBus<TextureUnitStateEvent> TEXTURE_UNIT_STATE = EventBus.create(TextureUnitStateEvent.class);
public static final EventBus<ProgramChangeEvent> PROGRAM_CHANGE = EventBus.create(ProgramChangeEvent.class);
public static final EventBus<ProgramDeleteEvent> PROGRAM_DELETE = EventBus.create(ProgramDeleteEvent.class);
public static final EventBus<BlendFuncChangeEvent> BLEND_FUNC_CHANGE = EventBus.create(BlendFuncChangeEvent.class);
public static final EventBus<FogStateChangeEvent> FOG_STATE_CHANGE = EventBus.create(FogStateChangeEvent.class);
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
public static final EventBus<LoadingCheckpointEvent> LOADING_CHECKPOINT = EventBus.create(LoadingCheckpointEvent.class);
// Reusable event instances
public static final TextureBindEvent textureBindEvent = new TextureBindEvent();
public static final TextureDeleteEvent textureDeleteEvent = new TextureDeleteEvent();
public static final TextureUnitStateEvent textureUnitStateEvent = new TextureUnitStateEvent();
public static final ProgramChangeEvent programChangeEvent = new ProgramChangeEvent();
public static final ProgramDeleteEvent programDeleteEvent = new ProgramDeleteEvent();
public static final BlendFuncChangeEvent blendFuncChangeEvent = new BlendFuncChangeEvent();
public static final FogStateChangeEvent fogStateChangeEvent = new FogStateChangeEvent();
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
public static final LoadingCheckpointEvent loadingCheckpointEvent = new LoadingCheckpointEvent();
private GLSMHooks() {}
}
@@ -2,19 +2,20 @@ package com.gtnewhorizons.angelica.glsm.hooks;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import lombok.Getter;
import java.util.function.Consumer;
public final class GLSMInitConfig {
private final boolean lwjglDebug;
private final StreamingUploader.UploadStrategy streamingUploadStrategy;
private final Consumer<DirectTessellator> directDrawer;
private final Runnable streamingDrawerDestroy;
private final int displayWidth;
private final int displayHeight;
private final Runnable postInitCallback;
private final boolean enableDSA;
private final boolean noErrorChecks;
@Getter private final boolean lwjglDebug;
@Getter private final StreamingUploader.UploadStrategy streamingUploadStrategy;
@Getter private final Consumer<DirectTessellator> directDrawer;
@Getter private final Runnable streamingDrawerDestroy;
@Getter private final int displayWidth;
@Getter private final int displayHeight;
@Getter private final Runnable postInitCallback;
@Getter private final boolean DSAEnabled;
@Getter private final boolean noErrorChecks;
private GLSMInitConfig(Builder builder) {
this.lwjglDebug = builder.lwjglDebug;
@@ -25,23 +26,13 @@ public final class GLSMInitConfig {
this.displayWidth = builder.displayWidth;
this.displayHeight = builder.displayHeight;
this.postInitCallback = builder.postInitCallback;
this.enableDSA = builder.enableDSA;
this.DSAEnabled = builder.enableDSA;
}
public static Builder builder() {
return new Builder();
}
public boolean isLwjglDebug() { return lwjglDebug; }
public StreamingUploader.UploadStrategy getStreamingUploadStrategy() { return streamingUploadStrategy; }
public Consumer<DirectTessellator> getDirectDrawer() { return directDrawer; }
public Runnable getStreamingDrawerDestroy() { return streamingDrawerDestroy; }
public int getDisplayWidth() { return displayWidth; }
public int getDisplayHeight() { return displayHeight; }
public Runnable getPostInitCallback() { return postInitCallback; }
public boolean isDSAEnabled() { return enableDSA; }
public boolean noErrorChecks() { return noErrorChecks; }
public static final class Builder {
private boolean lwjglDebug = false;
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
@@ -0,0 +1,5 @@
package com.gtnewhorizons.angelica.glsm.hooks;
public interface GlintColorHandler {
void onColorChanged(float red, float green, float blue, float alpha);
}
@@ -0,0 +1,18 @@
package com.gtnewhorizons.angelica.glsm.hooks;
import com.gtnewhorizons.angelica.glsm.shader.UniformType;
import java.util.List;
/**
* Shared per-frame uniform block for SDL GPU. Produced by Iris once per shader pack load. Every shader declares the
* whole block regardless of which members it reads.
*/
public record PerFrameUniformBlock(List<Member> members) {
public record Member(String name, UniformType type) {}
public boolean isEmpty() {
return members.isEmpty();
}
}
@@ -0,0 +1,13 @@
package com.gtnewhorizons.angelica.glsm.hooks;
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
import org.taumc.glsl.grammar.GLSLParser;
@FunctionalInterface
public interface ShaderTransformPostProcessor {
void onTransformed(String transformedSource, ShaderType shaderType);
default void onTransformed(String transformedSource, GLSLParser.Translation_unitContext bodyTree, int headerLen, ShaderType shaderType) {
onTransformed(transformedSource, shaderType);
}
}
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.glsm.hooks;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public interface ShaderWorkSubmitter {
<T> CompletableFuture<T> submit(Supplier<T> work);
}
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
/** Fired at loading-phase boundaries */
public final class LoadingCheckpointEvent extends MutableEvent {
public boolean requiresSync;
}
@@ -0,0 +1,7 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class ProgramDeleteEvent extends MutableEvent {
public int program;
}
@@ -21,11 +21,11 @@ public final class EcosystemNarrowRules {
"Reika.DragonAPI.ASM.StructureLootHooks"
)),
new NarrowRule("Xaeros_common", "xaero.common.core", List.of(
new NarrowRule("Xaeros", "xaero.common.core", List.of(
"xaero.common.core.transformer"
)),
new NarrowRule("Xaeros_map", "xaero.map.core", List.of(
new NarrowRule("Xaeros", "xaero.map.core", List.of(
"xaero.map.core.transformer"
)),
@@ -78,6 +78,13 @@ public final class EcosystemNarrowRules {
"alfheim.common.core.asm.hook.fixes.CorporeaInputFix",
"alfheim.common.core.asm.hook.fixes.GodAttributesHooks",
"alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix"
)),
new NarrowRule("LegendsMod", "com.tihyo.legends.management.asm", List.of(
"com.tihyo.legends.management.asm.LegendsLoadingPlugin",
"com.tihyo.legends.management.asm.AbstractTransformer",
"com.tihyo.legends.management.asm.ASMHelper",
"com.tihyo.legends.management.asm.Transformer"
))
);
@@ -25,7 +25,7 @@ public final class TransformerNarrower {
/**
* A rule that replaces a broad transformer exclusion with narrower ones.
* @param name Human-readable name for logging (e.g. "DragonAPI")
* @param name Blackboard/config key suffix. (e.g. "DragonAPI").
* @param broad The broad exclusion to remove (e.g. "Reika.DragonAPI.ASM")
* @param narrow Narrower exclusions to add in its place
*/
@@ -0,0 +1,422 @@
package com.gtnewhorizons.angelica.glsm.profiling;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.CaptureGate;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.sun.management.ThreadMXBean;
import lombok.Setter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@SuppressWarnings("ForLoopReplaceableByForEach")
public final class Tracy {
private static final Logger LOGGER = LogManager.getLogger("Tracy");
public static final boolean ENABLED;
public static final boolean FINE_ZONES;
private static final TracyBackend BACKEND;
private static volatile boolean SHUTTING_DOWN;
private static final ConcurrentHashMap<String, Long> CLIENT_SRC_LOCS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Long> SERVER_SRC_LOCS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Long> PLAIN_SRC_LOCS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Long> PLOT_NAMES = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Long> FRAME_NAMES = new ConcurrentHashMap<>();
private static final Set<String> WARNED_LEAKS = ConcurrentHashMap.newKeySet();
public static final int COLOR_CLIENT = 0x3A6EA5; // blue
public static final int COLOR_SERVER = 0xB5651D; // brown
public static final int COLOR_WORKER = 0x3F7F3F; // green
public static final int COLOR_SWAP = 0xAA3333; // red
public static final int COLOR_TERRAIN = 0x2E8B8B; // teal
public static final int COLOR_IRIS = 0x9E862E; // gold
public static final int COLOR_FFP = 0x7A4E9E; // purple
private static final ThreadLocal<ZoneStack> STACK = new ThreadLocal<>() {
@Override
protected ZoneStack initialValue() {
BACKEND.setCurrentThreadName(Thread.currentThread().getName());
return new ZoneStack();
}
};
static {
TracyBackend backend = null;
if (SystemProperties.TRACY) {
backend = loadAndInit();
}
BACKEND = backend;
ENABLED = backend != null;
FINE_ZONES = ENABLED && SystemProperties.TRACY_FINE_ZONES;
if (ENABLED) {
Runtime.getRuntime().addShutdownHook(new Thread(Tracy::shutdown, "Tracy-Shutdown"));
LOGGER.info("Tracy profiling enabled");
}
}
private Tracy() {}
private static void shutdown() {
SHUTTING_DOWN = true;
final Thread worker = new Thread(BACKEND::shutdown, "Tracy-Shutdown-Worker");
worker.setDaemon(true);
worker.start();
try {
worker.join(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static TracyBackend loadAndInit() {
TracyBackend found = null;
try {
final Iterator<TracyBackend> it = ServiceLoader.load(TracyBackend.class, TracyBackend.class.getClassLoader()).iterator();
while (found == null && it.hasNext()) {
found = it.next();
}
} catch (ServiceConfigurationError | LinkageError e) {
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but backend unavailable: {}", e.getMessage());
return null;
}
if (found == null) {
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but no TracyBackend service present");
return null;
}
final TracyBackend backend = found;
final CountDownLatch done = new CountDownLatch(1);
final AtomicReference<Boolean> result = new AtomicReference<>();
final Thread init = new Thread(new InitRunner(backend, done, result), "Tracy-Init");
init.setDaemon(true);
init.start();
try {
if (!done.await(15, TimeUnit.SECONDS) && result.compareAndSet(null, Boolean.FALSE)) {
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but init timed out; profiling disabled");
return null;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (!result.compareAndSet(null, Boolean.FALSE) && Boolean.TRUE.equals(result.get())) {
backend.shutdown();
}
return null;
}
if (!Boolean.TRUE.equals(result.get())) {
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but native init failed; profiling disabled");
return null;
}
return backend;
}
private record InitRunner(TracyBackend backend, CountDownLatch done, AtomicReference<Boolean> result) implements Runnable {
@Override
public void run() {
final boolean ok = backend.init();
if (!result.compareAndSet(null, ok) && ok) {
backend.shutdown();
}
done.countDown();
}
}
public static void beginSection(String name) {
if (!ENABLED) return;
if ("root".equals(name)) {
unwindToRoot(STACK.get());
}
if (!CaptureGate.markersThisFrame) {
skip(name);
return;
}
final boolean client = Thread.currentThread() == GLStateManager.getMainThread();
final ConcurrentHashMap<String, Long> map = client ? CLIENT_SRC_LOCS : SERVER_SRC_LOCS;
Long srcLoc = map.get(name);
if (srcLoc == null) {
srcLoc = map.computeIfAbsent(name, n -> BACKEND.internSrcLoc((client ? "cl:" : "sv:") + n, client ? classifySection(n) : COLOR_SERVER));
}
begin(srcLoc, name);
}
private static int classifySection(String name) {
if (name.startsWith("iris_")) return COLOR_IRIS;
if (name.startsWith("draw_chunk_layer")) return COLOR_TERRAIN;
return COLOR_CLIENT;
}
private static void unwindToRoot(ZoneStack stack) {
while (stack.depth() > 0 || stack.hasSkipped()) {
final long ctx = stack.pop();
if (ctx == ZoneStack.EMPTY) continue;
if (stack.poppedGpu()) {
BACKEND.gpuEndZone();
}
if (ctx != 0L) {
BACKEND.endZone(ctx);
}
final String name = stack.poppedName();
if (name != null && WARNED_LEAKS.add(name)) {
LOGGER.warn("Tracy: unbalanced profiler section '{}' left open (leaked by its caller); closed at next root", name);
BACKEND.message("unbalanced profiler section '" + name + "' closed at root");
}
}
}
public static void beginZone(String name) {
beginZone(name, COLOR_WORKER);
}
public static void beginZone(String name, int color) {
if (!ENABLED || SHUTTING_DOWN) return;
if (!CaptureGate.markersThisFrame) {
skip(name);
return;
}
Long srcLoc = PLAIN_SRC_LOCS.get(name);
if (srcLoc == null) {
srcLoc = PLAIN_SRC_LOCS.computeIfAbsent(name, n -> BACKEND.internSrcLoc(n, color));
}
begin(srcLoc, name);
}
public static final class ZoneId {
private final long srcLoc;
private final String name;
private ZoneId(long srcLoc, String name) {
this.srcLoc = srcLoc;
this.name = name;
}
}
public static ZoneId zoneId(String name) {
return zoneId(name, COLOR_WORKER);
}
public static ZoneId zoneId(String name, int color) {
if (!ENABLED) return new ZoneId(0L, name);
Long srcLoc = PLAIN_SRC_LOCS.get(name);
if (srcLoc == null) {
srcLoc = PLAIN_SRC_LOCS.computeIfAbsent(name, n -> BACKEND.internSrcLoc(n, color));
}
return new ZoneId(srcLoc, name);
}
public static void beginZone(ZoneId zone) {
if (!ENABLED || SHUTTING_DOWN) return;
begin(zone.srcLoc, zone.name);
}
private static void skip(String name) {
STACK.get().push(0L, false, name);
}
private static void begin(long srcLoc, String name) {
if (SHUTTING_DOWN) return;
final ZoneStack stack = STACK.get();
if (srcLoc == 0 || !CaptureGate.markersThisFrame || stack.atCap()) {
stack.push(0L, false, name);
return;
}
final long ctx = BACKEND.beginZone(srcLoc);
final boolean gpu = gpuReady && gpuZonesEnabled && Thread.currentThread() == GLStateManager.getMainThread() && BACKEND.gpuBeginZone(srcLoc);
stack.push(ctx, gpu, name);
if (srcLoc == BACKEND.dynamicSrcLoc()) {
BACKEND.zoneText(ctx, name);
}
}
public static void endZone() {
if (!ENABLED || SHUTTING_DOWN) return;
final ZoneStack stack = STACK.get();
final long ctx = stack.pop();
if (ctx == ZoneStack.EMPTY) return;
if (stack.poppedGpu()) {
BACKEND.gpuEndZone();
}
if (ctx != 0L) {
BACKEND.endZone(ctx);
}
}
public static void zoneText(String text) {
if (!ENABLED || SHUTTING_DOWN) return;
final long ctx = STACK.get().peek();
if (ctx != ZoneStack.EMPTY && ctx != 0L) {
BACKEND.zoneText(ctx, text);
}
}
public static void zoneValue(long value) {
if (!ENABLED || SHUTTING_DOWN) return;
final long ctx = STACK.get().peek();
if (ctx != ZoneStack.EMPTY && ctx != 0L) {
BACKEND.zoneValue(ctx, value);
}
}
private static boolean threadNameRefreshed;
public static void frameMark() {
if (!ENABLED || SHUTTING_DOWN) return;
if (!threadNameRefreshed) {
threadNameRefreshed = true;
BACKEND.setCurrentThreadName(Thread.currentThread().getName());
}
BACKEND.frameMark();
}
public static void frameMark(String name) {
if (!ENABLED || SHUTTING_DOWN) return;
Long ptr = FRAME_NAMES.get(name);
if (ptr == null) {
ptr = FRAME_NAMES.computeIfAbsent(name, BACKEND::internFrameName);
}
if (ptr != 0) BACKEND.frameMark(ptr);
}
public static void plotInt(String name, long value) {
plotInt(name, value, TracyBackend.PLOT_FORMAT_NUMBER);
}
public static void plotInt(String name, long value, int format) {
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
final long ptr = plotName(name, format);
if (ptr != 0) BACKEND.plotInt(ptr, value);
}
public static void plot(String name, double value) {
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
final long ptr = plotName(name, TracyBackend.PLOT_FORMAT_NUMBER);
if (ptr != 0) BACKEND.plot(ptr, value);
}
private static long plotName(String name, int format) {
Long ptr = PLOT_NAMES.get(name);
if (ptr == null) {
ptr = PLOT_NAMES.computeIfAbsent(name, n -> BACKEND.internPlotName(n, format));
}
return ptr;
}
public static long plotHandle(String name, int format) {
if (!ENABLED || SHUTTING_DOWN) return 0;
return plotName(name, format);
}
public static long plotHandle(String name) {
return plotHandle(name, TracyBackend.PLOT_FORMAT_NUMBER);
}
public static void plotInt(long handle, long value) {
if (handle == 0 || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
BACKEND.plotInt(handle, value);
}
public static void plot(long handle, double value) {
if (handle == 0 || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
BACKEND.plot(handle, value);
}
public static void message(String text) {
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
BACKEND.message(text);
}
public static boolean isConnected() {
return ENABLED && BACKEND.isConnected();
}
private static boolean gpuInitAttempted;
private static boolean gpuReady;
@Setter private static boolean gpuZonesEnabled = true;
public static void gpuCollect() {
if (!ENABLED) return;
if (!gpuInitAttempted) {
gpuInitAttempted = true;
gpuReady = BACKEND.gpuInit();
}
if (gpuReady) BACKEND.gpuCollect();
}
private static final class AllocBean {
static final ThreadMXBean BEAN;
static {
ThreadMXBean bean = null;
try {
if (ManagementFactory.getThreadMXBean() instanceof ThreadMXBean sun && sun.isThreadAllocatedMemorySupported()) {
bean = sun;
}
} catch (Throwable ignored) {}
BEAN = bean;
}
}
private static final ThreadLocal<long[]> PREV_ALLOC = ThreadLocal.withInitial(() -> new long[1]);
public static void plotAllocRate(long plotHandle) {
if (!ENABLED || SHUTTING_DOWN || AllocBean.BEAN == null) return;
if (!CaptureGate.markersThisFrame) {
PREV_ALLOC.get()[0] = 0;
return;
}
final long now = AllocBean.BEAN.getThreadAllocatedBytes(Thread.currentThread().threadId());
if (now < 0) return;
final long[] prev = PREV_ALLOC.get();
if (prev[0] != 0) {
plotInt(plotHandle, now - prev[0]);
}
prev[0] = now;
}
private static List<GarbageCollectorMXBean> gcBeans;
private static boolean gcBaselineValid;
private static long prevGcCount;
private static long prevGcTimeMs;
private static long gcCountHandle;
private static long gcTimeHandle;
public static void plotGcStats() {
if (!ENABLED || SHUTTING_DOWN) return;
if (!CaptureGate.markersThisFrame) {
gcBaselineValid = false;
return;
}
if (gcBeans == null) {
gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
gcCountHandle = plotHandle("gcCount");
gcTimeHandle = plotHandle("gcTimeMs");
}
long count = 0;
long timeMs = 0;
for (int i = 0; i < gcBeans.size(); i++) {
final GarbageCollectorMXBean bean = gcBeans.get(i);
final long c = bean.getCollectionCount();
final long t = bean.getCollectionTime();
if (c > 0) count += c;
if (t > 0) timeMs += t;
}
if (gcBaselineValid) {
plotInt(gcCountHandle, count - prevGcCount);
plotInt(gcTimeHandle, timeMs - prevGcTimeMs);
if (count > prevGcCount) {
message("GC: +" + (count - prevGcCount) + " collections, +" + (timeMs - prevGcTimeMs) + " ms");
}
}
gcBaselineValid = true;
prevGcCount = count;
prevGcTimeMs = timeMs;
}
}
@@ -0,0 +1,36 @@
package com.gtnewhorizons.angelica.glsm.profiling;
public interface TracyBackend {
int PLOT_FORMAT_NUMBER = 0;
int PLOT_FORMAT_MEMORY = 1;
int PLOT_FORMAT_PERCENTAGE = 2;
boolean init();
long internSrcLoc(String name, int color);
long dynamicSrcLoc();
long beginZone(long srcLoc);
void endZone(long ctx);
void zoneText(long ctx, String text);
void zoneValue(long ctx, long value);
void frameMark();
void frameMark(long namePtr);
long internFrameName(String name);
long internPlotName(String name, int format);
void plotInt(long namePtr, long value);
void plot(long namePtr, double value);
void message(String text);
void setCurrentThreadName(String name);
boolean isConnected();
void shutdown();
boolean gpuInit();
boolean gpuBeginZone(long srcLoc);
void gpuEndZone();
void gpuCollect();
}
@@ -0,0 +1,87 @@
package com.gtnewhorizons.angelica.glsm.profiling;
public final class ZoneStack {
public static final long EMPTY = -1L;
static final int MAX_DEPTH = 512;
private long[] stack = new long[64];
private boolean[] gpu = new boolean[64];
private String[] names = new String[64];
private int depth;
private int skipped;
private boolean lastPoppedGpu;
private String lastPoppedName;
public boolean atCap() {
return depth == MAX_DEPTH;
}
public boolean push(long ctx) {
return push(ctx, false, null);
}
public boolean push(long ctx, boolean gpuBracketed) {
return push(ctx, gpuBracketed, null);
}
public boolean push(long ctx, boolean gpuBracketed, String name) {
if (depth == MAX_DEPTH) {
skipped++;
return false;
}
if (depth == stack.length) {
final long[] grownStack = new long[stack.length * 2];
System.arraycopy(stack, 0, grownStack, 0, stack.length);
stack = grownStack;
final boolean[] grownGpu = new boolean[gpu.length * 2];
System.arraycopy(gpu, 0, grownGpu, 0, gpu.length);
gpu = grownGpu;
final String[] grownNames = new String[names.length * 2];
System.arraycopy(names, 0, grownNames, 0, names.length);
names = grownNames;
}
gpu[depth] = gpuBracketed;
names[depth] = name;
stack[depth++] = ctx;
return true;
}
public long pop() {
if (skipped > 0) {
skipped--;
lastPoppedGpu = false;
lastPoppedName = null;
return EMPTY;
}
if (depth == 0) {
lastPoppedGpu = false;
lastPoppedName = null;
return EMPTY;
}
lastPoppedGpu = gpu[--depth];
lastPoppedName = names[depth];
names[depth] = null;
return stack[depth];
}
public boolean poppedGpu() {
return lastPoppedGpu;
}
public String poppedName() {
return lastPoppedName;
}
public boolean hasSkipped() {
return skipped > 0;
}
public long peek() {
if (skipped > 0 || depth == 0) return EMPTY;
return stack[depth - 1];
}
public int depth() {
return depth;
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -10,16 +11,24 @@ import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public final class AttribSnapshot {
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
private static void warnOnce(String key, String fmt, Object... args) {
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
}
public record AttribDesc(int location, int size, int type, boolean normalized, int stride, long offset, int sourceVboId, ByteBuffer readBuffer, long readBufferBaseOffset) {
public int effectiveStride() {
return stride != 0 ? stride : size * VAOManager.Attrib.glTypeSizeBytes(type);
return stride != 0 ? stride : size * GLTypes.sizeBytes(type);
}
public int typeSizeBytes() {
return VAOManager.Attrib.glTypeSizeBytes(type);
return GLTypes.sizeBytes(type);
}
}
@@ -59,6 +68,7 @@ public final class AttribSnapshot {
final AttribDesc[] out = new AttribDesc[VAOManager.MAX_ATTRIBS];
final List<ByteBuffer> allocated = new ArrayList<>();
final int prevVBO = GLStateManager.getBoundVBO();
boolean success = false;
// Pass 1 — unioned [start,end] per vboId. Hot path: one source VBO, zero allocations.
int rangeSoleVboId = 0;
@@ -71,7 +81,7 @@ public final class AttribSnapshot {
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled || a.vboId == 0) continue;
final int stride = a.effectiveStride();
final long typeBytes = (long) a.size * VAOManager.Attrib.glTypeSizeBytes(a.type);
final long typeBytes = (long) a.size * GLTypes.sizeBytes(a.type);
final long start = a.offset + (long) firstVertex * stride;
final long end = a.offset + lastVtx * stride + typeBytes;
@@ -125,8 +135,8 @@ public final class AttribSnapshot {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
final int bufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (bufferSize <= 0) {
GLStateManager.LOGGER.warn("[VBO Readback] GL_BUFFER_SIZE={} for VBO {}", bufferSize, a.vboId);
continue;
warnOnce("vbo-orphaned", "[AttribSnapshot] GL_BUFFER_SIZE={} for VBO {} -- skipping capture", bufferSize, a.vboId);
return null;
}
final long rStart;
@@ -139,9 +149,16 @@ public final class AttribSnapshot {
rStart = rangeSoleStart;
rEnd = rangeSoleEnd;
}
if (rEnd > bufferSize) {
warnOnce("vbo-overrun", "[AttribSnapshot] attrib range {}..{} exceeds VBO {} size {} -- skipping capture", rStart, rEnd, a.vboId, bufferSize);
return null;
}
final long ro = Math.max(0, rStart);
final int rs = (int) (Math.min(bufferSize, rEnd) - ro);
if (rs <= 0) continue;
final int rs = (int) (rEnd - ro);
if (rs <= 0) {
warnOnce("vbo-empty-range", "[AttribSnapshot] empty attrib range {}..{} for VBO {} -- skipping capture", rStart, rEnd, a.vboId);
return null;
}
buf = MemoryUtilities.memAlloc(rs);
GLStateManager.glGetBufferSubData(GL15.GL_ARRAY_BUFFER, ro, buf);
@@ -169,10 +186,13 @@ public final class AttribSnapshot {
out[i] = new AttribDesc(i, a.size, a.type, a.normalized, a.stride, a.clientPointer.position(), 0, a.clientPointer, 0L);
}
}
success = true;
return new AttribSnapshot(out, allocated);
} finally {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
if (!success) {
for (ByteBuffer b : allocated) MemoryUtilities.memFree(b);
}
}
return new AttribSnapshot(out, allocated);
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
/**
* A class that stores multiple vertex formats & their corresponding buffers.
@@ -37,12 +38,14 @@ public final class DisplayListVBO {
private final int drawMode;
private final int start;
private final int count;
private final int vertexFlags;
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count) {
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count, int vertexFlags) {
this.vao = vao;
this.drawMode = drawMode;
this.start = start;
this.count = count;
this.vertexFlags = vertexFlags;
}
public int getStart() {
@@ -62,11 +65,15 @@ public final class DisplayListVBO {
}
public void render() {
if (vao == null) return;
vao.bind();
if (count <= 0) { vao.unbind(); return; }
VAOManager.setCurrentVertexFlags(vertexFlags);
vao.draw(drawMode, start, count);
vao.unbind();
}
public IVertexArrayObject getVAO() {
return vao;
}
@@ -72,17 +72,18 @@ public final class DisplayListVBOBuilder {
}
vertexCount = format.getVertexCount(size);
}
final int flags = format.getVertexFlags();
if (data.drawMode == GL11.GL_QUADS) {
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6);
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6, flags);
} else if (data.drawMode == GL11.GL_QUAD_STRIP) {
// GL_QUAD_STRIP is removed in core profile; an even-length GL_TRIANGLE_STRIP produces the same quads
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1);
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1, flags);
} else if (data.drawMode == GL11.GL_POLYGON) {
// GL_POLYGON is removed in core profile; convert to GL_TRIANGLE_FAN which is equivalent for convex polygons
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount);
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount, flags);
} else {
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount);
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount, flags);
}
@@ -10,6 +10,8 @@ import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Records immediate mode GL calls (glBegin/glEnd/glVertex) via {@link DirectTessellator}.
@@ -18,28 +20,37 @@ import java.util.Arrays;
public final class ImmediateModeRecorder {
private static final CallbackTessellator tessellator = new CallbackTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
/** Separate tessellator for the splash thread. Nulled after splash completes. */
private static DirectTessellator splashTessellator;
private record ThreadTessellator(Thread thread, DirectTessellator tessellator) {}
private static final Queue<ThreadTessellator> threadTessellators = new ConcurrentLinkedQueue<>();
private static final ThreadLocal<DirectTessellator> threadTessellator = ThreadLocal.withInitial(() -> {
final DirectTessellator t = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
threadTessellators.add(new ThreadTessellator(Thread.currentThread(), t));
cleanupOrphanTessellators();
return t;
});
private ImmediateModeRecorder() {
}
public static void initSplashTessellator() {
splashTessellator = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
public static int getThreadTessellatorCount() {
return threadTessellators.size();
}
public static void destroySplashTessellator() {
splashTessellator.delete();
splashTessellator = null;
public static synchronized void cleanupOrphanTessellators() {
threadTessellators.removeIf(tt -> {
if (tt.thread().isAlive()) return false;
tt.tessellator().delete();
return true;
});
}
private static DirectTessellator tessellator() {
final DirectTessellator splash = splashTessellator;
if (splash != null && Thread.currentThread() != GLStateManager.getMainThread()) {
return splash;
if (Thread.currentThread() == GLStateManager.getMainThread()) {
return tessellator;
}
return tessellator;
return threadTessellator.get();
}
public static DirectTessellator getInternalTessellator() {
@@ -192,6 +203,7 @@ public final class ImmediateModeRecorder {
*/
public static DirectTessellator processDrawArraysFromAttribs(int mode, int first, int count) {
final AttribSnapshot snap = AttribSnapshot.snapshot(first, count);
if (snap == null) return null;
try {
prepareReaders(snap);
if (positionReaderIndex < 0) return null;
@@ -296,36 +308,4 @@ public final class ImmediateModeRecorder {
return VAOManager.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import java.util.Arrays;
@@ -24,7 +24,7 @@ public final class AttribLayoutKey {
int base = 0;
int maxAlign = 1;
for (int i = 0; i < n; i++) {
final int typeSize = VAOManager.Attrib.glTypeSizeBytes(types[i]);
final int typeSize = GLTypes.sizeBytes(types[i]);
if (typeSize > maxAlign) maxAlign = typeSize;
base = (base + typeSize - 1) & -typeSize;
off[i] = base;
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.AttribSnapshot;
@@ -9,8 +10,6 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
@@ -52,51 +51,69 @@ public final class IndexedDrawCapture {
return freed;
}
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
private static void warnOnce(String key, String fmt, Object... args) {
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
}
public static IndexedDrawCapture create(int mode, int indicesCount, int srcIndexType, long indicesOffset, int eboId) {
if (indicesCount == 0) return null;
if (eboId == 0) {
warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
GLStateManager.warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
return null;
}
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
return null;
}
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
final int srcIndexSize = GLTypes.sizeBytes(srcIndexType);
if (srcIndexSize <= 0) return null;
final int prevEBO = GLStateManager.getBoundEBO();
final int prevVBO = GLStateManager.getBoundVBO();
ByteBuffer eboSrc = null;
try {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (eboBufferSize <= 0) {
GLStateManager.warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
return null;
}
final long eboReadSize = (long) indicesCount * srcIndexSize;
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
GLStateManager.warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
return null;
}
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
return bake(mode, indicesCount, srcIndexType, MemoryUtilities.memAddress0(eboSrc));
} finally {
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
}
}
public static IndexedDrawCapture createFromClientIndices(int mode, int indicesCount, int srcIndexType, long indexSrcAddr, long srcByteLength) {
if (indicesCount == 0) return null;
if (indexSrcAddr == 0L) return null;
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
return null;
}
final int idxSize = GLTypes.sizeBytes(srcIndexType);
if (idxSize <= 0) return null;
if ((long) indicesCount * idxSize > srcByteLength) {
GLStateManager.warnOnce("client-idx-range", "[IndexedDrawCapture] index count {} ({} bytes) exceeds client buffer {} bytes - skipping", indicesCount, (long) indicesCount * idxSize, srcByteLength);
return null;
}
return bake(mode, indicesCount, srcIndexType, indexSrcAddr);
}
private static IndexedDrawCapture bake(int mode, int indicesCount, int srcIndexType, long indexSrcAddr) {
AttribSnapshot snap = null;
ByteBuffer vertexData = null;
ByteBuffer indexData = null;
boolean success = false;
try {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (eboBufferSize <= 0) {
warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
return null;
}
final long eboReadSize = (long) indicesCount * srcIndexSize;
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
return null;
}
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
final long eboSrcAddr = MemoryUtilities.memAddress0(eboSrc);
final long minMax = QuadConverter.scanMinMaxIndex(eboSrcAddr, srcIndexType, indicesCount);
final long minMax = QuadConverter.scanMinMaxIndex(indexSrcAddr, srcIndexType, indicesCount);
if (minMax == -1L) {
warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
GLStateManager.warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
return null;
}
final int minVtx = (int) (minMax & 0xFFFFFFFFL);
@@ -104,13 +121,14 @@ public final class IndexedDrawCapture {
final int vertexCount = maxVtx - minVtx + 1;
snap = AttribSnapshot.snapshot(minVtx, vertexCount);
if (snap == null) return null;
int enabledCount = 0;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
if (snap.get(i) != null) enabledCount++;
}
if (enabledCount == 0) {
warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
GLStateManager.warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
return null;
}
final int[] locations = new int[enabledCount];
@@ -137,6 +155,13 @@ public final class IndexedDrawCapture {
for (int a = 0; a < enabledCount; a++) {
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
final int relOffset = (int) (d.offset() - d.readBufferBaseOffset());
final long readStart = relOffset + (long) minVtx * d.effectiveStride();
final long readEnd = relOffset + (long) maxVtx * d.effectiveStride() + (long) d.size() * d.typeSizeBytes();
if (readStart < 0 || readEnd > d.readBuffer().capacity()) {
GLStateManager.warnOnce("vtx-oob", "[IndexedDrawCapture] attrib {} read extent {} exceeds snapshot {} bytes - skipping", d.location(), readEnd, d.readBuffer().capacity());
return null;
}
if (a == 0) {
sharedSrcBuffer = d.readBuffer();
sharedSrcStride = d.effectiveStride();
@@ -169,12 +194,12 @@ public final class IndexedDrawCapture {
final int quadCount = indicesCount / 4;
bakedIndexCount = quadCount * 6;
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
QuadConverter.triangulateQuads(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
QuadConverter.triangulateQuads(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
bakedDrawMode = GL11.GL_TRIANGLES;
} else {
bakedIndexCount = indicesCount;
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
QuadConverter.widenIndices(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
QuadConverter.widenIndices(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
bakedDrawMode = mode;
}
@@ -184,14 +209,11 @@ public final class IndexedDrawCapture {
success = true;
return out;
} finally {
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
if (snap != null) snap.free();
if (!success) {
if (vertexData != null) MemoryUtilities.memFree(vertexData);
if (indexData != null) MemoryUtilities.memFree(indexData);
}
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -78,6 +79,6 @@ public record TexImage2DCmd(
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, GLDebug.getTextureFormatName(internalformat),
width, height, border, GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
width, height, border, GLDebug.getTextureFormatName(format), GLTypes.name(type), dataStr, unpack);
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -78,6 +79,6 @@ public record TexSubImage2DCmd(
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, xoffset, yoffset, width, height,
GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
GLDebug.getTextureFormatName(format), GLTypes.name(type), dataStr, unpack);
}
}
@@ -2,8 +2,11 @@ package com.gtnewhorizons.angelica.glsm.redirect;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
import com.gtnewhorizons.angelica.config.SystemProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -21,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Core GL call redirector for GLSM. Rewrites bytecode so that GL calls go through {@code GLStateManager} for state tracking and backend abstraction.
@@ -30,12 +34,107 @@ import java.util.Set;
*/
public class GLSMRedirector {
private static final boolean ASSERT_MAIN_THREAD = Boolean.getBoolean("angelica.assertMainThread");
private static final boolean LOG_SPAM = Boolean.getBoolean("angelica.redirectorLogspam");
private static final boolean LOG_SPAM = SystemProperties.REDIRECTOR_LOGSPAM;
private static final Logger LOGGER = LogManager.getLogger("GLSMRedirector");
private static final String LWJGL_GL_PACKAGE = "org/lwjgl/opengl/";
private static final Set<String> DEBUG_OWNERS = ImmutableSet.of(
"org/lwjgl/opengl/ARBDebugOutput",
"org/lwjgl/opengl/AMDDebugOutput"
);
private static final Set<String> DEBUG_METHODS = ImmutableSet.of(
"glDebugMessageCallback",
"glDebugMessageControl",
"glDebugMessageInsert",
"glGetDebugMessageLog",
"glObjectLabel",
"glGetObjectLabel",
"glObjectPtrLabel",
"glGetObjectPtrLabel",
"glPushDebugGroup",
"glPopDebugGroup"
);
private static final Set<String> UNMAPPED_SEEN = ConcurrentHashMap.newKeySet();
private static final Set<String> DEBUG_SEEN = ConcurrentHashMap.newKeySet();
private static boolean detectionEnabled() {
return SystemProperties.UNMAPPED_GL.detects();
}
private static final String GLSYNC_TYPE = "Lorg/lwjgl/opengl/GLSync;";
private static boolean isDebugCall(String owner, String name) {
if (!owner.startsWith(LWJGL_GL_PACKAGE)) return false;
return DEBUG_OWNERS.contains(owner) || DEBUG_METHODS.contains(name);
}
private static boolean isUnmappedGlCall(String owner, String name, String desc) {
if (!owner.startsWith(LWJGL_GL_PACKAGE)) return false;
if (!name.startsWith("gl") && !name.startsWith("ngl")) return false;
if (desc.contains(GLSYNC_TYPE)) return false;
return true;
}
private static boolean hasLwjglTypedDescriptor(String desc) {
return desc.contains("Lorg/lwjgl/");
}
private static final Set<String> AWARE_NON_DISPATCHING = ImmutableSet.of(
"org/lwjgl/opengl/GL getCapabilities",
"org/lwjgl/opengl/GL setCapabilities",
"org/lwjgl/opengl/GL getFunctionProvider",
"org/lwjgl/opengl/GL create",
"org/lwjgl/opengl/GLCapabilities getAddressBuffer",
"org/lwjgl/opengl/GLCapabilities initialize",
"org/lwjgl/opengl/GLDebugMessageCallback create",
"org/lwjgl/opengl/GLDebugMessageCallback createSafe",
"org/lwjgl/opengl/GLDebugMessageCallback getMessage"
);
private static boolean isAwareNonDispatching(String owner, String name) {
return AWARE_NON_DISPATCHING.contains(owner + " " + name);
}
private static void reportAwareUnroutable(String className, String owner, String name, String desc) {
if (!detectionEnabled()) return;
if (UNMAPPED_SEEN.add("aware " + owner + "." + name + desc)) {
LOGGER.warn("Aware-unroutable GL call {}.{}{} in {}; needs an awareOwnerRedirects entry (see AwareGLDebugShim)", owner, name, desc, className);
}
if (SystemProperties.UNMAPPED_GL.failsAwareUnroutable()) {
throw new IllegalStateException("Aware-unroutable GL call " + owner + "." + name + desc + " in " + className);
}
}
private static void reportUnmapped(String className, String owner, String name, String desc) {
if (!detectionEnabled()) return;
if (UNMAPPED_SEEN.add(owner + "." + name + desc)) {
LOGGER.warn("Unmapped GL call {}.{}{} in {} reached the redirector; on a non-GL backend this will crash at runtime.", owner, name, desc, className);
}
if (SystemProperties.UNMAPPED_GL.failsUnmapped()) {
throw new IllegalStateException("Unmapped GL call " + owner + "." + name + desc + " in " + className);
}
}
private static void reportDebugPassthrough(String className, String owner, String name, String desc) {
if (!detectionEnabled()) return;
if (DEBUG_SEEN.add(owner + "." + name + desc)) {
LOGGER.info("Debug-output call {}.{}{} in {} bypasses the backend (reaches real GL; unsupported on the SDL backend).", owner, name, desc, className);
}
}
private static final String Drawable = "org/lwjgl/opengl/Drawable";
private static final String SharedDrawable = "org/lwjgl/opengl/SharedDrawable";
private static final String Display = "org/lwjgl/opengl/Display";
private static boolean isDrawableOwner(String owner) {
return owner.startsWith(Drawable) || owner.equals(SharedDrawable);
}
private static final String GLStateManager = "com/gtnewhorizons/angelica/glsm/GLStateManager";
private static final String AwareGLDebugShim = "com/gtnewhorizons/angelica/sdlgpu/compat/AwareGLDebugShim";
private static final Map<String, String> awareOwnerRedirects = new HashMap<>();
private static final String GL_PREFIX = "org/lwjgl/opengl/GL";
private static final String ARBVertexArrayObject = "org/lwjgl/opengl/ARBVertexArrayObject";
private static final String APPLEVertexArrayObject = "org/lwjgl/opengl/APPLEVertexArrayObject";
@@ -48,19 +147,29 @@ public class GLSMRedirector {
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
private static final String ARBDrawInstanced = "org/lwjgl/opengl/ARBDrawInstanced";
private static final String EXTDrawInstanced = "org/lwjgl/opengl/EXTDrawInstanced";
private static final String ARBBufferObject = "org/lwjgl/opengl/ARBBufferObject";
private static final String ARBVertexBufferObject = "org/lwjgl/opengl/ARBVertexBufferObject";
private static final String ARBBufferStorage = "org/lwjgl/opengl/ARBBufferStorage";
private static final String ARBCopyBuffer = "org/lwjgl/opengl/ARBCopyBuffer";
private static final String ARBMapBufferRange = "org/lwjgl/opengl/ARBMapBufferRange";
private static final String ARBFramebufferObject = "org/lwjgl/opengl/ARBFramebufferObject";
private static final String EXTFramebufferObject = "org/lwjgl/opengl/EXTFramebufferObject";
private static final String ARBVertexShader = "org/lwjgl/opengl/ARBVertexShader";
private static final String ARBOcclusionQuery = "org/lwjgl/opengl/ARBOcclusionQuery";
private static final String ARBDrawElementsBaseVertex = "org/lwjgl/opengl/ARBDrawElementsBaseVertex";
private static final String ARBMultiDrawIndirect = "org/lwjgl/opengl/ARBMultiDrawIndirect";
private static final String ARBUniformBufferObject = "org/lwjgl/opengl/ARBUniformBufferObject";
private static final String EXTGpuShader4 = "org/lwjgl/opengl/EXTGpuShader4";
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";
// Redirect VAO related calls from NHLib
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
private static final String MinecraftClient = "net.minecraft.client";
private static final String SplashProgress = "cpw.mods.fml.client.SplashProgress";
private static final Set<String> ExcludedMinecraftMainThreadChecks = ImmutableSet.of(
"startGame", "func_71384_a",
"initializeTextures", "func_77474_a"
);
private static final Map<String, Map<String, String>> methodRedirects = new HashMap<>(32);
private static final Map<String, String> glMethodRedirects = new HashMap<>(256);
private static final Map<String, String> glDescRedirects = new HashMap<>();
private static final Map<Integer, String> glCapRedirects = new HashMap<>();
private static final Map<String, String> typeRedirects = new HashMap<>();
private static final ClassConstantPoolParser cstPoolParser;
@@ -73,15 +182,15 @@ public class GLSMRedirector {
};
static {
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_ALPHA_TEST, "AlphaTest");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_BLEND, "Blend");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_DEPTH_TEST, "DepthTest");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_CULL_FACE, "Cull");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_LIGHTING, "Lighting");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_TEXTURE_2D, "Texture");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_FOG, "Fog");
glCapRedirects.put(org.lwjgl.opengl.GL12.GL_RESCALE_NORMAL, "RescaleNormal");
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_SCISSOR_TEST, "ScissorTest");
glCapRedirects.put(GL11.GL_ALPHA_TEST, "AlphaTest");
glCapRedirects.put(GL11.GL_BLEND, "Blend");
glCapRedirects.put(GL11.GL_DEPTH_TEST, "DepthTest");
glCapRedirects.put(GL11.GL_CULL_FACE, "Cull");
glCapRedirects.put(GL11.GL_LIGHTING, "Lighting");
glCapRedirects.put(GL11.GL_TEXTURE_2D, "Texture");
glCapRedirects.put(GL11.GL_FOG, "Fog");
glCapRedirects.put(GL12.GL_RESCALE_NORMAL, "RescaleNormal");
glCapRedirects.put(GL11.GL_SCISSOR_TEST, "ScissorTest");
final var gl11 = RedirectMap.newMap()
// glEnable/Disable - Special cased in GLSMRedirector, but included here for the external API
@@ -112,6 +221,7 @@ public class GLSMRedirector {
.add("glGenLists")
.add("glGenTextures")
.add("glIsList")
.add("glIsTexture")
.add("glDeleteTextures")
.add("glDepthFunc")
.add("glDepthMask")
@@ -127,9 +237,17 @@ public class GLSMRedirector {
.add("glFogi")
.add("glFrustum")
.add("glGetBoolean")
.add("glGetClipPlane")
.add("glGetDouble")
.add("glGetDoublev", "glGetDouble")
.add("glGetFloat")
.add("glGetFloatv", "glGetFloat")
.add("glGetInteger")
.add("glGetIntegerv", "glGetInteger")
.add("glGetIntegeri")
.add("glGetIntegeri_v")
.add("glGetBooleani")
.add("glGetBooleani_v")
.add("glGetLight")
.add("glGetMaterial")
.add("glGetTexLevelParameteri")
@@ -150,6 +268,7 @@ public class GLSMRedirector {
.add("glListBase")
.add("glLoadIdentity")
.add("glLoadMatrix")
.add("glLoadMatrixf", "glLoadMatrix")
.add("glLogicOp")
.add("glMatrixMode")
.add("glMultMatrix")
@@ -231,6 +350,7 @@ public class GLSMRedirector {
.add("glPolygonStipple")
.add("glAccum")
.add("glReadBuffer")
.add("glReadPixels")
.add("glSampleCoverage")
.add("glScissor")
.add("glStencilFunc")
@@ -268,7 +388,8 @@ public class GLSMRedirector {
final var gl12 = RedirectMap.newMap()
.add("glTexImage3D")
.add("glTexSubImage3D")
.add("glCopyTexSubImage3D");
.add("glCopyTexSubImage3D")
.add("glDrawRangeElements");
final var gl13 = RedirectMap.newMap()
.add("glActiveTexture")
.add("glSampleCoverage")
@@ -279,18 +400,33 @@ public class GLSMRedirector {
final var gl14 = RedirectMap.newMap()
.add("glBlendFuncSeparate", "tryBlendFuncSeparate")
.add("glBlendColor")
.add("glBlendEquation");
.add("glBlendEquation")
.add("glMultiDrawArrays")
.add("glPointParameterf")
.add("glPointParameteri");
final var gl15 = RedirectMap.newMap()
.add("glGenBuffers")
.add("glBindBuffer")
.add("glDeleteBuffers")
.add("glBufferData")
.add("nglBufferData")
.add("glBufferSubData")
.add("nglBufferSubData")
.add("glMapBuffer")
.add("glUnmapBuffer")
.add("glGetBufferSubData")
.add("nglGetBufferSubData")
.add("glGetBufferParameteri")
.add("glIsBuffer");
.add("glGetBufferParameter")
.add("glGetBufferParameteriv")
.add("glIsBuffer")
.add("glIsQuery")
.add("glGenQueries")
.add("glDeleteQueries")
.add("glBeginQuery")
.add("glEndQuery")
.add("glGetQueryObjectui")
.add("glGetQueryObjecti");
final var gl20 = RedirectMap.newMap()
.add("glBlendEquationSeparate")
.add("glDrawBuffers")
@@ -310,6 +446,8 @@ public class GLSMRedirector {
.add("glValidateProgram")
.add("glGetUniformLocation")
.add("glGetAttribLocation")
.add("glIsShader")
.add("glIsProgram")
.add("glUniform1f")
.add("glUniform2f")
.add("glUniform3f")
@@ -322,9 +460,13 @@ public class GLSMRedirector {
.add("glUniform2")
.add("glUniform3")
.add("glUniform4")
.add("glUniform1fv", "glUniform1")
.add("glUniform3fv", "glUniform3")
.add("glUniform4fv", "glUniform4")
.add("glUniformMatrix2")
.add("glUniformMatrix3")
.add("glUniformMatrix4")
.add("glUniformMatrix3fv", "glUniformMatrix3")
.add("glUniformMatrix4fv", "glUniformMatrix4")
.add("glDeleteShader")
.add("glGetShaderi")
@@ -342,12 +484,14 @@ public class GLSMRedirector {
.add("glGetUniform")
.add("glVertexAttribPointer")
.add("glEnableVertexAttribArray")
.add("glDisableVertexAttribArray");
.add("glDisableVertexAttribArray")
.add("glGetActiveAttrib");
final var gl30 = RedirectMap.newMap()
.add("glGenVertexArrays")
.add("glBindVertexArray")
.add("glDeleteVertexArrays")
.add("glIsVertexArray")
.add("glBindFragDataLocation")
.add("glBindFramebuffer")
.add("glDeleteFramebuffers")
.add("glGenFramebuffers")
@@ -356,19 +500,39 @@ public class GLSMRedirector {
.add("glVertexAttribIPointer")
.add("glGenerateMipmap")
.add("glGetFramebufferAttachmentParameteri")
.add("glBlitFramebuffer");
.add("glBlitFramebuffer")
.add("glMapBufferRange")
.add("glFlushMappedBufferRange")
.add("glGenRenderbuffers")
.add("glDeleteRenderbuffers")
.add("glBindRenderbuffer")
.add("glRenderbufferStorage")
.add("glRenderbufferStorageMultisample")
.add("glFramebufferRenderbuffer")
.add("glIsFramebuffer")
.add("glIsRenderbuffer")
.add("glBindBufferBase");
final var gl31 = RedirectMap.newMap()
.add("glDrawElementsInstanced")
.add("glDrawArraysInstanced");
.add("glDrawArraysInstanced")
.add("glCopyBufferSubData")
.add("glPrimitiveRestartIndex")
.add("glGetUniformBlockIndex")
.add("glUniformBlockBinding");
final var gl32 = RedirectMap.newMap()
.add("glFramebufferTexture");
.add("glFramebufferTexture")
.add("glDrawRangeElementsBaseVertex")
.add("glDrawElementsBaseVertex");
final var gl33 = RedirectMap.newMap()
.add("glGenSamplers")
.add("glDeleteSamplers")
.add("glIsSampler")
.add("glBindSampler")
.add("glSamplerParameteri")
.add("glSamplerParameterf")
.add("glVertexAttribDivisor");
.add("glVertexAttribDivisor")
.add("glQueryCounter")
.add("glGetQueryObjectui64");
final var gl42 = RedirectMap.newMap()
.add("glBindImageTexture")
.add("glMemoryBarrier")
@@ -376,15 +540,25 @@ public class GLSMRedirector {
final var gl43 = RedirectMap.newMap()
.add("glDispatchCompute")
.add("glClearBufferSubData")
.add("glClearBufferData")
.add("glBindVertexBuffer")
.add("glVertexAttribFormat")
.add("glVertexAttribIFormat")
.add("glVertexAttribBinding");
.add("glVertexAttribBinding")
.add("glMultiDrawElementsIndirect");
final var gl44 = RedirectMap.newMap()
.add("glBufferStorage")
.add("glClearTexImage");
final var gl45 = RedirectMap.newMap()
.add("glCreateBuffers")
.add("glNamedBufferData")
.add("glNamedBufferSubData");
final var gl46 = RedirectMap.newMap()
.add("glPolygonOffsetClamp")
.add("glMultiDrawArraysIndirectCount")
.add("glMultiDrawElementsIndirectCount")
.add("glSpecializeShader");
// Merge all GL version maps into the flat lookup
glMethodRedirects.putAll(gl11);
glMethodRedirects.putAll(gl12);
glMethodRedirects.putAll(gl13);
@@ -398,6 +572,8 @@ public class GLSMRedirector {
glMethodRedirects.putAll(gl42);
glMethodRedirects.putAll(gl43);
glMethodRedirects.putAll(gl44);
glMethodRedirects.putAll(gl45);
glMethodRedirects.putAll(gl46);
// MINECRAFT
methodRedirects.put(OpenGlHelper, RedirectMap.newMap()
@@ -417,10 +593,77 @@ public class GLSMRedirector {
// ARB
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
.add("glActiveTextureARB")
.add("glClientActiveTextureARB", "glClientActiveTexture")
.add("glMultiTexCoord2fARB", "glMultiTexCoord2f")
);
methodRedirects.put(ARBVertexShader, RedirectMap.newMap()
.add("glGetAttribLocationARB", "glGetAttribLocation")
);
methodRedirects.put(ARBFramebufferObject, RedirectMap.newMap()
.add("glBindFramebuffer")
.add("glDeleteFramebuffers")
.add("glGenFramebuffers")
.add("glCheckFramebufferStatus")
.add("glFramebufferTexture2D")
.add("glBindRenderbuffer")
.add("glDeleteRenderbuffers")
.add("glGenRenderbuffers")
.add("glRenderbufferStorage")
.add("glFramebufferRenderbuffer")
);
methodRedirects.put(EXTFramebufferObject, RedirectMap.newMap()
.add("glBindFramebufferEXT", "glBindFramebuffer")
.add("glDeleteFramebuffersEXT", "glDeleteFramebuffers")
.add("glGenFramebuffersEXT", "glGenFramebuffers")
.add("glCheckFramebufferStatusEXT", "glCheckFramebufferStatus")
.add("glFramebufferTexture2DEXT", "glFramebufferTexture2D")
.add("glBindRenderbufferEXT", "glBindRenderbuffer")
.add("glDeleteRenderbuffersEXT", "glDeleteRenderbuffers")
.add("glGenRenderbuffersEXT", "glGenRenderbuffers")
.add("glRenderbufferStorageEXT", "glRenderbufferStorage")
.add("glFramebufferRenderbufferEXT", "glFramebufferRenderbuffer")
);
methodRedirects.put(ARBOcclusionQuery, RedirectMap.newMap()
.add("glGenQueriesARB", "glGenQueries")
.add("glBeginQueryARB", "glBeginQuery")
.add("glEndQueryARB", "glEndQuery")
.add("glGetQueryObjectuARB", "glGetQueryObjectui")
);
methodRedirects.put(ARBTimerQuery, RedirectMap.newMap()
.add("glQueryCounter")
.add("glGetQueryObjectui64")
);
methodRedirects.put(EXTTimerQuery, RedirectMap.newMap()
.add("glGetQueryObjectuEXT", "glGetQueryObjectui64")
.add("glGetQueryObjectui64EXT", "glGetQueryObjectui64")
);
glDescRedirects.put("org/lwjgl/opengl/GL32C glFenceSync(II)J", "glFenceSync");
glDescRedirects.put("org/lwjgl/opengl/GL32C glClientWaitSync(JIJ)I", "glClientWaitSync");
glDescRedirects.put("org/lwjgl/opengl/GL32C glWaitSync(JIJ)V", "glWaitSync");
glDescRedirects.put("org/lwjgl/opengl/GL32C glGetSynci(JILjava/nio/IntBuffer;)I", "glGetSynci");
glDescRedirects.put("org/lwjgl/opengl/GL32C glDeleteSync(J)V", "glDeleteSync");
glDescRedirects.put("org/lwjgl/opengl/GL15C nglMapBuffer(II)J", "nglMapBuffer");
glDescRedirects.put("org/lwjgl/opengl/GL32C nglMultiDrawElementsBaseVertex(IJIJIJ)V", "nglMultiDrawElementsBaseVertex");
for (String o : new String[]{"org/lwjgl/opengl/KHRDebug", "org/lwjgl/opengl/GL43", "org/lwjgl/opengl/GL43C"}) {
glDescRedirects.put(o + " glObjectLabel(IILjava/lang/CharSequence;)V", "glObjectLabel");
glDescRedirects.put(o + " glGetObjectLabel(III)Ljava/lang/String;", "glGetObjectLabel");
glDescRedirects.put(o + " glPushDebugGroup(IILjava/lang/CharSequence;)V", "glPushDebugGroup");
glDescRedirects.put(o + " glPopDebugGroup()V", "glPopDebugGroup");
glDescRedirects.put(o + " glDebugMessageInsert(IIIILjava/lang/CharSequence;)V", "glDebugMessageInsert");
glDescRedirects.put(o + " glDebugMessageControl(IIILjava/nio/IntBuffer;Z)V", "glDebugMessageControl");
glDescRedirects.put(o + " glGetDebugMessageLog(ILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/ByteBuffer;)I", "glGetDebugMessageLog");
glDescRedirects.put(o + " glDebugMessageCallback(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback");
awareOwnerRedirects.put(o + " glDebugMessageCallback(Lorg/lwjgl/opengl/GLDebugMessageCallbackI;J)V", AwareGLDebugShim);
}
awareOwnerRedirects.put("org/lwjgl/opengl/GLUtil setupDebugMessageCallback()Lorg/lwjgl/system/Callback;", AwareGLDebugShim);
awareOwnerRedirects.put("org/lwjgl/opengl/GLUtil setupDebugMessageCallback(Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;", AwareGLDebugShim);
methodRedirects.put(ARBShaderObjects, RedirectMap.newMap()
.add("glUseProgramObjectARB", "glUseProgram")
.add("glShaderSourceARB", "glShaderSource")
.add("nglShaderSourceARB", "nglShaderSource")
.add("glLinkProgramARB", "glLinkProgram")
.add("glCreateShaderObjectARB", "glCreateShader")
.add("glCompileShaderARB", "glCompileShader")
@@ -458,7 +701,23 @@ public class GLSMRedirector {
methodRedirects.put(ARBVertexArrayObject, RedirectMap.newMap()
.add("glBindVertexArray")
.add("glDeleteVertexArrays")
.add("glGenVertexArrays")
);
methodRedirects.put(ARBDrawElementsBaseVertex, RedirectMap.newMap()
.add("glDrawElementsBaseVertex")
);
methodRedirects.put(ARBMultiDrawIndirect, RedirectMap.newMap()
.add("glMultiDrawElementsIndirect")
);
final var uniformBufferObject = RedirectMap.newMap()
.add("glBindBufferBase")
.add("glGetUniformBlockIndex")
.add("glUniformBlockBinding");
methodRedirects.put(ARBUniformBufferObject, uniformBufferObject);
final var gpuShader4 = RedirectMap.newMap()
.add("glVertexAttribIPointerEXT", "glVertexAttribIPointer");
methodRedirects.put(EXTGpuShader4, gpuShader4);
methodRedirects.put(EXTGPUShader4, gpuShader4);
methodRedirects.put(ARBInstancedArrays, RedirectMap.newMap()
.add("glVertexAttribDivisorARB")
);
@@ -470,10 +729,37 @@ public class GLSMRedirector {
.add("glDrawArraysInstancedEXT", "glDrawArraysInstanced")
.add("glDrawElementsInstancedEXT", "glDrawElementsInstanced")
);
final var arbBufferObject = RedirectMap.newMap()
.add("glBindBufferARB", "glBindBuffer")
.add("glGenBuffersARB", "glGenBuffers")
.add("glDeleteBuffersARB", "glDeleteBuffers")
.add("glIsBufferARB", "glIsBuffer")
.add("glBufferDataARB", "glBufferData")
.add("glBufferSubDataARB", "glBufferSubData")
.add("glGetBufferSubDataARB", "glGetBufferSubData")
.add("glMapBufferARB", "glMapBuffer")
.add("glUnmapBufferARB", "glUnmapBuffer")
.add("glGetBufferParameterARB", "glGetBufferParameter")
.add("glGetBufferParameteriARB", "glGetBufferParameteri")
.add("glGetBufferParameterivARB", "glGetBufferParameteriv");
methodRedirects.put(ARBBufferObject, arbBufferObject);
methodRedirects.put(ARBVertexBufferObject, arbBufferObject);
methodRedirects.put(ARBBufferStorage, RedirectMap.newMap()
.add("glBufferStorage")
);
methodRedirects.put(ARBCopyBuffer, RedirectMap.newMap()
.add("glCopyBufferSubData")
);
methodRedirects.put(ARBMapBufferRange, RedirectMap.newMap()
.add("glMapBufferRange")
.add("glFlushMappedBufferRange")
);
// APPLE
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
.add("glBindVertexArrayAPPLE", "glBindVertexArray")
.add("glGenVertexArraysAPPLE", "glGenVertexArrays")
.add("glDeleteVertexArraysAPPLE", "glDeleteVertexArrays")
);
// GTNHLib VAO
@@ -487,12 +773,15 @@ public class GLSMRedirector {
.add("gluPerspective")
.add("gluLookAt")
.add("gluPickMatrix")
.add("gluUnProject")
);
methodRedirects.put(GLU, RedirectMap.newMap()
.add("gluPerspective")
.add("gluLookAt")
.add("gluOrtho2D")
.add("gluPickMatrix")
.add("gluBuild2DMipmaps")
.add("gluErrorString")
);
// Quadric type replacements
@@ -501,33 +790,30 @@ public class GLSMRedirector {
typeRedirects.put("org/lwjgl/util/glu/Disk", "com/gtnewhorizons/angelica/glsm/compat/lwjgl/AngelicaDisk");
typeRedirects.put("org/lwjgl/util/glu/PartialDisk", "com/gtnewhorizons/angelica/glsm/compat/lwjgl/AngelicaPartialDisk");
// Build constant pool scanner
final List<String> stringsToSearch = new ArrayList<>(32);
stringsToSearch.add(GL_PREFIX);
stringsToSearch.add(LWJGL_GL_PACKAGE);
stringsToSearch.add(Drawable);
stringsToSearch.add(SharedDrawable);
stringsToSearch.add(Display);
stringsToSearch.addAll(typeRedirects.keySet());
for (String key : methodRedirects.keySet()) {
stringsToSearch.add(key);
}
stringsToSearch.addAll(methodRedirects.keySet());
cstPoolParser = new ClassConstantPoolParser(stringsToSearch.toArray(new String[0]));
}
/** Core exclusions that GLSM always requires. */
public String[] getCoreExclusions() {
return CORE_EXCLUSIONS.clone();
}
/** The internal-name prefix used to match GL version classes (e.g., {@code "org/lwjgl/opengl/GL"}). */
public static String getGLPrefix() { return GL_PREFIX; }
/** The internal name of the redirect target class. */
public static String getTargetClassName() { return GLStateManager; }
/** Method redirects for GL-prefix classes. Key: original method name, Value: GLStateManager method name. */
/** Key: original method name, value: GLStateManager method name. */
public static Map<String, String> getGLPrefixMethodRedirects() {
return Collections.unmodifiableMap(glMethodRedirects);
}
/** Method redirects for named (non-GL-prefix) classes. Key: internal class name, Value: per-method redirect map. */
/** Key: internal class name, value: per-method redirect map. */
public static Map<String, Map<String, String>> getNamedClassMethodRedirects() {
return Collections.unmodifiableMap(methodRedirects);
}
@@ -536,8 +822,11 @@ public class GLSMRedirector {
return cstPoolParser.find(basicClass, true);
}
/** @return Was the class changed? */
public boolean transformClassNode(String transformedName, ClassNode cn) {
return transformClassNode(transformedName, cn, false);
}
public boolean transformClassNode(String transformedName, ClassNode cn, boolean lwjgl3Aware) {
boolean changed = false;
final boolean isOpenGlHelper = transformedName.equals("net.minecraft.client.renderer.OpenGlHelper");
@@ -545,10 +834,8 @@ public class GLSMRedirector {
if (isOpenGlHelper && (mn.name.equals("glBlendFunc") || mn.name.equals("func_148821_a"))) {
continue;
}
boolean redirectInMethod = false;
for (AbstractInsnNode node : mn.instructions.toArray()) {
if (node instanceof TypeInsnNode tNode) {
// Redirect NEW and CHECKCAST for quadric classes
if (tNode.getOpcode() == Opcodes.NEW || tNode.getOpcode() == Opcodes.CHECKCAST) {
final String redirect = typeRedirects.get(tNode.desc);
if (redirect != null) {
@@ -590,8 +877,7 @@ public class GLSMRedirector {
mn.instructions.remove(prevNode);
}
changed = true;
redirectInMethod = true;
} else if (mNode.name.equals("makeCurrent") && mNode.owner.startsWith(Drawable)) {
} else if (mNode.name.equals("makeCurrent") && isDrawableOwner(mNode.owner)) {
mNode.setOpcode(Opcodes.INVOKESTATIC);
mNode.owner = GLStateManager;
mNode.desc = "(L" + Drawable + ";)V";
@@ -600,10 +886,48 @@ public class GLSMRedirector {
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.makeCurrent()", transformedName);
}
} else if (mNode.name.equals("releaseContext") && isDrawableOwner(mNode.owner)) {
mNode.setOpcode(Opcodes.INVOKESTATIC);
mNode.owner = GLStateManager;
mNode.desc = "(L" + Drawable + ";)V";
mNode.itf = false;
changed = true;
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.releaseContext()", transformedName);
}
} else if (mNode.name.equals("swapBuffers") && mNode.owner.equals(Display) && mNode.desc.equals("()V")) {
mNode.owner = GLStateManager;
changed = true;
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.swapBuffers()", transformedName);
}
} else if (mNode.name.equals("update") && mNode.owner.equals(Display) && (mNode.desc.equals("()V") || mNode.desc.equals("(Z)V"))) {
mNode.owner = GLStateManager;
mNode.name = "updateDisplay";
changed = true;
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.updateDisplay{}", transformedName, mNode.desc);
}
} else if (mNode.name.equals("isCurrent") && mNode.owner.equals(Display) && mNode.desc.equals("()Z")) {
mNode.owner = GLStateManager;
changed = true;
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.isCurrent()", transformedName);
}
} else {
final Map<String, String> redirects = mNode.owner.startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(mNode.owner);
final String glsmName = redirects != null ? redirects.get(mNode.name) : null;
if (glsmName != null) {
String glsmName = glDescRedirects.get(mNode.owner + " " + mNode.name + mNode.desc);
if (glsmName == null) {
final Map<String, String> redirects = mNode.owner.startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(mNode.owner);
glsmName = redirects != null ? redirects.get(mNode.name) : null;
}
final String awareOwner = lwjgl3Aware ? awareOwnerRedirects.get(mNode.owner + " " + mNode.name + mNode.desc) : null;
if (awareOwner != null) {
if (LOG_SPAM) {
LOGGER.info("Redirecting aware call in {} from {}.{}{} to {}.{}", transformedName, mNode.owner, mNode.name, mNode.desc, awareOwner, mNode.name);
}
mNode.owner = awareOwner;
changed = true;
} else if (glsmName != null) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, glsmName, mNode.desc);
@@ -611,10 +935,14 @@ public class GLSMRedirector {
mNode.owner = GLStateManager;
mNode.name = glsmName;
changed = true;
redirectInMethod = true;
} else if (lwjgl3Aware && mNode.owner.startsWith(LWJGL_GL_PACKAGE) && hasLwjglTypedDescriptor(mNode.desc) && !isAwareNonDispatching(mNode.owner, mNode.name)) {
reportAwareUnroutable(transformedName, mNode.owner, mNode.name, mNode.desc);
} else if (isDebugCall(mNode.owner, mNode.name)) {
reportDebugPassthrough(transformedName, mNode.owner, mNode.name, mNode.desc);
} else if (isUnmappedGlCall(mNode.owner, mNode.name, mNode.desc)) {
reportUnmapped(transformedName, mNode.owner, mNode.name, mNode.desc);
}
}
// Redirect <init> calls for quadric classes
if (mNode.getOpcode() == Opcodes.INVOKESPECIAL && mNode.name.equals("<init>")) {
final String redirect = typeRedirects.get(mNode.owner);
if (redirect != null) {
@@ -629,22 +957,28 @@ public class GLSMRedirector {
// Redirect method handles in invokedynamic bootstrap arguments
for (int i = 0; i < dynNode.bsmArgs.length; i++) {
if (!(dynNode.bsmArgs[i] instanceof Handle handle)) continue;
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
final String glsmName = redirects != null ? redirects.get(handle.getName()) : null;
if (glsmName == null) continue;
String glsmName = glDescRedirects.get(handle.getOwner() + " " + handle.getName() + handle.getDesc());
if (glsmName == null) {
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
glsmName = redirects != null ? redirects.get(handle.getName()) : null;
}
if (glsmName == null) {
if (isDebugCall(handle.getOwner(), handle.getName())) {
reportDebugPassthrough(transformedName, handle.getOwner(), handle.getName(), handle.getDesc());
} else if (isUnmappedGlCall(handle.getOwner(), handle.getName(), handle.getDesc())) {
reportUnmapped(transformedName, handle.getOwner(), handle.getName(), handle.getDesc());
}
continue;
}
if (LOG_SPAM) {
final String shortOwner = handle.getOwner().substring(handle.getOwner().lastIndexOf("/") + 1);
LOGGER.info("Redirecting invokedynamic handle in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, handle.getName(), handle.getDesc(), glsmName, handle.getDesc());
}
dynNode.bsmArgs[i] = new Handle(handle.getTag(), GLStateManager, glsmName, handle.getDesc());
changed = true;
redirectInMethod = true;
}
}
}
if (ASSERT_MAIN_THREAD && redirectInMethod && !transformedName.startsWith(SplashProgress) && !(transformedName.startsWith(MinecraftClient) && ExcludedMinecraftMainThreadChecks.contains(mn.name))) {
mn.instructions.insert(new MethodInsnNode(Opcodes.INVOKESTATIC, GLStateManager, "assertMainThread", "()V", false));
}
}
return changed;
@@ -0,0 +1,306 @@
package com.gtnewhorizons.angelica.glsm.shader;
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL20;
import org.taumc.glsl.grammar.GLSLLexer;
import org.taumc.glsl.grammar.GLSLParser;
import org.taumc.glsl.grammar.GLSLPreParser;
import org.taumc.glsl.grammar.GLSLParserBaseListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class GlslVulkanPreprocess {
private static final Logger LOGGER = LogManager.getLogger("GlslVulkanPreprocess");
public static final String SAMPLER_RENAMED = "angelica_sampler_renamed";
public static final String SAMPLERLESS_EXTENSION = "#extension GL_EXT_samplerless_texture_functions : require";
private static final int MAX_VS_INPUT_LOCATIONS = 16;
private static final int CACHE_MAX = 256;
private static final Object2ObjectLinkedOpenHashMap<CacheKey, Result> CACHE = new Object2ObjectLinkedOpenHashMap<>();
private GlslVulkanPreprocess() {}
private record CacheKey(String source, int glShaderType, boolean separateReadOnlyImages) {}
public record Result(String rewrittenSource, Set<String> boolUniforms, Set<String> explicitVsInputs) {}
/** Replace {@code [startIdx, stopIdx]} with {@code replacement}. */
public record Edit(int startIdx, int stopIdx, String replacement) {}
public static int startIdx(ParserRuleContext ctx) {
return ctx.getStart().getStartIndex();
}
public static int stopIdx(ParserRuleContext ctx) {
return ctx.getStop().getStopIndex();
}
/** Apply non-overlapping edits to the source in one pass. */
public static String applyEdits(String src, List<Edit> edits) {
if (edits.isEmpty()) return src;
final List<Edit> sorted;
if (edits.size() == 1) {
sorted = edits;
} else {
sorted = new ArrayList<>(edits);
sorted.sort(Comparator.comparingInt(Edit::startIdx).thenComparingInt(Edit::stopIdx));
}
final StringBuilder sb = new StringBuilder(src.length());
int cursor = 0;
for (Edit e : sorted) {
if (e.startIdx < cursor) continue;
sb.append(src, cursor, e.startIdx);
sb.append(e.replacement);
cursor = e.stopIdx + 1;
}
sb.append(src, cursor, src.length());
return sb.toString();
}
/** Returns {@code null} on parse failure — callers treat that the same as a shaderc compile failure. */
public static @Nullable Result run(String source, int glShaderType, String debugName, boolean separateReadOnlyImages) {
final CacheKey key = new CacheKey(source, glShaderType, separateReadOnlyImages);
synchronized (CACHE) {
final Result hit = CACHE.getAndMoveToFirst(key);
if (hit != null) return hit;
}
final GLSLParser.Translation_unitContext root;
try {
root = GlslTransformUtils.parseFullQuiet(source);
} catch (Exception e) {
LOGGER.warn("glsl-transformation-lib parse failed for '{}': {}", debugName, e.getMessage());
return null;
}
final List<Edit> edits = new ArrayList<>();
final Metadata meta = collectEdits(source, root, glShaderType, debugName, separateReadOnlyImages, edits);
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs());
synchronized (CACHE) {
CACHE.putAndMoveToFirst(key, out);
while (CACHE.size() > CACHE_MAX) CACHE.removeLast();
}
return out;
}
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless) {}
public static Metadata collectEdits(String source, GLSLParser.Translation_unitContext root, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
final boolean isVertex = glShaderType == GL20.GL_VERTEX_SHADER;
final Set<String> bools = new HashSet<>();
final Set<String> explicitInputs = new HashSet<>();
final Set<String> readOnlyImages = new HashSet<>();
final boolean[] needsSamplerless = { false };
final int[] maxExplicitLoc = { -1 };
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
ParseTreeWalker.DEFAULT.walk(new GLSLParserBaseListener() {
@Override
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
if (single == null || single.fully_specified_type() == null) return;
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
final GLSLParser.Type_qualifierContext tq = fst.type_qualifier();
if (tq == null) return;
boolean hasUniform = false, hasIn = false, hasLocation = false, hasReadonly = false;
int locValue = -1;
int bindingValue = -1;
for (GLSLParser.Single_type_qualifierContext stq : tq.single_type_qualifier()) {
if (stq.storage_qualifier() != null) {
final String s = stq.storage_qualifier().getText();
if ("uniform".equals(s)) hasUniform = true;
else if ("in".equals(s)) hasIn = true;
else if ("readonly".equals(s)) hasReadonly = true;
} else if (stq.layout_qualifier() != null) {
for (GLSLParser.Layout_qualifier_idContext id : stq.layout_qualifier().layout_qualifier_id_list().layout_qualifier_id()) {
if (id.IDENTIFIER() == null) continue;
final String qualifier = id.IDENTIFIER().getText();
if ("location".equals(qualifier)) {
hasLocation = true;
if (id.constant_expression() != null) {
try { locValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
}
} else if ("binding".equals(qualifier) && id.constant_expression() != null) {
try { bindingValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
}
}
}
}
final boolean boolDecl = hasUniform && isBool(fst);
final boolean explicitInputDecl = isVertex && hasIn && hasLocation;
if (single.typeless_declaration() != null) {
handleDeclarator(single.typeless_declaration(), hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
}
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
handleDeclarator(td, hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
}
if (separateReadOnlyImages && hasUniform && fst.type_specifier() != null) {
final String typeText = fst.type_specifier().getText();
if (separateTextureType(typeText) != null) {
needsSamplerless[0] = true;
} else if (hasReadonly) {
final String asTexture = separateTextureTypeForImage(typeText);
if (asTexture != null) {
needsSamplerless[0] = true;
collectDeclaredNames(single, ctx, readOnlyImages);
final String binding = bindingValue >= 0 ? "layout(binding = " + bindingValue + ") " : "";
edits.add(new Edit(startIdx(fst), stopIdx(fst), binding + "uniform " + asTexture));
}
}
}
if (isVertex && hasIn) {
if (hasLocation && locValue >= 0) {
if (locValue > maxExplicitLoc[0]) maxExplicitLoc[0] = locValue;
} else if (!hasLocation) {
unlocatedVsInputStarts.add(ctx.getStart().getStartIndex());
}
}
}
@Override
public void enterPostfix_expression(GLSLParser.Postfix_expressionContext ctx) {
if (readOnlyImages.isEmpty() || ctx.LEFT_PAREN() == null || ctx.RIGHT_PAREN() == null) return;
final GLSLParser.Function_call_parametersContext params = ctx.function_call_parameters();
if (params == null || params.assignment_expression() == null || params.assignment_expression().isEmpty()) return;
if (!readOnlyImages.contains(params.assignment_expression(0).getText())) return;
final ParseTree callee = ctx.getChild(0);
if (!(callee instanceof ParserRuleContext calleeCtx)) return;
final String replacement = switch (calleeCtx.getText()) {
case "imageLoad" -> "texelFetch";
case "imageSize" -> "textureSize";
default -> null;
};
if (replacement == null) return;
edits.add(new Edit(startIdx(calleeCtx), stopIdx(calleeCtx), replacement));
final int rparen = ctx.RIGHT_PAREN().getSymbol().getStartIndex();
edits.add(new Edit(rparen, rparen - 1, ", 0"));
}
@Override
public void visitTerminal(TerminalNode node) {
final Token tok = node.getSymbol();
if (tok.getType() != GLSLLexer.IDENTIFIER) return;
final String text = tok.getText();
switch (text) {
case "sampler" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), SAMPLER_RENAMED));
case "gl_VertexID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_VertexIndex"));
case "gl_InstanceID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_InstanceIndex"));
default -> {}
}
}
}, root);
if (needsSamplerless[0] && source != null) {
final int at = versionDirectiveEnd(source);
if (at >= 0) edits.add(new Edit(at, at - 1, "\n" + SAMPLERLESS_EXTENSION));
}
if (maxExplicitLoc[0] >= 0 && !unlocatedVsInputStarts.isEmpty()) {
int next = maxExplicitLoc[0] + 1;
int dropped = 0;
for (int start : unlocatedVsInputStarts) {
if (next >= MAX_VS_INPUT_LOCATIONS) { dropped++; continue; }
edits.add(new Edit(start, start - 1, "layout(location = " + next + ") "));
next++;
}
if (dropped > 0) {
LOGGER.warn("'{}': dropped {} VS input location-conflict fixes (would exceed {} attribute slots)", debugName, dropped, MAX_VS_INPUT_LOCATIONS);
}
}
return new Metadata(bools, explicitInputs, needsSamplerless[0]);
}
public static void clearCache() {
synchronized (CACHE) { CACHE.clear(); }
}
private static void collectDeclaredNames(GLSLParser.Single_declarationContext single, GLSLParser.Init_declarator_listContext ctx, Set<String> out) {
if (single.typeless_declaration() != null && single.typeless_declaration().IDENTIFIER() != null) {
out.add(single.typeless_declaration().IDENTIFIER().getText());
}
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
if (td.IDENTIFIER() != null) out.add(td.IDENTIFIER().getText());
}
}
private static @Nullable String separateTextureTypeForImage(String typeText) {
final String dim = switch (typeText) {
case "image1D", "iimage1D", "uimage1D" -> "1D";
case "image2D", "iimage2D", "uimage2D" -> "2D";
case "image3D", "iimage3D", "uimage3D" -> "3D";
default -> null;
};
if (dim == null) return null;
return typeText.charAt(0) == 'i' ? "itexture" + dim : typeText.charAt(0) == 'u' ? "utexture" + dim : "texture" + dim;
}
private static @Nullable String separateTextureType(String typeText) {
return switch (typeText) {
case "texture1D", "itexture1D", "utexture1D",
"texture2D", "itexture2D", "utexture2D",
"texture3D", "itexture3D", "utexture3D" -> typeText;
default -> null;
};
}
private static int versionDirectiveEnd(String source) {
final GLSLPreParser.Translation_unitContext pre;
try {
pre = GlslTransformUtils.parsePreQuiet(source);
} catch (Exception e) {
return -1;
}
final GLSLPreParser.Version_directiveContext ctx = firstVersionDirective(pre);
return ctx == null || ctx.getStop() == null ? -1 : ctx.getStop().getStopIndex() + 1;
}
private static GLSLPreParser.@Nullable Version_directiveContext firstVersionDirective(ParseTree node) {
if (node instanceof GLSLPreParser.Version_directiveContext v) return v;
for (int i = 0; i < node.getChildCount(); i++) {
final GLSLPreParser.Version_directiveContext found = firstVersionDirective(node.getChild(i));
if (found != null) return found;
}
return null;
}
private static void handleDeclarator(GLSLParser.Typeless_declarationContext td, boolean hasUniform, boolean asBool, boolean asInput, Set<String> bools, Set<String> inputs, List<Edit> edits) {
if (td.IDENTIFIER() == null) return;
final String name = td.IDENTIFIER().getText();
if (asBool) bools.add(name);
if (asInput) inputs.add(name);
if (hasUniform && td.EQUAL() != null && td.initializer() != null) {
edits.add(new Edit(td.EQUAL().getSymbol().getStartIndex(), td.initializer().getStop().getStopIndex(), ""));
}
}
private static boolean isBool(GLSLParser.Fully_specified_typeContext fst) {
return fst.type_specifier() != null && fst.type_specifier().type_specifier_nonarray() != null && "bool".equals(fst.type_specifier().type_specifier_nonarray().getText());
}
}
@@ -1,6 +1,6 @@
// This file is based on code from Sodium by JellySquid, licensed under the LGPLv3 license.
package net.coderbot.iris.gl.shader;
package com.gtnewhorizons.angelica.glsm.shader;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
@@ -0,0 +1,183 @@
package com.gtnewhorizons.angelica.glsm.shader;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.util.shaderc.Shaderc;
import org.lwjgl.system.MemoryUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;
import static org.lwjgl.system.MemoryUtil.memFree;
import static org.lwjgl.system.MemoryUtil.memUTF8;
/** GLSL -> SPIR-V via shaderc */
@Lwjgl3Aware
public final class SpirvCompiler {
private static final Logger LOGGER = LogManager.getLogger("SpirvCompiler");
private static final ThreadLocal<Long> COMPILER = ThreadLocal.withInitial(() -> {
final long c = Shaderc.shaderc_compiler_initialize();
if (c == 0L) throw new IllegalStateException("shaderc_compiler_initialize failed");
return c;
});
private static final Path FAILURE_DIR = Paths.get("shaderc_failures");
private static final AtomicInteger FAILURE_COUNTER = new AtomicInteger();
private static final int CACHE_MAX = 256;
private static final Object2ObjectLinkedOpenHashMap<CacheKey, byte[]> CACHE = new Object2ObjectLinkedOpenHashMap<>();
private record CacheKey(String source, int shaderKind, Options opts) {}
private SpirvCompiler() {}
public record Options(int targetEnv, int envVersion, boolean vulkanRulesRelaxed, boolean autoBindUniforms, boolean autoMapLocations, boolean autoCombinedImageSampler, int forcedVersion, int forcedProfile) {
public static Options vulkanRelaxed() {
return new Options(Shaderc.shaderc_target_env_vulkan, Shaderc.shaderc_env_version_vulkan_1_0, true, true, true, true, 0, 0);
}
public static Options vulkanForced460Core() {
return new Options(Shaderc.shaderc_target_env_vulkan, Shaderc.shaderc_env_version_vulkan_1_0, true, true, true, false, 460, Shaderc.shaderc_profile_core);
}
}
public record Result(@Nullable ByteBuffer spirv, @Nullable String error, @Nullable Path dumpPath) {}
public static Result compile(String source, int shaderKind, String debugName, Options opts) {
final String cleanSrc = source.indexOf('\0') >= 0 ? source.replace("\0", "") : source;
final CacheKey key = new CacheKey(cleanSrc, shaderKind, opts);
final byte[] cached;
synchronized (CACHE) {
cached = CACHE.getAndMoveToFirst(key);
}
if (cached != null) {
final ByteBuffer copy = MemoryUtil.memAlloc(cached.length);
copy.put(cached).flip();
return new Result(copy, null, null);
}
final long compiler = COMPILER.get();
final long optionsHandle = Shaderc.shaderc_compile_options_initialize();
if (optionsHandle == 0L) {
return new Result(null, "shaderc_compile_options_initialize failed", null);
}
try {
configureOptions(optionsHandle, opts);
ByteBuffer srcBuf = null, nameBuf = null, entryBuf = null;
long result = 0L;
try {
srcBuf = memUTF8(cleanSrc, false);
nameBuf = memUTF8(debugName == null ? "shader" : debugName);
entryBuf = memUTF8("main");
result = Shaderc.shaderc_compile_into_spv(compiler, srcBuf, shaderKind, nameBuf, entryBuf, optionsHandle);
if (result == 0L) {
return new Result(null, "shaderc_compile_into_spv returned null", null);
}
final int status = Shaderc.shaderc_result_get_compilation_status(result);
if (status != Shaderc.shaderc_compilation_status_success) {
final String msg = Shaderc.shaderc_result_get_error_message(result);
final Path dumpPath = writeFailureDump(debugName, shaderKind, msg, cleanSrc);
return new Result(null, msg == null ? "(no error message)" : msg, dumpPath);
}
final ByteBuffer spirv = Shaderc.shaderc_result_get_bytes(result);
if (spirv == null || spirv.remaining() == 0) {
return new Result(null, "shaderc returned empty SPIR-V", null);
}
final ByteBuffer copy = MemoryUtil.memAlloc(spirv.remaining());
copy.put(spirv);
copy.flip();
final byte[] heap = new byte[copy.remaining()];
copy.duplicate().get(heap);
synchronized (CACHE) {
CACHE.putAndMoveToFirst(key, heap);
while (CACHE.size() > CACHE_MAX) CACHE.removeLast();
}
return new Result(copy, null, null);
} finally {
if (result != 0L) Shaderc.shaderc_result_release(result);
if (entryBuf != null) memFree(entryBuf);
if (nameBuf != null) memFree(nameBuf);
if (srcBuf != null) memFree(srcBuf);
}
} finally {
Shaderc.shaderc_compile_options_release(optionsHandle);
}
}
public static String preprocess(String source, int shaderKind, String debugName, Options opts) {
final String cleanSrc = source.indexOf('\0') >= 0 ? source.replace("\0", "") : source;
final long compiler = COMPILER.get();
final long optionsHandle = Shaderc.shaderc_compile_options_initialize();
if (optionsHandle == 0L) return null;
try {
configureOptions(optionsHandle, opts);
ByteBuffer srcBuf = null, nameBuf = null, entryBuf = null;
long result = 0L;
try {
srcBuf = memUTF8(cleanSrc, false);
nameBuf = memUTF8(debugName == null ? "shader" : debugName);
entryBuf = memUTF8("main");
result = Shaderc.shaderc_compile_into_preprocessed_text(compiler, srcBuf, shaderKind, nameBuf, entryBuf, optionsHandle);
if (result == 0L) return null;
if (Shaderc.shaderc_result_get_compilation_status(result) != Shaderc.shaderc_compilation_status_success) {
return null;
}
final ByteBuffer text = Shaderc.shaderc_result_get_bytes(result);
if (text == null || text.remaining() == 0) return null;
return MemoryUtil.memUTF8(text);
} finally {
if (result != 0L) Shaderc.shaderc_result_release(result);
if (entryBuf != null) memFree(entryBuf);
if (nameBuf != null) memFree(nameBuf);
if (srcBuf != null) memFree(srcBuf);
}
} finally {
Shaderc.shaderc_compile_options_release(optionsHandle);
}
}
private static void configureOptions(long h, Options o) {
Shaderc.shaderc_compile_options_set_target_env(h, o.targetEnv(), o.envVersion());
Shaderc.shaderc_compile_options_set_source_language(h, Shaderc.shaderc_source_language_glsl);
if (o.vulkanRulesRelaxed()) Shaderc.shaderc_compile_options_set_vulkan_rules_relaxed(h, true);
if (o.autoBindUniforms()) Shaderc.shaderc_compile_options_set_auto_bind_uniforms(h, true);
if (o.autoMapLocations()) Shaderc.shaderc_compile_options_set_auto_map_locations(h, true);
if (o.autoCombinedImageSampler()) Shaderc.shaderc_compile_options_set_auto_combined_image_sampler(h, true);
if (o.forcedVersion() != 0) {
Shaderc.shaderc_compile_options_set_forced_version_profile(h, o.forcedVersion(), o.forcedProfile());
}
}
public static void clearCache() {
synchronized (CACHE) { CACHE.clear(); }
}
private static @Nullable Path writeFailureDump(String debugName, int shaderKind, String msg, String source) {
try {
Files.createDirectories(FAILURE_DIR);
final int n = FAILURE_COUNTER.getAndIncrement();
final Path p = FAILURE_DIR.resolve(String.format("%03d_%s_kind0x%s.glsl", n, debugName == null ? "shader" : debugName, Integer.toHexString(shaderKind)));
final StringBuilder sb = new StringBuilder();
sb.append("// shaderc errors:\n");
if (msg != null) for (String line : msg.split("\\R")) sb.append("// ").append(line).append('\n');
sb.append('\n').append(source);
Files.writeString(p, sb.toString());
return p;
} catch (IOException e) {
LOGGER.warn("Failed to write shaderc failure dump for '{}': {}", debugName, e.getMessage());
return null;
}
}
}
@@ -0,0 +1,420 @@
package com.gtnewhorizons.angelica.glsm.shader;
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.Edit;
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.PointerBuffer;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL43;
import org.lwjgl.util.shaderc.Shaderc;
import org.lwjgl.util.spvc.Spvc;
import org.lwjgl.util.spvc.SpvcReflectedResource;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.taumc.glsl.grammar.GLSLParser;
import org.taumc.glsl.grammar.GLSLParserBaseListener;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.startIdx;
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.stopIdx;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.memFree;
/** GLSL 330 core -> SPIR-V -> GLSL ES 320 via shaderc + SPIRV-Cross */
@Lwjgl3Aware
public final class SpirvShaderTranslator {
private static final Logger LOGGER = LogManager.getLogger("SpirvShaderTranslator");
private static final int SPV_DECORATION_LOCATION = 30;
private static final int SPV_DECORATION_BINDING = 33;
private SpirvShaderTranslator() {}
public static @Nullable String glslToGlslEs(String source, int glShaderType, String debugName) {
return glslToGlslEsImpl(source, glShaderType, debugName, false);
}
static @Nullable String glslToGlslEsRawForTest(String source, int glShaderType, String debugName) {
return glslToGlslEsImpl(source, glShaderType, debugName, true);
}
private static @Nullable String glslToGlslEsImpl(String source, int glShaderType, String debugName, boolean rawOutput) {
final int shaderKind = shaderKindForGlType(glShaderType);
if (shaderKind < 0) {
LOGGER.warn("Unknown GL shader type 0x{} for '{}' - skipping SPIR-V translation", Integer.toHexString(glShaderType), debugName);
return null;
}
final GlslVulkanPreprocess.Result pre = GlslVulkanPreprocess.run(source, glShaderType, debugName, false);
if (pre == null) return null;
final SpirvCompiler.Result cr = SpirvCompiler.compile(pre.rewrittenSource(), shaderKind, debugName, SpirvCompiler.Options.vulkanRelaxed());
if (cr.spirv() == null) {
final String firstErr = cr.error() == null ? "(no error)" : cr.error().split("\\R", 2)[0];
LOGGER.error("shaderc failed for '{}' (kind=0x{}): {} (dumped to {})",
debugName, Integer.toHexString(shaderKind), firstErr, cr.dumpPath() == null ? "<dump failed>" : cr.dumpPath());
return null;
}
try {
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
if (emitted == null) return null;
return rawOutput ? emitted : postProcessEsOutput(emitted, pre.boolUniforms());
} finally {
memFree(cr.spirv());
}
}
private static @Nullable String postProcessEsOutput(String glslEs, Set<String> boolUniformNames) {
final GLSLParser.Translation_unitContext root;
try {
root = GlslTransformUtils.parseFullQuiet(glslEs);
} catch (Exception e) {
LOGGER.error("Failed to parse spvc ES output for post-processing: {}", e.getMessage());
return null;
}
final DefaultBlockInfo block = findDefaultBlock(root);
final List<Edit> edits = new ArrayList<>();
if (block != null) {
edits.add(new Edit(startIdx(block.structExternalDecl), stopIdx(block.structExternalDecl), buildLooseUniforms(block.members, boolUniformNames)));
edits.add(new Edit(startIdx(block.uniformExternalDecl), stopIdx(block.uniformExternalDecl), ""));
}
final UseSiteRewriter rewriter = new UseSiteRewriter(block, boolUniformNames, edits);
ParseTreeWalker.DEFAULT.walk(rewriter, root);
return GlslVulkanPreprocess.applyEdits(glslEs, edits);
}
private record DefaultBlockInfo(ParserRuleContext structExternalDecl, ParserRuleContext uniformExternalDecl, String blockVarName, List<MemberInfo> members) {}
private record MemberInfo(String typePrefix, String name) {}
private static String buildLooseUniforms(List<MemberInfo> members, Set<String> boolNames) {
final StringBuilder sb = new StringBuilder();
for (MemberInfo m : members) {
if (boolNames.contains(m.name)) {
sb.append("uniform bool ").append(m.name).append(";\n");
} else {
sb.append("uniform ").append(m.typePrefix).append(' ').append(m.name).append(";\n");
}
}
if (!sb.isEmpty() && sb.charAt(sb.length() - 1) == '\n') sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
private static @Nullable DefaultBlockInfo findDefaultBlock(GLSLParser.Translation_unitContext root) {
final DefaultBlockFinder finder = new DefaultBlockFinder();
ParseTreeWalker.DEFAULT.walk(finder, root);
if (finder.structExternalDecl == null || finder.uniformExternalDecl == null) return null;
return new DefaultBlockInfo(finder.structExternalDecl, finder.uniformExternalDecl, finder.blockVarName, finder.members);
}
private static final class DefaultBlockFinder extends GLSLParserBaseListener {
String structName;
ParserRuleContext structExternalDecl;
ParserRuleContext uniformExternalDecl;
String blockVarName;
final List<MemberInfo> members = new ArrayList<>();
@Override
public void enterStruct_specifier(GLSLParser.Struct_specifierContext ctx) {
if (structExternalDecl != null) return;
final TerminalNode id = ctx.IDENTIFIER();
if (id == null) return;
final String idText = id.getText();
if (!idText.startsWith("_RESERVED_IDENTIFIER_FIXUP_") || !idText.endsWith("DefaultUniformBlock")) return;
structName = idText;
structExternalDecl = findExternalDeclaration(ctx);
for (GLSLParser.Struct_declarationContext sd : ctx.struct_declaration_list().struct_declaration()) {
final StringBuilder typePrefix = new StringBuilder();
if (sd.type_qualifier() != null) typePrefix.append(textWithSpaces(sd.type_qualifier())).append(' ');
typePrefix.append(textWithSpaces(sd.type_specifier()));
for (GLSLParser.Struct_declaratorContext decl : sd.struct_declarator_list().struct_declarator()) {
final String arraySuffix = decl.array_specifier() == null ? "" : decl.array_specifier().getText();
members.add(new MemberInfo(typePrefix + arraySuffix, decl.IDENTIFIER().getText()));
}
}
}
@Override
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
if (structName == null || uniformExternalDecl != null) return;
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
if (single == null || single.typeless_declaration() == null) return;
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
if (fst == null || fst.type_qualifier() == null) return;
boolean isUniform = false;
for (GLSLParser.Single_type_qualifierContext stq : fst.type_qualifier().single_type_qualifier()) {
if (stq.storage_qualifier() != null && "uniform".equals(stq.storage_qualifier().getText())) {
isUniform = true;
break;
}
}
if (!isUniform) return;
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
if (ts.type_name() == null) return;
if (!structName.equals(ts.type_name().getText())) return;
blockVarName = single.typeless_declaration().IDENTIFIER().getText();
uniformExternalDecl = findExternalDeclaration(ctx);
}
private static String textWithSpaces(ParseTree tree) {
final StringBuilder sb = new StringBuilder();
appendTextWithSpaces(tree, sb);
return sb.toString().trim();
}
private static void appendTextWithSpaces(ParseTree tree, StringBuilder sb) {
if (tree instanceof TerminalNode) {
if (!sb.isEmpty() && sb.charAt(sb.length() - 1) != ' ') sb.append(' ');
sb.append(tree.getText());
return;
}
for (int i = 0; i < tree.getChildCount(); i++) {
appendTextWithSpaces(tree.getChild(i), sb);
}
}
}
private static ParserRuleContext findExternalDeclaration(ParserRuleContext ctx) {
ParseTree cur = ctx;
while (cur != null && !(cur instanceof GLSLParser.External_declarationContext)) {
cur = cur.getParent();
}
return (ParserRuleContext) cur;
}
private static final class UseSiteRewriter extends GLSLParserBaseListener {
private final @Nullable DefaultBlockInfo block;
private final Set<String> boolNames;
private final List<Edit> edits;
private final Set<String> memberNames;
UseSiteRewriter(@Nullable DefaultBlockInfo block, Set<String> boolNames, List<Edit> edits) {
this.block = block;
this.boolNames = boolNames;
this.edits = edits;
this.memberNames = new HashSet<>();
if (block != null) for (MemberInfo m : block.members) memberNames.add(m.name);
}
@Override
public void enterBinary_expression(GLSLParser.Binary_expressionContext ctx) {
if (ctx.getChildCount() != 3) return;
final ParseTree opNode = ctx.getChild(1);
if (!(opNode instanceof TerminalNode op)) return;
final String opText = op.getText();
final boolean isNe = "!=".equals(opText);
final boolean isEq = "==".equals(opText);
if (!isNe && !isEq) return;
final ParseTree rhs = ctx.getChild(2);
if (!"0u".equals(rhs.getText())) return;
final ParseTree lhs = ctx.getChild(0);
final String nameIfBool = resolveBoolOperand(lhs);
if (nameIfBool == null) return;
final String replacement = isNe ? nameIfBool : "!" + nameIfBool;
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), replacement));
}
@Override
public void enterPostfix_expression(GLSLParser.Postfix_expressionContext ctx) {
if (ctx.type_specifier() != null && ctx.LEFT_PAREN() != null
&& "bool".equals(ctx.type_specifier().getText())
&& ctx.function_call_parameters() != null
&& ctx.function_call_parameters().assignment_expression() != null
&& ctx.function_call_parameters().assignment_expression().size() == 1) {
final String nameIfBool = resolveBoolOperand(ctx.function_call_parameters().assignment_expression(0));
if (nameIfBool != null) {
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), nameIfBool));
return;
}
}
if (block != null && ctx.getChildCount() == 3 && ctx.DOT() != null && ctx.field_selection() != null) {
final ParseTree inner = ctx.getChild(0);
if (inner.getText().equals(block.blockVarName)) {
final String member = ctx.field_selection().getText();
if (memberNames.contains(member) && !coveredByAncestorBoolCompare(ctx)) {
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), member));
}
}
}
}
private @Nullable String resolveBoolOperand(ParseTree operand) {
final String text = operand.getText();
if (boolNames.contains(text)) return text;
if (block != null && text.startsWith(block.blockVarName + ".")) {
final String member = text.substring(block.blockVarName.length() + 1);
if (boolNames.contains(member) && memberNames.contains(member)) return member;
}
return null;
}
private boolean coveredByAncestorBoolCompare(GLSLParser.Postfix_expressionContext ctx) {
final int start = startIdx(ctx), stop = stopIdx(ctx);
for (Edit e : edits) {
if (e.startIdx() <= start && stop <= e.stopIdx() && e.stopIdx() > stop) return true;
}
return false;
}
}
private static @Nullable String emitGlslEs(ByteBuffer spirv, String debugName, int glShaderType, Set<String> explicitVsInputs) {
try (MemoryStack stack = stackPush()) {
final PointerBuffer pContext = stack.pointers(0);
if (Spvc.spvc_context_create(pContext) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_context_create failed for '{}'", debugName);
return null;
}
final long ctx = pContext.get(0);
try {
final IntBuffer spirvWords = spirv.asIntBuffer();
final PointerBuffer pIr = stack.pointers(0);
if (Spvc.spvc_context_parse_spirv(ctx, spirvWords, spirvWords.remaining(), pIr) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_context_parse_spirv failed for '{}': {}", debugName, lastSpvcError(ctx));
return null;
}
final PointerBuffer pCompiler = stack.pointers(0);
if (Spvc.spvc_context_create_compiler(ctx, Spvc.SPVC_BACKEND_GLSL, pIr.get(0), Spvc.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, pCompiler) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_context_create_compiler(GLSL) failed for '{}': {}", debugName, lastSpvcError(ctx));
return null;
}
final long compiler = pCompiler.get(0);
final PointerBuffer pOptions = stack.pointers(0);
if (Spvc.spvc_compiler_create_compiler_options(compiler, pOptions) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_compiler_create_compiler_options failed for '{}': {}", debugName, lastSpvcError(ctx));
return null;
}
final long options = pOptions.get(0);
Spvc.spvc_compiler_options_set_uint(options, Spvc.SPVC_COMPILER_OPTION_GLSL_VERSION, 320);
Spvc.spvc_compiler_options_set_bool(options, Spvc.SPVC_COMPILER_OPTION_GLSL_ES, true);
Spvc.spvc_compiler_options_set_bool(options, Spvc.SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS, true);
if (Spvc.spvc_compiler_install_compiler_options(compiler, options) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_compiler_install_compiler_options failed for '{}': {}", debugName, lastSpvcError(ctx));
return null;
}
applyResourceDecorationFixups(ctx, compiler, glShaderType, explicitVsInputs, debugName);
final PointerBuffer pSource = stack.pointers(0);
if (Spvc.spvc_compiler_compile(compiler, pSource) != Spvc.SPVC_SUCCESS) {
LOGGER.error("spvc_compiler_compile failed for '{}': {}", debugName, lastSpvcError(ctx));
return null;
}
final long sourcePtr = pSource.get(0);
if (sourcePtr == 0L) {
LOGGER.error("spvc_compiler_compile returned NULL source for '{}'", debugName);
return null;
}
return MemoryUtil.memUTF8(sourcePtr);
} finally {
Spvc.spvc_context_destroy(ctx);
}
}
}
private static void applyResourceDecorationFixups(long ctx, long compiler, int glShaderType, Set<String> explicitVsInputs, String debugName) {
if (glShaderType == GL20.GL_VERTEX_SHADER) {
unsetDecorationForResourceType(ctx, compiler, Spvc.SPVC_RESOURCE_TYPE_STAGE_OUTPUT, SPV_DECORATION_LOCATION, null, debugName);
unsetDecorationForResourceType(ctx, compiler, Spvc.SPVC_RESOURCE_TYPE_STAGE_INPUT, SPV_DECORATION_LOCATION, explicitVsInputs, debugName);
} else if (glShaderType == GL20.GL_FRAGMENT_SHADER) {
unsetDecorationForResourceType(ctx, compiler, Spvc.SPVC_RESOURCE_TYPE_STAGE_INPUT, SPV_DECORATION_LOCATION, null, debugName);
} else if (glShaderType == GL32.GL_GEOMETRY_SHADER || glShaderType == GL40.GL_TESS_CONTROL_SHADER || glShaderType == GL40.GL_TESS_EVALUATION_SHADER) {
unsetDecorationForResourceType(ctx, compiler, Spvc.SPVC_RESOURCE_TYPE_STAGE_OUTPUT, SPV_DECORATION_LOCATION, null, debugName);
unsetDecorationForResourceType(ctx, compiler, Spvc.SPVC_RESOURCE_TYPE_STAGE_INPUT, SPV_DECORATION_LOCATION, null, debugName);
}
if (glShaderType != GL43.GL_COMPUTE_SHADER) {
for (int resourceType : BINDING_STRIP_RESOURCE_TYPES) {
unsetDecorationForResourceType(ctx, compiler, resourceType, SPV_DECORATION_BINDING, null, debugName);
}
}
}
private static final int[] BINDING_STRIP_RESOURCE_TYPES = {
Spvc.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
Spvc.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
Spvc.SPVC_RESOURCE_TYPE_SEPARATE_IMAGE,
Spvc.SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS,
Spvc.SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
};
private static void unsetDecorationForResourceType(long ctx, long compiler, int resourceType, int decoration, Set<String> preserve, String debugName) {
try (MemoryStack stack = stackPush()) {
final PointerBuffer pResources = stack.pointers(0);
if (Spvc.spvc_compiler_create_shader_resources(compiler, pResources) != Spvc.SPVC_SUCCESS) {
LOGGER.warn("spvc_compiler_create_shader_resources failed for '{}': {}", debugName, lastSpvcError(ctx));
return;
}
final long resources = pResources.get(0);
final PointerBuffer pList = stack.pointers(0);
final PointerBuffer pCount = stack.pointers(0);
if (Spvc.spvc_resources_get_resource_list_for_type(resources, resourceType, pList, pCount) != Spvc.SPVC_SUCCESS) {
LOGGER.warn("spvc_resources_get_resource_list_for_type failed for '{}': {}", debugName, lastSpvcError(ctx));
return;
}
final long listAddr = pList.get(0);
final long count = pCount.get(0);
if (listAddr == 0L || count <= 0L) return;
final SpvcReflectedResource.Buffer list = SpvcReflectedResource.create(listAddr, (int) count);
for (int i = 0; i < list.remaining(); i++) {
final SpvcReflectedResource res = list.get(i);
if (preserve != null) {
final String name = res.nameString();
if (name != null && preserve.contains(name)) continue;
}
Spvc.spvc_compiler_unset_decoration(compiler, res.id(), decoration);
}
}
}
private static String lastSpvcError(long ctx) {
final String msg = Spvc.spvc_context_get_last_error_string(ctx);
return msg == null || msg.isEmpty() ? "(no error message)" : msg;
}
private static int shaderKindForGlType(int glShaderType) {
return switch (glShaderType) {
case GL20.GL_VERTEX_SHADER -> Shaderc.shaderc_vertex_shader;
case GL20.GL_FRAGMENT_SHADER -> Shaderc.shaderc_fragment_shader;
case GL32.GL_GEOMETRY_SHADER -> Shaderc.shaderc_geometry_shader;
case GL40.GL_TESS_CONTROL_SHADER -> Shaderc.shaderc_tess_control_shader;
case GL40.GL_TESS_EVALUATION_SHADER -> Shaderc.shaderc_tess_evaluation_shader;
case GL43.GL_COMPUTE_SHADER -> Shaderc.shaderc_compute_shader;
default -> -1;
};
}
}
@@ -1,4 +1,4 @@
package net.coderbot.iris.gl.uniform;
package com.gtnewhorizons.angelica.glsm.shader;
public enum UniformType {
INT,
@@ -38,4 +38,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -43,4 +43,8 @@ public class Color4Stack extends Color4 implements IStateStack<Color4Stack> {
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -38,4 +38,8 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -38,4 +38,8 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -45,4 +45,8 @@ public class IntegerStateStack extends IntegerState implements IStateStack<Integ
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -39,4 +39,8 @@ public class LineStateStack extends LineState implements IStateStack<LineStateSt
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -39,4 +39,8 @@ public class PointStateStack extends PointState implements IStateStack<PointStat
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -39,4 +39,8 @@ public class PolygonStateStack extends PolygonState implements IStateStack<Polyg
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -39,4 +39,8 @@ public class StencilStateStack extends StencilState implements IStateStack<Stenc
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -42,4 +42,8 @@ public class TextureBindingStack extends TextureBinding implements IStateStack<T
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
import org.lwjgl.opengl.GL13;
/**
@@ -37,10 +38,16 @@ public class TextureUnitBooleanStateStack extends BooleanStateStack {
}
private void setEnabledWithUnitSwitch(boolean enabled) {
final boolean bypass = GLStateManager.shouldBypassCache();
final boolean bypass = !GLStateManager.isCachingEnabled();
if (bypass || enabled != this.enabled) {
if (!bypass) this.enabled = enabled;
if (GLSMHooks.TEXTURE_UNIT_STATE.hasListeners()) {
GLSMHooks.textureUnitStateEvent.unit = unitIndex;
GLSMHooks.textureUnitStateEvent.enabled = enabled;
GLSMHooks.TEXTURE_UNIT_STATE.post(GLSMHooks.textureUnitStateEvent);
}
if (!ffpStateOnly) {
final int currentUnit = GLStateManager.getActiveTextureUnit();
final boolean needsSwitch = currentUnit != unitIndex;
@@ -38,4 +38,8 @@ public class ViewPortStateStack extends ViewportState implements IStateStack<Vie
public boolean isEmpty() {
return pointer == 0;
}
public boolean topChanged() {
return pointer > 0 && !sameAs(stack[pointer - 1]);
}
}
@@ -1,4 +1,4 @@
package net.coderbot.iris.gl.blending;
package com.gtnewhorizons.angelica.glsm.states;
import org.lwjgl.opengl.GL11;
@@ -22,6 +22,15 @@ public class BooleanState implements ISettableState<BooleanState> {
this.ffpStateOnly = ffpStateOnly;
}
public void applyToBackend() {
if (ffpStateOnly) return;
if (enabled) {
RENDER_BACKEND.enable(glCap);
} else {
RENDER_BACKEND.disable(glCap);
}
}
public void setUnknownState() {
stateUnknown = true;
}
@@ -35,12 +44,10 @@ public class BooleanState implements ISettableState<BooleanState> {
}
public void setEnabled(boolean enabled) {
final boolean bypass = GLStateManager.shouldBypassCache();
final boolean bypass = !GLStateManager.isCachingEnabled();
if (stateUnknown || bypass || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.vendorIsAMD() && GLStateManager.isPoppingAttributes())) {
stateUnknown = false;
if (!bypass) {
this.enabled = enabled;
}
this.enabled = enabled;
if (!ffpStateOnly) {
if (enabled) {
RENDER_BACKEND.enable(this.glCap);
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
/**
@@ -31,4 +32,10 @@ public class ClipPlaneState {
final float[] p = eyePlanes[index];
buf.put(p[0]).put(p[1]).put(p[2]).put(p[3]);
}
public void getEyePlane(int index, DoubleBuffer buf) {
final float[] p = eyePlanes[index];
final int pos = buf.position();
buf.put(pos, p[0]).put(pos + 1, p[1]).put(pos + 2, p[2]).put(pos + 3, p[3]);
}
}
@@ -0,0 +1,33 @@
package com.gtnewhorizons.angelica.glsm.states;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
public class ImageUnitArray {
private static final int MIN_UNITS = 8;
private volatile ImageUnitBinding[] bindings;
public ImageUnitBinding get(int unit) {
if (unit < 0) return null;
ImageUnitBinding[] local = bindings;
if (local == null) local = allocate();
return unit < local.length ? local[unit] : null;
}
public int size() {
final ImageUnitBinding[] local = bindings;
return local == null ? 0 : local.length;
}
private synchronized ImageUnitBinding[] allocate() {
ImageUnitBinding[] local = bindings;
if (local == null) {
final int count = Math.max(MIN_UNITS, RenderSystem.getMaxImageUnits());
local = new ImageUnitBinding[count];
for (int i = 0; i < count; i++) local[i] = new ImageUnitBinding();
bindings = local;
}
return local;
}
}
@@ -0,0 +1,44 @@
package com.gtnewhorizons.angelica.glsm.states;
import lombok.Getter;
public class ImageUnitBinding implements ISettableState<ImageUnitBinding> {
@Getter protected int texture;
@Getter protected int level;
@Getter protected boolean layered;
@Getter protected int layer;
@Getter protected int access;
@Getter protected int format;
public void setBinding(int texture, int level, boolean layered, int layer, int access, int format) {
this.texture = texture;
this.level = level;
this.layered = layered;
this.layer = layer;
this.access = access;
this.format = format;
}
@Override
public ImageUnitBinding set(ImageUnitBinding state) {
this.texture = state.texture;
this.level = state.level;
this.layered = state.layered;
this.layer = state.layer;
this.access = state.access;
this.format = state.format;
return this;
}
@Override
public boolean sameAs(Object state) {
if (this == state) return true;
if (!(state instanceof ImageUnitBinding other)) return false;
return texture == other.texture && level == other.level && layered == other.layered && layer == other.layer && access == other.access && format == other.format;
}
@Override
public ImageUnitBinding copy() {
return new ImageUnitBinding().set(this);
}
}
@@ -29,7 +29,7 @@ public class LightModelState implements ISettableState<LightModelState> {
public boolean setAmbient(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -39,7 +39,7 @@ public class LightModelState implements ISettableState<LightModelState> {
public boolean setAmbient(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -47,7 +47,7 @@ public class LightModelState implements ISettableState<LightModelState> {
}
public boolean setColorControl(int val) {
if (GLStateManager.shouldBypassCache() || this.colorControl != val) {
if (!GLStateManager.isCachingEnabled() || this.colorControl != val) {
this.colorControl = val;
return true;
}
@@ -59,7 +59,7 @@ public class LightModelState implements ISettableState<LightModelState> {
}
public boolean setLocalViewer(float val) {
if (GLStateManager.shouldBypassCache() || Float.compare(this.localViewer, val) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(this.localViewer, val) != 0) {
this.localViewer = val;
return true;
}
@@ -79,7 +79,7 @@ public class LightModelState implements ISettableState<LightModelState> {
}
public boolean setTwoSide(float val) {
if (GLStateManager.shouldBypassCache() || Float.compare(this.twoSide, val) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(this.twoSide, val) != 0) {
this.twoSide = val;
return true;
}
@@ -74,18 +74,18 @@ public class LightState implements ISettableState<LightState> {
this.rawPosition = new Vector4f(position);
this.position = position;
this.position.mul(GLStateManager.getModelViewMatrix());
this.posMvGeneration = GLStateManager.mvGeneration;
this.posMvGeneration = GLStateManager.getMvGeneration();
this.rawSpotDirection = new Vector3f(spotDirection);
this.spotDirection = spotDirection;
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
this.spotDirection.mul(matrix3f);
this.spotDirLinearGeneration = GLStateManager.mvLinearGeneration;
this.spotDirLinearGeneration = GLStateManager.getMvLinearGeneration();
}
public boolean setAmbient(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -95,7 +95,7 @@ public class LightState implements ISettableState<LightState> {
public boolean setAmbient(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -104,7 +104,7 @@ public class LightState implements ISettableState<LightState> {
public boolean setDiffuse(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.diffuse.equals(vector4f)) {
this.diffuse.set(vector4f);
return true;
}
@@ -114,7 +114,7 @@ public class LightState implements ISettableState<LightState> {
public boolean setDiffuse(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.diffuse.equals(vector4f)) {
this.diffuse.set(vector4f);
return true;
}
@@ -123,7 +123,7 @@ public class LightState implements ISettableState<LightState> {
public boolean setSpecular(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.specular.equals(vector4f)) {
this.specular.set(vector4f);
return true;
}
@@ -133,7 +133,7 @@ public class LightState implements ISettableState<LightState> {
public boolean setSpecular(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.specular.equals(vector4f)) {
this.specular.set(vector4f);
return true;
}
@@ -152,8 +152,8 @@ public class LightState implements ISettableState<LightState> {
}
private boolean applyPosition(Vector4f raw) {
final int mvGen = GLStateManager.mvGeneration;
if (GLStateManager.shouldBypassCache() || this.posMvGeneration != mvGen || !this.rawPosition.equals(raw)) {
final int mvGen = GLStateManager.getMvGeneration();
if (!GLStateManager.isCachingEnabled() || this.posMvGeneration != mvGen || !this.rawPosition.equals(raw)) {
this.rawPosition.set(raw);
this.posMvGeneration = mvGen;
this.position.set(raw);
@@ -175,8 +175,8 @@ public class LightState implements ISettableState<LightState> {
}
private boolean applySpotDirection(Vector3f raw) {
final int mvLinearGen = GLStateManager.mvLinearGeneration;
if (GLStateManager.shouldBypassCache() || this.spotDirLinearGeneration != mvLinearGen || !this.rawSpotDirection.equals(raw)) {
final int mvLinearGen = GLStateManager.getMvLinearGeneration();
if (!GLStateManager.isCachingEnabled() || this.spotDirLinearGeneration != mvLinearGen || !this.rawSpotDirection.equals(raw)) {
this.rawSpotDirection.set(raw);
this.spotDirLinearGeneration = mvLinearGen;
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
@@ -200,7 +200,7 @@ public class LightState implements ISettableState<LightState> {
}
public boolean setSpotExponent(float f) {
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.spotExponent) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(f, this.spotExponent) != 0) {
this.spotExponent = f;
return true;
}
@@ -220,7 +220,7 @@ public class LightState implements ISettableState<LightState> {
}
public boolean setSpotCutoff(float f) {
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.spotCutoff) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(f, this.spotCutoff) != 0) {
this.spotCutoff = f;
this.spotCosCutoff = (float) Math.cos(Math.toRadians(f));
return true;
@@ -241,7 +241,7 @@ public class LightState implements ISettableState<LightState> {
}
public boolean setConstantAttenuation(float f) {
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.constantAttenuation) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(f, this.constantAttenuation) != 0) {
this.constantAttenuation = f;
return true;
}
@@ -261,7 +261,7 @@ public class LightState implements ISettableState<LightState> {
}
public boolean setLinearAttenuation(float f) {
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.linearAttenuation) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(f, this.linearAttenuation) != 0) {
this.linearAttenuation = f;
return true;
}
@@ -281,7 +281,7 @@ public class LightState implements ISettableState<LightState> {
}
public boolean setQuadraticAttenuation(float f) {
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.quadraticAttenuation) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(f, this.quadraticAttenuation) != 0) {
this.quadraticAttenuation = f;
return true;
}
@@ -39,7 +39,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setAmbient(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -49,7 +49,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setAmbient(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.ambient.equals(vector4f)) {
this.ambient.set(vector4f);
return true;
}
@@ -58,7 +58,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setDiffuse(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.diffuse.equals(vector4f)) {
this.diffuse.set(vector4f);
return true;
}
@@ -68,7 +68,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setDiffuse(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.diffuse.equals(vector4f)) {
this.diffuse.set(vector4f);
return true;
}
@@ -77,7 +77,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setSpecular(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.specular.equals(vector4f)) {
this.specular.set(vector4f);
return true;
}
@@ -87,7 +87,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setSpecular(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.specular.equals(vector4f)) {
this.specular.set(vector4f);
return true;
}
@@ -96,7 +96,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setEmission(FloatBuffer newBuffer) {
vector4f.set(newBuffer);
if (GLStateManager.shouldBypassCache() || !this.emission.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.emission.equals(vector4f)) {
this.emission.set(vector4f);
return true;
}
@@ -106,7 +106,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setEmission(IntBuffer newBuffer) {
vector4i.set(newBuffer);
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
if (GLStateManager.shouldBypassCache() || !this.emission.equals(vector4f)) {
if (!GLStateManager.isCachingEnabled() || !this.emission.equals(vector4f)) {
this.emission.set(vector4f);
return true;
}
@@ -114,7 +114,7 @@ public class MaterialState implements ISettableState<MaterialState> {
}
public boolean setShininess(float val) {
if (GLStateManager.shouldBypassCache() || Float.compare(this.shininess, val) != 0) {
if (!GLStateManager.isCachingEnabled() || Float.compare(this.shininess, val) != 0) {
this.shininess = val;
return true;
}
@@ -141,7 +141,7 @@ public class MaterialState implements ISettableState<MaterialState> {
vector3f.set(newBuffer);
}
if (GLStateManager.shouldBypassCache() || !this.colorIndexes.equals(vector3f)) {
if (!GLStateManager.isCachingEnabled() || !this.colorIndexes.equals(vector3f)) {
this.colorIndexes.set(vector3f);
return true;
}
@@ -151,7 +151,7 @@ public class MaterialState implements ISettableState<MaterialState> {
public boolean setColorIndexes(IntBuffer newBuffer) {
vector3i.set(newBuffer.get(0), newBuffer.get(2), newBuffer.get(1));
vector3f.set((float) vector3i.x, (float) vector3i.y, (float) vector3i.z);
if (GLStateManager.shouldBypassCache() || !this.colorIndexes.equals(vector3f)) {
if (!GLStateManager.isCachingEnabled() || !this.colorIndexes.equals(vector3f)) {
this.colorIndexes.set(vector3f);
return true;
}
Loaded 100 of 866 files, more files were not shown because too many files have changed in this diff. Show more