mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
SDL GPU backend, TESR Batching/caching API, Tracy Integration (#1993)
This commit is contained in:
1 parent
413df76ad4
commit
8da561d3b6
647 files changed
+60152
-5331
No files matched your search
Executable
+64
@@ -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
|
||||
@@ -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 :tracy-client:publish
|
||||
@@ -1,4 +1,5 @@
|
||||
.gradle
|
||||
.kotlin/
|
||||
.settings
|
||||
/.idea/
|
||||
/.vs/
|
||||
@@ -38,3 +39,6 @@ addon.local.gradle.kts
|
||||
addon.late.local.gradle
|
||||
addon.late.local.gradle.kts
|
||||
local/
|
||||
tracy-client/src/main/resources/natives/
|
||||
dist/tracy-natives/
|
||||
*.tracy
|
||||
@@ -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
|
||||
+181
-29
@@ -8,33 +8,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
|
||||
// Ensure flyby args are passed 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.flyby.")) {
|
||||
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")) {
|
||||
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 +144,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 +152,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 +160,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()
|
||||
}
|
||||
|
||||
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.check { dependsOn(glCoreTest) }
|
||||
|
||||
tasks.shadowJar {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
@@ -89,13 +236,18 @@ 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")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
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))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") { from(files("../gradle/libs.versions.toml")) }
|
||||
}
|
||||
}
|
||||
@@ -1,189 +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.19: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.8-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
// BQ Testing
|
||||
// devOnlyNonPublishable("com.github.GTNewHorizons:BetterQuesting:3.8.74-GTNH:dev")
|
||||
|
||||
|
||||
// Display List Testing
|
||||
/*
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-749-GTNH:dev")
|
||||
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("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
|
||||
|
||||
// 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')
|
||||
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:8.5.18")
|
||||
testRuntimeOnly("org.joml:joml:1.10.8")
|
||||
}
|
||||
}
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { transitive = false }
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+115
-33
@@ -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,42 +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")}")
|
||||
testRuntimeOnly("it.unimi.dsi:fastutil:${property("fastutilVersion")}")
|
||||
testRuntimeOnly("org.joml:joml:${property("jomlVersion")}") { isTransitive = false }
|
||||
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 {
|
||||
@@ -118,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 {
|
||||
@@ -136,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
|
||||
@@ -161,7 +203,7 @@ fun Test.configureGlsmJava8() {
|
||||
}
|
||||
}
|
||||
|
||||
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeDepsForTest)
|
||||
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeTestFixturesClasses, downgradeDepsForTest)
|
||||
|
||||
val mainClassesDirs = sourceSets["main"].output.classesDirs
|
||||
.plus(sourceSets["stubs"].output.classesDirs)
|
||||
@@ -175,7 +217,9 @@ fun Test.configureGlsmJava8() {
|
||||
classpath = downgradedTest
|
||||
.plus(downgradedMain)
|
||||
.plus(downgradedDeps)
|
||||
.plus(sourceSets["test"].runtimeClasspath.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)
|
||||
@@ -184,7 +228,10 @@ fun Test.configureGlsmJava8() {
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags = GL_TASK_TAGS.values.toSet()
|
||||
excludeTags = GL_TASK_TAGS.values.toSet() + "lwjgl3"
|
||||
}
|
||||
filter {
|
||||
excludeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
}
|
||||
configureGlsmJava8()
|
||||
}
|
||||
@@ -225,6 +272,41 @@ val verifyTestsRan by tasks.registering {
|
||||
}
|
||||
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") {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
lwjglVersion = 2.9.4-nightly-20150209
|
||||
eventbusVersion = 7.0-beta.15
|
||||
celeritasVersion = 2.5.8-GTNH
|
||||
lombokVersion = 1.18.42
|
||||
log4jVersion = 2.0-beta9
|
||||
guavaVersion = 18.0
|
||||
gtnhlibVersion = 0.11.19
|
||||
fastutilVersion = 8.5.18
|
||||
jomlVersion = 1.10.8
|
||||
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,122 @@
|
||||
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 boolean USE_SDL_GPU = Boolean.getBoolean("angelica.sdlgpu.enable");
|
||||
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
|
||||
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
|
||||
// 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");
|
||||
|
||||
// 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,8 +1,12 @@
|
||||
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;
|
||||
@@ -15,6 +19,7 @@ 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;
|
||||
@@ -33,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>
|
||||
@@ -71,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) {
|
||||
@@ -103,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);
|
||||
@@ -144,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;"
|
||||
@@ -172,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;"
|
||||
@@ -181,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) {
|
||||
@@ -199,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) {
|
||||
@@ -226,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();
|
||||
@@ -242,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;
|
||||
@@ -310,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));");
|
||||
}
|
||||
@@ -485,18 +530,19 @@ public class CompatShaderTransformer {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -524,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.
|
||||
@@ -548,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");
|
||||
}
|
||||
|
||||
@@ -581,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)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,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);
|
||||
}
|
||||
@@ -475,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);
|
||||
}
|
||||
@@ -490,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);
|
||||
}
|
||||
@@ -500,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);
|
||||
}
|
||||
@@ -556,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);
|
||||
}
|
||||
@@ -623,7 +624,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();
|
||||
}
|
||||
}
|
||||
@@ -636,7 +637,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();
|
||||
}
|
||||
}
|
||||
@@ -654,7 +655,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();
|
||||
}
|
||||
}
|
||||
@@ -676,7 +677,7 @@ public class DisplayListManager {
|
||||
pendingTransformOps.add("glMultMatrixf(...)");
|
||||
}
|
||||
|
||||
if (DEBUG_DISPLAY_LISTS) {
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
flushMatrix();
|
||||
}
|
||||
}
|
||||
@@ -694,7 +695,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();
|
||||
}
|
||||
}
|
||||
@@ -709,7 +710,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();
|
||||
}
|
||||
}
|
||||
@@ -780,10 +781,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<>();
|
||||
@@ -804,9 +805,11 @@ public class DisplayListManager {
|
||||
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) {
|
||||
@@ -888,7 +891,7 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -989,7 +992,7 @@ public class DisplayListManager {
|
||||
tessellator, copyLast
|
||||
);
|
||||
accumulatedDraws.add(pendingDraw);
|
||||
if (DEBUG_DISPLAY_LISTS) {
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
drawBarrier();
|
||||
}
|
||||
}
|
||||
@@ -1017,29 +1020,38 @@ 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 {
|
||||
final CompiledDisplayList compiled = displayListCache.get(list);
|
||||
if (compiled != null) {
|
||||
compiled.render(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to VBOManager or legacy display list
|
||||
if (list < 0) {
|
||||
// Negative IDs are VBOManager space (entity models, etc.)
|
||||
final VertexBuffer vbo = VBOManager.get(list);
|
||||
if (vbo != null) {
|
||||
vbo.render();
|
||||
}
|
||||
// Per OpenGL spec: if list is undefined, glCallList has no effect
|
||||
}
|
||||
// An uncached positive-ID list = no-op (per OpenGL spec)
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to VBOManager or legacy display list
|
||||
if (list < 0) {
|
||||
// Negative IDs are VBOManager space (entity models, etc.)
|
||||
final VertexBuffer vbo = VBOManager.get(list);
|
||||
if (vbo != null) {
|
||||
vbo.render();
|
||||
}
|
||||
// Per OpenGL spec: if list is undefined, glCallList has no effect
|
||||
}
|
||||
// An uncached positive-ID list = no-op (per OpenGL spec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete display lists and free their VBO resources.
|
||||
*/
|
||||
|
||||
@@ -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,198 @@
|
||||
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.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
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 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();
|
||||
@@ -581,6 +570,10 @@ public final class GLDebug {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -606,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,19 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.antlr.v4.runtime.BufferedTokenStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
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,6 +88,77 @@ public class GlslTransformUtils {
|
||||
return source;
|
||||
}
|
||||
|
||||
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 = {""};
|
||||
|
||||
@@ -90,13 +90,33 @@ 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) {
|
||||
final int quadCount = vertexCount / 4;
|
||||
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();
|
||||
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);
|
||||
@@ -184,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);
|
||||
|
||||
@@ -18,6 +18,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 +41,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 +54,11 @@ 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;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
|
||||
@@ -64,30 +71,38 @@ 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();
|
||||
@@ -99,8 +114,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,7 +157,10 @@ 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;
|
||||
@@ -168,6 +188,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);
|
||||
@@ -327,12 +351,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() {
|
||||
@@ -340,13 +368,49 @@ 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 supportsSSBO() {
|
||||
return supportsSSBO;
|
||||
}
|
||||
|
||||
public static boolean supportsMultiDrawIndirect() {
|
||||
return supportsMultiDrawIndirect;
|
||||
}
|
||||
|
||||
public static boolean supportsBufferStorage() {
|
||||
return supportsBufferStorage;
|
||||
}
|
||||
@@ -427,9 +491,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() {
|
||||
@@ -443,7 +512,7 @@ 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;
|
||||
}
|
||||
@@ -453,6 +522,10 @@ public class RenderSystem {
|
||||
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) {
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
}
|
||||
@@ -505,6 +578,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static boolean supportsSnormFormats() {
|
||||
if (isGLES()) return false;
|
||||
return GLStateManager.capabilities.OpenGL31;
|
||||
}
|
||||
|
||||
@@ -552,9 +626,17 @@ public class RenderSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460" */
|
||||
static String parseGlVersionString(String info) {
|
||||
final Matcher matcher = SEMVER_PATTERN.matcher(Objects.requireNonNull(info));
|
||||
/** 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 + "\"");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+206
-6
@@ -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,10 @@ 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.EXTDirectStateAccess;
|
||||
import org.lwjgl.opengl.EXTShaderImageLoadStore;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -27,6 +30,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 +101,26 @@ 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
|
||||
public void setVSyncEnabled(boolean enabled) {Display.setVSyncEnabled(enabled);}
|
||||
|
||||
@Override
|
||||
public boolean isAnisotropicSupported() {
|
||||
return caps != null && caps.GL_EXT_texture_filter_anisotropic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {GL11.glFlush();}
|
||||
|
||||
@@ -322,7 +344,7 @@ 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
|
||||
@@ -332,8 +354,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
|
||||
@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
|
||||
@@ -927,6 +948,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);
|
||||
@@ -1058,6 +1105,16 @@ 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);
|
||||
@@ -1284,6 +1341,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);
|
||||
@@ -1350,6 +1412,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);
|
||||
@@ -1366,7 +1488,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
|
||||
@@ -1391,7 +1519,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);
|
||||
@@ -1406,7 +1534,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);
|
||||
@@ -1520,4 +1648,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,8 +1,12 @@
|
||||
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 me.eigenraven.lwjgl3ify.api.SwapchainInvalidatingChange;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -16,14 +20,19 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class RenderBackend {
|
||||
|
||||
public void onPostWindowCreate(long window, boolean debug) {}
|
||||
|
||||
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 +44,46 @@ 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() {}
|
||||
public abstract void setVSyncEnabled(boolean enabled);
|
||||
|
||||
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(SwapchainInvalidatingChange 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 abstract void enable(int cap);
|
||||
public abstract void enablei(int cap, int index);
|
||||
public abstract void disable(int cap);
|
||||
@@ -60,6 +101,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 +127,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 +195,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);
|
||||
@@ -158,6 +218,7 @@ public abstract class RenderBackend {
|
||||
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);
|
||||
@@ -179,7 +240,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);
|
||||
@@ -196,6 +260,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);
|
||||
@@ -224,6 +290,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);
|
||||
@@ -251,6 +322,16 @@ public abstract class RenderBackend {
|
||||
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);
|
||||
@@ -300,10 +381,12 @@ public abstract class RenderBackend {
|
||||
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 abstract boolean getBoolean(int pname);
|
||||
@@ -315,8 +398,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);
|
||||
@@ -342,4 +447,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; }
|
||||
|
||||
}
|
||||
@@ -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,19 +93,20 @@ 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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
+1
-20
@@ -61,31 +61,12 @@ public final class FragmentShaderGenerator {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("uniform int u_LineStipple;\n");
|
||||
}
|
||||
// Per-unit texEnvColor uniforms
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i) && key.unitNeedsEnvColor(i)) {
|
||||
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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,180 +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;
|
||||
int stippleViewportX = Integer.MIN_VALUE, stippleViewportY = Integer.MIN_VALUE;
|
||||
int stippleViewportW = -1, stippleViewportH = -1;
|
||||
int lineStipple = -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;
|
||||
|
||||
// Line stipple emulation
|
||||
public int locViewport = -1;
|
||||
public int locLineStipple = -1;
|
||||
|
||||
// Fragment uniforms
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
public int locAlphaRef = -1;
|
||||
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");
|
||||
|
||||
// Line stipple emulation
|
||||
locViewport = loc("u_Viewport");
|
||||
locLineStipple = loc("u_LineStipple");
|
||||
|
||||
// Fragment
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locSampler[i] = loc("u_Sampler" + i);
|
||||
}
|
||||
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() {
|
||||
@@ -241,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]);
|
||||
@@ -250,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++) {
|
||||
@@ -268,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);
|
||||
|
||||
@@ -296,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,12 +1,15 @@
|
||||
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;
|
||||
@@ -14,7 +17,6 @@ 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;
|
||||
@@ -53,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() {
|
||||
@@ -110,6 +119,7 @@ public final class ShaderManager {
|
||||
lastBoundProgramId = -1;
|
||||
currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
currentFKLen = 0;
|
||||
GLStateManager.forceAttribDefaultsDirty();
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
@@ -133,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);
|
||||
@@ -152,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;
|
||||
}
|
||||
@@ -159,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++;
|
||||
@@ -186,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(); }
|
||||
}
|
||||
@@ -8,503 +8,434 @@ 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;
|
||||
}
|
||||
|
||||
if (program.getVertexKey().lightingEnabled()) {
|
||||
final int litGen = GLStateManager.lightingGeneration;
|
||||
if (litGen != st.lightingGen) {
|
||||
uploadLighting(program);
|
||||
st.lightingGen = litGen;
|
||||
}
|
||||
final int mvLinG = GLStateManager.getMvLinearGeneration();
|
||||
if (mvLinG != mvLinearGen) {
|
||||
stageNormalMatrix();
|
||||
mvLinearGen = mvLinG;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
final int texMatG = GLStateManager.getTexMatrixGeneration();
|
||||
if (texMatG != texMatGen) {
|
||||
stageTextureMatrices();
|
||||
texMatGen = texMatG;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (!program.getVertexKey().hasVertexNormal() && program.getVertexKey().lightingEnabled()) {
|
||||
final int normalGen = ShaderManager.getNormalGeneration();
|
||||
if (normalGen != st.normalGen) {
|
||||
uploadCurrentNormal(program);
|
||||
st.normalGen = normalGen;
|
||||
}
|
||||
final int litG = GLStateManager.getLightingGeneration();
|
||||
if (litG != lightingGen) {
|
||||
stageLighting();
|
||||
lightingGen = litG;
|
||||
stagedLighting++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (program.getVertexKey().anyUnitTexCoordEnabled()) {
|
||||
final int texGen = ShaderManager.getTexCoordGeneration();
|
||||
if (texGen != st.texCoordGen) {
|
||||
uploadCurrentTexCoords(program);
|
||||
st.texCoordGen = texGen;
|
||||
}
|
||||
final int colG = GLStateManager.getColorGeneration();
|
||||
if (colG != colorGen) {
|
||||
stageCurrentColor();
|
||||
colorGen = colG;
|
||||
stagedColor++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (program.getVertexKey().texGenEnabled()) {
|
||||
final int tgGen = GLStateManager.texGenGeneration;
|
||||
if (tgGen != st.texGenGen) {
|
||||
uploadTexGen(program);
|
||||
st.texGenGen = tgGen;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (program.getVertexKey().clipPlanesEnabled()) {
|
||||
final int cpGen = GLStateManager.clipPlaneGeneration;
|
||||
if (cpGen != st.clipPlaneGen) {
|
||||
uploadClipPlanes(program);
|
||||
st.clipPlaneGen = cpGen;
|
||||
}
|
||||
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 fragGen = GLStateManager.fragmentGeneration;
|
||||
if (fragGen != st.fragmentGen) {
|
||||
uploadFragmentUniforms(program);
|
||||
st.fragmentGen = fragGen;
|
||||
final int tgG = GLStateManager.getTexGenGeneration();
|
||||
if (tgG != texGenGen) {
|
||||
stageTexGen();
|
||||
texGenGen = tgG;
|
||||
stagedTexGen++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// Wide line emulation uniforms
|
||||
if (program.locLineWidth != -1 && program.locViewportSize != -1) {
|
||||
uploadWideLineUniforms(program);
|
||||
final int cpG = GLStateManager.getClipPlaneGeneration();
|
||||
if (cpG != clipPlaneGen) {
|
||||
stageClipPlanes();
|
||||
clipPlaneGen = cpG;
|
||||
stagedClipPlanes++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (program.locLineStipple != -1) {
|
||||
uploadLineStippleUniforms(program);
|
||||
final int fragG = GLStateManager.getFragmentGeneration();
|
||||
if (fragG != fragmentGen) {
|
||||
stageFragment();
|
||||
fragmentGen = fragG;
|
||||
stagedFragment++;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadLineStippleUniforms(Program program) {
|
||||
final Program.UploadState st = program.uploadState;
|
||||
final LineState line = GLStateManager.getLineState();
|
||||
|
||||
final int factor = line.getStippleFactor() < 1 ? 1 : line.getStippleFactor();
|
||||
final int packed = (line.getStipplePattern() & 0xFFFF) | (factor << 16);
|
||||
if (packed != st.lineStipple) {
|
||||
RENDER_BACKEND.uniform1i(program.locLineStipple, packed);
|
||||
st.lineStipple = packed;
|
||||
final 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 != st.stippleViewportX || vp.y != st.stippleViewportY || vp.width != st.stippleViewportW || vp.height != st.stippleViewportH) {
|
||||
RENDER_BACKEND.uniform4f(program.locViewport, vp.x, vp.y, vp.width, vp.height);
|
||||
st.stippleViewportX = vp.x;
|
||||
st.stippleViewportY = vp.y;
|
||||
st.stippleViewportW = vp.width;
|
||||
st.stippleViewportH = vp.height;
|
||||
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 (dirty || !bound) {
|
||||
if (!contentChanged && bound) {
|
||||
blockSkips++;
|
||||
return;
|
||||
}
|
||||
if (ring == null) {
|
||||
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
|
||||
}
|
||||
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;
|
||||
@@ -315,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) {
|
||||
|
||||
@@ -51,6 +51,7 @@ public final class VertexKey {
|
||||
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;
|
||||
@@ -100,6 +101,7 @@ public final class VertexKey {
|
||||
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
|
||||
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
|
||||
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
|
||||
public boolean instancedDraw() { return bit(BIT_INSTANCED); }
|
||||
|
||||
public 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; }
|
||||
@@ -233,6 +235,10 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
if (GLStateManager.instancedFfpDrawActive) {
|
||||
bits |= (1L << BIT_INSTANCED);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -258,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());
|
||||
}
|
||||
}
|
||||
+35
-122
@@ -69,6 +69,14 @@ 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');
|
||||
}
|
||||
|
||||
@@ -78,119 +86,10 @@ public final class VertexShaderGenerator {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("uniform vec4 u_Viewport;\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");
|
||||
@@ -218,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");
|
||||
@@ -250,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
|
||||
@@ -345,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');
|
||||
}
|
||||
@@ -369,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");
|
||||
|
||||
@@ -8,5 +8,7 @@ public final class GLSMConfig {
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
private GLSMConfig() {}
|
||||
}
|
||||
@@ -9,28 +9,36 @@ public final class GLSMHooks {
|
||||
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,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();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ShaderTransformPostProcessor {
|
||||
void onTransformed(String transformedSource, ShaderType 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);
|
||||
}
|
||||
+8
@@ -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;
|
||||
}
|
||||
+7
@@ -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;
|
||||
}
|
||||
@@ -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,12 @@ public final class DisplayListVBO {
|
||||
}
|
||||
|
||||
public void render() {
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
vao.draw(drawMode, start, count);
|
||||
vao.unbind();
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
VAOManager.setCurrentVertexFlags(vertexFlags);
|
||||
vao.draw(drawMode, start, count);
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-4
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-32
@@ -203,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;
|
||||
@@ -307,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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+4
-2
@@ -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;
|
||||
@@ -60,7 +61,7 @@ public final class IndexedDrawCapture {
|
||||
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();
|
||||
@@ -95,7 +96,7 @@ public final class IndexedDrawCapture {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int idxSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
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);
|
||||
@@ -120,6 +121,7 @@ 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++) {
|
||||
|
||||
+2
-1
@@ -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
-1
@@ -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";
|
||||
@@ -53,19 +152,23 @@ public class GLSMRedirector {
|
||||
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";
|
||||
|
||||
// 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;
|
||||
@@ -78,15 +181,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
|
||||
@@ -117,6 +220,7 @@ public class GLSMRedirector {
|
||||
.add("glGenLists")
|
||||
.add("glGenTextures")
|
||||
.add("glIsList")
|
||||
.add("glIsTexture")
|
||||
.add("glDeleteTextures")
|
||||
.add("glDepthFunc")
|
||||
.add("glDepthMask")
|
||||
@@ -135,6 +239,11 @@ public class GLSMRedirector {
|
||||
.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")
|
||||
@@ -155,6 +264,7 @@ public class GLSMRedirector {
|
||||
.add("glListBase")
|
||||
.add("glLoadIdentity")
|
||||
.add("glLoadMatrix")
|
||||
.add("glLoadMatrixf", "glLoadMatrix")
|
||||
.add("glLogicOp")
|
||||
.add("glMatrixMode")
|
||||
.add("glMultMatrix")
|
||||
@@ -236,6 +346,7 @@ public class GLSMRedirector {
|
||||
.add("glPolygonStipple")
|
||||
.add("glAccum")
|
||||
.add("glReadBuffer")
|
||||
.add("glReadPixels")
|
||||
.add("glSampleCoverage")
|
||||
.add("glScissor")
|
||||
.add("glStencilFunc")
|
||||
@@ -273,7 +384,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")
|
||||
@@ -284,7 +396,10 @@ 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")
|
||||
@@ -300,7 +415,14 @@ public class GLSMRedirector {
|
||||
.add("glGetBufferParameteri")
|
||||
.add("glGetBufferParameter")
|
||||
.add("glGetBufferParameteriv")
|
||||
.add("glIsBuffer");
|
||||
.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")
|
||||
@@ -320,6 +442,8 @@ public class GLSMRedirector {
|
||||
.add("glValidateProgram")
|
||||
.add("glGetUniformLocation")
|
||||
.add("glGetAttribLocation")
|
||||
.add("glIsShader")
|
||||
.add("glIsProgram")
|
||||
.add("glUniform1f")
|
||||
.add("glUniform2f")
|
||||
.add("glUniform3f")
|
||||
@@ -332,9 +456,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")
|
||||
@@ -352,12 +480,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")
|
||||
@@ -368,20 +498,37 @@ public class GLSMRedirector {
|
||||
.add("glGetFramebufferAttachmentParameteri")
|
||||
.add("glBlitFramebuffer")
|
||||
.add("glMapBufferRange")
|
||||
.add("glFlushMappedBufferRange");
|
||||
.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("glCopyBufferSubData");
|
||||
.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")
|
||||
@@ -389,10 +536,12 @@ 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");
|
||||
@@ -400,8 +549,12 @@ public class GLSMRedirector {
|
||||
.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);
|
||||
@@ -416,6 +569,7 @@ public class GLSMRedirector {
|
||||
glMethodRedirects.putAll(gl43);
|
||||
glMethodRedirects.putAll(gl44);
|
||||
glMethodRedirects.putAll(gl45);
|
||||
glMethodRedirects.putAll(gl46);
|
||||
|
||||
// MINECRAFT
|
||||
methodRedirects.put(OpenGlHelper, RedirectMap.newMap()
|
||||
@@ -435,10 +589,73 @@ 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")
|
||||
);
|
||||
|
||||
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")
|
||||
@@ -476,7 +693,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")
|
||||
);
|
||||
@@ -517,6 +750,8 @@ public class GLSMRedirector {
|
||||
// APPLE
|
||||
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
|
||||
.add("glBindVertexArrayAPPLE", "glBindVertexArray")
|
||||
.add("glGenVertexArraysAPPLE", "glGenVertexArrays")
|
||||
.add("glDeleteVertexArraysAPPLE", "glDeleteVertexArrays")
|
||||
);
|
||||
|
||||
// GTNHLib VAO
|
||||
@@ -530,12 +765,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
|
||||
@@ -544,33 +782,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);
|
||||
}
|
||||
@@ -579,8 +814,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");
|
||||
|
||||
@@ -588,10 +826,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) {
|
||||
@@ -633,8 +869,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";
|
||||
@@ -643,10 +878,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);
|
||||
@@ -654,10 +927,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) {
|
||||
@@ -672,22 +949,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) {}
|
||||
|
||||
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]) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
-1
@@ -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
-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]);
|
||||
}
|
||||
}
|
||||
+8
-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
-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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -14,12 +14,7 @@ public class MatrixMode implements ISettableState<MatrixMode> {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean bypass = GLStateManager.shouldBypassCache();
|
||||
if (bypass || this.mode != mode) {
|
||||
if (!bypass) {
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public int getMatrix() {
|
||||
|
||||
@@ -14,6 +14,7 @@ public class PolygonState implements ISettableState<PolygonState> {
|
||||
private int backMode = GL11.GL_FILL;
|
||||
private float offsetFactor = 0.0f;
|
||||
private float offsetUnits = 0.0f;
|
||||
private float offsetClamp = 0.0f;
|
||||
private int cullFaceMode = GL11.GL_BACK;
|
||||
private int frontFace = GL11.GL_CCW;
|
||||
|
||||
@@ -23,6 +24,7 @@ public class PolygonState implements ISettableState<PolygonState> {
|
||||
this.backMode = state.backMode;
|
||||
this.offsetFactor = state.offsetFactor;
|
||||
this.offsetUnits = state.offsetUnits;
|
||||
this.offsetClamp = state.offsetClamp;
|
||||
this.cullFaceMode = state.cullFaceMode;
|
||||
this.frontFace = state.frontFace;
|
||||
return this;
|
||||
@@ -36,6 +38,7 @@ public class PolygonState implements ISettableState<PolygonState> {
|
||||
&& backMode == polygonState.backMode
|
||||
&& Float.compare(offsetFactor, polygonState.offsetFactor) == 0
|
||||
&& Float.compare(offsetUnits, polygonState.offsetUnits) == 0
|
||||
&& Float.compare(offsetClamp, polygonState.offsetClamp) == 0
|
||||
&& cullFaceMode == polygonState.cullFaceMode
|
||||
&& frontFace == polygonState.frontFace;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
public class TextureBinding implements ISettableState<TextureBinding> {
|
||||
@Setter protected int binding;
|
||||
@Getter protected int binding;
|
||||
@Getter protected int target;
|
||||
protected TextureInfo info;
|
||||
|
||||
public void setBinding(int binding) {
|
||||
setBinding(binding, 0);
|
||||
}
|
||||
|
||||
public void setBinding(int binding, int target) {
|
||||
this.binding = binding;
|
||||
this.target = target;
|
||||
this.info = null;
|
||||
}
|
||||
|
||||
public TextureInfo getOrResolveInfo() {
|
||||
TextureInfo resolved = info;
|
||||
if (resolved == null) {
|
||||
resolved = TextureInfoCache.INSTANCE.getInfo(binding);
|
||||
info = resolved;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureBinding set(TextureBinding state) {
|
||||
this.binding = state.binding;
|
||||
this.target = state.target;
|
||||
this.info = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ public class ViewportState implements ISettableState<ViewportState> {
|
||||
params.put(3, height);
|
||||
}
|
||||
|
||||
public void get(int[] params) {
|
||||
params[0] = x;
|
||||
params[1] = y;
|
||||
params[2] = width;
|
||||
params[3] = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewportState set(ViewportState state) {
|
||||
this.x = state.x;
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Persistent-mapped fence-reclaimed ring over any buffer target. Callers reserve a range and
|
||||
* memCopy into address() + offset themselves.
|
||||
*/
|
||||
final class GlStreamingRing {
|
||||
|
||||
private final int target;
|
||||
private final int capacity;
|
||||
private final ByteBuffer mappedBuffer;
|
||||
private final long mappedAddress;
|
||||
private int bufferId;
|
||||
|
||||
private static final long SYNC_WAIT_SLICE_NANOS = 10_000_000L;
|
||||
private static final long SYNC_WAIT_TOTAL_NANOS = 5_000_000_000L;
|
||||
|
||||
private final ObjectArrayFIFOQueue<FencedRegion> fenceQueue = new ObjectArrayFIFOQueue<>();
|
||||
private int writePos;
|
||||
private int remaining;
|
||||
private int pendingBytes;
|
||||
private int wraps;
|
||||
private int fencesIssued;
|
||||
private int forcedReclaims;
|
||||
|
||||
private GlStreamingRing(int target, int capacity, int bufferId, ByteBuffer mappedBuffer) {
|
||||
this.target = target;
|
||||
this.capacity = capacity;
|
||||
this.bufferId = bufferId;
|
||||
this.mappedBuffer = mappedBuffer;
|
||||
this.mappedAddress = memAddress0(mappedBuffer);
|
||||
this.remaining = capacity;
|
||||
}
|
||||
|
||||
static GlStreamingRing create(int target, int capacity) {
|
||||
return create(target, capacity, SystemProperties.FORCE_ORPHAN_STREAMING);
|
||||
}
|
||||
|
||||
static GlStreamingRing create(int target, int capacity, boolean forceOrphan) {
|
||||
if (forceOrphan || !RenderSystem.supportsBufferStorage()) return null;
|
||||
final int id = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(target, id);
|
||||
final int storageFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT | GL44.GL_CLIENT_STORAGE_BIT;
|
||||
RenderSystem.bufferStorage(target, capacity, storageFlags);
|
||||
final int mapFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
|
||||
final ByteBuffer mapped = RENDER_BACKEND.mapBufferRange(target, 0, capacity, mapFlags);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
if (mapped == null) {
|
||||
GLStateManager.glDeleteBuffers(id);
|
||||
GLStateManager.LOGGER.warn("Persistent map failed for streaming ring (target=0x{}, {} bytes)", Integer.toHexString(target), capacity);
|
||||
return null;
|
||||
}
|
||||
GLStateManager.LOGGER.info("Streaming ring created (target=0x{}, {}KB)", Integer.toHexString(target), capacity / 1024);
|
||||
return new GlStreamingRing(target, capacity, id, mapped);
|
||||
}
|
||||
|
||||
/** Byte offset of a reserved range, or -1 when it can never be served. */
|
||||
long reserve(int bytes, int alignment) {
|
||||
if (bytes > capacity) return -1;
|
||||
if (remaining < capacity / 2) {
|
||||
reclaim();
|
||||
}
|
||||
int offset = (writePos + alignment - 1) / alignment * alignment;
|
||||
int waste = offset - writePos;
|
||||
if (offset + bytes > capacity) {
|
||||
waste = capacity - writePos;
|
||||
offset = 0;
|
||||
wraps++;
|
||||
}
|
||||
if (!ensureRemaining(waste + bytes)) return -1;
|
||||
writePos = offset + bytes;
|
||||
remaining -= waste + bytes;
|
||||
pendingBytes += waste + bytes;
|
||||
if (pendingBytes >= capacity / 4) {
|
||||
fence();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private boolean ensureRemaining(int needed) {
|
||||
while (remaining < needed) {
|
||||
if (fenceQueue.isEmpty()) {
|
||||
if (pendingBytes == 0) return false;
|
||||
fence();
|
||||
}
|
||||
syncOldest();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void fence() {
|
||||
if (pendingBytes > 0) {
|
||||
fenceQueue.enqueue(new FencedRegion(RENDER_BACKEND.fenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0), pendingBytes));
|
||||
pendingBytes = 0;
|
||||
fencesIssued++;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean signaled(int status) {
|
||||
return status == GL32.GL_ALREADY_SIGNALED || status == GL32.GL_CONDITION_SATISFIED;
|
||||
}
|
||||
|
||||
private void reclaim() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
if (!signaled(RENDER_BACKEND.clientWaitSync(region.fenceId, 0, 0L))) break;
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
private void syncOldest() {
|
||||
final FencedRegion region = fenceQueue.dequeue();
|
||||
forcedReclaims++;
|
||||
final long deadline = System.nanoTime() + SYNC_WAIT_TOTAL_NANOS;
|
||||
long remainingNanos = SYNC_WAIT_TOTAL_NANOS;
|
||||
int status;
|
||||
do {
|
||||
status = RENDER_BACKEND.clientWaitSync(region.fenceId, GL32.GL_SYNC_FLUSH_COMMANDS_BIT, Math.min(SYNC_WAIT_SLICE_NANOS, remainingNanos));
|
||||
remainingNanos = deadline - System.nanoTime();
|
||||
} while (status == GL32.GL_TIMEOUT_EXPIRED && remainingNanos > 0);
|
||||
if (!signaled(status)) {
|
||||
GLStateManager.LOGGER.warn("Streaming ring fence wait did not signal (status=0x{}); reclaiming anyway", Integer.toHexString(status));
|
||||
}
|
||||
GLStateManager.glDeleteSync(region.fenceId);
|
||||
remaining += region.bytes;
|
||||
reclaim();
|
||||
}
|
||||
|
||||
void endFrame() {
|
||||
fence();
|
||||
reclaim();
|
||||
}
|
||||
|
||||
long address() { return mappedAddress; }
|
||||
ByteBuffer mapped() { return mappedBuffer; }
|
||||
int bufferId() { return bufferId; }
|
||||
int capacity() { return capacity; }
|
||||
int remaining() { return remaining; }
|
||||
int wraps() { return wraps; }
|
||||
int fencesIssued() { return fencesIssued; }
|
||||
int forcedReclaims() { return forcedReclaims; }
|
||||
|
||||
void destroy() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
GLStateManager.glDeleteSync(fenceQueue.dequeue().fenceId);
|
||||
}
|
||||
if (bufferId != 0) {
|
||||
RENDER_BACKEND.bindBuffer(target, bufferId);
|
||||
RENDER_BACKEND.unmapBuffer(target);
|
||||
RENDER_BACKEND.bindBuffer(target, 0);
|
||||
GLStateManager.glDeleteBuffers(bufferId);
|
||||
bufferId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private record FencedRegion(long fenceId, int bytes) {}
|
||||
}
|
||||
+31
-155
@@ -1,184 +1,60 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
||||
import org.embeddedt.embeddium.impl.gl.sync.GlFence;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class PersistentStreamingBuffer {
|
||||
|
||||
/**
|
||||
* Persistent-mapped ring buffer for streaming vertex data on GL4.4+
|
||||
*/
|
||||
public class PersistentStreamingBuffer implements StreamingBuffer {
|
||||
public static final int DEFAULT_CAPACITY = 16 * 1024 * 1024;
|
||||
|
||||
public static final int DEFAULT_CAPACITY = 16 * 1024 * 1024; // 16 MB
|
||||
private final GlStreamingRing ring;
|
||||
|
||||
private int bufferId;
|
||||
private final int capacity;
|
||||
private final ByteBuffer mappedBuffer;
|
||||
private final long mappedAddress;
|
||||
private final ObjectArrayFIFOQueue<FencedRegion> fenceQueue = new ObjectArrayFIFOQueue<>();
|
||||
|
||||
private int writePos;
|
||||
private int remaining;
|
||||
private int pendingBytes;
|
||||
|
||||
public PersistentStreamingBuffer() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
private PersistentStreamingBuffer(GlStreamingRing ring) {
|
||||
this.ring = ring;
|
||||
}
|
||||
|
||||
public PersistentStreamingBuffer(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.remaining = capacity;
|
||||
|
||||
this.bufferId = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
|
||||
final int storageFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT | GL44.GL_CLIENT_STORAGE_BIT;
|
||||
RenderSystem.bufferStorage(GL15.GL_ARRAY_BUFFER, capacity, storageFlags);
|
||||
|
||||
final int mapFlags = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
|
||||
this.mappedBuffer = RENDER_BACKEND.mapBufferRange(GL15.GL_ARRAY_BUFFER, 0, capacity, mapFlags);
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
if (this.mappedBuffer == null) {
|
||||
GLStateManager.glDeleteBuffers(bufferId);
|
||||
bufferId = 0;
|
||||
throw new RuntimeException("Failed to persistently map streaming buffer");
|
||||
}
|
||||
this.mappedAddress = memAddress0(mappedBuffer);
|
||||
public static PersistentStreamingBuffer createOrNull(int capacity) {
|
||||
return createOrNull(capacity, SystemProperties.FORCE_ORPHAN_STREAMING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public static PersistentStreamingBuffer createOrNull(int capacity, boolean forceOrphan) {
|
||||
final GlStreamingRing ring = GlStreamingRing.create(GL15.GL_ARRAY_BUFFER, capacity, forceOrphan);
|
||||
return ring == null ? null : new PersistentStreamingBuffer(ring);
|
||||
}
|
||||
|
||||
|
||||
public int upload(ByteBuffer data, int vertexStride) {
|
||||
final int dataSize = data.remaining();
|
||||
if (dataSize > capacity) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
reclaim();
|
||||
|
||||
final int alignUnit = vertexStride * 4;
|
||||
final int alignedPos = ((writePos + alignUnit - 1) / alignUnit) * alignUnit;
|
||||
final int alignmentWaste = alignedPos - writePos;
|
||||
final int tailSpace = capacity - alignedPos;
|
||||
|
||||
if (dataSize <= tailSpace) {
|
||||
// Fits at tail — just need enough remaining budget
|
||||
if (ensureRemaining(alignmentWaste + dataSize)) {
|
||||
return writeAt(alignedPos, data, dataSize, alignmentWaste, vertexStride);
|
||||
}
|
||||
} else {
|
||||
// Must wrap to start — burn remaining tail space
|
||||
final int wrapWaste = capacity - writePos;
|
||||
if (ensureRemaining(wrapWaste + dataSize)) {
|
||||
return writeAtStart(data, dataSize, wrapWaste);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return upload(data, vertexStride, 1);
|
||||
}
|
||||
|
||||
/** Sync fences until {@code needed} bytes are available, or no fences remain. */
|
||||
private boolean ensureRemaining(int needed) {
|
||||
while (remaining < needed) {
|
||||
if (!syncOldest()) return false;
|
||||
}
|
||||
return true;
|
||||
public int upload(ByteBuffer data, int vertexStride, int vertexAlignment) {
|
||||
final int size = data.remaining();
|
||||
final long offset = ring.reserve(size, vertexStride * vertexAlignment);
|
||||
if (offset < 0) return -1;
|
||||
memCopy(memAddress0(data) + data.position(), ring.address() + offset, size);
|
||||
RENDER_BACKEND.onPersistentBufferWrite(ring.bufferId(), offset, size);
|
||||
return (int) (offset / vertexStride);
|
||||
}
|
||||
|
||||
private int writeAt(int offset, ByteBuffer data, int dataSize, int waste, int vertexStride) {
|
||||
memCopy(memAddress0(data) + data.position(), mappedAddress + offset, dataSize);
|
||||
public int getBufferId() { return ring.bufferId(); }
|
||||
public int getCapacity() { return ring.capacity(); }
|
||||
public int getRemaining() { return ring.remaining(); }
|
||||
public int getForcedReclaims() { return ring.forcedReclaims(); }
|
||||
public int getWraps() { return ring.wraps(); }
|
||||
|
||||
final int totalCost = waste + dataSize;
|
||||
writePos = offset + dataSize;
|
||||
remaining -= totalCost;
|
||||
pendingBytes += totalCost;
|
||||
return offset / vertexStride;
|
||||
}
|
||||
ByteBuffer getMappedBuffer() { return ring.mapped(); }
|
||||
|
||||
private int writeAtStart(ByteBuffer data, int dataSize, int wrapWaste) {
|
||||
memCopy(memAddress0(data) + data.position(), mappedAddress, dataSize);
|
||||
|
||||
final int totalCost = wrapWaste + dataSize;
|
||||
writePos = dataSize;
|
||||
remaining -= totalCost;
|
||||
pendingBytes += totalCost;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public int getRemaining() { return remaining; }
|
||||
|
||||
@Override
|
||||
public int getBufferId() {
|
||||
return bufferId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDraw() {
|
||||
if (pendingBytes > 0) {
|
||||
final long fenceId = RENDER_BACKEND.fenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
fenceQueue.enqueue(new FencedRegion(new GlFence(fenceId), pendingBytes));
|
||||
pendingBytes = 0;
|
||||
}
|
||||
ring.endFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
fenceQueue.dequeue().fence.delete();
|
||||
}
|
||||
if (bufferId != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
|
||||
RENDER_BACKEND.unmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(bufferId);
|
||||
bufferId = 0;
|
||||
}
|
||||
ring.destroy();
|
||||
}
|
||||
|
||||
private void reclaim() {
|
||||
while (!fenceQueue.isEmpty()) {
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
if (!region.fence.isCompleted()) break;
|
||||
region.fence.delete();
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sync on oldest fence. Returns true if bytes were reclaimed. */
|
||||
private boolean syncOldest() {
|
||||
if (fenceQueue.isEmpty()) return false;
|
||||
final FencedRegion region = fenceQueue.first();
|
||||
if (!region.fence.sync()) {
|
||||
GLStateManager.LOGGER.warn("Streaming buffer fence wait did not signal; reclaiming {} bytes anyway", region.bytes);
|
||||
}
|
||||
region.fence.delete();
|
||||
fenceQueue.dequeue();
|
||||
remaining += region.bytes;
|
||||
reclaim(); // opportunistically reclaim any additional completed fences
|
||||
return true;
|
||||
}
|
||||
|
||||
// Package-private accessors for testing
|
||||
ByteBuffer getMappedBuffer() { return mappedBuffer; }
|
||||
|
||||
private record FencedRegion(GlFence fence, int bytes) {}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface StreamingBuffer {
|
||||
|
||||
int upload(ByteBuffer data, int vertexStride);
|
||||
int getBufferId();
|
||||
void postDraw();
|
||||
int getCapacity();
|
||||
void destroy();
|
||||
}
|
||||
@@ -45,8 +45,12 @@ public final class StreamingUploader {
|
||||
return data.remaining();
|
||||
}
|
||||
final int dataSize = data.remaining();
|
||||
final ByteBuffer mapped = RENDER_BACKEND.mapBufferRange(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
memCopy(memAddress0(data), memAddress0(mapped), dataSize);
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL15.GL_ARRAY_BUFFER, 0, dataSize, MAP_WRITE_INVALIDATE_BUFFER);
|
||||
if (dst == 0L) {
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
return data.remaining();
|
||||
}
|
||||
memCopy(memAddress0(data), dst, dataSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
return capacity;
|
||||
}
|
||||
|
||||
+91
-18
@@ -4,9 +4,13 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
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.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -16,6 +20,7 @@ import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static org.joml.Math.clamp;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
@@ -28,6 +33,8 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
*/
|
||||
public class TessellatorStreamingDrawer {
|
||||
|
||||
private static final Tracy.ZoneId Z_SDL_STREAM_DRAW = Tracy.zoneId("sdlStreamDraw", Tracy.COLOR_FFP);
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("TessellatorStreamingDrawer");
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE; // 16
|
||||
|
||||
@@ -43,6 +50,14 @@ public class TessellatorStreamingDrawer {
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
// Tracy profiling counters
|
||||
public static long streamedBytes;
|
||||
public static long streamDraws;
|
||||
public static long orphanFallbacks;
|
||||
public static long streamContiguous;
|
||||
private static int lastStreamEndVertex = -1;
|
||||
private static int lastStreamDrawMode = -1;
|
||||
|
||||
static {
|
||||
// Initial repack buffer: 64KB
|
||||
repackCapacity = 0x10000;
|
||||
@@ -54,15 +69,7 @@ public class TessellatorStreamingDrawer {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
if (RenderSystem.supportsBufferStorage() && !Boolean.getBoolean("angelica.forceOrphanStreaming")) {
|
||||
try {
|
||||
persistentBuffer = new PersistentStreamingBuffer();
|
||||
LOGGER.info("Persistent streaming buffer created ({}MB)", PersistentStreamingBuffer.DEFAULT_CAPACITY / (1024 * 1024));
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to create persistent streaming buffer, using orphan fallback", e);
|
||||
persistentBuffer = null;
|
||||
}
|
||||
}
|
||||
persistentBuffer = PersistentStreamingBuffer.createOrNull(PersistentStreamingBuffer.DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +91,57 @@ public class TessellatorStreamingDrawer {
|
||||
|
||||
// Determine the optimal vertex format from the tessellator's flags
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
|
||||
final int effectiveFlags;
|
||||
if (GLSMConfig.expandVertexFormats) {
|
||||
effectiveFlags = VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT | VertexFlags.NORMAL_BIT | VertexFlags.BRIGHTNESS_BIT;
|
||||
if (effectiveFlags != flags) {
|
||||
final int[] rawBuffer = tess.rawBuffer;
|
||||
final int defaultColor;
|
||||
if (!tess.hasColor) {
|
||||
final var c = GLStateManager.getColor();
|
||||
defaultColor = ((int)(clamp(0f, 1f, c.getAlpha()) * 255) << 24) | ((int)(clamp(0f, 1f, c.getBlue()) * 255) << 16)
|
||||
| ((int)(clamp(0f, 1f, c.getGreen()) * 255) << 8) | (int)(clamp(0f, 1f, c.getRed()) * 255);
|
||||
} else {
|
||||
defaultColor = 0;
|
||||
}
|
||||
|
||||
final int defaultBrightness;
|
||||
if (!tess.hasBrightness) {
|
||||
defaultBrightness = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
|
||||
} else {
|
||||
defaultBrightness = 0;
|
||||
}
|
||||
|
||||
final int defaultNormal;
|
||||
if (!tess.hasNormals) {
|
||||
final var n = ShaderManager.getCurrentNormal();
|
||||
defaultNormal = ((int)(clamp(-1f, 1f, n.z) * 127) << 16) | (((int)(clamp(-1f, 1f, n.y) * 127) & 0xFF) << 8) | ((int)(clamp(-1f, 1f, n.x) * 127) & 0xFF);
|
||||
} else {
|
||||
defaultNormal = 0;
|
||||
}
|
||||
|
||||
final int defaultTexU, defaultTexV;
|
||||
if (!tess.hasTexture) {
|
||||
final var tc = ShaderManager.getCurrentTexCoord();
|
||||
defaultTexU = Float.floatToRawIntBits(tc.x);
|
||||
defaultTexV = Float.floatToRawIntBits(tc.y);
|
||||
} else {
|
||||
defaultTexU = 0; defaultTexV = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
final int base = i * 8;
|
||||
if (!tess.hasTexture) { rawBuffer[base + 3] = defaultTexU; rawBuffer[base + 4] = defaultTexV; }
|
||||
if (!tess.hasColor) { rawBuffer[base + 5] = defaultColor; }
|
||||
if (!tess.hasNormals) { rawBuffer[base + 6] = defaultNormal; }
|
||||
if (!tess.hasBrightness) { rawBuffer[base + 7] = defaultBrightness; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
effectiveFlags = flags;
|
||||
}
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[effectiveFlags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
final int requiredBytes = vertexCount * vertexSize;
|
||||
@@ -96,7 +153,7 @@ public class TessellatorStreamingDrawer {
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
@@ -166,9 +223,9 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
cachedDebugInfo = String.format("Stream: Persistent %s (%s free) + %d orphan (%s)",
|
||||
cachedDebugInfo = String.format("Stream: Persistent %s (%s free, %d forced) + %d orphan (%s)",
|
||||
formatBytes(persistentBuffer.getCapacity()), formatBytes(persistentBuffer.getRemaining()),
|
||||
orphanCount, formatBytes(orphanBytes));
|
||||
persistentBuffer.getForcedReclaims(), orphanCount, formatBytes(orphanBytes));
|
||||
} else {
|
||||
cachedDebugInfo = String.format("Stream: Orphan (%d bufs, %s)", orphanCount, formatBytes(orphanBytes));
|
||||
}
|
||||
@@ -192,31 +249,49 @@ public class TessellatorStreamingDrawer {
|
||||
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
|
||||
*/
|
||||
private static void uploadAndDraw(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
|
||||
Tracy.beginZone(Z_SDL_STREAM_DRAW);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
ensureVAO(flags, format);
|
||||
|
||||
if (Tracy.ENABLED) {
|
||||
streamedBytes += packed.remaining();
|
||||
streamDraws++;
|
||||
}
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize);
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize, drawMode == GL11.GL_QUADS ? 4 : 1);
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
final boolean fromRing = firstVertex >= 0;
|
||||
if (fromRing) {
|
||||
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
|
||||
} else {
|
||||
if (Tracy.ENABLED && persistentBuffer != null) orphanFallbacks++;
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(packed);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
if (Tracy.ENABLED) {
|
||||
if (fromRing && firstVertex == lastStreamEndVertex && drawMode == lastStreamDrawMode) streamContiguous++;
|
||||
lastStreamEndVertex = fromRing ? firstVertex + vertexCount : -1;
|
||||
lastStreamDrawMode = drawMode;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
public static long ringWraps() {
|
||||
return persistentBuffer == null ? 0L : persistentBuffer.getWraps();
|
||||
}
|
||||
|
||||
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
@@ -264,7 +339,6 @@ public class TessellatorStreamingDrawer {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
format.setupBufferState(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
if (persistentBuffer != null && persistentVAOs[flags] == 0) {
|
||||
@@ -273,7 +347,6 @@ public class TessellatorStreamingDrawer {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, persistentBuffer.getBufferId());
|
||||
format.setupBufferState(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class UniformRingBuffer {
|
||||
|
||||
private static final int OFFSET_ALIGNMENT = 256;
|
||||
|
||||
private final int capacity;
|
||||
private final int blockSize;
|
||||
private final int stride;
|
||||
private final GlStreamingRing ring;
|
||||
private int orphanBufferId;
|
||||
|
||||
private int writePos;
|
||||
private int orphanWraps;
|
||||
|
||||
public UniformRingBuffer(int capacity, int blockSize) {
|
||||
this(capacity, blockSize, SystemProperties.FORCE_ORPHAN_STREAMING);
|
||||
}
|
||||
|
||||
public UniformRingBuffer(int capacity, int blockSize, boolean forceOrphan) {
|
||||
this.capacity = capacity;
|
||||
this.blockSize = blockSize;
|
||||
this.stride = (blockSize + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT;
|
||||
this.ring = GlStreamingRing.create(GL31.GL_UNIFORM_BUFFER, capacity, forceOrphan);
|
||||
if (ring == null) {
|
||||
orphanBufferId = RENDER_BACKEND.genBuffers();
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public int writeBlock(ByteBuffer src) {
|
||||
final long srcAddress = memAddress0(src);
|
||||
if (ring != null) {
|
||||
final long offset = ring.reserve(stride, OFFSET_ALIGNMENT);
|
||||
if (offset < 0) {
|
||||
throw new IllegalStateException("uniform ring exhausted with nothing in flight (needed=" + stride + ")");
|
||||
}
|
||||
memCopy(srcAddress, ring.address() + offset, blockSize);
|
||||
return (int) offset;
|
||||
}
|
||||
return writeOrphan(srcAddress);
|
||||
}
|
||||
|
||||
private int writeOrphan(long srcAddress) {
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, orphanBufferId);
|
||||
int offset = writePos;
|
||||
if (offset + stride > capacity) {
|
||||
RENDER_BACKEND.bufferData(GL31.GL_UNIFORM_BUFFER, capacity, GL15.GL_STREAM_DRAW);
|
||||
offset = 0;
|
||||
orphanWraps++;
|
||||
}
|
||||
final long dst = RENDER_BACKEND.mapBufferRangeAddress(GL31.GL_UNIFORM_BUFFER, offset, blockSize, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
if (dst == 0L) {
|
||||
throw new IllegalStateException("mapBufferRange failed on uniform ring (offset=" + offset + ")");
|
||||
}
|
||||
memCopy(srcAddress, dst, blockSize);
|
||||
RENDER_BACKEND.unmapBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
RENDER_BACKEND.bindBuffer(GL31.GL_UNIFORM_BUFFER, 0);
|
||||
writePos = offset + stride;
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void endFrame() {
|
||||
if (ring != null) {
|
||||
ring.endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public int getBufferId() { return ring != null ? ring.bufferId() : orphanBufferId; }
|
||||
public int getBlockSize() { return blockSize; }
|
||||
public int getStride() { return stride; }
|
||||
public boolean isPersistent() { return ring != null; }
|
||||
public int getWraps() { return ring != null ? ring.wraps() : orphanWraps; }
|
||||
public int getFencesIssued() { return ring != null ? ring.fencesIssued() : 0; }
|
||||
public int getForcedReclaims() { return ring != null ? ring.forcedReclaims() : 0; }
|
||||
|
||||
public void destroy() {
|
||||
if (ring != null) {
|
||||
ring.destroy();
|
||||
} else if (orphanBufferId != 0) {
|
||||
GLStateManager.glDeleteBuffers(orphanBufferId);
|
||||
orphanBufferId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -1,4 +1,4 @@
|
||||
package net.coderbot.iris.gl.texture;
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -77,4 +77,12 @@ public enum DepthBufferFormat {
|
||||
public boolean isCombinedStencil() {
|
||||
return combinedStencil;
|
||||
}
|
||||
|
||||
public DepthBufferFormat stripStencil() {
|
||||
return switch (this) {
|
||||
case DEPTH_STENCIL, DEPTH24_STENCIL8 -> DEPTH24;
|
||||
case DEPTH32F_STENCIL8 -> DEPTH32F;
|
||||
default -> this;
|
||||
};
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package net.coderbot.iris.gl;
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
public enum GlVersion {
|
||||
GL_11,
|
||||
Loaded 100 of 647 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user