mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-14 09:15:06 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
996fb54cb0 | ||
|
|
d1e7838cf9 | ||
|
|
a12d2bb4b0 | ||
|
|
60d1a3de3c | ||
|
|
367f9d9161 | ||
|
|
d69fd20a8b | ||
|
|
9493d630a0 | ||
|
|
aee989d23b | ||
|
|
3876218405 | ||
|
|
a6d014a9eb | ||
|
|
52444510d0 | ||
|
|
8840c2109f | ||
|
|
e64a4e7214 | ||
|
|
e46eb46343 | ||
|
|
5da38c9180 | ||
|
|
85bde5827f | ||
|
|
ea2d34589e | ||
|
|
2b46b4f602 | ||
|
|
8e099e9529 | ||
|
|
bbbdc12b2a | ||
|
|
08aa37712f | ||
|
|
ed6dd12db7 | ||
|
|
91e67f8d5d | ||
|
|
9bbb0537ee | ||
|
|
a019b7eed8 | ||
|
|
1772e9eae2 | ||
|
|
17a2ed7f37 | ||
|
|
34ed1d101b | ||
|
|
7cc9cea337 | ||
|
|
6c5e146f96 | ||
|
|
ea50e0a0b6 | ||
|
|
713509a87b | ||
|
|
102c6cad86 | ||
|
|
8af9340be6 | ||
|
|
fc8d3e8b89 | ||
|
|
e84c8c8d7a | ||
|
|
73d61d385c | ||
|
|
69124e3f2f | ||
|
|
98703a11be | ||
|
|
67ca46a220 | ||
|
|
a037a96fa4 | ||
|
|
ed260a34c4 | ||
|
|
e98c927a93 | ||
|
|
b446968e66 | ||
|
|
32f8eef15b | ||
|
|
2d9d1a45b7 | ||
|
|
fa8842a64b | ||
|
|
69019b9a62 | ||
|
|
482dece0f7 | ||
|
|
ab3bf17696 | ||
|
|
cfc6e12e00 | ||
|
|
8537835d0c | ||
|
|
8e1809622c | ||
|
|
f81ee4b03a | ||
|
|
49db8e111d | ||
|
|
bd9a3c3c5c | ||
|
|
01ff612d1d | ||
|
|
0cae2be1f6 | ||
|
|
85d0641a73 | ||
|
|
46f8157087 | ||
|
|
d3dee841e8 | ||
|
|
3d3d0f9e18 | ||
|
|
23aa04361d | ||
|
|
69428c12f6 | ||
|
|
3978be161d | ||
|
|
2ed838acf1 | ||
|
|
d29a8278cd | ||
|
|
2f1f380e8e | ||
|
|
990dadc03f | ||
|
|
1946c2ad65 | ||
|
|
bfb66d84ee | ||
|
|
cae4fea2bb | ||
|
|
78abc05711 | ||
|
|
159f7ff8cc | ||
|
|
0d959c2b90 | ||
|
|
ce2462cad3 | ||
|
|
5457df5369 | ||
|
|
acedba4eac | ||
|
|
21a054a4db | ||
|
|
d056bf6fa7 | ||
|
|
5ed1c92590 | ||
|
|
3389b952d0 | ||
|
|
b49d37d827 | ||
|
|
12857006c6 | ||
|
|
38af64f10c | ||
|
|
10922edca3 | ||
|
|
2a8c8756d3 | ||
|
|
23e36f2b76 | ||
|
|
fcf8ad064a | ||
|
|
6dfb0312b1 | ||
|
|
0a4d48964e | ||
|
|
231a2ab216 | ||
|
|
61bb779dea | ||
|
|
7cd97d1216 | ||
|
|
fe82cd320a | ||
|
|
c7a0f3a208 | ||
|
|
338d94197c | ||
|
|
75a0278138 | ||
|
|
c97fb7004f | ||
|
|
bdcb4d1882 | ||
|
|
37afddab5d | ||
|
|
d876a203da | ||
|
|
85892c0f7f | ||
|
|
1587a12cd7 | ||
|
|
38b1d9a0c3 | ||
|
|
12fa08ab46 | ||
|
|
80e5b544c0 | ||
|
|
db7d328481 | ||
|
|
9f19d94c5f | ||
|
|
bc08e4baed | ||
|
|
8387b4e9cd | ||
|
|
6c888a634a | ||
|
|
fe67a22d5a | ||
|
|
66d2a548fc | ||
|
|
d16c1e7cf6 | ||
|
|
a8e95f70c1 | ||
|
|
9cb35c67cd | ||
|
|
cbd16b1a76 | ||
|
|
cd0a9608d3 | ||
|
|
fa71eda3db | ||
|
|
9705722469 | ||
|
|
977db3d604 | ||
|
|
c10d30b2da | ||
|
|
2f4ac2a9b7 | ||
|
|
9081f4c3e8 | ||
|
|
1f274a64ea | ||
|
|
9814a96b10 |
No files matched your search
@@ -1,14 +1,14 @@
|
||||
# Description of your *PR* and other info
|
||||
<!-- Please include a summary of the change -->
|
||||
## Summary
|
||||
<!-- Briefly describe what this PR changes and why. -->
|
||||
<!-- Any details that you think are important to review this PR? -->
|
||||
<!-- Are there any Issues / PRs related to this one? -->
|
||||
|
||||
<!-- Add a screenshot or a video demonstration for new features -->
|
||||
|
||||
<details open><summary>Template</summary> <!-- OPEN is intentional -->
|
||||
|
||||
### Before submitting a *pull request* make sure you have:
|
||||
- [ ] Read the [AI Contribution Policy](https://github.com/GTNewHorizons/Angelica/blob/master/AI_POLICY.md)
|
||||
- [ ] At least [skimmed](https://github.com/GTNewHorizons/Angelica/search?q=is%3Apr&type=Issues) the tracker for similar pull requests
|
||||
|
||||
</details>
|
||||
## Checklist
|
||||
- [ ] I have tested this PR in DevEnv
|
||||
- [ ] I have tested this PR in Fullpack
|
||||
- [ ] This PR is in compliance with the [GTNH AI Policy](https://github.com/GTNewHorizons/GTNH-Dev-Doc/blob/master/AI_POLICY.md)
|
||||
- [ ] I have at least [skimmed](https://github.com/GTNewHorizons/Angelica/search?q=is%3Apr&type=Issues) the tracker for similar pull requests
|
||||
- [ ] This PR requires another PR in order to merge
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: companion-version.sh <name> <catalog-key>; TAG is <name>-v<version>[-N] or unset
|
||||
set -euo pipefail
|
||||
|
||||
name="${1:?name required}"
|
||||
key="${2:?catalog key required}"
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
|
||||
base="$(sed -nE "s/^$key = \"(.*)\"\$/\\1/p" gradle/libs.versions.toml)"
|
||||
[[ -n "$base" ]] || { echo "$key not found" >&2; exit 1; }
|
||||
|
||||
tag="${TAG:-}"
|
||||
if [[ -z "$tag" ]]; then
|
||||
echo "$base"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
version="${tag#"$name"-v}"
|
||||
if [[ "$version" == "$tag" || ( "$version" != "$base" && ! "$version" =~ ^"$base"-[1-9][0-9]*$ ) ]]; then
|
||||
echo "bad tag '$tag': want $name-v$base[-N]" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$version"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: release-companion-jar.sh <jar> <notes>; TAG is <name>-v<version>
|
||||
set -euo pipefail
|
||||
|
||||
: "${TAG:?TAG required}"
|
||||
jar="${1:?jar required}"
|
||||
notes="${2:?notes required}"
|
||||
|
||||
gh release create "$TAG" "$jar" --latest=false --title "angelica-${TAG%%-v*} ${TAG#*-v}" --notes "$notes"
|
||||
@@ -10,4 +10,44 @@ on:
|
||||
jobs:
|
||||
build-and-test:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||
with:
|
||||
disable-spotless-pr: true
|
||||
java-distribution: temurin
|
||||
cache-rfg: true
|
||||
secrets: inherit
|
||||
|
||||
sdl-gpu-tests:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
concurrency:
|
||||
group: sdl-gpu-tests-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Install a software Vulkan driver
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y xvfb mesa-vulkan-drivers libgl1-mesa-dri
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: |
|
||||
21
|
||||
25
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
- name: Run SDL-GPU tests
|
||||
run: xvfb-run -a --server-args="-screen 0 1366x768x24" ./gradlew --build-cache --stacktrace --continue :sdl-gpu:glsmSdlTest :sdl-gpu:sdlGpuDeviceTest :lwjgl3-backend:glesTest
|
||||
- name: Check LWJGL did not extract natives to the shared temp dir
|
||||
run: test ! -e "/tmp/lwjgl_$USER"
|
||||
- name: Attach SDL-GPU test results
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: sdl-gpu-test-results
|
||||
path: |
|
||||
sdl-gpu/build/test-results/
|
||||
sdl-gpu/build/reports/tests/
|
||||
sdl-gpu/hs_err_pid*.log
|
||||
lwjgl3-backend/build/test-results/
|
||||
lwjgl3-backend/build/reports/tests/
|
||||
lwjgl3-backend/hs_err_pid*.log
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Build angelica-moltenvk
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "moltenvk-v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish:
|
||||
description: Publish angelica-moltenvk.jar to the GTNH maven
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
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: Compute version
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
|
||||
run: |
|
||||
version="$(bash .github/scripts/companion-version.sh moltenvk lwjgl3Release)"
|
||||
echo "MOLTENVK_VERSION=$version" >> "$GITHUB_ENV"
|
||||
- name: Build angelica-moltenvk.jar
|
||||
run: ./gradlew -PmoltenvkVersion="$MOLTENVK_VERSION" :moltenvk:distJar
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: angelica-moltenvk
|
||||
path: moltenvk/build/libs/angelica-moltenvk-*.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 -PpublishMoltenVK=true -PmoltenvkVersion="$MOLTENVK_VERSION" :moltenvk:publish
|
||||
- name: Create GitHub release
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: bash .github/scripts/release-companion-jar.sh moltenvk/build/libs/angelica-moltenvk-*.jar "MoltenVK for the SDL GPU Vulkan driver on macOS."
|
||||
@@ -12,6 +12,9 @@ on:
|
||||
required: false
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
@@ -66,8 +69,15 @@ jobs:
|
||||
cp "$d"/* "$dest/"
|
||||
done
|
||||
find tracy-client/src/main/resources/natives -type f
|
||||
- name: Compute version
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
|
||||
run: |
|
||||
version="$(bash .github/scripts/companion-version.sh tracy tracy)"
|
||||
echo "TRACY_JAR_VERSION=$version" >> "$GITHUB_ENV"
|
||||
- name: Build angelica-tracy.jar
|
||||
run: ./gradlew :tracy-client:distJar
|
||||
run: ./gradlew -PtracyJarVersion="$TRACY_JAR_VERSION" :tracy-client:distJar
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: angelica-tracy
|
||||
@@ -78,4 +88,10 @@ jobs:
|
||||
env:
|
||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
run: ./gradlew -PpublishTracy=true :tracy-client:publish
|
||||
run: ./gradlew -PpublishTracy=true -PtracyJarVersion="$TRACY_JAR_VERSION" :tracy-client:publish
|
||||
- name: Create GitHub release
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: bash .github/scripts/release-companion-jar.sh tracy-client/build/libs/angelica-tracy-*.jar "Tracy profiler backend for Angelica."
|
||||
@@ -3,7 +3,7 @@ name: Release tagged build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ '*', '!tracy-v*' ]
|
||||
tags: [ '*', '!tracy-v*', '!moltenvk-v*' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -47,3 +47,4 @@ dist/tracy-natives/
|
||||
*.jfr
|
||||
|
||||
tracy-client/angelica/
|
||||
shaderc_failures/
|
||||
@@ -2,31 +2,59 @@
|
||||
|
||||
<sup>**[CurseForge](https://www.curseforge.com/minecraft/mc-mods/angelica) | [Modrinth](https://modrinth.com/mod/angelica)**</sup>
|
||||
|
||||
🌐 **Languages:** [English](README.MD) | [Українська](README_uk.MD)
|
||||
|
||||
* The little Angle [sic] that supports shaders while saving your fps from a certain death -boubou
|
||||
* Is it obtuse, or accute? -Caedis
|
||||
* It's a cute angle -mitchej123
|
||||
* It's the angel that pulls you out of OptiFine's hell -Roadhog360
|
||||
|
||||
## **Beta Quality - Here there be Dragons**
|
||||
* Shaders - Beta
|
||||
## What's in it
|
||||
* Shaders - Iris backport & enhancements
|
||||
* Terrain - Celeritas 2.5.12 with threaded chunk building and translucency sorting
|
||||
* GL 3.3 core profile with Java based fixed function pipeline emulation
|
||||
* Renderer selection - OpenGL, or SDL GPU for Vulkan/Metal/Direct3D 12
|
||||
* Batching and instancing for entities, tile entities, items, particles and weather
|
||||
* Dynamic lights
|
||||
* HUD caching
|
||||
* Custom fonts and text effects
|
||||
* MCPatcher - connected textures, custom item textures, custom colors, random mobs, better skies
|
||||
* Bundled NotFine, zoom, FPS reducer
|
||||
|
||||
## Status
|
||||
* OpenGL + Celeritas terrain - Default
|
||||
* Shaders - Generally Available
|
||||
* Complementary + Euphoria Patches is the primary compatibility target
|
||||
* Sildurs, Seus, and any other shaders might work
|
||||
* SDL GPU renderer - Experimental
|
||||
* MCPatcher features - Alpha
|
||||
* HUD caching - Semi-stable
|
||||
|
||||
## SDL GPU renderer
|
||||
Optional, OpenGL is the default
|
||||
* Vulkan, Metal (macOS) and Direct3D 12 (Windows) through SDL3
|
||||
* Needs [LWJGL3ify](https://github.com/GTNewHorizons/lwjgl3ify/releases/) 3.0.25+
|
||||
* Falls back to OpenGL when no device can be created
|
||||
* Vulkan on macOS via MoltenVK from the optional `angelica-moltenvk` jar
|
||||
|
||||
## Core Profile
|
||||
Targets GL 3.3+ core profile
|
||||
* FFP emulation via generated GLSL shaders
|
||||
* Display list emulation
|
||||
* Software matrix stacks and state management through GLSM
|
||||
* GL_QUADS → GL_TRIANGLES conversion
|
||||
* GL_QUADS -> GL_TRIANGLES conversion
|
||||
* Automatic transformation of mod shaders that use compat-only GLSL builtins (`gl_ModelViewMatrix`, `texture2D`, etc.)
|
||||
|
||||
|
||||
# Required Dependencies
|
||||
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.23
|
||||
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.11.19
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
|
||||
* NOTE: Some mods are not required, but a specific version is required if present - see: `Minimum versions` below.
|
||||
|
||||
# Minimum versions (if present)
|
||||
* [LWJGL3ify](https://github.com/GTNewHorizons/lwjgl3ify/releases/) 1.5.3+ (3.0.25+ for the SDL GPU renderer)
|
||||
* [Hodgepodge](https://github.com/GTNewHorizons/Hodgepodge/releases) 2.4.4+
|
||||
* [ArchaicFix](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) 0.7.0+
|
||||
* [CodeChickenCore](https://github.com/GTNewHorizons/CodeChickenCore/releases) 1.2.0+ - Threading issues below that
|
||||
* [Amazing Trophies](https://github.com/GTNewHorizons/Amazing-Trophies/releases) 1.2.2+
|
||||
|
||||
# Known (temporary) Incompatibilities
|
||||
* Forge Relocation - Currently non Thread Safe; safe if not in use
|
||||
@@ -45,15 +73,15 @@ Targets GL 3.3+ core profile
|
||||
* FastCraft [Disabled, won't fix]
|
||||
* BetterFPS [Disabled, won't fix]
|
||||
* OptimizationsAndTweaks [Disabled, won't fix]
|
||||
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - Use 1.2.2+
|
||||
* [LWJGL3ify 1.5.4 and below](https://github.com/GTNewHorizons/lwjgl3ify/releases/) - Use the latest version available, 1.5.7+
|
||||
* [ArchaicFix 0.6.2 and below](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) - Use 0.7.0 or above
|
||||
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 2.4.5 or above
|
||||
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
|
||||
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)
|
||||
* SmoothFont - Equivalent functionality added
|
||||
* ClientFixer - Use a resource pack like [GTNH-FTI-Standard-Font](https://github.com/Eldrinn-Elantey/GTNH-FTI-Standard-Font)
|
||||
|
||||
# Building
|
||||
* `./gradlew build`, `./gradlew test`
|
||||
* Subprojects: `glsm` (state manager, FFP emulation, backends), `lwjgl3-backend`, `sdl-gpu`, `tracy-client`, `moltenvk`
|
||||
* Read the [AI policy](AI_POLICY.md) before opening a PR
|
||||
|
||||
# Credits
|
||||
* ShadersMod
|
||||
* Original ShadersMods: karyonix, sonic ether, id_miner, daxnitro
|
||||
@@ -96,7 +124,7 @@ Targets GL 3.3+ core profile
|
||||
* FalsePattern for [FalseTweaks](https://github.com/falsepattern/falsetweaks) - Item Rendering Optimizations taken from FalseTweaks under the LGPL 3.0 license
|
||||
* Beddium
|
||||
* Venn and Falsepattern for [Beddium](https://github.com/vfx-dev/Beddium) - MinimapRenderer, SafeChunkTracker, and some debug commands adapted under the LGPL 3.0 License
|
||||
* [Mesa](https://gitlab.freedesktop.org/mesa/mesa) — Code ported and/or inspired from Mesa - under the MIT licensed
|
||||
* [Mesa](https://gitlab.freedesktop.org/mesa/mesa) - Code ported and/or inspired from Mesa - under the MIT license
|
||||
* Display list ID allocator ported from [util_idalloc](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/util/u_idalloc.c) (Copyright 2017 Valve Corporation)
|
||||
* FFP vertex shader generation inspired by ffvertex_prog.c (Copyright 2007 VMware, Inc.)
|
||||
* FFP fragment shader generation inspired by ff_fragment_shader.c (Copyright 2007, 2009 VMware, Inc. / 2010-2011 Intel Corporation)
|
||||
|
||||
+72
-2
@@ -6,6 +6,8 @@ plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
apply(from = "gradle/mod-version.gradle.kts")
|
||||
|
||||
val lwjglDebug = false
|
||||
val gpuHud = false
|
||||
val renderdoc = false
|
||||
@@ -87,8 +89,11 @@ tasks.register<Copy>("copyDependencies") {
|
||||
val embedOnly: Configuration by configurations
|
||||
val shadowImplementation: Configuration by configurations
|
||||
|
||||
val dependencyClasspath: FileCollection = sourceSets.main.get().runtimeClasspath - sourceSets.main.get().output
|
||||
|
||||
val downgradeEmbedOnlyForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = embedOnly
|
||||
classpath = dependencyClasspath
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
@@ -102,6 +107,7 @@ val stripModuleInfoFromShadow by tasks.registering(Jar::class) {
|
||||
|
||||
val downgradeShadowImplForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = files(stripModuleInfoFromShadow.map { it.archiveFile })
|
||||
classpath = dependencyClasspath
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
@@ -116,6 +122,7 @@ val glsmTestFixtures: Configuration by configurations.creating {
|
||||
|
||||
val downgradeGlsmTestFixturesForTest by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = glsmTestFixtures
|
||||
classpath = dependencyClasspath
|
||||
outputs.dir(temporaryDir)
|
||||
}
|
||||
|
||||
@@ -146,9 +153,49 @@ fun Test.configureAngelicaJava8() {
|
||||
}
|
||||
}
|
||||
|
||||
if (isMacOs && providers.gradleProperty("clientJvmVendor").isPresent) {
|
||||
for (javaVersion in listOf(17, 21, 25)) {
|
||||
tasks.named<JavaExec>("runClient$javaVersion") {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
vendor = JvmVendorSpec.matching(providers.gradleProperty("clientJvmVendor").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forge's asm-debug-all 5.0.3 cannot read the Java 25 class files ArchScan walks; this ASM has to resolve first.
|
||||
val archScanAsm: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmRedirectAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmMutableStaticsAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||
glsmMutableStaticsAgent(project(":glsm", "mutableStaticsAgent"))
|
||||
archScanAsm(libs.asm.tree.test)
|
||||
archScanAsm(libs.asm.commons.test)
|
||||
}
|
||||
|
||||
sourceSets.test {
|
||||
compileClasspath = archScanAsm + compileClasspath
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
classpath = archScanAsm + classpath
|
||||
javaAgent(glsmMutableStaticsAgent)
|
||||
jvmArgs("-Dceleritas.lwjglService=org.taumc.celeritas.lwjgl.HeadlessTestLWJGLService")
|
||||
dependsOn(":glsm:classes", ":lwjgl3-backend:classes", ":sdl-gpu:classes")
|
||||
}
|
||||
@@ -156,6 +203,13 @@ tasks.test {
|
||||
val downgradeRootMainClasses = tasks.named<DowngradeFiles>("downgradeMainClasses")
|
||||
val downgradeRootTestClasses = tasks.named<DowngradeFiles>("downgradeTestClasses")
|
||||
|
||||
// JVMDowngrader only declares outputs for inputs that exist at configuration, so a clean tree has none.
|
||||
for (name in listOf("main", "mixin", "test")) {
|
||||
tasks.named<DowngradeFiles>("downgrade${name.replaceFirstChar { it.uppercase() }}Classes") {
|
||||
sourceSets[name].output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
}
|
||||
}
|
||||
|
||||
val glCoreTest by tasks.registering(Test::class) {
|
||||
description = "Runs the gl-core tests in their own JVM with a real GL context."
|
||||
group = "verification"
|
||||
@@ -171,13 +225,29 @@ val glCoreTest by tasks.registering(Test::class) {
|
||||
.plus(downgraded(downgradeRootMainClasses))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
|
||||
|
||||
javaAgent(glsmRedirectAgent)
|
||||
|
||||
configureAngelicaJava8()
|
||||
mustRunAfter(tasks.test)
|
||||
}
|
||||
|
||||
tasks.test { finalizedBy(glCoreTest) }
|
||||
val verifyTestsRan by tasks.registering
|
||||
listOf(tasks.test, glCoreTest).forEach { testTask ->
|
||||
val name = testTask.name
|
||||
val verify = tasks.register("verify${name.replaceFirstChar { it.uppercase() }}Ran") {
|
||||
val resultsDir = layout.buildDirectory.dir("test-results/$name")
|
||||
dependsOn(testTask)
|
||||
doLast {
|
||||
val dir = resultsDir.get().asFile
|
||||
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
|
||||
check(xmls.isNotEmpty()) { ":$name produced no TEST-*.xml in $dir - test task likely went NO-SOURCE" }
|
||||
}
|
||||
}
|
||||
verifyTestsRan.configure { dependsOn(verify) }
|
||||
}
|
||||
tasks.check { dependsOn(verifyTestsRan) }
|
||||
|
||||
apply(from = "gradle/angelica-shadow-common.gradle.kts")
|
||||
apply(from = "experimental-surround/surround.root.gradle.kts")
|
||||
|
||||
tasks.withType<TaskPublishCurseForge>().configureEach {
|
||||
uploadArtifacts.forEach { it.addGameVersion("Client") }
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
|
||||
fun Test.javaAgent(jar: FileCollection) {
|
||||
inputs.files(jar).withPropertyName("javaAgent")
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${jar.singleFile.path}") })
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.ArchiveOperations
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class UnpackLwjgl3Natives : Sync() {
|
||||
@get:Inject
|
||||
abstract val archives: ArchiveOperations
|
||||
}
|
||||
|
||||
// LWJGL extracts jar-packed natives to a shared temp dir with no cross-process safety; plain files it loads in place.
|
||||
fun Project.registerLwjgl3NativesUnpack(): TaskProvider<UnpackLwjgl3Natives> {
|
||||
val jars = the<SourceSetContainer>()["test"].runtimeClasspath.filter {
|
||||
it.name.startsWith("lwjgl") && it.name.contains("-natives-") && !it.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
return tasks.register<UnpackLwjgl3Natives>("unpackLwjgl3Natives") {
|
||||
val archives = archives
|
||||
from(jars.elements.map { files -> files.map { archives.zipTree(it) } })
|
||||
exclude("META-INF/**")
|
||||
into(layout.buildDirectory.dir("lwjgl3-natives"))
|
||||
}
|
||||
}
|
||||
|
||||
fun Test.loadLwjgl3NativesFrom(unpack: TaskProvider<UnpackLwjgl3Natives>) {
|
||||
classpath = project.files(unpack) + classpath
|
||||
}
|
||||
@@ -62,9 +62,10 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
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")
|
||||
compileOnly("com.github.GTNewHorizons:Botania:1.13.32-GTNH:dev") { isTransitive = false }
|
||||
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:OpenComputers:1.12.64-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")
|
||||
@@ -93,6 +94,9 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
shadowImplementation(project(":lwjgl3-backend")) { isTransitive = false }
|
||||
shadowImplementation(project(":sdl-gpu")) { isTransitive = false }
|
||||
runtimeOnlyNonPublishable(project(":tracy-client")) { isTransitive = false }
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")) {
|
||||
runtimeOnlyNonPublishable(project(":moltenvk")) { isTransitive = false }
|
||||
}
|
||||
devOnlyNonPublishable(libs.async.profiler)
|
||||
shadowImplementation(libs.eventbus)
|
||||
|
||||
@@ -182,6 +186,8 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:LiquidLogged:0.2.0:dev") { isTransitive = false }
|
||||
|
||||
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { isTransitive = false }
|
||||
}
|
||||
|
||||
@@ -201,11 +207,12 @@ afterEvaluate {
|
||||
testImplementation(libs.junit.platform.reporting)
|
||||
testImplementation(libs.junit.platform.launcher)
|
||||
|
||||
testImplementation(libs.archunit.junit5)
|
||||
testImplementation(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
|
||||
glsmTestFixtures(testFixtures(project(":glsm")) as ModuleDependency) { isTransitive = false }
|
||||
testCompileOnly(testFixtures(project(":glsm")))
|
||||
testImplementation("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
testImplementation("com.github.GTNewHorizons:LiquidLogged:0.2.0:dev") { isTransitive = false }
|
||||
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
apply(from = rootProject.file("experimental-surround/surround.project.gradle.kts"))
|
||||
|
||||
val mixinProviderSpec = rootProject.extra["mixinProviderSpec"] as String
|
||||
|
||||
val refmapProbe = sourceSets.create("refmapProbe")
|
||||
|
||||
dependencies {
|
||||
compileOnly(mixinProviderSpec)
|
||||
testImplementation(mixinProviderSpec)
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(project(":experimental-surround-runtime"))
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
"refmapProbeCompileOnly"(mixinProviderSpec)
|
||||
"refmapProbeCompileOnly"(project(":experimental-surround-runtime"))
|
||||
"refmapProbeAnnotationProcessor"(mixinProviderSpec)
|
||||
"refmapProbeAnnotationProcessor"(sourceSets.main.get().output)
|
||||
}
|
||||
|
||||
val probeRefmaps = layout.buildDirectory.dir("refmap-probe")
|
||||
val probeSrg = file("src/refmapProbe/mappings.srg")
|
||||
val surroundAp = "com.gtnewhorizons.angelica.experimental.surround.ap.SurroundProcessor"
|
||||
val mixinAps = "org.spongepowered.tools.obfuscation.MixinObfuscationProcessorInjection,org.spongepowered.tools.obfuscation.MixinObfuscationProcessorTargets"
|
||||
|
||||
fun JavaCompile.probe(name: String, processors: String?) {
|
||||
val refmap = probeRefmaps.map { it.file("refmap-probe/$name.json") }
|
||||
val srg = probeSrg
|
||||
javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(25) }
|
||||
outputs.file(refmap)
|
||||
inputs.file(srg)
|
||||
options.compilerArgumentProviders.add(CommandLineArgumentProvider {
|
||||
listOf("-proc:only", "-AoutRefMapFile=" + refmap.get().asFile.absolutePath,
|
||||
"-AreobfSrgFile=" + srg.absolutePath, "-AdefaultObfuscationEnv=searge") +
|
||||
(if (processors == null) emptyList() else listOf("-processor", processors))
|
||||
})
|
||||
}
|
||||
|
||||
fun JavaCompile.probeOrdered(name: String, classesDir: String, processors: String) {
|
||||
source = refmapProbe.java
|
||||
classpath = refmapProbe.compileClasspath
|
||||
options.annotationProcessorPath = refmapProbe.annotationProcessorPath
|
||||
destinationDirectory = layout.buildDirectory.dir("classes/$classesDir")
|
||||
probe(name, processors)
|
||||
}
|
||||
|
||||
tasks.named<JavaCompile>(refmapProbe.compileJavaTaskName) { probe("discovered", null) }
|
||||
val probeSurroundFirst by tasks.registering(JavaCompile::class) { probeOrdered("surround-first", "refmapProbeFirst", "$surroundAp,$mixinAps") }
|
||||
val probeSurroundLast by tasks.registering(JavaCompile::class) { probeOrdered("surround-last", "refmapProbeLast", "$mixinAps,$surroundAp") }
|
||||
|
||||
sourceSets.test { resources.srcDir(files(probeRefmaps).builtBy(refmapProbe.compileJavaTaskName, probeSurroundFirst, probeSurroundLast)) }
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.ap;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.tools.obfuscation.mirror.AnnotationHandle;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@SupportedAnnotationTypes("com.gtnewhorizons.angelica.experimental.surround.Surround")
|
||||
public class SurroundProcessor extends AbstractProcessor {
|
||||
|
||||
record Registration(TypeElement mixin, ExecutableElement method, AnnotationMirror surround) {
|
||||
}
|
||||
|
||||
private final List<Registration> registrations = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latestSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (roundEnv.processingOver()) {
|
||||
if (!this.registrations.isEmpty()) {
|
||||
register();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (TypeElement annotation : annotations) {
|
||||
for (Element annotated : roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||
if (annotated.getEnclosingElement() instanceof TypeElement mixin && AnnotationHandle.of(mixin, Mixin.class).exists()) {
|
||||
for (AnnotationMirror mirror : annotated.getAnnotationMirrors()) {
|
||||
if (mirror.getAnnotationType().asElement().equals(annotation)) {
|
||||
this.registrations.add(new Registration(mixin, (ExecutableElement) annotated, mirror));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void register() {
|
||||
final ProcessingEnvironment env = this.processingEnv;
|
||||
try {
|
||||
// pinned by UniMixins 0.3.1; recheck on bump
|
||||
final Class<?> annotatedMixins = Class.forName("org.spongepowered.tools.obfuscation.AnnotatedMixins", false, AnnotationHandle.class.getClassLoader());
|
||||
final Method forEnvironment = annotatedMixins.getDeclaredMethod("getMixinsForEnvironment", ProcessingEnvironment.class);
|
||||
final Method registerInjector = annotatedMixins.getDeclaredMethod("registerInjector", TypeElement.class, ExecutableElement.class, AnnotationHandle.class);
|
||||
final Method writeReferences = annotatedMixins.getDeclaredMethod("writeReferences");
|
||||
forEnvironment.setAccessible(true);
|
||||
registerInjector.setAccessible(true);
|
||||
writeReferences.setAccessible(true);
|
||||
final Object mixins = forEnvironment.invoke(null, env);
|
||||
for (Registration registration : this.registrations) {
|
||||
registerInjector.invoke(mixins, registration.mixin(), registration.method(), AnnotationHandle.of(registration.surround()));
|
||||
}
|
||||
writeReferences.invoke(mixins);
|
||||
} catch (ReflectiveOperationException | LinkageError | RuntimeException e) {
|
||||
env.getMessager().printMessage(Diagnostic.Kind.ERROR, "@Surround: refmap entries were not written: " + e + (e.getCause() == null ? "" : " caused by " + e.getCause()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.gtnewhorizons.angelica.experimental.surround.ap.SurroundProcessor
|
||||
@@ -0,0 +1,20 @@
|
||||
package probe;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(RefmapTarget.class)
|
||||
class MixinRefmapTarget {
|
||||
@Surround(method = "run()V")
|
||||
private void wholeMethod() {}
|
||||
|
||||
@Surround.Finally
|
||||
private void always() {}
|
||||
|
||||
@Surround(method = "run(Ljava/lang/String;)V", id = "call", at = @At(value = "INVOKE", target = "Lprobe/RefmapTarget;helper()V"))
|
||||
private void callForm() {}
|
||||
|
||||
@Surround.Finally("call")
|
||||
private void alwaysCall() {}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package probe;
|
||||
|
||||
public class RefmapTarget {
|
||||
void run() {}
|
||||
void run(String s) { helper(); }
|
||||
void helper() {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
MD: probe/RefmapTarget/run ()V probe/RefmapTarget/func_1_a ()V
|
||||
MD: probe/RefmapTarget/run (Ljava/lang/String;)V probe/RefmapTarget/func_2_b (Ljava/lang/String;)V
|
||||
MD: probe/RefmapTarget/helper ()V probe/RefmapTarget/func_3_c ()V
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.ap;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class RefmapRegistrationTest {
|
||||
|
||||
private static final Pattern ENTRY = Pattern.compile("\"([^\"]+)\": \"([^\"]+)\"");
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"discovered", "surround-first", "surround-last"})
|
||||
void surroundTargetsAreRemappedInTheRefmap(String variant) throws IOException {
|
||||
final Map<String, String> expected = new HashMap<>();
|
||||
expected.put("run()V", "Lprobe/RefmapTarget;func_1_a()V");
|
||||
expected.put("run(Ljava/lang/String;)V", "Lprobe/RefmapTarget;func_2_b(Ljava/lang/String;)V");
|
||||
expected.put("Lprobe/RefmapTarget;helper()V", "Lprobe/RefmapTarget;func_3_c()V");
|
||||
assertEquals(expected, mappings(variant, "probe/MixinRefmapTarget"));
|
||||
}
|
||||
|
||||
private static Map<String, String> mappings(String variant, String mixin) throws IOException {
|
||||
final String json = new String(RefmapRegistrationTest.class.getResourceAsStream("/refmap-probe/" + variant + ".json").readAllBytes(), UTF_8);
|
||||
final int start = json.indexOf('{', json.indexOf("\"" + mixin + "\"", json.indexOf("\"mappings\"")));
|
||||
final Matcher m = ENTRY.matcher(json.substring(start, json.indexOf('}', start)));
|
||||
final Map<String, String> entries = new HashMap<>();
|
||||
while (m.find()) entries.put(m.group(1), m.group(2));
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
apply(plugin = "xyz.wagyourtail.jvmdowngrader")
|
||||
|
||||
apply(from = rootProject.file("experimental-surround/surround.project.gradle.kts"))
|
||||
|
||||
// Required by RFG consumers (runClient17 java17Dependencies) to pick a variant.
|
||||
val rfgObfAttr = Attribute.of("com.gtnewhorizons.retrofuturagradle.obfuscation", String::class.java)
|
||||
val rfgTransformedAttr = Attribute.of("rfgDeobfuscatorTransformed", Boolean::class.javaObjectType)
|
||||
|
||||
configurations {
|
||||
listOf("apiElements", "runtimeElements").forEach { name ->
|
||||
named(name) {
|
||||
attributes {
|
||||
attribute(rfgObfAttr, "mcp")
|
||||
attribute(rfgTransformedAttr, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val mixinProviderSpec = rootProject.extra["mixinProviderSpec"] as String
|
||||
|
||||
dependencies {
|
||||
compileOnly(mixinProviderSpec)
|
||||
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(mixinProviderSpec)
|
||||
testImplementation(libs.launchwrapper)
|
||||
testImplementation(libs.commons.io)
|
||||
testImplementation(libs.guava)
|
||||
testImplementation(libs.log4j.core)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.jvmdowngrader.java.api) { artifact { classifier = "downgraded-8" } }
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "WagYourTail"
|
||||
url = uri("https://maven.wagyourtail.xyz/releases")
|
||||
content { includeGroup("xyz.wagyourtail.jvmdowngrader") }
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeFiles>().configureEach {
|
||||
outputs.cacheIf { true }
|
||||
downgradeTo.set(JavaVersion.VERSION_1_8)
|
||||
multiReleaseOriginal.set(false)
|
||||
multiReleaseVersions.set(emptySet())
|
||||
logLevel.set("FATAL")
|
||||
}
|
||||
|
||||
fun SourceSet.registerDowngrade(taskName: String) = tasks.register<DowngradeFiles>(taskName) {
|
||||
inputCollection = output.classesDirs
|
||||
classpath = compileClasspath
|
||||
output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
dependsOn(tasks.named(classesTaskName))
|
||||
}
|
||||
|
||||
val downgradeMainClasses = sourceSets["main"].registerDowngrade("downgradeMainClasses")
|
||||
val downgradeTestClasses = sourceSets["test"].registerDowngrade("downgradeTestClasses")
|
||||
|
||||
val goldenDir = layout.projectDirectory.dir("src/test/resources/golden").asFile.absolutePath
|
||||
|
||||
// Runs on Java 8 against the JVMDowngrader output, the bytecode production ships.
|
||||
fun Test.configureSurroundTest(runDirName: String) {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (System.getProperty("os.name").lowercase().contains("mac") && System.getProperty("os.arch") == "aarch64") {
|
||||
vendor = JvmVendorSpec.AZUL
|
||||
}
|
||||
}
|
||||
dependsOn(downgradeMainClasses, downgradeTestClasses)
|
||||
val downgradedTest = files(downgradeTestClasses.map { it.outputCollection })
|
||||
testClassesDirs = downgradedTest
|
||||
classpath = downgradedTest
|
||||
.plus(files(downgradeMainClasses.map { it.outputCollection }))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(sourceSets["main"].output.classesDirs).minus(sourceSets["test"].output.classesDirs))
|
||||
val dir = layout.buildDirectory.dir(runDirName).get().asFile
|
||||
workingDir = dir
|
||||
systemProperty("surround.golden.dir", goldenDir)
|
||||
systemProperty("mixin.debug.verify", "true")
|
||||
val javac = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(25) }
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-Dsurround.javac=" + javac.get().executablePath.asFile.absolutePath) })
|
||||
doFirst { dir.mkdirs() }
|
||||
}
|
||||
|
||||
fun Test.configureUntaggedSurroundTest(runDirName: String, vararg systemProps: Pair<String, String>) {
|
||||
useJUnitPlatform { excludeTags("mixinextras") }
|
||||
for ((key, value) in systemProps) {
|
||||
systemProperty(key, value)
|
||||
}
|
||||
configureSurroundTest(runDirName)
|
||||
}
|
||||
|
||||
fun Test.configureTaggedSurroundTest(runDirName: String, tag: String, vararg systemProps: Pair<String, String>) {
|
||||
useJUnitPlatform { includeTags(tag) }
|
||||
for ((key, value) in systemProps) {
|
||||
systemProperty(key, value)
|
||||
}
|
||||
configureSurroundTest(runDirName)
|
||||
}
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
configureUntaggedSurroundTest("test-run")
|
||||
}
|
||||
|
||||
val mixinExtrasTest by tasks.registering(Test::class) {
|
||||
configureTaggedSurroundTest(
|
||||
"test-run-mixinextras",
|
||||
"mixinextras",
|
||||
"surround.test.mixinextras" to "true"
|
||||
)
|
||||
}
|
||||
|
||||
val mixinExtrasAdditionsFirstTest by tasks.registering(Test::class) {
|
||||
configureTaggedSurroundTest(
|
||||
"test-run-mixinextras-additions-first",
|
||||
"mixinextras",
|
||||
"surround.test.mixinextras" to "true",
|
||||
"surround.test.additionsFirst" to "true"
|
||||
)
|
||||
}
|
||||
|
||||
val obfTest by tasks.registering(Test::class) {
|
||||
configureUntaggedSurroundTest("test-run-obf", "surround.test.obf" to "true")
|
||||
filter { excludeTestsMatching("*.RejectionTest") }
|
||||
}
|
||||
|
||||
tasks.named("check") {
|
||||
dependsOn(mixinExtrasTest, mixinExtrasAdditionsFirstTest, obfTest)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Exits;
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
|
||||
|
||||
record CallSiteSpec(MethodNode target, InjectionNode node, MethodNode entry, String entryDesc, Type[] operands, Type result, Exits exits, String description, int[] entryLocals, int[][] locals) {}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Experimental and internal to Angelica; the API may change without notice.
|
||||
* <p>
|
||||
* Surrounds one method call ({@link #at()} set) or a whole method ({@link #at()} empty) with try/catch/finally.
|
||||
* The call form replaces MixinExtras' {@code @WrapOperation} and the whole-method form replaces {@code @WrapMethod},
|
||||
* without their per-call {@code Operation} and {@code Object[]} allocation. Use it when the original runs at most once
|
||||
* with its original arguments.
|
||||
* <p>
|
||||
* A surround can:
|
||||
* <ul>
|
||||
* <li>run code before the original: the entry handler, the annotated method itself;</li>
|
||||
* <li>hand state from that code to everything after it: {@link Carry @Surround.Carry} locals;</li>
|
||||
* <li>read the call's operands, the target's parameters, and locals at the call: operands,
|
||||
* {@link Local @Surround.Local};</li>
|
||||
* <li>skip the original and supply its result instead: {@link Skip @Surround.Skip} + {@link Skipped @Surround.Skipped};</li>
|
||||
* <li>replace the result after a normal completion: {@link Return @Surround.Return};</li>
|
||||
* <li>react to an exception, rethrowing or (calls only) swallowing it and supplying the result:
|
||||
* {@link Catch @Surround.Catch};</li>
|
||||
* <li>run code on every way out: {@link Finally @Surround.Finally}.</li>
|
||||
* </ul>
|
||||
* It cannot call the original twice or with different arguments.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <pre>
|
||||
* @Surround(method = "doRender(Lnet/minecraft/entity/item/EntityItem;DDDFF)V")
|
||||
* private void enter(EntityItem entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
* @Surround.Carry int prevItemId = ItemIdManager.getItemId();
|
||||
* ItemIdManager.setItemId(entity.getEntityItem());
|
||||
* }
|
||||
*
|
||||
* @Surround.Finally
|
||||
* private void restore(@Surround.Carry int prevItemId) {
|
||||
* ItemIdManager.setItemIdRaw(prevItemId);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Handlers</h2>
|
||||
* The annotated method is the entry handler. Exit handlers are methods in the same mixin whose {@code value} is this
|
||||
* surround's {@link #id()}:
|
||||
* <pre>
|
||||
* @Surround void enter(operands, @Local params) before the original
|
||||
* @Surround.Skipped T skipped(operands, @Local params, @Carry params) instead of the original
|
||||
* @Surround.Return T ret(T result, operands, @Local params, @Carry params) after it completes normally
|
||||
* @Surround.Catch void caught(E error, operands, @Local params, @Carry params) when it throws E
|
||||
* @Surround.Finally void always(operands, @Local params, @Carry params) on every way out
|
||||
* </pre>
|
||||
* {@code T} is the return type of the call or method. When it is {@code void}, {@code ret} takes no result and
|
||||
* {@code Skipped} returns {@code void}.
|
||||
* <ul>
|
||||
* <li>The entry handler returns {@code void}. Exit handlers are static exactly when the entry handler is.</li>
|
||||
* <li>Per surround: at most one {@code Skipped}, one {@code Return} and one {@code Finally}, and one {@code Catch} per
|
||||
* exception type. A method has one role.</li>
|
||||
* <li>{@link #id()} is required when the mixin declares more than one {@code @Surround}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Parameters</h2>
|
||||
* Declare them in this order, each group optional:
|
||||
* <ol>
|
||||
* <li>Operands, as a leading prefix. For a call: the receiver (unless static), then the arguments. A reference
|
||||
* operand may be declared as a supertype. For a whole method: the target's parameters.</li>
|
||||
* <li>{@link Local @Surround.Local} parameters (call form only). Exit handlers see the value as of entry.</li>
|
||||
* <li>{@link Carry @Surround.Carry} parameters (exit handlers only).</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h2>Carrying state</h2>
|
||||
* Exit handlers see entry-handler locals only through {@link Carry @Surround.Carry}; see there for the rules. Two
|
||||
* carries of one kind are told apart by name:
|
||||
* <pre>
|
||||
* @Surround(method = ..., at = @At(value = "INVOKE", target = ...))
|
||||
* private void enter() {
|
||||
* @Surround.Carry("depth") int depth = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
* @Surround.Carry("prev") int prevProgram = GLSMPrograms.getActiveProgram();
|
||||
* @Surround.Carry Class<?> prevRenderable = TesrAttribution.currentRenderable;
|
||||
* }
|
||||
*
|
||||
* @Surround.Finally
|
||||
* private void exit(@Surround.Carry("depth") int depth, @Surround.Carry("prev") int prevProgram,
|
||||
* @Surround.Carry Class<?> prevRenderable) { ... }
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Entry-handler body</h2>
|
||||
* <ul>
|
||||
* <li>Every path must fall through to the end: no early {@code return}. Branch with {@code if} instead.</li>
|
||||
* <li>Do not assign its parameters. To change arguments, use {@code @ModifyVariable} or {@code @WrapMethod}.</li>
|
||||
* <li>In the call form it is inlined at every matched call, as are the exit-handler calls. Keep it short: move bulky
|
||||
* logic into a helper and carry the helper's result. Huge targets stop being JIT-compiled past 8000 bytes.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Skipping</h2>
|
||||
* A {@link Skip @Surround.Skip} local in the entry handler decides whether the original runs; see there for the rules.
|
||||
*
|
||||
* <h2>Order and exceptions</h2>
|
||||
* <ol>
|
||||
* <li>The entry handler. If it throws, nothing else runs.</li>
|
||||
* <li>The original, or {@link Skipped} when skipped.</li>
|
||||
* <li>{@link Return}, only if the original completed normally (not after {@code Skipped}). Its return value replaces
|
||||
* the result.</li>
|
||||
* <li>{@link Finally}, on every way out: normal, after a catch, or rethrowing.</li>
|
||||
* </ol>
|
||||
* {@link Catch} covers step 2, not {@code Return}, and only the most specific matching one runs. It rethrows when
|
||||
* it returns, unless {@link Catch#handle()} is set (call form only): the exception then ends there, and the
|
||||
* handler's return value becomes the call's result.
|
||||
*
|
||||
* <h2>Whole-method form</h2>
|
||||
* <ul>
|
||||
* <li>The original body becomes a private {@code name$surround} method, so stack traces gain that frame.</li>
|
||||
* <li>A {@code synchronized} target holds its lock across the handlers too.</li>
|
||||
* <li>An entry handler that calls the target goes through the surround again.</li>
|
||||
* <li>{@code @Local}, {@link #slice()} and {@code handle} are call-form only.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>With other injectors</h2>
|
||||
* <ul>
|
||||
* <li>A call surround encloses a {@code @Redirect} or {@code @WrapOperation} on the same call. A whole-method surround
|
||||
* encloses every other injection in the method, including call surrounds.</li>
|
||||
* <li>Several surrounds on one call or method nest, lowest priority innermost. An outer skip skips the inner ones
|
||||
* entirely, and {@code Return} replacements chain from inner to outer.</li>
|
||||
* <li>A matched call that another injector removed is skipped with a warning.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Surround {
|
||||
|
||||
String[] method() default {};
|
||||
|
||||
/**
|
||||
* Calls to surround, as INVOKE-style {@code @At} without shift or by; each match is surrounded separately. Empty
|
||||
* surrounds the whole method.
|
||||
*/
|
||||
At[] at() default {};
|
||||
|
||||
/**
|
||||
* As for {@code @Inject}; calls only.
|
||||
*/
|
||||
Slice[] slice() default {};
|
||||
|
||||
/**
|
||||
* Names this surround for its exit handlers. Required when the mixin declares more than one {@code @Surround};
|
||||
* unique per mixin.
|
||||
*/
|
||||
String id() default "";
|
||||
|
||||
boolean remap() default true;
|
||||
|
||||
/**
|
||||
* As for {@code @Inject}, as are {@link #expect()} and {@link #allow()}; each surrounded call or whole target counts once.
|
||||
*/
|
||||
int require() default -1;
|
||||
|
||||
int expect() default 1;
|
||||
|
||||
int allow() default -1;
|
||||
|
||||
/**
|
||||
* Carries a value from the entry handler to the exit handlers.
|
||||
* <p>
|
||||
* Annotate a local in the entry handler, then declare a parameter with the same annotation in any exit handler that
|
||||
* needs the value:
|
||||
* <pre>
|
||||
* @Surround(method = ...)
|
||||
* private void enter() {
|
||||
* @Surround.Carry int prevItemId = ItemIdManager.getItemId();
|
||||
* ItemIdManager.setItemId(...);
|
||||
* }
|
||||
*
|
||||
* @Surround.Finally
|
||||
* private void restore(@Surround.Carry int prevItemId) {
|
||||
* ItemIdManager.setItemIdRaw(prevItemId);
|
||||
* }
|
||||
* </pre>
|
||||
* Declaring the local:
|
||||
* <ul>
|
||||
* <li>Annotate the declaration itself: {@code @Surround.Carry int x = ...}. Not a parameter of the entry handler
|
||||
* (use operands for those), and not part of the type (e.g. an array component).</li>
|
||||
* <li>Declare it at the top level of the entry handler and initialize it there. It must still be in scope and
|
||||
* definitely assigned when the entry handler ends.</li>
|
||||
* </ul>
|
||||
* Receiving it:
|
||||
* <ul>
|
||||
* <li>Carry parameters come last, after operands and {@code @Local} parameters. An exit handler may take any
|
||||
* subset.</li>
|
||||
* <li>Parameters bind to locals by kind: reference, {@code int}-like ({@code boolean}, {@code byte},
|
||||
* {@code char}, {@code short}, {@code int}), {@code long}, {@code float}, {@code double}. If the entry handler
|
||||
* carries two locals of one kind, name every one of that kind with the same {@link #value()} on the local and on
|
||||
* each parameter that reads it: {@code @Surround.Carry("depth") int depth}. Names are unique within a handler.</li>
|
||||
* <li>A reference is cast to the parameter's type on read. Every exit handler must read a given carry as the same
|
||||
* type.</li>
|
||||
* </ul>
|
||||
* Each carry lives in a local slot of the target method, with no allocation, so nested and recursive invocations
|
||||
* keep separate values. The entry handler returns {@code void}; carry its results instead.
|
||||
*/
|
||||
@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Carry {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether the original runs.
|
||||
* <p>
|
||||
* Annotate one {@code boolean} or {@code int}-like local in the entry handler, declared like a {@link Carry}: at
|
||||
* the top level, initialized at its declaration, in scope at the end:
|
||||
* <pre>
|
||||
* @Surround(method = ..., at = @At(value = "INVOKE", target = ...))
|
||||
* private void enter(RenderBlocks renderer, Block block) {
|
||||
* @Surround.Skip boolean skip = FallingBlockRendering.isActive();
|
||||
* }
|
||||
*
|
||||
* @Surround.Skipped
|
||||
* private boolean replacement(RenderBlocks renderer, Block block) { ... }
|
||||
* </pre>
|
||||
* <ul>
|
||||
* <li>If the local is nonzero when the entry handler ends, the original does not run and {@link Skipped} runs in
|
||||
* its place; {@link Return} does not run after it, {@link Catch} and {@link Finally} still do.</li>
|
||||
* <li>{@link Skipped} is required unless the result is {@code void}, and rejected without a {@code Skip}
|
||||
* local.</li>
|
||||
* <li>At most one per entry handler.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Skip {
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the result in place of a skipped original. Required unless that result is {@code void}.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Skipped {
|
||||
|
||||
/**
|
||||
* The {@link Surround#id()} of the surround this handler belongs to; empty for the default.
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs after the original completes normally. Takes the result first (none for {@code void}) and returns its
|
||||
* replacement.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Return {
|
||||
|
||||
/**
|
||||
* The {@link Surround#id()} of the surround this handler belongs to; empty for the default.
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs when the original or {@link Skipped} throws the first parameter's type, unless a more specific
|
||||
* {@code Catch} matches. Rethrows when it returns.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Catch {
|
||||
|
||||
/**
|
||||
* The {@link Surround#id()} of the surround this handler belongs to; empty for the default.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Call form only: the exception ends here and the returned value becomes the call's result.
|
||||
*/
|
||||
boolean handle() default false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs on every way out.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Finally {
|
||||
|
||||
/**
|
||||
* The {@link Surround#id()} of the surround this handler belongs to; empty for the default.
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
/**
|
||||
* As MixinExtras' {@code @Local}, read at the call; exit handlers see its value at entry. Calls only.
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Local {
|
||||
|
||||
int ordinal() default -1;
|
||||
|
||||
int index() default -1;
|
||||
|
||||
String[] name() default {};
|
||||
|
||||
boolean argsOnly() default false;
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.llamalad7.mixinextras.utils.MixinInternals;
|
||||
import org.spongepowered.asm.lib.tree.AnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.IExtension;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.ITargetClassContext;
|
||||
import org.spongepowered.asm.util.Annotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
final class SurroundApplicatorExtension implements IExtension {
|
||||
|
||||
private static final String MIXINEXTRAS_PACKAGE = "com.llamalad7.mixinextras.";
|
||||
private static final Pattern HANDLER_NAME = Pattern.compile("surround\\$[a-z]{3,}[0-9a-f]{3,}\\$.*");
|
||||
|
||||
private final Map<ITargetClassContext, Pending> pending = new WeakHashMap<>();
|
||||
|
||||
private record Pending(List<CallSiteSpec> calls, List<CallSiteSpec> wholes, Set<String> declarers) {}
|
||||
|
||||
private static final class Holder {
|
||||
|
||||
static final SurroundApplicatorExtension INSTANCE = create();
|
||||
|
||||
private static SurroundApplicatorExtension create() {
|
||||
final SurroundApplicatorExtension extension = new SurroundApplicatorExtension();
|
||||
// MixinExtras 0.5.0 internal (pinned by UniMixins 0.3.1); recheck on bump
|
||||
MixinInternals.registerExtension(extension, false);
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
static SurroundApplicatorExtension instance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
void declare(ITargetClassContext context, String mixin) {
|
||||
this.pending(context).declarers.add(mixin);
|
||||
}
|
||||
|
||||
void offer(ITargetClassContext context, CallSiteSpec spec) {
|
||||
final Pending pending = this.pending(context);
|
||||
(spec.node() == null ? pending.wholes : pending.calls).add(spec);
|
||||
}
|
||||
|
||||
private Pending pending(ITargetClassContext context) {
|
||||
return this.pending.computeIfAbsent(context, key -> new Pending(new ArrayList<>(), new ArrayList<>(), new HashSet<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkActive(MixinEnvironment environment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(ITargetClassContext context) {
|
||||
// MixinExtras 0.5.0 internal (pinned by UniMixins 0.3.1); recheck on bump
|
||||
final List<IExtension> active = MixinInternals.getExtensions().getActiveExtensions();
|
||||
boolean seen = false;
|
||||
for (int i = 0, n = active.size(); i < n; i++) {
|
||||
final IExtension extension = active.get(i);
|
||||
if (extension == this) {
|
||||
seen = true;
|
||||
} else if (seen && extension.getClass().getName().startsWith(MIXINEXTRAS_PACKAGE)) {
|
||||
MixinInternals.unregisterExtension(this);
|
||||
MixinInternals.registerExtension(this, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(ITargetClassContext context) {
|
||||
final ClassNode targetClass = context.getClassNode();
|
||||
final Pending pending = this.pending.remove(context);
|
||||
checkHandlersRenamed(targetClass, pending == null ? Set.of() : pending.declarers);
|
||||
if (pending == null) {
|
||||
return;
|
||||
}
|
||||
final List<CallSiteSpec> calls = pending.calls;
|
||||
for (int i = 0, n = calls.size(); i < n; i++) {
|
||||
SurroundCallSite.apply(targetClass, calls.get(i));
|
||||
}
|
||||
final List<CallSiteSpec> wholes = pending.wholes;
|
||||
for (int i = 0, n = wholes.size(); i < n; i++) {
|
||||
SurroundCallSite.apply(targetClass, wholes.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(MixinEnvironment env, String name, boolean force, ClassNode classNode) {
|
||||
}
|
||||
|
||||
static void checkHandlersRenamed(ClassNode classNode, Set<String> declarers) {
|
||||
for (MethodNode method : classNode.methods) {
|
||||
if (method.visibleAnnotations == null) {
|
||||
continue;
|
||||
}
|
||||
for (AnnotationNode annotation : method.visibleAnnotations) {
|
||||
final String desc = annotation.desc;
|
||||
if (SurroundSpec.CATCH.equals(desc) || SurroundSpec.FINALLY.equals(desc) || SurroundSpec.SKIPPED.equals(desc) || SurroundSpec.RETURN.equals(desc)) {
|
||||
final String mixin = SurroundSpec.mergedBy(method);
|
||||
if (mixin != null && !declarers.contains(mixin)) {
|
||||
throw new IllegalStateException("@Surround " + SurroundBinding.exitRef(method) + " " + SurroundInjector.noSurroundForId(Annotations.<String>getValue(annotation, "value", ""), Set.of()));
|
||||
}
|
||||
} else if (SurroundSpec.SURROUND.equals(desc) && !HANDLER_NAME.matcher(method.name).matches()) {
|
||||
throw new IllegalStateException(method.name + method.desc + " in " + classNode.name + " (" + SurroundSpec.describeOwner(method) + ") carries @Surround but was never parsed as an injector: the InjectionInfo registry lacked @Surround when mixins were applied to this target; SurroundBootstrap.init() must run before any class a @Surround targets is transformed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.TypePath;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.AnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.FrameNode;
|
||||
import org.spongepowered.asm.lib.tree.IincInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.LabelNode;
|
||||
import org.spongepowered.asm.lib.tree.LocalVariableAnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.LocalVariableNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.lib.tree.VarInsnNode;
|
||||
import org.spongepowered.asm.util.Annotations;
|
||||
import org.spongepowered.asm.util.Bytecode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
final class SurroundBinding {
|
||||
|
||||
private static final int TOP = -1;
|
||||
private static final String[] SUGARS = {"Local", "Share", "Cancellable"};
|
||||
static final String SUGAR_HINT = ", which a @Surround handler cannot resolve; use @Surround.Carry, or @Surround.Local on a call";
|
||||
|
||||
enum Role {
|
||||
ENTRY(SurroundSpec.SURROUND), CATCH(SurroundSpec.CATCH), FINALLY(SurroundSpec.FINALLY), SKIPPED(SurroundSpec.SKIPPED), RETURN(SurroundSpec.RETURN);
|
||||
|
||||
static final Role[] EXITS = {CATCH, FINALLY, SKIPPED, RETURN};
|
||||
|
||||
final String annotation;
|
||||
|
||||
Role(String annotation) {
|
||||
this.annotation = annotation;
|
||||
}
|
||||
}
|
||||
|
||||
record Load(int slot, Type type) {}
|
||||
|
||||
static final class Handler {
|
||||
|
||||
final MethodNode method;
|
||||
final Role role;
|
||||
final Type[] params;
|
||||
final boolean isStatic;
|
||||
final boolean handles;
|
||||
final int leading;
|
||||
final int localStart;
|
||||
final int carryStart;
|
||||
private final String[] carryIds;
|
||||
int index;
|
||||
Load[] loads;
|
||||
|
||||
Handler(MethodNode method, Role role, int leading) {
|
||||
this.method = method;
|
||||
this.role = role;
|
||||
this.params = Type.getArgumentTypes(method.desc);
|
||||
this.isStatic = Bytecode.isStatic(method);
|
||||
final AnnotationNode caught = role == Role.CATCH ? Annotations.get(method.visibleAnnotations, SurroundSpec.CATCH) : null;
|
||||
this.handles = caught != null && Annotations.<Boolean>getValue(caught, "handle", Boolean.FALSE).booleanValue();
|
||||
this.leading = leading;
|
||||
final int count = this.params.length;
|
||||
int firstLocal = count;
|
||||
int firstCarry = count;
|
||||
int section = 0;
|
||||
String[] ids = null;
|
||||
for (int i = 0; i < count; i++) {
|
||||
final AnnotationNode carry = carry(method, i);
|
||||
final boolean local = local(method, i) != null;
|
||||
if (carry != null && role == Role.ENTRY) {
|
||||
throw this.reject("has @Surround.Carry on " + paramRef(method, i) + "; only the entry handler's own locals can be carried");
|
||||
}
|
||||
if (local && carry != null) {
|
||||
throw this.reject(paramRef(method, i) + " cannot be both @Surround.Local and @Surround.Carry");
|
||||
}
|
||||
if ((local || carry != null) && i < leading) {
|
||||
throw this.reject("cannot mark its " + (role == Role.CATCH ? "caught Throwable" : "result") + " parameter @Surround." + (local ? "Local" : "Carry"));
|
||||
}
|
||||
final int next = carry != null ? 2 : local ? 1 : 0;
|
||||
if (next < section) {
|
||||
throw this.reject(paramRef(method, i) + " is out of order; declare operands first, then @Surround.Local, then @Surround.Carry parameters");
|
||||
}
|
||||
section = next;
|
||||
if (local && firstLocal == count) {
|
||||
firstLocal = i;
|
||||
}
|
||||
if (carry != null) {
|
||||
if (ids == null) {
|
||||
firstCarry = i;
|
||||
ids = new String[count - i];
|
||||
}
|
||||
ids[i - firstCarry] = Annotations.<String>getValue(carry, "value", "");
|
||||
}
|
||||
}
|
||||
this.localStart = Math.min(firstLocal, firstCarry);
|
||||
this.carryStart = firstCarry;
|
||||
this.carryIds = ids;
|
||||
}
|
||||
|
||||
Type caught() {
|
||||
return this.params[0];
|
||||
}
|
||||
|
||||
String ref() {
|
||||
return this.role == Role.ENTRY ? "entry handler" : exitRef(this.method);
|
||||
}
|
||||
|
||||
SurroundRejection reject(String message) {
|
||||
return new SurroundRejection(this.ref() + " " + message);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Exits {
|
||||
|
||||
final Handler[] handlers;
|
||||
final int catches;
|
||||
final Handler finallyHandler;
|
||||
final Handler skipped;
|
||||
final Handler returnHandler;
|
||||
final boolean handles;
|
||||
final int skipSlot;
|
||||
final Type[] carried;
|
||||
final int argsSize;
|
||||
|
||||
Exits(Handler[] handlers, int skipSlot, Type[] carried) {
|
||||
this.handlers = handlers;
|
||||
int catches = 0;
|
||||
Handler finallyHandler = null;
|
||||
Handler skipped = null;
|
||||
Handler returnHandler = null;
|
||||
boolean handles = false;
|
||||
int argsSize = 0;
|
||||
for (int i = 0; i < handlers.length; i++) {
|
||||
final Handler handler = handlers[i];
|
||||
handler.index = i;
|
||||
handles |= handler.handles;
|
||||
argsSize = Math.max(argsSize, (handler.isStatic ? 0 : 1) + Bytecode.getArgsSize(handler.params));
|
||||
switch (handler.role) {
|
||||
case CATCH -> catches++;
|
||||
case FINALLY -> finallyHandler = handler;
|
||||
case SKIPPED -> skipped = handler;
|
||||
case RETURN -> returnHandler = handler;
|
||||
}
|
||||
}
|
||||
this.catches = catches;
|
||||
this.finallyHandler = finallyHandler;
|
||||
this.skipped = skipped;
|
||||
this.returnHandler = returnHandler;
|
||||
this.handles = handles;
|
||||
this.skipSlot = skipSlot;
|
||||
this.carried = carried;
|
||||
this.argsSize = argsSize;
|
||||
}
|
||||
}
|
||||
|
||||
private SurroundBinding() {
|
||||
}
|
||||
|
||||
static Type[] bind(MethodNode entry, int[] kinds, Handler[] handlers) {
|
||||
checkNoParameterStores(entry);
|
||||
final List<Carried> carried = carriedLocals(entry, kinds);
|
||||
final Type[] slots = new Type[entry.maxLocals];
|
||||
for (Handler exit : handlers) {
|
||||
final Load[] loads = new Load[exit.params.length - exit.carryStart];
|
||||
for (int i = exit.carryStart; i < exit.params.length; i++) {
|
||||
loads[i - exit.carryStart] = resolve(carried, slots, exit, i);
|
||||
}
|
||||
exit.loads = loads;
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
static int skipSlot(MethodNode entry, int[] kinds) {
|
||||
final List<LocalVariableAnnotationNode> annotations = entry.visibleLocalVariableAnnotations;
|
||||
int slot = -1;
|
||||
for (int a = 0, n = annotations == null ? 0 : annotations.size(); a < n; a++) {
|
||||
final LocalVariableAnnotationNode annotation = annotations.get(a);
|
||||
if (!SurroundSpec.SKIP.equals(annotation.desc)) {
|
||||
continue;
|
||||
}
|
||||
if (slot != -1) {
|
||||
throw new SurroundRejection("declares two @Surround.Skip locals; there can only be one");
|
||||
}
|
||||
slot = annotatedSlot(entry, annotation, 0);
|
||||
checkLocalAnnotation(entry, kinds, "@Surround.Skip", annotation, slot, "");
|
||||
if (kinds[slot] != Type.INT) {
|
||||
throw new SurroundRejection("has a @Surround.Skip local that is " + withArticle(kinds[slot]) + "; it must be boolean or int-like");
|
||||
}
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
private static AnnotationNode carry(MethodNode method, int index) {
|
||||
final List<AnnotationNode> invisible = parameter(method.invisibleParameterAnnotations, index);
|
||||
for (int i = 0, n = invisible == null ? 0 : invisible.size(); i < n; i++) {
|
||||
final String name = Type.getType(invisible.get(i).desc).getClassName();
|
||||
for (String sugar : SUGARS) {
|
||||
if (name.endsWith("mixinextras.sugar." + sugar)) {
|
||||
throw new SurroundRejection(paramRef(method, index) + " of '" + method.name + "' is annotated @" + sugar + SUGAR_HINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Annotations.get(parameter(method.visibleParameterAnnotations, index), SurroundSpec.CARRY);
|
||||
}
|
||||
|
||||
static AnnotationNode local(MethodNode method, int index) {
|
||||
return Annotations.get(parameter(method.visibleParameterAnnotations, index), SurroundSpec.LOCAL);
|
||||
}
|
||||
|
||||
private static List<AnnotationNode> parameter(List<AnnotationNode>[] table, int index) {
|
||||
return table == null || index >= table.length ? null : table[index];
|
||||
}
|
||||
|
||||
static String paramRef(MethodNode method, int index) {
|
||||
final String name = paramName(method, index);
|
||||
return name == null || name.isEmpty() ? "parameter " + index : "parameter " + index + " ('" + name + "')";
|
||||
}
|
||||
|
||||
static String paramName(MethodNode method, int index) {
|
||||
if (method.localVariables == null) {
|
||||
return null;
|
||||
}
|
||||
final int slot = (Bytecode.isStatic(method) ? 0 : 1) + Bytecode.getArgsSize(Type.getArgumentTypes(method.desc), 0, index);
|
||||
LocalVariableNode earliest = null;
|
||||
int earliestPos = Integer.MAX_VALUE;
|
||||
for (LocalVariableNode local : method.localVariables) {
|
||||
if (local.index != slot) {
|
||||
continue;
|
||||
}
|
||||
final int pos = method.instructions.indexOf(local.start);
|
||||
if (pos < earliestPos) {
|
||||
earliestPos = pos;
|
||||
earliest = local;
|
||||
}
|
||||
}
|
||||
return earliest == null ? null : earliest.name;
|
||||
}
|
||||
|
||||
static AbstractInsnNode soleReturn(MethodNode entry) {
|
||||
AbstractInsnNode sole = null;
|
||||
AbstractInsnNode last = null;
|
||||
int returns = 0;
|
||||
for (AbstractInsnNode insn = entry.instructions.getFirst(); insn != null; insn = insn.getNext()) {
|
||||
if (insn.getOpcode() < 0) {
|
||||
continue;
|
||||
}
|
||||
last = insn;
|
||||
if (insn.getOpcode() == Opcodes.RETURN) {
|
||||
sole = insn;
|
||||
returns++;
|
||||
}
|
||||
}
|
||||
if (returns != 1 || sole != last) {
|
||||
throw new SurroundRejection("must reach its end to hand over to the body; restructure it so every path falls through to its end, with no early return, throw or endless loop");
|
||||
}
|
||||
return sole;
|
||||
}
|
||||
|
||||
private static Load resolve(List<Carried> carried, Type[] slots, Handler exit, int index) {
|
||||
final Type type = exit.params[index];
|
||||
final String id = exit.carryIds[index - exit.carryStart];
|
||||
final int kind = kindOf(type);
|
||||
final boolean named = !id.isEmpty();
|
||||
boolean idDeclared = false;
|
||||
int matches = 0;
|
||||
Carried match = null;
|
||||
for (int i = 0, n = carried.size(); i < n; i++) {
|
||||
final Carried candidate = carried.get(i);
|
||||
if (named && !id.equals(candidate.id())) {
|
||||
continue;
|
||||
}
|
||||
idDeclared = true;
|
||||
if (candidate.kind() == kind) {
|
||||
matches++;
|
||||
match = candidate;
|
||||
}
|
||||
}
|
||||
final String param = paramRef(exit.method, index);
|
||||
if (named && !idDeclared) {
|
||||
throw exit.reject(param + " asks for the carried local '" + id + "', which the entry handler does not declare" + declaredList(carried));
|
||||
}
|
||||
if (matches == 0) {
|
||||
throw exit.reject(param + " is " + type.getClassName() + " (" + withArticle(kind) + "), which matches no carried local of that kind" + declaredList(carried));
|
||||
}
|
||||
if (matches > 1) {
|
||||
throw exit.reject(param + " is " + type.getClassName() + " (" + withArticle(kind) + "), which matches " + matches + " carried locals of that kind; name them with @Surround.Carry(\"...\") on both sides");
|
||||
}
|
||||
final Type existing = slots[match.slot()];
|
||||
if (existing != null && !existing.equals(type)) {
|
||||
throw exit.reject(param + " reads carried local " + match + " as " + type.getClassName() + ", but another exit handler reads it as " + existing.getClassName());
|
||||
}
|
||||
slots[match.slot()] = type;
|
||||
return new Load(match.slot(), type);
|
||||
}
|
||||
|
||||
private static List<Carried> carriedLocals(MethodNode entry, int[] kinds) {
|
||||
final List<Carried> carried = new ArrayList<>();
|
||||
final List<LocalVariableAnnotationNode> annotations = entry.visibleLocalVariableAnnotations;
|
||||
for (int a = 0, n = annotations == null ? 0 : annotations.size(); a < n; a++) {
|
||||
final LocalVariableAnnotationNode annotation = annotations.get(a);
|
||||
if (!SurroundSpec.CARRY.equals(annotation.desc)) {
|
||||
continue;
|
||||
}
|
||||
final int slot = annotatedSlot(entry, annotation, 0);
|
||||
final String id = Annotations.<String>getValue(annotation, "value", "");
|
||||
checkLocalAnnotation(entry, kinds, "@Surround.Carry", annotation, slot, id);
|
||||
if (!id.isEmpty()) {
|
||||
for (int i = 0, count = carried.size(); i < count; i++) {
|
||||
if (id.equals(carried.get(i).id())) {
|
||||
throw new SurroundRejection("declares two @Surround.Carry locals named '" + id + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
carried.add(new Carried(slot, kinds[slot], id));
|
||||
}
|
||||
return carried;
|
||||
}
|
||||
|
||||
// FML's deobf remapper (ASM LocalVariablesSorter) corrupts annotation indices of long/double locals; the LVT stays right.
|
||||
private static int annotatedSlot(MethodNode entry, LocalVariableAnnotationNode annotation, int range) {
|
||||
final int raw = annotation.index.get(range).intValue();
|
||||
final LabelNode start = annotation.start.get(range);
|
||||
final LabelNode end = annotation.end.get(range);
|
||||
boolean any = false;
|
||||
boolean rawWide = false;
|
||||
boolean rawFound = false;
|
||||
int wide = -1;
|
||||
int wideCount = 0;
|
||||
for (int i = 0, n = entry.localVariables == null ? 0 : entry.localVariables.size(); i < n; i++) {
|
||||
final LocalVariableNode local = entry.localVariables.get(i);
|
||||
if (local.start != start || local.end != end) {
|
||||
continue;
|
||||
}
|
||||
any = true;
|
||||
final boolean isWide = Type.getType(local.desc).getSize() == 2;
|
||||
if (isWide) {
|
||||
wide = local.index;
|
||||
wideCount++;
|
||||
}
|
||||
if (local.index == raw) {
|
||||
rawFound = true;
|
||||
rawWide = isWide;
|
||||
}
|
||||
}
|
||||
final int slot = !any || rawFound && !(rawWide && wideCount > 1) ? raw : wideCount == 1 && !rawFound ? wide : -1;
|
||||
if (slot < 0) {
|
||||
throw new SurroundRejection("has a long or double annotated local that shares its scope with another; initialize it at its declaration");
|
||||
}
|
||||
if (slot >= entry.maxLocals) {
|
||||
throw new SurroundRejection("has an annotated local outside its method's locals (slot " + slot + " of " + entry.maxLocals + "); compile with local variable tables");
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
private static void checkLocalAnnotation(MethodNode entry, int[] kinds, String what, LocalVariableAnnotationNode annotation, int slot, String id) {
|
||||
final String local = id.isEmpty() ? "in slot " + slot : "named '" + id + "'";
|
||||
final TypePath path = annotation.typePath;
|
||||
for (int i = 0, length = path == null ? 0 : path.getLength(); i < length; i++) {
|
||||
if (path.getStep(i) != TypePath.ARRAY_ELEMENT) {
|
||||
throw new SurroundRejection("has a " + what + " " + local + " inside the local's type (" + path + "); " + what + " must annotate the local itself, not part of its type");
|
||||
}
|
||||
}
|
||||
for (int i = 1, count = annotation.index.size(); i < count; i++) {
|
||||
if (annotatedSlot(entry, annotation, i) != slot) {
|
||||
throw new SurroundRejection("has a " + what + " local " + local + " recorded in several slots " + annotation.index + "; initialize it at its declaration");
|
||||
}
|
||||
}
|
||||
LabelNode end = annotation.end.get(0);
|
||||
for (int i = 1, n = annotation.end.size(); i < n; i++) {
|
||||
if (entry.instructions.indexOf(annotation.end.get(i)) > entry.instructions.indexOf(end)) {
|
||||
end = annotation.end.get(i);
|
||||
}
|
||||
}
|
||||
for (AbstractInsnNode insn = end.getNext(); insn != null; insn = insn.getNext()) {
|
||||
if (insn.getOpcode() >= 0 && insn.getOpcode() != Opcodes.RETURN) {
|
||||
throw new SurroundRejection("has a " + what + " local " + local + " that goes out of scope before the entry handler ends; declare it in the entry handler's outermost block");
|
||||
}
|
||||
}
|
||||
if (kinds[slot] == TOP) {
|
||||
throw new SurroundRejection("has a " + what + " local " + local + " that is not definitely assigned when the entry handler ends; initialize it at its declaration");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkNoParameterStores(MethodNode entry) {
|
||||
final int limit = Bytecode.getFirstNonArgLocalIndex(entry);
|
||||
for (AbstractInsnNode insn = entry.instructions.getFirst(); insn != null; insn = insn.getNext()) {
|
||||
final int var;
|
||||
if (insn instanceof VarInsnNode varInsn && storeKind(insn.getOpcode()) != -1) {
|
||||
var = varInsn.var;
|
||||
} else if (insn instanceof IincInsnNode iinc) {
|
||||
var = iinc.var;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (var < limit) {
|
||||
throw new SurroundRejection("must not assign its parameters; use @ModifyVariable or @WrapMethod to change arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int[] kindsAtEnd(MethodNode entry, AbstractInsnNode soleReturn) {
|
||||
final int maxLocals = entry.maxLocals;
|
||||
int[] vars = initialVars(entry);
|
||||
int count = vars.length;
|
||||
int[] state = toSlots(vars, count, maxLocals);
|
||||
for (AbstractInsnNode insn = entry.instructions.getFirst(); insn != soleReturn; insn = insn.getNext()) {
|
||||
if (insn instanceof FrameNode frame) {
|
||||
final int locals = frame.local == null ? 0 : frame.local.size();
|
||||
switch (frame.type) {
|
||||
case Opcodes.F_NEW, Opcodes.F_FULL, Opcodes.F_APPEND -> {
|
||||
if (frame.type != Opcodes.F_APPEND) {
|
||||
count = 0;
|
||||
}
|
||||
if (count + locals > vars.length) {
|
||||
vars = Arrays.copyOf(vars, count + locals);
|
||||
}
|
||||
for (int i = 0; i < locals; i++) {
|
||||
vars[count++] = frameKind(frame.local.get(i));
|
||||
}
|
||||
}
|
||||
case Opcodes.F_CHOP -> count -= locals;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
state = toSlots(vars, count, maxLocals);
|
||||
} else if (insn instanceof VarInsnNode var && storeKind(insn.getOpcode()) != -1) {
|
||||
store(state, var.var, storeKind(insn.getOpcode()));
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private static void store(int[] state, int var, int kind) {
|
||||
if (var > 0 && wide(state[var - 1])) {
|
||||
state[var - 1] = TOP;
|
||||
}
|
||||
state[var] = kind;
|
||||
if (wide(kind) && var + 1 < state.length) {
|
||||
state[var + 1] = TOP;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean wide(int kind) {
|
||||
return kind == Type.LONG || kind == Type.DOUBLE;
|
||||
}
|
||||
|
||||
private static int[] initialVars(MethodNode entry) {
|
||||
final Type[] args = Type.getArgumentTypes(entry.desc);
|
||||
final int receiver = Bytecode.isStatic(entry) ? 0 : 1;
|
||||
final int[] vars = new int[receiver + args.length];
|
||||
if (receiver != 0) {
|
||||
vars[0] = Type.OBJECT;
|
||||
}
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
vars[receiver + i] = kindOf(args[i]);
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
private static int frameKind(Object entry) {
|
||||
if (entry instanceof String || Opcodes.NULL.equals(entry) || Opcodes.UNINITIALIZED_THIS.equals(entry)) {
|
||||
return Type.OBJECT;
|
||||
} else if (Opcodes.INTEGER.equals(entry)) {
|
||||
return Type.INT;
|
||||
} else if (Opcodes.FLOAT.equals(entry)) {
|
||||
return Type.FLOAT;
|
||||
} else if (Opcodes.LONG.equals(entry)) {
|
||||
return Type.LONG;
|
||||
} else if (Opcodes.DOUBLE.equals(entry)) {
|
||||
return Type.DOUBLE;
|
||||
}
|
||||
return TOP;
|
||||
}
|
||||
|
||||
private static int[] toSlots(int[] vars, int count, int maxLocals) {
|
||||
final int[] slots = new int[maxLocals];
|
||||
Arrays.fill(slots, TOP);
|
||||
int slot = 0;
|
||||
for (int i = 0; i < count && slot < maxLocals; i++) {
|
||||
slots[slot] = vars[i];
|
||||
slot += wide(vars[i]) ? 2 : 1;
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static int storeKind(int opcode) {
|
||||
return switch (opcode) {
|
||||
case Opcodes.ISTORE -> Type.INT;
|
||||
case Opcodes.LSTORE -> Type.LONG;
|
||||
case Opcodes.FSTORE -> Type.FLOAT;
|
||||
case Opcodes.DSTORE -> Type.DOUBLE;
|
||||
case Opcodes.ASTORE -> Type.OBJECT;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int kindOf(Type type) {
|
||||
return switch (type.getSort()) {
|
||||
case Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT, Type.INT -> Type.INT;
|
||||
case Type.LONG -> Type.LONG;
|
||||
case Type.FLOAT -> Type.FLOAT;
|
||||
case Type.DOUBLE -> Type.DOUBLE;
|
||||
default -> Type.OBJECT;
|
||||
};
|
||||
}
|
||||
|
||||
private static String kindDescription(int kind) {
|
||||
return switch (kind) {
|
||||
case Type.INT -> "int-like value";
|
||||
case Type.LONG -> "long";
|
||||
case Type.FLOAT -> "float";
|
||||
case Type.DOUBLE -> "double";
|
||||
default -> "reference";
|
||||
};
|
||||
}
|
||||
|
||||
private static String withArticle(int kind) {
|
||||
return (kind == Type.INT ? "an " : "a ") + kindDescription(kind);
|
||||
}
|
||||
|
||||
private static String declaredList(List<Carried> carried) {
|
||||
final StringJoiner all = new StringJoiner(", ", "; the entry handler carries ", "").setEmptyValue("; the entry handler carries nothing");
|
||||
for (Carried local : carried) {
|
||||
all.add(local.toString());
|
||||
}
|
||||
return all.toString();
|
||||
}
|
||||
|
||||
static String exitRef(MethodNode exit) {
|
||||
return "exit handler '" + exit.name + "'";
|
||||
}
|
||||
|
||||
private record Carried(int slot, int kind, String id) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.id.isEmpty() ? "an unnamed " + kindDescription(this.kind) : "'" + this.id + "', " + withArticle(this.kind)) + " in slot " + this.slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.launch.GlobalProperties;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
|
||||
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
|
||||
|
||||
public final class SurroundBootstrap {
|
||||
|
||||
private SurroundBootstrap() {
|
||||
}
|
||||
|
||||
private static final class Registration {
|
||||
|
||||
static {
|
||||
InjectionInfo.register(SurroundInjectionInfo.class);
|
||||
}
|
||||
|
||||
static void ensure() {
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
Registration.ensure();
|
||||
if (GlobalProperties.get(GlobalProperties.Keys.INIT) != null && MixinEnvironment.getDefaultEnvironment().getActiveTransformer() instanceof IMixinTransformer) {
|
||||
SurroundApplicatorExtension.instance();
|
||||
}
|
||||
}
|
||||
}
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Exits;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Handler;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Load;
|
||||
import com.llamalad7.mixinextras.utils.ASMUtils;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.FrameNode;
|
||||
import org.spongepowered.asm.lib.tree.IincInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.InsnList;
|
||||
import org.spongepowered.asm.lib.tree.InsnNode;
|
||||
import org.spongepowered.asm.lib.tree.JumpInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.LabelNode;
|
||||
import org.spongepowered.asm.lib.tree.LocalVariableNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.lib.tree.TryCatchBlockNode;
|
||||
import org.spongepowered.asm.lib.tree.TypeInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.VarInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.analysis.Analyzer;
|
||||
import org.spongepowered.asm.lib.tree.analysis.AnalyzerException;
|
||||
import org.spongepowered.asm.lib.tree.analysis.BasicInterpreter;
|
||||
import org.spongepowered.asm.lib.tree.analysis.BasicValue;
|
||||
import org.spongepowered.asm.lib.tree.analysis.Frame;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
|
||||
import org.spongepowered.asm.service.MixinService;
|
||||
import org.spongepowered.asm.util.Bytecode;
|
||||
import org.spongepowered.asm.util.Bytecode.Visibility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
final class SurroundCallSite {
|
||||
|
||||
private static final String REGION = "surround:region";
|
||||
private static final Type[] NOTHING = new Type[0];
|
||||
|
||||
record Region(LabelNode start, LabelNode end) {}
|
||||
|
||||
private SurroundCallSite() {
|
||||
}
|
||||
|
||||
static Type[] operands(MethodInsnNode call, String targetClass) {
|
||||
final Type[] args = Type.getArgumentTypes(call.desc);
|
||||
if (call.getOpcode() == Opcodes.INVOKESTATIC) {
|
||||
return args;
|
||||
}
|
||||
final Type[] all = new Type[args.length + 1];
|
||||
all[0] = Type.getObjectType(call.getOpcode() == Opcodes.INVOKESPECIAL ? targetClass : call.owner);
|
||||
System.arraycopy(args, 0, all, 1, args.length);
|
||||
return all;
|
||||
}
|
||||
|
||||
static void apply(ClassNode classNode, CallSiteSpec spec) {
|
||||
final InjectionNode node = spec.node();
|
||||
if (node == null) {
|
||||
final MethodNode target = spec.target();
|
||||
final MethodNode body = relocate(classNode, target);
|
||||
final MethodInsnNode call = (MethodInsnNode) ASMUtils.getInvokeInstruction(classNode, body);
|
||||
target.instructions.add(call);
|
||||
target.instructions.add(new InsnNode(spec.result().getOpcode(Opcodes.IRETURN)));
|
||||
final Type[] args = operands(call, classNode.name);
|
||||
final int[] argSlots = new int[args.length];
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
argSlots[i] = argSlots[i - 1] + args[i - 1].getSize();
|
||||
}
|
||||
emit(classNode, spec, target, call, null, Bytecode.isStatic(target) ? 0 : 1, argSlots);
|
||||
return;
|
||||
}
|
||||
final AbstractInsnNode champion = node.getCurrentTarget();
|
||||
final Region inner = node.<Region>getDecoration(REGION);
|
||||
final MethodNode owner = champion == null ? null : owner(classNode, inner == null ? champion : inner.start());
|
||||
if (owner == null) {
|
||||
MixinService.getService().getLogger("mixin").warn(spec.description() + " skipped: the surrounded call was removed by another injector");
|
||||
return;
|
||||
}
|
||||
if (!spec.entryDesc().equals(spec.entry().desc)) {
|
||||
throw cannotFollow(spec, "the entry handler's descriptor changed from " + spec.entryDesc() + " to " + spec.entry().desc + " after injection", null);
|
||||
}
|
||||
if (!(champion instanceof MethodInsnNode call)) {
|
||||
throw cannotFollow(spec, "the call was replaced by " + Bytecode.describeNode(champion, false) + ", which is not a method call", null);
|
||||
}
|
||||
final int offset = node.isReplaced() && call.getOpcode() != Opcodes.INVOKESTATIC ? 1 : 0;
|
||||
checkOperands(spec, call, operands(call, classNode.name), offset);
|
||||
final Exits exits = spec.exits();
|
||||
final Type result = spec.result();
|
||||
final Type actual = Type.getReturnType(call.desc);
|
||||
if ((exits.handles || exits.skipSlot >= 0 || exits.returnHandler != null) && (actual.getSize() != result.getSize() || actual.getSize() > 0 && SurroundBinding.kindOf(actual) != SurroundBinding.kindOf(result))) {
|
||||
throw cannotFollow(spec, "the call was replaced by " + Bytecode.describeNode(call, false) + ", which returns " + actual.getClassName() + " where the surround supplies or replaces a " + result.getClassName(), null);
|
||||
}
|
||||
emit(classNode, spec, owner, call, inner, offset, null);
|
||||
}
|
||||
|
||||
private static void emit(ClassNode classNode, CallSiteSpec spec, MethodNode owner, MethodInsnNode call, Region inner, int offset, int[] argSlots) {
|
||||
final boolean slots = argSlots != null;
|
||||
final Type[] args = operands(call, classNode.name);
|
||||
final Exits exits = spec.exits();
|
||||
final boolean skips = exits.skipSlot >= 0;
|
||||
final Handler returnHandler = exits.returnHandler;
|
||||
final Type result = spec.result();
|
||||
final Type actual = Type.getReturnType(call.desc);
|
||||
|
||||
final MethodNode entry = spec.entry();
|
||||
final Type[] operands = spec.operands();
|
||||
final int argBase = Bytecode.isStatic(spec.target()) ? 0 : 1;
|
||||
final Type[] entryParams = Type.getArgumentTypes(entry.desc);
|
||||
final int entryOperands = entryParams.length - spec.entryLocals().length;
|
||||
final int firstNonArg = argBase + Bytecode.getArgsSize(entryParams);
|
||||
final MethodNode inlined = inlineable(entry);
|
||||
|
||||
final boolean restoresBelow = exits.handles || !inlined.tryCatchBlocks.isEmpty();
|
||||
final boolean relocated = owner != spec.target();
|
||||
owner.maxLocals = Math.max(Math.max(owner.maxLocals, spec.target().maxLocals), usedLocals(owner));
|
||||
int next = owner.maxLocals;
|
||||
final AbstractInsnNode anchor = inner == null ? call : inner.start();
|
||||
final Frame<BasicValue> frame = !slots && (restoresBelow || relocated && hasLocals(spec)) ? frameAt(owner, anchor, spec) : null;
|
||||
final int shift = relocated ? Bytecode.getFirstNonArgLocalIndex(owner) - Bytecode.getFirstNonArgLocalIndex(spec.target()) : 0;
|
||||
|
||||
final Type[] below = restoresBelow ? below(spec, frame, args.length) : NOTHING;
|
||||
final int spillFrom = slots || restoresBelow || skips ? 0 : offset;
|
||||
final boolean spill = slots || restoresBelow || skips || args.length > offset && (entryOperands > 0 || prefixed(spec));
|
||||
final int[] spillSlots = slots ? argSlots : new int[args.length];
|
||||
if (spill && !slots) {
|
||||
for (int i = spillFrom; i < args.length; i++) {
|
||||
spillSlots[i] = next;
|
||||
next += args[i].getSize();
|
||||
}
|
||||
}
|
||||
final int[] belowSlots = new int[below.length];
|
||||
for (int i = 0; i < below.length; i++) {
|
||||
belowSlots[i] = next;
|
||||
next += below[i].getSize();
|
||||
}
|
||||
final int entryBase = next;
|
||||
final int delta = entryBase - firstNonArg;
|
||||
final int excSlot = entryBase + Math.max(0, inlined.maxLocals - firstNonArg);
|
||||
final int resSlot = excSlot + 1;
|
||||
final boolean storesResult = returnHandler != null && result.getSize() > 0;
|
||||
|
||||
final int[] entryLocals = localSlots(spec, owner, anchor, frame, shift, spec.entryLocals(), entryParams, entryOperands);
|
||||
remapLocals(inlined, argBase, firstNonArg, entryParams, entryOperands, spillSlots, offset, entryLocals, delta);
|
||||
final Handler[] handlers = exits.handlers;
|
||||
final int[][] locals = new int[handlers.length][];
|
||||
for (Handler exit : handlers) {
|
||||
locals[exit.index] = localSlots(spec, owner, anchor, frame, shift, spec.locals()[exit.index], exit.params, exit.localStart);
|
||||
}
|
||||
final Emitter emitter = new Emitter(classNode, exits, operands, Arrays.copyOfRange(spillSlots, offset, offset + operands.length), locals, delta);
|
||||
|
||||
final LabelNode sStart = new LabelNode();
|
||||
final LabelNode tStart = inner == null || skips ? new LabelNode() : inner.start();
|
||||
final LabelNode tEnd = inner == null ? new LabelNode() : inner.end();
|
||||
final LabelNode sEnd = new LabelNode();
|
||||
final LabelNode join = skips ? new LabelNode() : null;
|
||||
|
||||
final InsnList head = new InsnList();
|
||||
head.add(sStart);
|
||||
if (spill && !slots) {
|
||||
for (int i = args.length - 1; i >= spillFrom; i--) {
|
||||
final int j = i - offset;
|
||||
if (j >= 0 && j < operands.length && operands[j].getSort() >= Type.ARRAY && !args[i].equals(operands[j])) {
|
||||
head.add(new TypeInsnNode(Opcodes.CHECKCAST, operands[j].getInternalName()));
|
||||
}
|
||||
head.add(new VarInsnNode(args[i].getOpcode(Opcodes.ISTORE), spillSlots[i]));
|
||||
}
|
||||
for (int i = below.length - 1; i >= 0; i--) {
|
||||
head.add(new VarInsnNode(below[i].getOpcode(Opcodes.ISTORE), belowSlots[i]));
|
||||
}
|
||||
}
|
||||
head.add(inlined.instructions);
|
||||
if (skips) {
|
||||
final LabelNode callPath = new LabelNode();
|
||||
head.add(new VarInsnNode(Opcodes.ILOAD, exits.skipSlot + delta));
|
||||
head.add(new JumpInsnNode(Opcodes.IFEQ, callPath));
|
||||
head.add(tStart);
|
||||
loadBelow(head, below, belowSlots);
|
||||
emitter.callSkipped(head);
|
||||
head.add(new JumpInsnNode(Opcodes.GOTO, join));
|
||||
head.add(callPath);
|
||||
}
|
||||
if (spill) {
|
||||
loadBelow(head, below, belowSlots);
|
||||
for (int i = spillFrom; i < args.length; i++) {
|
||||
head.add(new VarInsnNode(args[i].getOpcode(Opcodes.ILOAD), spillSlots[i]));
|
||||
}
|
||||
}
|
||||
if (inner == null && !skips) {
|
||||
head.add(tStart);
|
||||
}
|
||||
owner.instructions.insertBefore(anchor, head);
|
||||
|
||||
final InsnList tail = new InsnList();
|
||||
if (inner == null) {
|
||||
tail.add(tEnd);
|
||||
}
|
||||
final LabelNode retStart = returnHandler == null ? null : new LabelNode();
|
||||
final LabelNode retEnd = returnHandler == null ? null : new LabelNode();
|
||||
if (returnHandler != null) {
|
||||
tail.add(retStart);
|
||||
if (result.getSize() > 0) {
|
||||
if (result.getSort() >= Type.ARRAY && !actual.equals(result)) {
|
||||
tail.add(new TypeInsnNode(Opcodes.CHECKCAST, result.getInternalName()));
|
||||
}
|
||||
tail.add(new VarInsnNode(result.getOpcode(Opcodes.ISTORE), resSlot));
|
||||
}
|
||||
emitter.callReturn(tail, result, resSlot);
|
||||
tail.add(retEnd);
|
||||
}
|
||||
if (skips) {
|
||||
tail.add(join);
|
||||
}
|
||||
emitter.callFinally(tail);
|
||||
tail.add(new JumpInsnNode(Opcodes.GOTO, sEnd));
|
||||
final AbstractInsnNode beforeHandlers = tail.getLast();
|
||||
|
||||
final List<TryCatchBlockNode> blocks = new ArrayList<>(inlined.tryCatchBlocks);
|
||||
emitter.emitHandlers(tail, blocks, tStart, tEnd, retStart, retEnd, excSlot, below, belowSlots, sEnd);
|
||||
final AbstractInsnNode firstHandler = beforeHandlers.getNext();
|
||||
tail.add(sEnd);
|
||||
owner.instructions.insert(inner == null ? call : inner.end(), tail);
|
||||
|
||||
owner.tryCatchBlocks.addAll(insertionIndex(owner, sStart, sEnd), blocks);
|
||||
owner.maxLocals = Math.max(owner.maxLocals, storesResult ? resSlot + result.getSize() : excSlot + 1);
|
||||
owner.maxStack = Math.max(owner.maxStack, spec.target().maxStack) + Math.max(inlined.maxStack, exits.argsSize + 2);
|
||||
if (slots) {
|
||||
final LabelNode end = new LabelNode();
|
||||
owner.instructions.add(end);
|
||||
final List<LocalVariableNode> names = new ArrayList<>();
|
||||
nameParameters(names, classNode, spec, argBase, entryOperands, sStart, end);
|
||||
nameEntryLocals(names, inlined, owner.instructions, exits.carried, firstNonArg, delta, end);
|
||||
if (firstHandler != null) {
|
||||
names.add(new LocalVariableNode("surround$error", SurroundSpec.THROWABLE_DESC, null, (LabelNode) firstHandler, end, excSlot));
|
||||
}
|
||||
if (storesResult) {
|
||||
names.add(new LocalVariableNode("surround$result", result.getDescriptor(), null, retStart, retEnd, resSlot));
|
||||
}
|
||||
owner.localVariables = names;
|
||||
} else {
|
||||
spec.node().decorate(REGION, new Region(sStart, sEnd));
|
||||
}
|
||||
}
|
||||
|
||||
private static void nameParameters(List<LocalVariableNode> names, ClassNode classNode, CallSiteSpec spec, int argBase, int entryOperands, LabelNode start, LabelNode end) {
|
||||
final Type[] params = spec.operands();
|
||||
final MethodNode entry = spec.entry();
|
||||
if (argBase > 0) {
|
||||
names.add(new LocalVariableNode("this", Type.getObjectType(classNode.name).getDescriptor(), null, start, end, 0));
|
||||
}
|
||||
for (int i = 0, slot = argBase; i < params.length; slot += params[i++].getSize()) {
|
||||
final String name = i < entryOperands ? SurroundBinding.paramName(entry, i) : null;
|
||||
names.add(new LocalVariableNode(name != null ? name : "arg" + i, params[i].getDescriptor(), null, start, end, slot));
|
||||
}
|
||||
}
|
||||
|
||||
private static void nameEntryLocals(List<LocalVariableNode> names, MethodNode inlined, InsnList insns, Type[] carried, int firstNonArg, int delta, LabelNode end) {
|
||||
final LocalVariableNode[] furthest = new LocalVariableNode[carried.length];
|
||||
for (LocalVariableNode local : inlined.localVariables) {
|
||||
if (!isCarried(carried, local.index)) {
|
||||
continue;
|
||||
}
|
||||
final LocalVariableNode current = furthest[local.index];
|
||||
if (current == null || insns.indexOf(local.end) > insns.indexOf(current.end)) {
|
||||
furthest[local.index] = local;
|
||||
}
|
||||
}
|
||||
for (LocalVariableNode local : inlined.localVariables) {
|
||||
if (local.index < firstNonArg) {
|
||||
continue;
|
||||
}
|
||||
if (isCarried(carried, local.index) && local == furthest[local.index]) {
|
||||
final Type type = carried[local.index];
|
||||
names.add(new LocalVariableNode(local.name, type.getSort() >= Type.ARRAY ? local.desc : type.getDescriptor(), null, local.start, end, local.index + delta));
|
||||
} else {
|
||||
local.index += delta;
|
||||
names.add(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCarried(Type[] carried, int slot) {
|
||||
return slot < carried.length && carried[slot] != null;
|
||||
}
|
||||
|
||||
private static MethodNode inlineable(MethodNode entry) {
|
||||
final MethodNode copy = new MethodNode(entry.access, entry.name, entry.desc, null, null);
|
||||
entry.accept(copy);
|
||||
for (AbstractInsnNode insn = copy.instructions.getFirst(); insn != null; ) {
|
||||
final AbstractInsnNode next = insn.getNext();
|
||||
if (insn instanceof FrameNode) {
|
||||
copy.instructions.remove(insn);
|
||||
}
|
||||
insn = next;
|
||||
}
|
||||
AbstractInsnNode last = copy.instructions.getLast();
|
||||
while (last.getOpcode() < 0) {
|
||||
last = last.getPrevious();
|
||||
}
|
||||
copy.instructions.remove(last);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static MethodNode relocate(ClassNode classNode, MethodNode target) {
|
||||
final MethodNode body = new MethodNode(target.access & ~Opcodes.ACC_SYNCHRONIZED, unique(classNode, target.name + "$surround"), target.desc, null, null);
|
||||
Bytecode.setVisibility(body, Visibility.PRIVATE);
|
||||
body.instructions = target.instructions;
|
||||
body.instructions.resetLabels();
|
||||
body.tryCatchBlocks = target.tryCatchBlocks;
|
||||
body.localVariables = target.localVariables;
|
||||
body.visibleLocalVariableAnnotations = target.visibleLocalVariableAnnotations;
|
||||
body.invisibleLocalVariableAnnotations = target.invisibleLocalVariableAnnotations;
|
||||
body.maxStack = target.maxStack;
|
||||
body.maxLocals = target.maxLocals;
|
||||
|
||||
target.instructions = new InsnList();
|
||||
target.tryCatchBlocks = new ArrayList<>();
|
||||
target.visibleLocalVariableAnnotations = null;
|
||||
target.invisibleLocalVariableAnnotations = null;
|
||||
target.maxLocals = target.maxStack = Bytecode.getFirstNonArgLocalIndex(target);
|
||||
classNode.methods.add(body);
|
||||
return body;
|
||||
}
|
||||
|
||||
private static String unique(ClassNode classNode, String base) {
|
||||
String name = base;
|
||||
for (int i = 1; ; name = base + "$" + i++) {
|
||||
final String candidate = name;
|
||||
if (classNode.methods.stream().noneMatch(method -> method.name.equals(candidate))) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasLocals(CallSiteSpec spec) {
|
||||
if (spec.entryLocals().length > 0) {
|
||||
return true;
|
||||
}
|
||||
for (int[] locals : spec.locals()) {
|
||||
if (locals.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Frame<BasicValue> frameAt(MethodNode owner, AbstractInsnNode anchor, CallSiteSpec spec) {
|
||||
final int index = owner.instructions.indexOf(anchor);
|
||||
for (owner.maxStack = Math.max(owner.maxStack, spec.target().maxStack) + 16; ; owner.maxStack <<= 1) {
|
||||
try {
|
||||
return new Analyzer<>(new BasicInterpreter()).analyze(owner.name, owner)[index];
|
||||
} catch (AnalyzerException e) {
|
||||
if (!(e.getCause() instanceof IndexOutOfBoundsException) || owner.maxStack > 0xFFFF) {
|
||||
throw cannotFollow(spec, "cannot analyze " + owner.name + owner.desc, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Type[] below(CallSiteSpec spec, Frame<BasicValue> frame, int args) {
|
||||
final int count = frame == null ? 0 : frame.getStackSize() - args;
|
||||
if (count <= 0) {
|
||||
return NOTHING;
|
||||
}
|
||||
final Type[] below = new Type[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
final BasicValue value = frame.getStack(i);
|
||||
final Type type = value.getType();
|
||||
if (type == null || type.getSort() == Type.VOID) {
|
||||
throw cannotFollow(spec, "stack value " + i + " below the call is " + value + ", which cannot be restored", null);
|
||||
}
|
||||
below[i] = value.isReference() ? Type.getObjectType(SurroundSpec.OBJECT) : type;
|
||||
}
|
||||
return below;
|
||||
}
|
||||
|
||||
private static int[] localSlots(CallSiteSpec spec, MethodNode owner, AbstractInsnNode anchor, Frame<BasicValue> frame, int shift, int[] resolved, Type[] params, int first) {
|
||||
if (resolved.length == 0 || owner == spec.target()) {
|
||||
return resolved;
|
||||
}
|
||||
final int firstNonArg = Bytecode.getFirstNonArgLocalIndex(spec.target());
|
||||
final int[] slots = new int[resolved.length];
|
||||
for (int i = 0; i < resolved.length; i++) {
|
||||
final int slot = resolved[i] < firstNonArg ? resolved[i] : resolved[i] + shift;
|
||||
checkLocal(spec, owner, anchor, frame, slot, params[first + i], resolved[i]);
|
||||
slots[i] = slot;
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static void checkLocal(CallSiteSpec spec, MethodNode owner, AbstractInsnNode anchor, Frame<BasicValue> frame, int slot, Type type, int resolved) {
|
||||
String found = null;
|
||||
if (owner.localVariables != null) {
|
||||
final int at = owner.instructions.indexOf(anchor);
|
||||
for (LocalVariableNode local : owner.localVariables) {
|
||||
if (local.index == slot && owner.instructions.indexOf(local.start) <= at && at < owner.instructions.indexOf(local.end) && !type.getDescriptor().equals(local.desc)) {
|
||||
found = Type.getType(local.desc).getClassName() + " '" + local.name + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found == null && frame != null) {
|
||||
final BasicValue value = slot < frame.getLocals() ? frame.getLocal(slot) : null;
|
||||
final Type actual = value == null ? null : value.getType();
|
||||
if (actual == null || actual.getSort() == Type.VOID || actual.getSize() != type.getSize() || SurroundBinding.kindOf(actual) != SurroundBinding.kindOf(type)) {
|
||||
found = value == null ? "nothing" : String.valueOf(value);
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
throw cannotFollow(spec, "@Surround.Local " + type.getClassName() + " resolved to local " + resolved + " at injection, but after the body moved to " + owner.name + owner.desc + " local " + slot + " holds " + found, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodNode owner(ClassNode classNode, AbstractInsnNode insn) {
|
||||
AbstractInsnNode first = insn;
|
||||
while (first.getPrevious() != null) {
|
||||
first = first.getPrevious();
|
||||
}
|
||||
final List<MethodNode> methods = classNode.methods;
|
||||
for (int i = 0, n = methods.size(); i < n; i++) {
|
||||
if (methods.get(i).instructions.getFirst() == first) {
|
||||
return methods.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void checkOperands(CallSiteSpec spec, MethodInsnNode call, Type[] args, int offset) {
|
||||
final Type[] operands = spec.operands();
|
||||
if (args.length - offset < operands.length) {
|
||||
throw cannotFollow(spec, "the call was replaced by " + Bytecode.describeNode(call, false) + ", which takes fewer arguments than the surrounded call", null);
|
||||
}
|
||||
for (int i = 0; i < operands.length; i++) {
|
||||
if (SurroundBinding.kindOf(args[offset + i]) != SurroundBinding.kindOf(operands[i])) {
|
||||
throw cannotFollow(spec, "the call was replaced by " + Bytecode.describeNode(call, false) + ", whose argument " + (offset + i) + " is " + args[offset + i].getClassName() + " where the surrounded call passed " + operands[i].getClassName(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalStateException cannotFollow(CallSiteSpec spec, String detail, Throwable cause) {
|
||||
return new IllegalStateException(spec.description() + " cannot be applied, another transformation left the site in a shape it cannot follow: " + detail, cause);
|
||||
}
|
||||
|
||||
private static boolean prefixed(CallSiteSpec spec) {
|
||||
for (Handler exit : spec.exits().handlers) {
|
||||
if (exit.localStart > exit.leading) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int usedLocals(MethodNode owner) {
|
||||
int max = Bytecode.getFirstNonArgLocalIndex(owner);
|
||||
for (AbstractInsnNode insn = owner.instructions.getFirst(); insn != null; insn = insn.getNext()) {
|
||||
if (insn instanceof VarInsnNode varInsn) {
|
||||
final int opcode = insn.getOpcode();
|
||||
final boolean wide = opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE;
|
||||
max = Math.max(max, varInsn.var + (wide ? 2 : 1));
|
||||
} else if (insn instanceof IincInsnNode iinc) {
|
||||
max = Math.max(max, iinc.var + 1);
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private static void remapLocals(MethodNode inlined, int argBase, int firstNonArg, Type[] entryParams, int entryOperands, int[] spillSlots, int offset, int[] entryLocals, int delta) {
|
||||
final int[] paramSlots = new int[firstNonArg];
|
||||
for (int i = 0, slot = argBase; i < entryParams.length; i++) {
|
||||
paramSlots[slot] = i < entryOperands ? spillSlots[offset + i] : entryLocals[i - entryOperands];
|
||||
slot += entryParams[i].getSize();
|
||||
}
|
||||
for (AbstractInsnNode insn = inlined.instructions.getFirst(); insn != null; insn = insn.getNext()) {
|
||||
if (insn instanceof VarInsnNode var) {
|
||||
var.var = remap(var.var, argBase, firstNonArg, paramSlots, delta);
|
||||
} else if (insn instanceof IincInsnNode iinc) {
|
||||
iinc.var = remap(iinc.var, argBase, firstNonArg, paramSlots, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int remap(int var, int argBase, int firstNonArg, int[] paramSlots, int delta) {
|
||||
if (var < argBase) {
|
||||
return var;
|
||||
}
|
||||
if (var < firstNonArg) {
|
||||
return paramSlots[var];
|
||||
}
|
||||
return var + delta;
|
||||
}
|
||||
|
||||
private static int insertionIndex(MethodNode owner, LabelNode start, LabelNode end) {
|
||||
final List<TryCatchBlockNode> blocks = owner.tryCatchBlocks;
|
||||
final InsnList insns = owner.instructions;
|
||||
final int from = insns.indexOf(start);
|
||||
final int to = insns.indexOf(end);
|
||||
for (int i = 0, n = blocks.size(); i < n; i++) {
|
||||
final TryCatchBlockNode block = blocks.get(i);
|
||||
final int s = insns.indexOf(block.start);
|
||||
final int e = insns.indexOf(block.end);
|
||||
if (s < to && e > from && (s < from || e > to)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return blocks.size();
|
||||
}
|
||||
|
||||
private static void loadBelow(InsnList insns, Type[] below, int[] belowSlots) {
|
||||
for (int i = 0; i < below.length; i++) {
|
||||
insns.add(new VarInsnNode(below[i].getOpcode(Opcodes.ILOAD), belowSlots[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private record Emitter(ClassNode classNode, Exits exits, Type[] operands, int[] operandSlots, int[][] locals, int delta) {
|
||||
|
||||
private void invoke(InsnList insns, Handler exit, Type leading, int leadingSlot) {
|
||||
final int[] locals = this.locals[exit.index];
|
||||
if (!exit.isStatic) {
|
||||
insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
}
|
||||
if (leading != null) {
|
||||
insns.add(new VarInsnNode(leading.getOpcode(Opcodes.ILOAD), leadingSlot));
|
||||
}
|
||||
for (int i = 0, n = exit.localStart - exit.leading; i < n; i++) {
|
||||
insns.add(new VarInsnNode(this.operands[i].getOpcode(Opcodes.ILOAD), this.operandSlots[i]));
|
||||
}
|
||||
for (int i = exit.localStart; i < exit.carryStart; i++) {
|
||||
insns.add(new VarInsnNode(exit.params[i].getOpcode(Opcodes.ILOAD), locals[i - exit.localStart]));
|
||||
}
|
||||
for (Load load : exit.loads) {
|
||||
final Type type = load.type();
|
||||
insns.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), load.slot() + this.delta));
|
||||
if (type.getSort() >= Type.ARRAY && !SurroundSpec.OBJECT.equals(type.getInternalName())) {
|
||||
insns.add(new TypeInsnNode(Opcodes.CHECKCAST, type.getInternalName()));
|
||||
}
|
||||
}
|
||||
// MixinExtras 0.5.0 internal (pinned by UniMixins 0.3.1); recheck on bump
|
||||
insns.add(ASMUtils.getInvokeInstruction(this.classNode, exit.method));
|
||||
}
|
||||
|
||||
void callFinally(InsnList insns) {
|
||||
if (this.exits.finallyHandler != null) {
|
||||
this.invoke(insns, this.exits.finallyHandler, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void callSkipped(InsnList insns) {
|
||||
if (this.exits.skipped != null) {
|
||||
this.invoke(insns, this.exits.skipped, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void callReturn(InsnList insns, Type result, int resSlot) {
|
||||
this.invoke(insns, this.exits.returnHandler, result.getSize() > 0 ? result : null, resSlot);
|
||||
}
|
||||
|
||||
void emitHandlers(InsnList tail, List<TryCatchBlockNode> blocks, LabelNode start, LabelNode end, LabelNode retStart, LabelNode retEnd, int excSlot, Type[] below, int[] belowSlots, LabelNode handledExit) {
|
||||
final Handler[] handlers = this.exits.handlers;
|
||||
final boolean hasFinally = this.exits.finallyHandler != null;
|
||||
final LabelNode catchAll = hasFinally ? new LabelNode() : null;
|
||||
final List<TryCatchBlockNode> catchAlls = hasFinally ? new ArrayList<>() : null;
|
||||
for (int k = 0; k < this.exits.catches; k++) {
|
||||
final Handler exit = handlers[k];
|
||||
final LabelNode handler = new LabelNode();
|
||||
final LabelNode exitStart = new LabelNode();
|
||||
final LabelNode exitEnd = new LabelNode();
|
||||
|
||||
tail.add(handler);
|
||||
tail.add(new VarInsnNode(Opcodes.ASTORE, excSlot));
|
||||
if (exit.handles) {
|
||||
loadBelow(tail, below, belowSlots);
|
||||
}
|
||||
tail.add(exitStart);
|
||||
this.invoke(tail, exit, exit.caught(), excSlot);
|
||||
tail.add(exitEnd);
|
||||
|
||||
this.callFinally(tail);
|
||||
if (exit.handles) {
|
||||
tail.add(new JumpInsnNode(Opcodes.GOTO, handledExit));
|
||||
} else {
|
||||
tail.add(new VarInsnNode(Opcodes.ALOAD, excSlot));
|
||||
tail.add(new InsnNode(Opcodes.ATHROW));
|
||||
}
|
||||
|
||||
blocks.add(new TryCatchBlockNode(start, end, handler, exit.caught().getInternalName()));
|
||||
if (hasFinally) {
|
||||
catchAlls.add(new TryCatchBlockNode(exitStart, exitEnd, catchAll, null));
|
||||
}
|
||||
}
|
||||
if (hasFinally) {
|
||||
tail.add(catchAll);
|
||||
tail.add(new VarInsnNode(Opcodes.ASTORE, excSlot));
|
||||
this.callFinally(tail);
|
||||
tail.add(new VarInsnNode(Opcodes.ALOAD, excSlot));
|
||||
tail.add(new InsnNode(Opcodes.ATHROW));
|
||||
|
||||
if (retStart != null) {
|
||||
catchAlls.add(new TryCatchBlockNode(retStart, retEnd, catchAll, null));
|
||||
}
|
||||
catchAlls.add(new TryCatchBlockNode(start, end, catchAll, null));
|
||||
blocks.addAll(catchAlls);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.lib.tree.AnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.code.Injector;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
|
||||
import org.spongepowered.asm.mixin.injection.throwables.InvalidInjectionException;
|
||||
import org.spongepowered.asm.mixin.transformer.MixinTargetContext;
|
||||
import org.spongepowered.asm.util.Annotations;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@InjectionInfo.AnnotationType(Surround.class)
|
||||
@InjectionInfo.HandlerPrefix("surround")
|
||||
public final class SurroundInjectionInfo extends InjectionInfo {
|
||||
|
||||
boolean callSite;
|
||||
|
||||
public SurroundInjectionInfo(MixinTargetContext mixin, MethodNode method, AnnotationNode annotation) {
|
||||
super(mixin, method, annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readInjectionPoints() {
|
||||
final List<AnnotationNode> ats = Annotations.<AnnotationNode>getValue(this.annotation, this.atKey, false);
|
||||
this.callSite = ats != null && !ats.isEmpty();
|
||||
if (this.callSite) {
|
||||
super.readInjectionPoints();
|
||||
return;
|
||||
}
|
||||
final List<AnnotationNode> slices = Annotations.<AnnotationNode>getValue(this.annotation, "slice", false);
|
||||
if (slices != null && !slices.isEmpty()) {
|
||||
throw this.reject("declares @Surround.slice, which only applies when @Surround.at selects a call");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseInjectionPoints(List<AnnotationNode> ats) {
|
||||
if (!this.callSite) {
|
||||
this.injectionPoints.add(new SurroundInjectionPoint());
|
||||
return;
|
||||
}
|
||||
for (int i = 0, n = ats.size(); i < n; i++) {
|
||||
final AnnotationNode at = ats.get(i);
|
||||
final String code = Annotations.<String>getValue(at, "value");
|
||||
if (!selectsCall(code)) {
|
||||
throw this.reject("has @At(\"" + code + "\"), which does not select a method call");
|
||||
}
|
||||
if (Annotations.<At.Shift>getValue(at, "shift", At.Shift.class, At.Shift.NONE) != At.Shift.NONE || Annotations.<Integer>getValue(at, "by", Integer.valueOf(0)).intValue() != 0) {
|
||||
throw this.reject("has @At(\"" + code + "\") with a non-default shift or by, which moves the match off the call");
|
||||
}
|
||||
}
|
||||
super.parseInjectionPoints(ats);
|
||||
}
|
||||
|
||||
private static boolean selectsCall(String atCode) {
|
||||
return switch (atCode) {
|
||||
case "HEAD", "RETURN", "TAIL", "CTOR_HEAD", "FIELD", "NEW", "CONSTANT", "JUMP", "INVOKE_ASSIGN", "LOAD", "STORE" -> false;
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
|
||||
private InvalidInjectionException reject(String message) {
|
||||
return new InvalidInjectionException(this, "@Surround " + this.getMethodName() + " " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Injector parseInjector(AnnotationNode injectAnnotation) {
|
||||
return new SurroundInjector(this);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.InsnList;
|
||||
import org.spongepowered.asm.mixin.injection.InjectionPoint;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
final class SurroundInjectionPoint extends InjectionPoint {
|
||||
|
||||
@Override
|
||||
public boolean checkPriority(int targetPriority, int ownerPriority) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean find(String desc, InsnList insns, Collection<AbstractInsnNode> nodes) {
|
||||
return nodes.add(insns.getFirst());
|
||||
}
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Exits;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Handler;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.Role;
|
||||
import com.llamalad7.mixinextras.utils.InjectorUtils;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.AnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.LocalVariableAnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.injection.InjectionPoint;
|
||||
import org.spongepowered.asm.mixin.injection.code.Injector;
|
||||
import org.spongepowered.asm.mixin.injection.modify.InvalidImplicitDiscriminatorException;
|
||||
import org.spongepowered.asm.mixin.injection.modify.LocalVariableDiscriminator;
|
||||
import org.spongepowered.asm.mixin.injection.modify.LocalVariableDiscriminator.Context;
|
||||
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
|
||||
import org.spongepowered.asm.mixin.injection.struct.Target;
|
||||
import org.spongepowered.asm.mixin.injection.throwables.InvalidInjectionException;
|
||||
import org.spongepowered.asm.mixin.transformer.ClassInfo;
|
||||
import org.spongepowered.asm.mixin.transformer.ClassInfo.Traversal;
|
||||
import org.spongepowered.asm.mixin.transformer.MixinTargetContext;
|
||||
import org.spongepowered.asm.util.Annotations;
|
||||
import org.spongepowered.asm.util.Bytecode;
|
||||
import org.spongepowered.asm.util.asm.MethodNodeEx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.exitRef;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.SurroundBinding.paramRef;
|
||||
|
||||
final class SurroundInjector extends Injector {
|
||||
|
||||
private final String id;
|
||||
private final boolean callSite;
|
||||
private final MixinTargetContext mixin;
|
||||
private Handler entry;
|
||||
private Exits exits;
|
||||
|
||||
SurroundInjector(SurroundInjectionInfo info) {
|
||||
super(info, "@Surround");
|
||||
this.id = info.getAnnotation().getValue("id", "");
|
||||
this.callSite = info.callSite;
|
||||
this.mixin = (MixinTargetContext) info.getMixin();
|
||||
SurroundApplicatorExtension.instance().declare(this.mixin.getTarget(), this.mixin.getClassName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(Target target, InjectionNode node) {
|
||||
this.checkTargetModifiers(target, true);
|
||||
try {
|
||||
if (this.callSite) {
|
||||
this.surroundCall(target, node);
|
||||
} else {
|
||||
final Type result = Type.getReturnType(target.method.desc);
|
||||
this.prepare(target.arguments, result, target.toString());
|
||||
this.offer(target, null, target.arguments, result, target.toString());
|
||||
}
|
||||
} catch (SurroundRejection e) {
|
||||
throw error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void surroundCall(Target target, InjectionNode node) {
|
||||
final AbstractInsnNode insn = node.getOriginalTarget();
|
||||
if (!(insn instanceof MethodInsnNode call)) {
|
||||
throw error("at must select a method call, not " + Bytecode.describeNode(insn, false) + " in " + target);
|
||||
}
|
||||
final String site = "call " + call.owner + "." + call.name + call.desc + " in " + target;
|
||||
if ("<init>".equals(call.name)) {
|
||||
throw error("at must select a method call, not the constructor " + site);
|
||||
}
|
||||
final Type[] operands = SurroundCallSite.operands(call, this.classNode.name);
|
||||
final Type result = Type.getReturnType(call.desc);
|
||||
this.prepare(operands, result, site);
|
||||
final boolean insideNew = isInsideNew(call);
|
||||
if (insideNew && this.methodNode.tryCatchBlocks != null && !this.methodNode.tryCatchBlocks.isEmpty()) {
|
||||
throw error("must not catch exceptions when it surrounds a call inside a constructor call's arguments (between NEW and <init>); move the try into a method it calls");
|
||||
}
|
||||
if (insideNew && this.exits.handles) {
|
||||
throw error("has a @Surround.Catch" + this.forId() + " that declares handle = true, but the call is inside a constructor call's arguments (between NEW and <init>), where the values below it cannot be restored");
|
||||
}
|
||||
this.offer(target, node, operands, result, site);
|
||||
}
|
||||
|
||||
private void offer(Target target, InjectionNode node, Type[] operands, Type result, String site) {
|
||||
final Handler[] handlers = this.exits.handlers;
|
||||
final int[][] locals = new int[handlers.length][];
|
||||
for (Handler exit : handlers) {
|
||||
locals[exit.index] = this.resolveLocals(target, node, exit);
|
||||
}
|
||||
final int[] entryLocals = this.resolveLocals(target, node, this.entry);
|
||||
this.info.addCallbackInvocation(this.methodNode);
|
||||
final String description = "@Surround " + this.mixin.getClassName() + "::" + this.info.getMethodName() + " surrounding " + site;
|
||||
SurroundApplicatorExtension.instance().offer(this.mixin.getTarget(), new CallSiteSpec(target.method, node, this.methodNode, this.methodNode.desc, operands, result, this.exits, description, entryLocals, locals));
|
||||
}
|
||||
|
||||
private static boolean isInsideNew(MethodInsnNode call) {
|
||||
int constructed = 0;
|
||||
for (AbstractInsnNode insn = call.getPrevious(); insn != null; insn = insn.getPrevious()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKESPECIAL && "<init>".equals(((MethodInsnNode) insn).name)) {
|
||||
constructed++;
|
||||
} else if (insn.getOpcode() == Opcodes.NEW) {
|
||||
if (constructed == 0) {
|
||||
return true;
|
||||
}
|
||||
constructed--;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int[] resolveLocals(Target target, InjectionNode node, Handler handler) {
|
||||
final int from = handler.localStart;
|
||||
final int[] slots = new int[handler.carryStart - from];
|
||||
for (int i = from; i < handler.carryStart; i++) {
|
||||
final Type type = handler.params[i];
|
||||
final LocalVariableDiscriminator discriminator = LocalVariableDiscriminator.parse(SurroundBinding.local(handler.method, i));
|
||||
// MixinExtras 0.5.0 internal (pinned by UniMixins 0.3.1); recheck on bump
|
||||
final Context context = InjectorUtils.getOrCreateLocalContext(target, node, this.info, type, discriminator.isArgsOnly());
|
||||
String detail;
|
||||
try {
|
||||
slots[i - from] = discriminator.findLocal(context);
|
||||
detail = slots[i - from] < 0 ? "no " + discriminator.toString(context) : null;
|
||||
} catch (InvalidImplicitDiscriminatorException e) {
|
||||
detail = e.getMessage();
|
||||
}
|
||||
if (detail != null) {
|
||||
throw error(handler.ref() + " " + paramRef(handler.method, i) + " is @Surround.Local " + type.getClassName() + ", which matches no single local at the call: " + detail);
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
private void prepare(Type[] expected, Type result, String against) {
|
||||
if (this.exits == null) {
|
||||
this.analyze(result);
|
||||
}
|
||||
this.checkArgPrefix(this.entry, expected, against);
|
||||
for (Handler exit : this.exits.handlers) {
|
||||
if (exit.role == Role.SKIPPED || exit.role == Role.RETURN || exit.handles) {
|
||||
final Type declared = Type.getReturnType(exit.method.desc);
|
||||
if (!declared.equals(result)) {
|
||||
throw error(exit.ref() + " must return " + result.getClassName() + " to match what it surrounds, not " + declared.getClassName());
|
||||
}
|
||||
}
|
||||
if (exit.role == Role.RETURN && exit.leading == 1 && (exit.params.length == 0 || !exit.params[0].equals(result))) {
|
||||
throw error(exit.ref() + " must declare the " + result.getClassName() + " result as its first parameter");
|
||||
}
|
||||
this.checkArgPrefix(exit, expected, against);
|
||||
}
|
||||
if (this.exits.skipped == null && this.exits.skipSlot >= 0 && result.getSize() > 0) {
|
||||
throw error("can skip a " + result.getClassName() + " result but has no @Surround.Skipped" + this.forId() + " to supply it");
|
||||
}
|
||||
}
|
||||
|
||||
private void analyze(Type result) {
|
||||
if (!this.returnType.equals(Type.VOID_TYPE)) {
|
||||
throw error("must return void; carry state out with @Surround.Carry locals instead");
|
||||
}
|
||||
if (Bytecode.hasFlag(this.methodNode, Opcodes.ACC_ABSTRACT)) {
|
||||
throw error("must not be abstract");
|
||||
}
|
||||
final Handler entry = new Handler(this.methodNode, Role.ENTRY, 0);
|
||||
if (this.hasStrippedSugar()) {
|
||||
throw error("one of its parameters is annotated @Local, @Share or @Cancellable" + SurroundBinding.SUGAR_HINT);
|
||||
}
|
||||
this.checkNoLocal(entry);
|
||||
final AbstractInsnNode soleReturn = SurroundBinding.soleReturn(this.methodNode);
|
||||
|
||||
final Set<String> ids = new LinkedHashSet<>();
|
||||
for (MethodNode method : this.mixin.getMixin().getClassNode(0).methods) {
|
||||
final AnnotationNode surround = Annotations.get(method.visibleAnnotations, SurroundSpec.SURROUND);
|
||||
if (surround == null) {
|
||||
continue;
|
||||
}
|
||||
final String declared = Annotations.<String>getValue(surround, "id", "");
|
||||
if (!ids.add(declared) && declared.equals(this.id)) {
|
||||
throw error("duplicate @Surround " + describeId(this.id) + "; ids must be unique per mixin");
|
||||
}
|
||||
}
|
||||
final List<Handler> handlers = new ArrayList<>();
|
||||
for (MethodNode method : this.mixin.getClassNode().methods) {
|
||||
Role role = null;
|
||||
AnnotationNode annotation = null;
|
||||
for (Role candidate : Role.EXITS) {
|
||||
final AnnotationNode found = Annotations.get(method.visibleAnnotations, candidate.annotation);
|
||||
if (found == null) {
|
||||
continue;
|
||||
}
|
||||
if (role != null) {
|
||||
throw error(exitRef(method) + " can be only one of @Surround.Catch, @Surround.Finally, @Surround.Skipped and @Surround.Return");
|
||||
}
|
||||
role = candidate;
|
||||
annotation = found;
|
||||
}
|
||||
if (role == null) {
|
||||
continue;
|
||||
}
|
||||
final String exitId = Annotations.<String>getValue(annotation, "value", "");
|
||||
if (!ids.contains(exitId)) {
|
||||
throw error(exitRef(method) + " " + noSurroundForId(exitId, ids));
|
||||
}
|
||||
if (this.id.equals(exitId)) {
|
||||
handlers.add(this.exitHandler(this.own(method), role, role == Role.CATCH || role == Role.RETURN && result.getSize() > 0 ? 1 : 0));
|
||||
}
|
||||
}
|
||||
handlers.sort(Comparator.comparing(handler -> handler.role));
|
||||
boolean skipped = false;
|
||||
for (int i = 0, n = handlers.size(); i < n; i++) {
|
||||
final Role role = handlers.get(i).role;
|
||||
skipped |= role == Role.SKIPPED;
|
||||
if (i > 0 && role != Role.CATCH && role == handlers.get(i - 1).role) {
|
||||
throw error("declares more than one @Surround." + role.name().charAt(0) + role.name().substring(1).toLowerCase(Locale.ROOT) + this.forId() + "; there can only be one");
|
||||
}
|
||||
}
|
||||
final Handler[] sorted = handlers.toArray(new Handler[0]);
|
||||
this.sortMostSpecificFirst(sorted);
|
||||
final List<LocalVariableAnnotationNode> annotations = this.methodNode.visibleLocalVariableAnnotations;
|
||||
final int[] kinds = annotations == null || annotations.isEmpty() ? null : SurroundBinding.kindsAtEnd(this.methodNode, soleReturn);
|
||||
final int skipSlot = SurroundBinding.skipSlot(this.methodNode, kinds);
|
||||
if (skipped && skipSlot < 0) {
|
||||
throw error("has a @Surround.Skipped" + this.forId() + " but declares no @Surround.Skip local, so nothing is ever skipped");
|
||||
}
|
||||
final Type[] carried = SurroundBinding.bind(this.methodNode, kinds, sorted);
|
||||
this.entry = entry;
|
||||
this.exits = new Exits(sorted, skipSlot, carried);
|
||||
}
|
||||
|
||||
// MixinExtras strips sugar params before parse; the annotation table can then be null.
|
||||
private boolean hasStrippedSugar() {
|
||||
final List<AnnotationNode>[] invisible = this.methodNode.invisibleParameterAnnotations;
|
||||
if (invisible != null) {
|
||||
return invisible.length != this.methodArgs.length;
|
||||
}
|
||||
final ClassInfo mixin = this.mixin.getClassInfo();
|
||||
final String desc = this.methodNode.desc;
|
||||
return mixin.findMethod(MethodNodeEx.getName(this.methodNode), desc, ClassInfo.INCLUDE_ALL) == null && mixin.findMethod(this.methodNode.name, desc, ClassInfo.INCLUDE_ALL) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sanityCheck(Target target, List<InjectionPoint> injectionPoints) {
|
||||
super.sanityCheck(target, injectionPoints);
|
||||
if (this.isInterface) {
|
||||
throw error("cannot target an interface method " + target);
|
||||
}
|
||||
if (target.method.name.endsWith("init>")) {
|
||||
throw error("cannot target the initializer " + target);
|
||||
}
|
||||
if (Bytecode.hasFlag(target.method, Opcodes.ACC_ABSTRACT) || Bytecode.hasFlag(target.method, Opcodes.ACC_NATIVE)) {
|
||||
throw error("cannot target the abstract or native method " + target);
|
||||
}
|
||||
}
|
||||
|
||||
private Handler exitHandler(MethodNode method, Role role, int leading) {
|
||||
final Handler exit = new Handler(method, role, leading);
|
||||
if (exit.handles && !this.callSite) {
|
||||
throw error(exit.ref() + " declares handle = true, which only applies when @Surround.at selects a call");
|
||||
}
|
||||
if (Bytecode.hasFlag(method, Opcodes.ACC_ABSTRACT)) {
|
||||
throw error(exit.ref() + " must not be abstract");
|
||||
}
|
||||
if ((role == Role.FINALLY || role == Role.CATCH && !exit.handles) && !Type.getReturnType(method.desc).equals(Type.VOID_TYPE)) {
|
||||
throw error(exit.ref() + " must return void; an exit handler cannot suppress or replace anything");
|
||||
}
|
||||
if (Bytecode.isStatic(method) != this.isStatic) {
|
||||
throw error(exit.ref() + " must be " + (this.isStatic ? "static" : "non-static") + " to match the entry handler" + this.forId());
|
||||
}
|
||||
if (role == Role.CATCH) {
|
||||
if (exit.params.length == 0) {
|
||||
throw error(exit.ref() + " must declare the Throwable it catches as its first parameter");
|
||||
}
|
||||
final Type caught = exit.caught();
|
||||
if (caught.getSort() != Type.OBJECT || !SurroundSpec.THROWABLE.equals(caught.getInternalName()) && !extendsClass(caught, SurroundSpec.THROWABLE)) {
|
||||
throw error(exit.ref() + " " + paramRef(method, 0) + " catches " + caught.getClassName() + ", which is not a java.lang.Throwable subtype");
|
||||
}
|
||||
}
|
||||
this.checkNoLocal(exit);
|
||||
return exit;
|
||||
}
|
||||
|
||||
private void checkNoLocal(Handler handler) {
|
||||
if (!this.callSite && handler.localStart < handler.carryStart) {
|
||||
throw error(handler.ref() + " " + paramRef(handler.method, handler.localStart) + " is @Surround.Local, which only applies when @Surround.at selects a call");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkArgPrefix(Handler handler, Type[] expected, String against) {
|
||||
final Type[] params = handler.params;
|
||||
if (handler.localStart - handler.leading > expected.length) {
|
||||
throw error(handler.ref() + " has more parameters than " + against);
|
||||
}
|
||||
for (int i = handler.leading, arg = 0; i < handler.localStart; i++, arg++) {
|
||||
if (params[i].equals(expected[arg]) || this.callSite && isAssignable(params[i], expected[arg])) {
|
||||
continue;
|
||||
}
|
||||
throw error(handler.ref() + " " + paramRef(handler.method, i) + " is " + params[i].getClassName() + ", which does not match the " + expected[arg].getClassName() + " operand of " + against);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssignable(Type param, Type operand) {
|
||||
if (param.getSort() != Type.OBJECT || operand.getSort() != Type.OBJECT && operand.getSort() != Type.ARRAY) {
|
||||
return false;
|
||||
}
|
||||
if (SurroundSpec.OBJECT.equals(param.getInternalName())) {
|
||||
return true;
|
||||
}
|
||||
if (operand.getSort() != Type.OBJECT) {
|
||||
return false;
|
||||
}
|
||||
final ClassInfo info = ClassInfo.forName(operand.getInternalName());
|
||||
return info != null && info.findSuperClass(param.getInternalName(), Traversal.ALL, true) != null;
|
||||
}
|
||||
|
||||
private void sortMostSpecificFirst(Handler[] exits) {
|
||||
for (int k = 1; k < exits.length && exits[k].role == Role.CATCH; k++) {
|
||||
final Handler exit = exits[k];
|
||||
final Type caught = exit.caught();
|
||||
int at = k;
|
||||
for (int i = 0; i < k; i++) {
|
||||
final Type other = exits[i].caught();
|
||||
if (caught.equals(other)) {
|
||||
throw error(exit.ref() + " is a second @Surround.Catch for " + caught.getClassName() + this.forId() + "; each type may be caught once");
|
||||
}
|
||||
if (at == k && extendsClass(caught, other.getInternalName())) {
|
||||
at = i;
|
||||
}
|
||||
}
|
||||
System.arraycopy(exits, at, exits, at + 1, k - at);
|
||||
exits[at] = exit;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean extendsClass(Type type, String superName) {
|
||||
final ClassInfo info = ClassInfo.forName(type.getInternalName());
|
||||
return info != null && info.hasSuperClass(superName);
|
||||
}
|
||||
|
||||
private MethodNode own(MethodNode exit) {
|
||||
final MethodNode method = Bytecode.findMethod(this.classNode, exit.name, exit.desc);
|
||||
if (method == null) {
|
||||
throw new IllegalStateException(exit.name + exit.desc + " is missing from " + this.classNode.name);
|
||||
}
|
||||
if (!this.mixin.getClassName().equals(SurroundSpec.mergedBy(method))) {
|
||||
throw error(exitRef(exit) + " collides with " + exit.name + exit.desc + " in " + this.classNode.name + ", which is " + SurroundSpec.describeOwner(method));
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private String forId() {
|
||||
return this.id.isEmpty() ? "" : " for id '" + this.id + "'";
|
||||
}
|
||||
|
||||
private static String describeId(String id) {
|
||||
return id.isEmpty() ? "the default id" : "id '" + id + "'";
|
||||
}
|
||||
|
||||
static String noSurroundForId(String id, Collection<String> declared) {
|
||||
final StringJoiner ids = new StringJoiner(", ", "; declared ids are ", "").setEmptyValue("; this mixin has no @Surround at all");
|
||||
for (String other : declared) {
|
||||
ids.add(describeId(other));
|
||||
}
|
||||
return "refers to " + describeId(id) + " but no @Surround in this mixin declares it" + ids;
|
||||
}
|
||||
|
||||
private InvalidInjectionException error(String message) {
|
||||
return new InvalidInjectionException(this.info, "@Surround " + this.info.getMethodName() + " " + message);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
final class SurroundRejection extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
SurroundRejection(String message) {
|
||||
super(message, null, false, false);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.tree.AnnotationNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
|
||||
import org.spongepowered.asm.util.Annotations;
|
||||
|
||||
interface SurroundSpec {
|
||||
|
||||
String SURROUND = Type.getDescriptor(Surround.class);
|
||||
String CATCH = Type.getDescriptor(Surround.Catch.class);
|
||||
String FINALLY = Type.getDescriptor(Surround.Finally.class);
|
||||
String CARRY = Type.getDescriptor(Surround.Carry.class);
|
||||
String SKIP = Type.getDescriptor(Surround.Skip.class);
|
||||
String SKIPPED = Type.getDescriptor(Surround.Skipped.class);
|
||||
String RETURN = Type.getDescriptor(Surround.Return.class);
|
||||
String LOCAL = Type.getDescriptor(Surround.Local.class);
|
||||
String MIXIN_MERGED = Type.getDescriptor(MixinMerged.class);
|
||||
String OBJECT = Type.getInternalName(Object.class);
|
||||
String THROWABLE = Type.getInternalName(Throwable.class);
|
||||
String THROWABLE_DESC = Type.getDescriptor(Throwable.class);
|
||||
|
||||
static String mergedBy(MethodNode method) {
|
||||
final AnnotationNode merged = Annotations.get(method.visibleAnnotations, MIXIN_MERGED);
|
||||
return merged == null ? null : Annotations.<String>getValue(merged, "mixin");
|
||||
}
|
||||
|
||||
static String describeOwner(MethodNode method) {
|
||||
final String owner = mergedBy(method);
|
||||
return owner == null ? "not merged from a mixin" : "merged from " + owner;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Entity;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.ReportedException;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.lib.tree.TryCatchBlockNode;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.CALL;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.call;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.method;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.transformedNode;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CallSiteTest {
|
||||
|
||||
@BeforeEach
|
||||
void resetTrace() {
|
||||
Trace.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void aRedirectedCallInsideJavacTryCatchesRethrowsInsideTheEnclosingTry() throws Exception {
|
||||
assertEquals(true, call(CALL, "render", new Entity("pig"), 1.0d, 2.0d, 3.0d, 0.5f, 0.25f, true));
|
||||
final Entity creeper = new Entity("creeper");
|
||||
creeper.explode = true;
|
||||
final InvocationTargetException thrown = assertThrows(InvocationTargetException.class, () -> call(CALL, "render", creeper, 1.0d, 2.0d, 3.0d, 0.5f, 0.25f, false));
|
||||
|
||||
final ReportedException outer = (ReportedException) thrown.getCause();
|
||||
assertEquals("Rendering entity (outer)", outer.getMessage());
|
||||
assertEquals("Rendering entity in world", outer.getCause().getMessage());
|
||||
assertSame(RuntimeException.class, outer.getCause().getCause().getClass());
|
||||
assertEquals("gpu", outer.getCause().getCause().getMessage());
|
||||
Trace.assertEvents("enterDoRender pig 1", "redirect pig", "doRender pig 1.0 0.25", "exitDoRender pig 1", "shadow pig", "boundingBox pig", "enterDoRender creeper 2", "redirect creeper", "doRender creeper 1.0 0.25", "caughtDoRender creeper gpu 2", "exitDoRender creeper 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void theSurroundedCallMatchesTheGoldens() {
|
||||
MixinTestBootstrap.assertGolden(CALL, "valueSkip", "call-skip");
|
||||
MixinTestBootstrap.assertGolden(CALL, "valueBelow", "call-handle");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedSurroundsShareOneCallAndOrderTheirTryRangesInnerFirst() {
|
||||
final MethodNode target = method(transformedNode(CALL), "innerSkipOuterReturn");
|
||||
int calls = 0;
|
||||
for (AbstractInsnNode insn : target.instructions.toArray()) {
|
||||
calls += insn instanceof MethodInsnNode m && m.name.equals("twice") ? 1 : 0;
|
||||
}
|
||||
assertEquals(1, calls);
|
||||
final List<TryCatchBlockNode> blocks = target.tryCatchBlocks;
|
||||
boolean nests = false;
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
for (int j = 0; j < blocks.size(); j++) {
|
||||
final boolean encloses = encloses(target, blocks.get(i), blocks.get(j));
|
||||
nests |= encloses;
|
||||
assertTrue(!encloses || i > j, "block " + i + " encloses block " + j);
|
||||
}
|
||||
}
|
||||
assertTrue(nests, "no try block encloses another");
|
||||
}
|
||||
|
||||
private static boolean encloses(MethodNode target, TryCatchBlockNode outer, TryCatchBlockNode inner) {
|
||||
final int outerStart = target.instructions.indexOf(outer.start);
|
||||
final int outerEnd = target.instructions.indexOf(outer.end);
|
||||
final int innerStart = target.instructions.indexOf(inner.start);
|
||||
final int innerEnd = target.instructions.indexOf(inner.end);
|
||||
return outerStart <= innerStart && innerEnd <= outerEnd && (outerStart < innerStart || innerEnd < outerEnd);
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import com.llamalad7.mixinextras.utils.MixinInternals;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.Type;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.InsnNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.Extensions;
|
||||
import org.spongepowered.asm.mixin.transformer.ext.IExtension;
|
||||
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.WRAP;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.of;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.scenario;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CompositionTest {
|
||||
|
||||
private static final Set<String> RUN_BEFORE = Set.of("LateInjectionApplicatorExtension", "SugarPostProcessingExtension", "WrapMethodApplicatorExtension");
|
||||
private static final String CHECK_CLASS = "ExtensionCheckClass";
|
||||
|
||||
@BeforeEach
|
||||
void resetTrace() {
|
||||
Trace.reset();
|
||||
}
|
||||
|
||||
private static Object wrap(String name, Object... args) throws Exception {
|
||||
return MixinTestBootstrap.call(WRAP, name, args);
|
||||
}
|
||||
|
||||
static Stream<Arguments> mixinExtras() {
|
||||
return Stream.of(
|
||||
scenario(WRAP, "the surround is outside @WrapMethod", "wrapped", of(3), of(6), "entry 3", "wrap-before 3", "body 3", "wrap-after 6", "finally"),
|
||||
scenario(WRAP, "the entry sees the operand before @WrapOperation shifts it", "originalOperands", of(5), of(210), "enter 5", "wrap 5", "twice 105", "exit 5"));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("mixinExtras")
|
||||
@Tag("mixinextras")
|
||||
void mixinExtras(String method, String target, Object[] inputs, Object[] outputs, String[] events) throws Exception {
|
||||
MixinTestBootstrap.assertScenario(target, method, inputs, outputs, events);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("mixinextras")
|
||||
void aLocalFollowsTheBodyWhenAShareShiftsItsSlot() throws Exception {
|
||||
assertEquals(10, wrap("shifted", 3));
|
||||
Trace.assertEvents("wrap 3", "enterShifted 4 4 L4", "twice 4", "exitShifted 4 L4");
|
||||
MethodNode body = null;
|
||||
for (MethodNode method : MixinTestBootstrap.transformedNode(WRAP).methods) {
|
||||
body = method.name.startsWith("shifted$") ? method : body;
|
||||
}
|
||||
assertTrue(body != null && body.desc.startsWith("(IL"), "the body did not move and gain the share");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("mixinextras")
|
||||
void ourExtensionRunsAfterMixinExtrasAndBeforeCheckClass() throws Exception {
|
||||
MixinTestBootstrap.transformer();
|
||||
final SurroundApplicatorExtension ours = SurroundApplicatorExtension.instance();
|
||||
MixinTestBootstrap.transform(WRAP);
|
||||
final Extensions extensions = MixinInternals.getExtensions();
|
||||
for (List<IExtension> list : List.of(extensions.getExtensions(), extensions.getActiveExtensions())) {
|
||||
final int at = assertSlot(list, ours);
|
||||
for (String name : RUN_BEFORE) {
|
||||
final int index = names(list).indexOf(name);
|
||||
assertTrue(index >= 0 && index < at, names(list).toString());
|
||||
assertTrue(list.get(index).getClass().getName().startsWith("com.llamalad7.mixinextras."));
|
||||
}
|
||||
}
|
||||
assertCheckClassActiveUnderVerify(extensions);
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnrenamedSurroundHandlerIsFatal() {
|
||||
final String mixin = "com.example.mixin.MixinLate";
|
||||
final MethodNode method = new MethodNode(Opcodes.ACC_PRIVATE, "surround$enter", "()V", null, null);
|
||||
method.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
method.visitAnnotation(Type.getDescriptor(Surround.class), true).visitEnd();
|
||||
method.visitAnnotation(Type.getDescriptor(MixinMerged.class), true).visit("mixin", mixin);
|
||||
final ClassNode target = new ClassNode();
|
||||
target.version = Opcodes.V1_8;
|
||||
target.access = Opcodes.ACC_PUBLIC;
|
||||
target.name = "com/example/Target";
|
||||
target.superName = "java/lang/Object";
|
||||
target.methods.add(method);
|
||||
|
||||
final String message = assertThrows(IllegalStateException.class, () -> SurroundApplicatorExtension.checkHandlersRenamed(target, Set.of())).getMessage();
|
||||
assertTrue(message.startsWith("surround$enter()V in com/example/Target (merged from " + mixin + ") carries @Surround but was never parsed as an injector"), message);
|
||||
assertTrue(message.contains("SurroundBootstrap.init() must run before"), message);
|
||||
}
|
||||
|
||||
private static void assertCheckClassActiveUnderVerify(Extensions extensions) {
|
||||
final List<String> active = names(extensions.getActiveExtensions());
|
||||
assertEquals(Boolean.getBoolean("mixin.debug.verify"), active.contains(CHECK_CLASS), active.toString());
|
||||
}
|
||||
|
||||
private static List<String> names(List<IExtension> list) {
|
||||
final List<String> names = new ArrayList<>(list.size());
|
||||
for (IExtension extension : list) {
|
||||
names.add(extension.getClass().getSimpleName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static int assertSlot(List<IExtension> list, IExtension ours) {
|
||||
final List<String> names = names(list);
|
||||
assertEquals(list.indexOf(ours), list.lastIndexOf(ours), "registered twice: " + names);
|
||||
final int at = list.indexOf(ours);
|
||||
assertTrue(at >= 0, "missing: " + names);
|
||||
for (String after : names.subList(at + 1, names.size())) {
|
||||
assertFalse(RUN_BEFORE.contains(after), after + " runs after us: " + names);
|
||||
}
|
||||
final int check = names.indexOf(CHECK_CLASS);
|
||||
assertTrue(check < 0 || check > at, CHECK_CLASS + " runs before us: " + names);
|
||||
return at;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import makamys.mixingasm.api.MixinSafeTransformer;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.commons.LocalVariablesSorter;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@MixinSafeTransformer
|
||||
public final class LocalSortingTransformer implements IClassTransformer {
|
||||
|
||||
public static final Set<String> SEEN = ConcurrentHashMap.newKeySet();
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] bytes) {
|
||||
SEEN.add(transformedName);
|
||||
if (bytes == null || ((bytes[6] & 0xFF) << 8 | bytes[7] & 0xFF) > Opcodes.V1_8) {
|
||||
return bytes;
|
||||
}
|
||||
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
new ClassReader(bytes).accept(new ClassVisitor(Opcodes.ASM5, writer) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
return new LocalVariablesSorter(Opcodes.ASM5, access, desc, super.visitMethod(access, name, desc, signature, exceptions)) {
|
||||
};
|
||||
}
|
||||
}, ClassReader.EXPAND_FRAMES);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
}
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import com.llamalad7.mixinextras.MixinExtrasBootstrap;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
import org.junit.jupiter.api.Named;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.spongepowered.asm.launch.MixinBootstrap;
|
||||
import org.spongepowered.asm.lib.ClassReader;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
import org.spongepowered.asm.lib.util.Textifier;
|
||||
import org.spongepowered.asm.lib.util.TraceClassVisitor;
|
||||
import org.spongepowered.asm.mixin.EnvironmentStateTweaker;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
import org.spongepowered.asm.mixin.Mixins;
|
||||
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public final class MixinTestBootstrap {
|
||||
|
||||
private MixinTestBootstrap() {
|
||||
}
|
||||
|
||||
private static final class Holder {
|
||||
|
||||
static final IMixinTransformer TRANSFORMER = boot();
|
||||
}
|
||||
|
||||
public static IMixinTransformer transformer() {
|
||||
return Holder.TRANSFORMER;
|
||||
}
|
||||
|
||||
private static final Map<String, byte[]> TRANSFORMED = new HashMap<>();
|
||||
private static final Map<String, Class<?>> DEFINED = new HashMap<>();
|
||||
|
||||
public static byte[] transformedBytes(String binaryName) {
|
||||
return TRANSFORMED.computeIfAbsent(binaryName, name -> transformer().transformClassBytes(name, name, read(name)));
|
||||
}
|
||||
|
||||
public static Class<?> transform(String binaryName) {
|
||||
return DEFINED.computeIfAbsent(binaryName, name -> define(name, transformedBytes(name)));
|
||||
}
|
||||
|
||||
public static Object call(String target, String name, Object... args) throws Exception {
|
||||
final Class<?> owner = transform(target);
|
||||
for (Method method : owner.getMethods()) {
|
||||
if (method.getName().equals(name) && method.getParameterCount() == args.length) {
|
||||
final boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
return method.invoke(isStatic ? null : owner.getDeclaredConstructor().newInstance(), args);
|
||||
}
|
||||
}
|
||||
throw new AssertionError("no " + name + " in " + target);
|
||||
}
|
||||
|
||||
static final String WHOLE = "com.gtnewhorizons.angelica.experimental.surround.integration.WholeTarget";
|
||||
static final String CALL = "com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget";
|
||||
static final String WRAP = "com.gtnewhorizons.angelica.experimental.surround.integration.WrapTarget";
|
||||
|
||||
static ClassNode transformedNode(String target) {
|
||||
return parse(transformedBytes(target));
|
||||
}
|
||||
|
||||
static ClassNode originalNode(String binaryName) {
|
||||
return parse(read(binaryName));
|
||||
}
|
||||
|
||||
private static ClassNode parse(byte[] bytes) {
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(bytes).accept(node, 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
static MethodNode method(ClassNode node, String name) {
|
||||
for (MethodNode method : node.methods) {
|
||||
if (method.name.equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("no method " + name + " in " + node.name);
|
||||
}
|
||||
|
||||
static void assertGolden(String target, String method, String golden) {
|
||||
final ClassNode full = transformedNode(target);
|
||||
final ClassNode slim = new ClassNode();
|
||||
slim.visit(full.version, full.access, full.name, null, full.superName, null);
|
||||
slim.methods.add(method(full, method));
|
||||
for (MethodNode candidate : full.methods) {
|
||||
if (candidate.name.startsWith(method + "$surround")) {
|
||||
slim.methods.add(candidate);
|
||||
}
|
||||
}
|
||||
final StringWriter out = new StringWriter();
|
||||
slim.accept(new TraceClassVisitor(null, new Textifier(), new PrintWriter(out)));
|
||||
final String stripped = out.toString().replaceAll("(?m)^ *LINENUMBER \\d+ L\\d+\\r?\\n", "");
|
||||
final String actual = normalize(stripped.replaceAll("\\b([a-z]+)\\$[a-z]{3,}[0-9a-f]{3,}\\$", "$1\\$\\$").replaceAll("\\bmd[0-9a-f]+\\$jvmdowngrader\\$", "md\\$jvmdowngrader\\$"));
|
||||
final Path file = Paths.get(System.getProperty("surround.golden.dir")).resolve(golden + ".asm");
|
||||
try {
|
||||
if (!Files.exists(file)) {
|
||||
Files.write(file, actual.getBytes(StandardCharsets.UTF_8));
|
||||
fail("Created golden " + file + ". Inspect it, then re-run.");
|
||||
}
|
||||
assertEquals(normalize(new String(Files.readAllBytes(file), StandardCharsets.UTF_8)), actual, "output changed; inspect the diff before updating " + file);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String normalize(String text) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (String line : text.replace("\r\n", "\n").split("\n", -1)) {
|
||||
result.append(line.replaceAll("\\s+$", "").replaceAll("(\\$jvmdowngrader\\$concat\\$[\\w$]*?\\$\\d+)\\$[0-9a-f]+\\b", "$1")).append('\n');
|
||||
}
|
||||
return result.toString().trim() + "\n";
|
||||
}
|
||||
|
||||
static final Object VOID = null;
|
||||
|
||||
static Arguments scenario(String target, String name, String method, Object[] inputs, Object[] outputs, String... events) {
|
||||
return Arguments.of(Named.of(name, method), target, inputs, outputs, events);
|
||||
}
|
||||
|
||||
static Object[] of(Object... values) {
|
||||
return values;
|
||||
}
|
||||
|
||||
static List<Object> args(Object... values) {
|
||||
return Arrays.asList(values);
|
||||
}
|
||||
|
||||
static void assertScenario(String target, String method, Object[] inputs, Object[] outputs, String[] events) throws Exception {
|
||||
Trace.reset();
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
final Object[] args = inputs[i] instanceof List<?> list ? list.toArray() : new Object[]{inputs[i]};
|
||||
if (outputs[i] instanceof Class<?> type && Throwable.class.isAssignableFrom(type)) {
|
||||
final InvocationTargetException thrown = assertThrows(InvocationTargetException.class, () -> call(target, method, args));
|
||||
assertEquals(outputs[i], thrown.getCause().getClass());
|
||||
} else {
|
||||
assertEquals(outputs[i], call(target, method, args));
|
||||
}
|
||||
}
|
||||
Trace.assertEvents(events);
|
||||
}
|
||||
|
||||
private static final boolean MIXINEXTRAS = Boolean.getBoolean("surround.test.mixinextras");
|
||||
private static final boolean ADDITIONS_FIRST = Boolean.getBoolean("surround.test.additionsFirst");
|
||||
private static final boolean OBF = Boolean.getBoolean("surround.test.obf");
|
||||
|
||||
private static IMixinTransformer boot() {
|
||||
Launch.blackboard = new HashMap<>();
|
||||
Launch.classLoader = new LaunchClassLoader(classpath());
|
||||
if (OBF) {
|
||||
Launch.classLoader.addTransformerExclusion(LocalSortingTransformer.class.getName());
|
||||
Launch.classLoader.registerTransformer(LocalSortingTransformer.class.getName());
|
||||
if (Launch.classLoader.getTransformers().stream().noneMatch(t -> t.getClass().getName().equals(LocalSortingTransformer.class.getName()))) {
|
||||
throw new IllegalStateException("LocalSortingTransformer was not registered");
|
||||
}
|
||||
}
|
||||
|
||||
if (!MIXINEXTRAS) {
|
||||
SurroundBootstrap.init();
|
||||
}
|
||||
MixinBootstrap.init();
|
||||
if (MIXINEXTRAS) {
|
||||
if (ADDITIONS_FIRST) {
|
||||
SurroundBootstrap.init();
|
||||
}
|
||||
MixinExtrasBootstrap.init();
|
||||
if (!ADDITIONS_FIRST) {
|
||||
SurroundBootstrap.init();
|
||||
}
|
||||
}
|
||||
Mixins.addConfiguration("mixins.surround.test.json");
|
||||
if (!OBF) {
|
||||
Mixins.addConfiguration(RejectionCases.compileConfig(MIXINEXTRAS));
|
||||
}
|
||||
if (MIXINEXTRAS) {
|
||||
Mixins.addConfiguration("mixins.surround.mixinextras.test.json");
|
||||
}
|
||||
|
||||
final EnvironmentStateTweaker tweaker = new EnvironmentStateTweaker();
|
||||
tweaker.injectIntoClassLoader(Launch.classLoader);
|
||||
tweaker.getLaunchArguments();
|
||||
|
||||
return active();
|
||||
}
|
||||
|
||||
private static IMixinTransformer active() {
|
||||
final Object transformer = MixinEnvironment.getCurrentEnvironment().getActiveTransformer();
|
||||
if (!(transformer instanceof IMixinTransformer mixinTransformer)) {
|
||||
throw new IllegalStateException("no active mixin transformer, got " + transformer);
|
||||
}
|
||||
return mixinTransformer;
|
||||
}
|
||||
|
||||
private static URL[] classpath() {
|
||||
final List<URL> urls = new ArrayList<>();
|
||||
for (String entry : System.getProperty("java.class.path").split(File.pathSeparator)) {
|
||||
try {
|
||||
urls.add(new File(entry).toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalStateException(entry, e);
|
||||
}
|
||||
}
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
|
||||
static byte[] read(String binaryName) {
|
||||
try {
|
||||
final byte[] bytes = Launch.classLoader.getClassBytes(binaryName);
|
||||
if (bytes == null) {
|
||||
throw new IllegalStateException("missing " + binaryName);
|
||||
}
|
||||
return bytes;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(binaryName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> define(String name, byte[] bytes) {
|
||||
try {
|
||||
return new DefiningClassLoader(name, bytes).loadClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DefiningClassLoader extends ClassLoader {
|
||||
|
||||
private final String name;
|
||||
private final byte[] bytes;
|
||||
|
||||
DefiningClassLoader(String name, byte[] bytes) {
|
||||
super(MixinTestBootstrap.class.getClassLoader());
|
||||
this.name = name;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (!this.name.equals(name)) {
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
Class<?> loaded = findLoadedClass(name);
|
||||
if (loaded == null) {
|
||||
loaded = defineClass(name, this.bytes, 0, this.bytes.length);
|
||||
}
|
||||
if (resolve) {
|
||||
resolveClass(loaded);
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
final class RejectionCases {
|
||||
|
||||
static final String PACKAGE = "rejection";
|
||||
static final String CONFIG = "mixins.surround.rejection.test.json";
|
||||
static final String ABS = "at = @At(value = \"INVOKE\", target = \"Ljava/lang/Math;abs(I)I\")";
|
||||
static final String WHOLE_ENTRY = "@Surround(method = \"run\") private void invalid$enter(int a) {}";
|
||||
static final String FINALLY = " @Surround.Finally private void invalid$always() {}";
|
||||
static final String RUN = cls("public int run(int a) { return Math.abs(a); }");
|
||||
static final String IN_NEW = cls("public int run(int a) { return new StringBuilder(Math.abs(a)).length(); }");
|
||||
|
||||
static final Case[] CASES = {
|
||||
new Case("Initializer", "@Surround invalid$enter cannot target the initializer", RUN, mixin("@Surround(method = \"<init>()V\") private void invalid$enter() {}")),
|
||||
new Case("Staticness", "must be non-static to match the entry handler", RUN, mixin(WHOLE_ENTRY + " @Surround.Finally private static void invalid$always() {}")),
|
||||
new Case("CarriedThrowable", "cannot mark its caught Throwable parameter @Surround.Carry", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) {" + " @Surround.Carry Throwable state = null; }" + " @Surround.Catch private void invalid$caught(@Surround.Carry Throwable e) {}")),
|
||||
new Case("LocalCall", "is @Surround.Local java.lang.String, which matches no single local at the call", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a, @Surround.Local String s) {}")),
|
||||
new Case("HandleInsideNew", "declares handle = true, but the call is inside a constructor call's arguments", IN_NEW, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {}" + " @Surround.Catch(handle = true) private int invalid$caught(Throwable error) { return 0; }")),
|
||||
new Case("SkippedRequired", "can skip a int result but has no @Surround.Skipped", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {" + " @Surround.Skip boolean skip = a > 0; }")),
|
||||
new Case("Shift", "@Surround invalid$enter has @At(\"INVOKE\") with a non-default shift or by, which moves the match off the call", RUN, mixin("@Surround(method = \"run\", at = @At(value = \"INVOKE\", target = \"Ljava/lang/Math;abs(I)I\", shift = At.Shift.AFTER)) private void invalid$enter() {}")),
|
||||
new Case("AbstractTarget", "cannot target the abstract or native method", "public abstract class $T { public abstract int run(int a); }", "@Mixin(value = $T.class, remap = false) public abstract class $M {" + " @Surround(method = \"run\") private void invalid$enter(int a) {} }"),
|
||||
new Case("InitAt", "at must select a method call, not the constructor call java/lang/StringBuilder.<init>()V", cls("public Object run(int a) { return new StringBuilder(); }"), mixin("@Surround(method = \"run\", at = @At(value = \"INVOKE\", target = \"Ljava/lang/StringBuilder;<init>()V\")) private void invalid$enter() {}")),
|
||||
new Case("CatchingEntry", "must not catch exceptions when it surrounds a call inside a constructor call's arguments", IN_NEW, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {" + " try { Math.abs(a); } catch (RuntimeException e) { Math.abs(0); } }" + FINALLY)),
|
||||
new Case("TooManyOperands", "entry handler has more parameters than call java/lang/Math.abs(I)I", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a, int b) {}")),
|
||||
new Case("SkipNotAssigned", "has a @Surround.Skip local in slot 2 that is not definitely assigned", cls("public void run(int a) {}"), mixin("@Surround(method = \"run\") private void invalid$enter(int a) {" + " @Surround.Skip boolean skip; if (a > 0) { skip = true; String.valueOf(a); } }")),
|
||||
new Case("Collision", "exit handler 'surround$exit' collides with surround$exit()V in " + PACKAGE + "/Collision, which is merged from " + PACKAGE + ".mixin.CollisionMixin2", RUN, "@Mixin(value = $T.class, priority = 900, remap = false) public class $M {" + " @Surround(method = \"run\") private void surround$enter(int a) {}" + " @Surround.Finally private void surround$exit() {} }", "@Mixin(value = $T.class, priority = 1100, remap = false) public class $M {" + " private void surround$exit() {} }"),
|
||||
new Case("MultipleRoles", "can be only one of @Surround.Catch, @Surround.Finally, @Surround.Skipped and @Surround.Return", RUN, mixin(WHOLE_ENTRY + " @Surround.Catch @Surround.Finally private void invalid$exit(Throwable e) {}")),
|
||||
new Case("DuplicateId", "duplicate @Surround the default id; ids must be unique per mixin", RUN, mixin(WHOLE_ENTRY + " @Surround(method = \"run\") private void invalid$enter2(int a) {}")),
|
||||
new Case("OrphanExit", "no @Surround in this mixin declares it", RUN, mixin(WHOLE_ENTRY + " @Surround.Finally(\"typo\") private void invalid$always() {}")),
|
||||
new Case("CarryOnEntryParameter", "only the entry handler's own locals can be carried", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(@Surround.Carry int a) {}")),
|
||||
new Case("CarryIdNotDeclared", "asks for the carried local 'frist'", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry(\"first\") int first = 1; }" + " @Surround.Finally private void invalid$always(@Surround.Carry(\"frist\") int typo) {}")),
|
||||
new Case("NoCarryOfKind", "matches no carried local of that kind", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry int count = 1; }" + " @Surround.Finally private void invalid$always(@Surround.Carry float missing) {}")),
|
||||
new Case("AmbiguousCarry", "matches 2 carried locals of that kind", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry int first = 1; @Surround.Carry int second = 2; }" + " @Surround.Finally private void invalid$always(@Surround.Carry int either) {}")),
|
||||
new Case("CarryReadAsDifferentType", "but another exit handler reads it as java.lang.String", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry Object value = null; }" + " @Surround.Catch private void invalid$caught(java.io.IOException e, @Surround.Carry String value) {}" + " @Surround.Finally private void invalid$always(@Surround.Carry Integer value) {}")),
|
||||
new Case("AmbiguousWideLocal", "shares its scope with another", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry long first; long second; try { first = a; second = a + 1L; } catch (RuntimeException e) { throw e; } }")),
|
||||
new Case("DuplicateCarryId", "declares two @Surround.Carry locals named 'x'", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Carry(\"x\") int first = 1; @Surround.Carry(\"x\") long second = 2L; }")),
|
||||
new Case("CarryInsideType", "@Surround.Carry must annotate the local itself, not part of its type", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { java.util.List<@Surround.Carry String> l = null; }")),
|
||||
new Case("DuplicateSkip", "declares two @Surround.Skip locals", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Skip boolean first = a > 0; @Surround.Skip boolean second = a < 0; }")),
|
||||
new Case("SkipNotIntLike", "it must be boolean or int-like", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { @Surround.Skip long skip = a; }")),
|
||||
new Case("SkippedWithoutSkip", "but declares no @Surround.Skip local, so nothing is ever skipped", RUN, mixin(WHOLE_ENTRY + " @Surround.Skipped private int invalid$skipped(int a) { return 0; }")),
|
||||
new Case("CarryScopedEarly", "goes out of scope before the entry handler ends", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) {" + " { @Surround.Carry int scoped = a; String.valueOf(scoped); } int reused = a + 1; String.valueOf(reused); }" + " @Surround.Finally private void invalid$always(@Surround.Carry int value) {}")),
|
||||
new Case("EarlyReturn", "every path falls through to its end", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) {" + " if (a > 0) { return; } String.valueOf(a); }" + FINALLY)),
|
||||
new Case("PartialAfterInt", "is not definitely assigned when the entry handler ends", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) {" + " { int tmp = a + 5; String.valueOf(tmp); }" + " @Surround.Carry int flag; if (a > 0) { flag = 1; String.valueOf(flag); } }" + " @Surround.Finally private void invalid$always(@Surround.Carry int flag) {}")),
|
||||
new Case("AssignsParameter", "must not assign its parameters", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { a = a + 1; }" + FINALLY)),
|
||||
new Case("LocalOnWholeMethod", "is @Surround.Local, which only applies when @Surround.at selects a call", RUN, mixin(WHOLE_ENTRY + " @Surround.Finally private void invalid$always(@Surround.Local int b) {}")),
|
||||
new Case("IncrementsParameter", "must not assign its parameters", RUN, mixin("@Surround(method = \"run\") private void invalid$enter(int a) { a++; }" + FINALLY)),
|
||||
new Case("NonCallAt", "@Surround invalid$enter has @At(\"HEAD\"), which does not select a method call", RUN, mixin("@Surround(method = \"run\", at = @At(\"HEAD\")) private void invalid$enter() {}")),
|
||||
new Case("CancellableExit", "parameter 0 ('ci') of 'invalid$always' is annotated @Cancellable, which a @Surround handler cannot resolve", RUN, mixin(WHOLE_ENTRY + " @Surround.Finally private void invalid$always(@Cancellable Object ci) {}")),
|
||||
new Case("DuplicateFinally", "declares more than one @Surround.Finally", RUN, mixin(WHOLE_ENTRY + FINALLY + " @Surround.Finally private void invalid$always2() {}")),
|
||||
new Case("ReturnWrongType", "must return java.lang.String to match what it surrounds, not int", cls("public String run(String s) { return s.trim(); }"), mixin("@Surround(method = \"run\", at = @At(value = \"INVOKE\", target = \"Ljava/lang/String;trim()Ljava/lang/String;\")) private void invalid$enter(String s) {}" + " @Surround.Return private int invalid$ret(int result) { return result; }")),
|
||||
new Case("ReturnResultNotFirst", "must declare the int result as its first parameter", RUN, mixin(WHOLE_ENTRY + " @Surround.Return private int invalid$ret(String wrong) { return 0; }")),
|
||||
new Case("ReturnCarriedResult", "cannot mark its result parameter @Surround.Carry", RUN, mixin(WHOLE_ENTRY + " @Surround.Return private int invalid$ret(@Surround.Carry int result) { return result; }")),
|
||||
new Case("NonVoidFinally", "must return void; an exit handler cannot suppress or replace anything", RUN, mixin(WHOLE_ENTRY + " @Surround.Finally private int invalid$always() { return 1; }")),
|
||||
new Case("NonVoidEntry", "must return void; carry state out with @Surround.Carry locals instead", RUN, mixin("@Surround(method = \"run\") private long invalid$enter(int a) { return 0L; }")),
|
||||
new Case("CatchNotThrowable", "parameter 0 ('notAnError') catches java.lang.String, which is not a java.lang.Throwable subtype", RUN, mixin(WHOLE_ENTRY + " @Surround.Catch private void invalid$caught(String notAnError) {}")),
|
||||
new Case("DuplicateCatch", "is a second @Surround.Catch for java.io.IOException", RUN, mixin(WHOLE_ENTRY + " @Surround.Catch private void invalid$first(java.io.IOException e) {} @Surround.Catch private void invalid$second(java.io.IOException e) {}")),
|
||||
new Case("CatchWithoutParameters", "must declare the Throwable it catches as its first parameter", RUN, mixin(WHOLE_ENTRY + " @Surround.Catch private void invalid$caught() {}")),
|
||||
new Case("AbstractEntry", "must not be abstract", RUN, "@Mixin(value = $T.class, remap = false) public abstract class $M { @Surround(method = \"run\") protected abstract void invalid$enter(int a); }"),
|
||||
new Case("AbstractCatch", "must not be abstract", RUN, "@Mixin(value = $T.class, remap = false) public abstract class $M { " + WHOLE_ENTRY + " @Surround.Catch protected abstract void invalid$caught(Throwable t); }"),
|
||||
new Case("MisorderedLocalAfterCarry", "parameter 1 ('y') is out of order; declare operands first, then @Surround.Local, then @Surround.Carry parameters", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {}" + " @Surround.Finally private void invalid$always(@Surround.Carry int x, @Surround.Local int y) {}")),
|
||||
new Case("LocalAndCarry", "parameter 0 ('a') cannot be both @Surround.Local and @Surround.Carry", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {}" + " @Surround.Finally private void invalid$always(@Surround.Local @Surround.Carry int a) {}")),
|
||||
new Case("LocalCaught", "cannot mark its caught Throwable parameter @Surround.Local", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int a) {}" + " @Surround.Catch private void invalid$caught(@Surround.Local Throwable e) {}")),
|
||||
new Case("HandleWrongReturn", "must return java.lang.String to match what it surrounds, not int", cls("public String run(String s) { return s.trim(); }"), mixin("@Surround(method = \"run\", at = @At(value = \"INVOKE\", target = \"Ljava/lang/String;trim()Ljava/lang/String;\")) private void invalid$enter(String s) {}" + " @Surround.Catch(handle = true) private int invalid$caught(Throwable e, String s) { return 0; }")),
|
||||
new Case("HandleWithoutAt", "declares handle = true, which only applies when @Surround.at selects a call", RUN, mixin(WHOLE_ENTRY + " @Surround.Catch(handle = true) private void invalid$caught(Throwable e) {}")),
|
||||
new Case("SliceWithoutAt", "declares @Surround.slice, which only applies when @Surround.at selects a call", RUN, mixin("@Surround(method = \"run\", slice = @Slice(from = @At(\"HEAD\"))) private void invalid$enter(int a) {}")),
|
||||
new Case("EntryOperandMismatch", "parameter 0 ('notAnInt') is java.lang.String, which does not match the int operand of call java/lang/Math.abs(I)I", RUN, mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(String notAnInt) {}")),
|
||||
new Case("OrphanWithoutSurround", "@Surround exit handler 'invalid$always' refers to the default id but no @Surround in this mixin declares it; this mixin has no @Surround at all", RUN, mixin(WHOLE_ENTRY), mixin("@Surround.Finally private void invalid$always() {}"))
|
||||
};
|
||||
|
||||
static final Case SUGAR = new Case("Sugar", "@Surround invalid$enter one of its parameters is annotated @Local, @Share or @Cancellable, which a @Surround handler cannot resolve", cls("public int run(int a) { int local = a + 1; return Math.abs(local); }"), mixin("@Surround(method = \"run\", " + ABS + ") private void invalid$enter(int local, @Local(ordinal = 0) int a) {}"));
|
||||
|
||||
static final Case REQUIRE_ZERO_MISS = new Case("RequireZeroMiss", "", RUN, mixin("@Surround(method = \"run\", require = 0, at = @At(value = \"INVOKE\", target = \"Ljava/lang/Math;max(II)I\")) private void invalid$enter(int a, int b) {}" + FINALLY));
|
||||
|
||||
static String compileConfig(boolean mixinExtras) {
|
||||
final List<Case> cases = new ArrayList<>();
|
||||
if (mixinExtras) {
|
||||
cases.add(SUGAR);
|
||||
} else {
|
||||
cases.addAll(Arrays.asList(CASES));
|
||||
cases.add(REQUIRE_ZERO_MISS);
|
||||
}
|
||||
final List<Unit> units = new ArrayList<>();
|
||||
final List<String> mixins = new ArrayList<>();
|
||||
for (Case c : cases) {
|
||||
units.add(new Unit(PACKAGE + "." + c.name, "package " + PACKAGE + "; " + c.target.replace("$T", c.name)));
|
||||
for (int i = 0; i < c.mixins.length; i++) {
|
||||
final String name = c.name + "Mixin" + (i + 1);
|
||||
mixins.add('"' + name + '"');
|
||||
units.add(new Unit(PACKAGE + ".mixin." + name, "package " + PACKAGE + ".mixin; import " + PACKAGE + ".*;" + " import com.gtnewhorizons.angelica.experimental.surround.Surround;" + " import com.llamalad7.mixinextras.sugar.Cancellable;" + " import com.llamalad7.mixinextras.sugar.Local;" + " import org.spongepowered.asm.mixin.Mixin;" + " import org.spongepowered.asm.mixin.injection.At;" + " import org.spongepowered.asm.mixin.injection.Slice;" + c.mixins[i].replace("$T", c.name).replace("$M", name)));
|
||||
}
|
||||
}
|
||||
try {
|
||||
final Path dir = Paths.get("rejection-classes").toAbsolutePath();
|
||||
if (Files.exists(dir)) {
|
||||
try (Stream<Path> stale = Files.walk(dir)) {
|
||||
stale.sorted(Comparator.reverseOrder()).forEach(path -> path.toFile().delete());
|
||||
}
|
||||
}
|
||||
final Path sources = Files.createDirectories(dir.resolve("src"));
|
||||
final List<String> command = new ArrayList<>(Arrays.asList(System.getProperty("surround.javac"), "-classpath", System.getProperty("java.class.path"), "-d", dir.toString(), "-proc:none", "-g", "--release", "8", "-Xlint:-options"));
|
||||
for (Unit unit : units) {
|
||||
final Path file = sources.resolve(unit.className().replace('.', '/') + ".java");
|
||||
Files.createDirectories(file.getParent());
|
||||
Files.write(file, unit.source().getBytes(StandardCharsets.UTF_8));
|
||||
command.add(file.toString());
|
||||
}
|
||||
final Process javac = new ProcessBuilder(command).redirectErrorStream(true).start();
|
||||
final String output = new String(javac.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
|
||||
if (javac.waitFor() != 0) {
|
||||
throw new IllegalStateException(output);
|
||||
}
|
||||
Files.write(dir.resolve(CONFIG), ("{\"required\": true, \"minVersion\": \"0.8\", \"package\": \"" + PACKAGE + ".mixin\"," + " \"compatibilityLevel\": \"JAVA_8\", \"mixins\": " + mixins + "}").getBytes(StandardCharsets.UTF_8));
|
||||
Launch.classLoader.addURL(dir.toUri().toURL());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
private static String cls(String members) {
|
||||
return "public class $T { " + members + " }";
|
||||
}
|
||||
|
||||
private static String mixin(String members) {
|
||||
return "@Mixin(value = $T.class, remap = false) public class $M { " + members + " }";
|
||||
}
|
||||
|
||||
private record Unit(String className, String source) {
|
||||
}
|
||||
|
||||
static final class Case {
|
||||
|
||||
final String name;
|
||||
final String expected;
|
||||
final String target;
|
||||
final String[] mixins;
|
||||
|
||||
Case(String name, String expected, String target, String... mixins) {
|
||||
this.name = name;
|
||||
this.expected = expected;
|
||||
this.target = target;
|
||||
this.mixins = mixins;
|
||||
}
|
||||
|
||||
void assertRejected() {
|
||||
final String messages = causeChain(transformExpectingFailure(PACKAGE + "." + this.name));
|
||||
assertTrue(messages.contains(this.expected), messages);
|
||||
}
|
||||
|
||||
private static Throwable transformExpectingFailure(String binaryName) {
|
||||
try {
|
||||
MixinTestBootstrap.transformer().transformClassBytes(binaryName, binaryName, MixinTestBootstrap.read(binaryName));
|
||||
} catch (Throwable t) {
|
||||
return t;
|
||||
}
|
||||
throw new AssertionError(binaryName + " transformed without the expected failure");
|
||||
}
|
||||
|
||||
private static String causeChain(Throwable thrown) {
|
||||
final StringBuilder all = new StringBuilder();
|
||||
for (Throwable t = thrown; t != null; t = t.getCause()) {
|
||||
all.append(t).append('\n');
|
||||
}
|
||||
return all.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.junit.jupiter.api.Named;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
class RejectionTest {
|
||||
|
||||
static Stream<Named<RejectionCases.Case>> rejections() {
|
||||
return Arrays.stream(RejectionCases.CASES).map(c -> Named.of(c.name, c));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("rejections")
|
||||
void anInvalidMixinIsRejectedAtLoad(RejectionCases.Case rejection) {
|
||||
rejection.assertRejected();
|
||||
}
|
||||
|
||||
@Test
|
||||
void aRequireZeroCallSurroundThatMatchesNoCallIsNotRejected() {
|
||||
assertDoesNotThrow(() -> MixinTestBootstrap.transform(RejectionCases.PACKAGE + "." + RejectionCases.REQUIRE_ZERO_MISS.name));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("mixinextras")
|
||||
void mixinExtrasSugarOnACallSiteEntryHandlerIsRejected() {
|
||||
RejectionCases.SUGAR.assertRejected();
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.CALL;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.VOID;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.WHOLE;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.args;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.of;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.scenario;
|
||||
|
||||
class ScenarioTest {
|
||||
|
||||
static Stream<Arguments> scenarios() {
|
||||
return Stream.of(
|
||||
scenario(WHOLE, "a surround encloses the body and every other injector", "compute", of(7), of(14), "enter 7 1", "head 7", "body 7", "tail 14", "exit 1"),
|
||||
scenario(WHOLE, "the narrowest catch handler wins and finally runs when the body throws", "boom", of(args()), of(IllegalStateException.class), "enterBoom 1", "boomHead", "boom", "caught 1 boom", "exitBoom 1"),
|
||||
scenario(WHOLE, "an instance monitor covers the handlers and the body", "lockedInstance", of(7), of(14), "enterInstance true", "body true", "exitInstance true"),
|
||||
scenario(WHOLE, "a unique exit handler renamed on collision still binds", "run", of(args()), of(VOID), "enter", "run", "target exit", "handler exit"),
|
||||
scenario(WHOLE, "a lower priority mixin is the inner surround", "priority", of(5), of(60), "enterHigh", "enterLow", "body 5", "returnLow 5", "exitLow", "returnHigh 6", "exitHigh"),
|
||||
scenario(WHOLE, "static wide arguments", "wideBody", of(args(2.5d, 5L)), of(7.5d), "enter 1", "body", "exit 1"),
|
||||
scenario(WHOLE, "a throwing entry handler skips the body and the exit handler", "throwingEntry", of(5), of(IllegalArgumentException.class), "enterThrows 5"),
|
||||
scenario(WHOLE, "a throwing finally replaces the return value", "throwingFinally", of(args(9, 4)), of(UnsupportedOperationException.class), "enter 1", "gt", "exitThrows 1"),
|
||||
scenario(WHOLE, "pre-existing exception handlers still fire", "caught", of(4, -4), of(4, -1), "enter 1", "inner-finally", "exit 1", "enter 2", "caught neg", "inner-finally", "exit 2"),
|
||||
scenario(WHOLE, "three kinds bind by kind, not position", "carryThree", of(7), of(14), "enter 8 107 L7", "body 7", "exit 8 107 L7"),
|
||||
scenario(WHOLE, "frame naming an unbuilt object", "carryUninitialized", of(5), of(10), "enter pos", "body 5", "exit pos"),
|
||||
scenario(WHOLE, "entry handler's own try-catch", "carryGuarded", of(-3), of(-6), "recovered", "enter -1", "body -3", "exit -1"),
|
||||
scenario(WHOLE, "assigned in both branches at the end", "carrySplitAtEnd", of(-3), of(-6), "body -3", "exit 2"),
|
||||
scenario(WHOLE, "wide carry ahead of an int across a frame", "carryWideBeforeFrame", of(3), of(6), "positive", "body 3", "exit 3 4"),
|
||||
scenario(WHOLE, "array locals", "carryArrays", of(3), of(6), "body 3", "exit n3 3"),
|
||||
scenario(WHOLE, "widths and an uncarried local between carried ones survive the handler frame", "throwWide", of(3), of(IllegalStateException.class), "enter 4 1.5 3 s3", "body 3", "caught 4 1.5 3", "exit 4 1.5 3"),
|
||||
scenario(WHOLE, "a reference carry is cast to each exit handler's type and a throwing catch handler still runs finally", "throwObject", of(3), of(UnsupportedOperationException.class), "enter o3", "body 3", "caught 2 o3", "exit 2 o3"),
|
||||
scenario(WHOLE, "a reused slot carries the local's value", "reused", of(3), of(6), "tmp 8", "enter 10", "body 3", "exit 10"),
|
||||
scenario(CALL, "a static target takes static handlers around a static callee", "staticTarget", of(4), of(8), "enterStaticTarget 4", "twiceStatic 4", "exitStaticTarget 4 8"),
|
||||
scenario(CALL, "an interface callee is typed as the interface or a supertype", "interfaceCall", of(3), of(4), "enterInterface 3 true", "apply 3", "exitInterface true"),
|
||||
scenario(CALL, "a private callee's receiver is the target itself", "privateCall", of(3), of(4), "enterPrivate true 3", "priv 3", "exitPrivate true 3"),
|
||||
scenario(CALL, "every match is surrounded separately", "all", of(2), of(18), "enterAll 2 1", "twice 2", "exitAll 2 1", "enterAll 3 2", "twice 3", "exitAll 3 2", "enterAll 4 3", "twice 4", "exitAll 4 3"),
|
||||
scenario(CALL, "a slice restricts the matches", "sliced", of(2), of(10), "twice 2", "marker", "enterSliced 3", "twice 3", "exitSliced 3"),
|
||||
scenario(CALL, "a throwing entry handler runs no exit handler and skips the call", "throwingEntry", of(args()), of(IllegalArgumentException.class), "enterThrowingEntry 1"),
|
||||
scenario(CALL, "inject before, modify arg and inject after stay outside", "composed", of(5), of(210), "before 5", "modifyArg 5", "enterComposed 105", "twice 105", "exitComposed 105", "after 5"),
|
||||
scenario(WHOLE, "a void target skips without a skipped handler", "skippedVoid", of(-1, 1), of(VOID, VOID), "enterSkippedVoid -1", "exitSkippedVoid", "enterSkippedVoid 1", "skippedVoid 1", "exitSkippedVoid"),
|
||||
scenario(WHOLE, "an entry handler may call the target directly and then skip", "reentrant", of(1), of(24), "enterReentrant 11 false", "reentrant 11", "exitReentrant 11", "enterReentrant 1 true", "exitReentrant 1"),
|
||||
scenario(WHOLE, "return does not run after a skip and a reference skipped value is returned", "skipAndReturn", of("", "a", "x"), of(null, "a!?", IllegalStateException.class), "enterSkipAndReturn ", "skippedSkipAndReturn", "exitSkipAndReturn ", "enterSkipAndReturn a", "skipAndReturn a", "returnSkipAndReturn a!", "exitSkipAndReturn a", "enterSkipAndReturn x", "skipAndReturn x", "returnSkipAndReturn x!", "exitSkipAndReturn x"),
|
||||
scenario(WHOLE, "an entry handler may catch and rethrow around a direct call", "entryCatches", of(3, -1), of(6, IllegalStateException.class), "entryCatches 3", "exitEntryCatches false", "enterEntryCatches 3", "entryCatches 3", "exitEntryCatches true", "entryCatches -1", "exitEntryCatches false", "entryRethrow negative -1"),
|
||||
scenario(CALL, "a local is selected by name between operands and carries", "byName", of(3), of(49), "enterByName 23 1", "twice 13", "exitByName 13 23 1"),
|
||||
scenario(CALL, "wide locals bind", "wideLocals", of(3), of(31L), "enterWideLocals 30 1.5", "wide 30 1.5", "exitWideLocals 1.5 30"),
|
||||
scenario(CALL, "a handling catch replaces the result over the values below", "valueBelow", of(-3, 4), of(67, 84), "enterValueBelow -3 1", "explode -3", "handleValueBelow -3 1", "exitValueBelow -3 1", "enterValueBelow 4 2", "explode 4", "exitValueBelow 4 2"),
|
||||
scenario(CALL, "a skipped handler supplies the value of a skipped call", "valueSkip", of(-3, 3), of(-300, 6), "enterValueSkip -3 1", "skippedValueSkip -3 1", "exitValueSkip 1", "enterValueSkip 3 2", "twice 3", "exitValueSkip 2"),
|
||||
scenario(CALL, "a void return handler observes and does not run after a handled exception", "observed", of(2, -1), of(VOID, VOID), "enterObserved 2", "maybeBoom 2", "observe 2", "exitObserved 2", "enterObserved -1", "maybeBoom -1", "handleObserved -1", "exitObserved -1"),
|
||||
scenario(CALL, "a handled value bypasses the return handler", "returnAndHandle", of(4, -1), of(80, 7), "enterReturnAndHandle 4", "explode 4", "returnReturnAndHandle 8", "exitReturnAndHandle", "enterReturnAndHandle -1", "explode -1", "handleReturnAndHandle", "exitReturnAndHandle"),
|
||||
scenario(CALL, "the skip path is covered by catch and finally but not by return", "skipFinallyCatch", of(2, -3, 0), of(1004, 3, IllegalStateException.class), "enterSfc 2", "explode 2", "returnSfc 4", "exitSfc 2", "enterSfc -3", "skippedSfc -3", "exitSfc -3", "enterSfc 0", "skippedSfc 0", "caughtSfc zero", "exitSfc 0"),
|
||||
scenario(CALL, "an outer skip runs no inner handler and return handlers chain inner to outer", "nestedSkip", of(-2, 2), of(-1, 50), "enterNestedOuter -2", "skippedNestedOuter", "exitNestedOuter -2", "enterNestedOuter 2", "enterNestedInner 2", "twice 2", "returnNestedInner 4", "exitNestedInner 2", "returnNestedOuter 5", "exitNestedOuter 2"),
|
||||
scenario(CALL, "a skip skips the redirect handler inside", "redirectedSkip", of(-1, 3), of(0, 7), "enterRedirectedSkip -1", "exitRedirectedSkip -1", "enterRedirectedSkip 3", "redirectSkip 3", "twice 3", "exitRedirectedSkip 3"),
|
||||
scenario(CALL, "a final skip derived from a carry computed by a unique helper binds", "helperSkip", of(-4, 4), of(-1, 8), "part -4", "enterHelper -1", "skippedHelper", "exitHelper -1", "part 4", "enterHelper 4", "twice 4", "exitHelper 4"),
|
||||
scenario(CALL, "wide operands skip and return with an int skip flag", "wideSkip", of(args(-2L, 3.0d), args(5L, 2.5d)), of(-19L, 108L), "enterWideSkip -2 3.0", "skippedWideSkip -2 1.5", "exitWideSkip -2 3.0", "enterWideSkip 5 2.5", "wide 5 2.5", "returnWideSkip 7 1.25", "exitWideSkip 5 2.5"),
|
||||
scenario(CALL, "an entry-only call surround needs no exit handler even with a skip", "entryOnlySkip", of(1, -1), of(1, -1), "enterOnlySkip 1", "enterOnlySkip -1", "render -1"),
|
||||
scenario(CALL, "an entry-only whole-method surround", "entryOnlyWhole", of(args()), of(VOID), "enterOnlyWhole", "entryOnlyWhole"),
|
||||
scenario(CALL, "a skip restores the values below the call when the entry catches", "skipBelowRestored", of(-2, 3, 200), of(980, 1006, 1400), "enterSkipBelowRestored -2", "skippedSkipBelowRestored -2", "exitSkipBelowRestored -2", "enterSkipBelowRestored 3", "twice 3", "exitSkipBelowRestored 3", "entryCaughtSkipBelowRestored 200", "twice 200", "exitSkipBelowRestored 200"),
|
||||
scenario(CALL, "an inner skip completes normally for an outer return handler", "innerSkipOuterReturn", of(-2, 3), of(-199, 7), "enterOuterReturn -2", "enterInnerSkip -2", "skippedInnerSkip -2", "exitInnerSkip -2", "returnOuterReturn -200", "exitOuterReturn -2", "enterOuterReturn 3", "enterInnerSkip 3", "twice 3", "exitInnerSkip 3", "returnOuterReturn 6", "exitOuterReturn 3"),
|
||||
scenario(CALL, "a call inside a constructor call's arguments can be skipped", "skipInsideNew", of(-2, 3), of(-20, 6), "enterSkipInsideNew -2", "skippedSkipInsideNew -2", "exitSkipInsideNew -2", "enterSkipInsideNew 3", "twice 3", "exitSkipInsideNew 3"),
|
||||
scenario(CALL, "a call surround skips inside a body moved by a whole-method surround", "movedBody", of(-2, 3), of(990, 1008), "enterMovedWhole -2", "enterMovedCall -1 -2", "skippedMovedCall -1 -2", "exitMovedCall -1 -2", "exitMovedWhole -2", "enterMovedWhole 3", "enterMovedCall 4 3", "twice 4", "exitMovedCall 4 3", "exitMovedWhole 3"),
|
||||
scenario(WHOLE, "a branching entry handler around the whole method", "branching", of(-3), of(-6), "enterBranching negative", "body -3", "exitBranching negative")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("scenarios")
|
||||
void scenarios(String method, String target, Object[] inputs, Object[] outputs, String[] events) throws Exception {
|
||||
MixinTestBootstrap.assertScenario(target, method, inputs, outputs, events);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.LabelNode;
|
||||
import org.spongepowered.asm.lib.tree.LineNumberNode;
|
||||
import org.spongepowered.asm.lib.tree.LocalVariableNode;
|
||||
import org.spongepowered.asm.lib.tree.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.WHOLE;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.MixinTestBootstrap.call;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class WholeMethodTest {
|
||||
|
||||
@BeforeEach
|
||||
void resetTrace() {
|
||||
Trace.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneEntryHandlerCanSurroundSeveralTargets() throws Exception {
|
||||
call(WHOLE, "alpha");
|
||||
call(WHOLE, "beta");
|
||||
Trace.assertEvents("enterBoth 1", "alpha", "exitBoth 1", "enterBoth 2", "beta", "exitBoth 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void theWrapperKeepsTheEntryHandlersLinesAndTheBodyKeepsItsOwn() {
|
||||
final ClassNode merged = MixinTestBootstrap.transformedNode(WHOLE);
|
||||
final List<Integer> entryLines = lines(bySuffix(merged, "$surround$enterBranching"));
|
||||
assertFalse(entryLines.isEmpty(), "fixture compiled without line numbers");
|
||||
assertEquals(entryLines, lines(MixinTestBootstrap.method(merged, "branching")));
|
||||
assertEquals(lines(MixinTestBootstrap.method(MixinTestBootstrap.originalNode(WHOLE), "branching")), lines(MixinTestBootstrap.method(merged, "branching$surround")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aReusedSlotNamesTheCarriedLocal() {
|
||||
final MethodNode wrapper = MixinTestBootstrap.method(MixinTestBootstrap.transformedNode(WHOLE), "reused");
|
||||
LabelNode last = null;
|
||||
for (AbstractInsnNode insn = wrapper.instructions.getLast(); last == null; insn = insn.getPrevious()) {
|
||||
last = insn instanceof LabelNode label ? label : null;
|
||||
}
|
||||
final List<String> toEnd = new ArrayList<>();
|
||||
for (LocalVariableNode local : wrapper.localVariables) {
|
||||
if (local.end == last) {
|
||||
toEnd.add(local.name);
|
||||
}
|
||||
}
|
||||
assertTrue(toEnd.contains("prev") && !toEnd.contains("tmp"), "locals live to the end: " + toEnd);
|
||||
}
|
||||
|
||||
@Test
|
||||
void theWrapperMatchesTheGoldens() {
|
||||
MixinTestBootstrap.assertGolden(WHOLE, "skipAndReturn", "whole-skip-return");
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIfSystemProperty(named = "surround.test.obf", matches = "true")
|
||||
void theLaunchClassLoaderTransformersSeeTheMixinClass() throws Exception {
|
||||
MixinTestBootstrap.transform(WHOLE);
|
||||
final Set<?> seen = (Set<?>) Launch.classLoader.loadClass(LocalSortingTransformer.class.getName()).getField("SEEN").get(null);
|
||||
assertTrue(seen.contains("com.gtnewhorizons.angelica.experimental.surround.integration.mixin.MixinWholeTarget"), seen.toString());
|
||||
}
|
||||
|
||||
private static MethodNode bySuffix(ClassNode node, String suffix) {
|
||||
for (MethodNode method : node.methods) {
|
||||
if (method.name.endsWith(suffix)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
final List<String> names = new ArrayList<>();
|
||||
for (MethodNode method : node.methods) {
|
||||
names.add(method.name);
|
||||
}
|
||||
throw new AssertionError("no method ending " + suffix + " in " + names);
|
||||
}
|
||||
|
||||
private static List<Integer> lines(MethodNode method) {
|
||||
final List<Integer> lines = new ArrayList<>();
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn instanceof LineNumberNode ln) {
|
||||
lines.add(ln.line);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
public class CallTarget {
|
||||
|
||||
public static final String CALLEE = "Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;";
|
||||
public static final String TWICE = CALLEE + "twice(I)I";
|
||||
|
||||
private final Callee callee = new Callee();
|
||||
private final IntUnaryOperator op = new Callee();
|
||||
private final Render renderer = new Render();
|
||||
|
||||
public static int staticTarget(int a) {
|
||||
return Callee.twiceStatic(a);
|
||||
}
|
||||
|
||||
public int interfaceCall(int a) {
|
||||
return this.op.applyAsInt(a);
|
||||
}
|
||||
|
||||
public int privateCall(int a) {
|
||||
return this.priv(a);
|
||||
}
|
||||
|
||||
private int priv(int a) {
|
||||
Trace.add("priv " + a);
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
public int all(int a) {
|
||||
return this.callee.twice(a) + this.callee.twice(a + 1) + this.callee.twice(a + 2);
|
||||
}
|
||||
|
||||
public int sliced(int a) {
|
||||
final int first = this.callee.twice(a);
|
||||
Callee.marker();
|
||||
return first + this.callee.twice(a + 1);
|
||||
}
|
||||
|
||||
public void throwingEntry() {
|
||||
this.callee.twice(1);
|
||||
}
|
||||
|
||||
public int composed(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public boolean render(Entity entity, double x, double y, double z, float yaw, float partial, boolean flag) {
|
||||
Render render = null;
|
||||
try {
|
||||
render = this.renderer;
|
||||
if (render != null && entity != null) {
|
||||
try {
|
||||
render.doRender(entity, x, y, z, yaw, partial);
|
||||
} catch (Throwable throwable1) {
|
||||
throw new ReportedException("Rendering entity in world", throwable1);
|
||||
}
|
||||
try {
|
||||
render.doRenderShadowAndFire(entity, x, y, z, yaw, partial);
|
||||
} catch (Throwable throwable2) {
|
||||
throw new ReportedException("Post-rendering entity in world", throwable2);
|
||||
}
|
||||
if (flag) {
|
||||
Trace.add("boundingBox " + entity.name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable throwable) {
|
||||
throw new ReportedException("Rendering entity (outer)", throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public int byName(int a) {
|
||||
final int first = a + 10;
|
||||
final int second = a + 20;
|
||||
return this.callee.twice(first) + second;
|
||||
}
|
||||
|
||||
public long wideLocals(int a) {
|
||||
final long big = a * 10L;
|
||||
final double half = a / 2.0;
|
||||
return this.callee.wide(big, half);
|
||||
}
|
||||
|
||||
public int valueBelow(int a) {
|
||||
return a + this.callee.explode(a) * 10;
|
||||
}
|
||||
|
||||
public int valueSkip(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public void observed(int a) {
|
||||
this.callee.maybeBoom(a);
|
||||
}
|
||||
|
||||
public int returnAndHandle(int a) {
|
||||
return this.callee.explode(a);
|
||||
}
|
||||
|
||||
public int skipFinallyCatch(int a) {
|
||||
return this.callee.explode(a);
|
||||
}
|
||||
|
||||
public int nestedSkip(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public int redirectedSkip(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public int helperSkip(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public long wideSkip(long a, double b) {
|
||||
return this.callee.wide(a, b) + 1L;
|
||||
}
|
||||
|
||||
public int entryOnlySkip(int a) {
|
||||
this.callee.render(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
public void entryOnlyWhole() {
|
||||
Trace.add("entryOnlyWhole");
|
||||
}
|
||||
|
||||
public int skipBelowRestored(int a) {
|
||||
return 1000 + this.callee.twice(a);
|
||||
}
|
||||
|
||||
public int innerSkipOuterReturn(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public int skipInsideNew(int a) {
|
||||
return new AtomicInteger(this.callee.twice(a)).get();
|
||||
}
|
||||
|
||||
public int movedBody(int a) {
|
||||
final int b = a + 1;
|
||||
return 1000 + this.callee.twice(b);
|
||||
}
|
||||
|
||||
public static class Callee implements IntUnaryOperator {
|
||||
|
||||
public int twice(int a) {
|
||||
Trace.add("twice " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public static int twiceStatic(int a) {
|
||||
Trace.add("twiceStatic " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public static void marker() {
|
||||
Trace.add("marker");
|
||||
}
|
||||
|
||||
public long wide(long a, double b) {
|
||||
Trace.add("wide " + a + " " + b);
|
||||
return a + (long) b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int applyAsInt(int a) {
|
||||
Trace.add("apply " + a);
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
public int explode(int a) {
|
||||
Trace.add("explode " + a);
|
||||
if (a < 0) {
|
||||
throw new IllegalStateException("negative " + a);
|
||||
}
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public void maybeBoom(int a) {
|
||||
Trace.add("maybeBoom " + a);
|
||||
if (a < 0) {
|
||||
throw new IllegalStateException("boom " + a);
|
||||
}
|
||||
}
|
||||
|
||||
public void render(int a) {
|
||||
Trace.add("render " + a);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Entity {
|
||||
|
||||
public final String name;
|
||||
public boolean explode;
|
||||
|
||||
public Entity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Render {
|
||||
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partial) {
|
||||
Trace.add("doRender " + entity.name + " " + x + " " + partial);
|
||||
if (entity.explode) {
|
||||
throw new RuntimeException("gpu");
|
||||
}
|
||||
}
|
||||
|
||||
public void doRenderShadowAndFire(Entity entity, double x, double y, double z, float yaw, float partial) {
|
||||
Trace.add("shadow " + entity.name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReportedException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ReportedException(String description, Throwable cause) {
|
||||
super(description, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
|
||||
public class WholeTarget {
|
||||
|
||||
public static boolean guard;
|
||||
|
||||
public int compute(int a) {
|
||||
Trace.add("body " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public void boom() {
|
||||
Trace.add("boom");
|
||||
throw new IllegalStateException("boom");
|
||||
}
|
||||
|
||||
public void alpha() {
|
||||
Trace.add("alpha");
|
||||
}
|
||||
|
||||
public void beta() {
|
||||
Trace.add("beta");
|
||||
}
|
||||
|
||||
public synchronized int lockedInstance(int a) {
|
||||
Trace.add("body " + Thread.holdsLock(this));
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Trace.add("run");
|
||||
exit();
|
||||
}
|
||||
|
||||
private void exit() {
|
||||
Trace.add("target exit");
|
||||
}
|
||||
|
||||
public int priority(int a) {
|
||||
Trace.add("body " + a);
|
||||
return a;
|
||||
}
|
||||
|
||||
public int branching(int a) {
|
||||
Trace.add("body " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public void skippedVoid(int a) {
|
||||
Trace.add("skippedVoid " + a);
|
||||
}
|
||||
|
||||
public int reentrant(int a) {
|
||||
Trace.add("reentrant " + a);
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
public String skipAndReturn(String a) {
|
||||
Trace.add("skipAndReturn " + a);
|
||||
return a + "!";
|
||||
}
|
||||
|
||||
public static int entryCatches(int a) {
|
||||
Trace.add("entryCatches " + a);
|
||||
if (a < 0) {
|
||||
throw new IllegalStateException("negative " + a);
|
||||
}
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public int throwingFinally(int a, int b) {
|
||||
return pick(a, b);
|
||||
}
|
||||
|
||||
private static int pick(int a, int b) {
|
||||
if (a > b) {
|
||||
Trace.add("gt");
|
||||
return a;
|
||||
}
|
||||
if (a < b) {
|
||||
Trace.add("lt");
|
||||
return b;
|
||||
}
|
||||
Trace.add("eq");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static double wideBody(double a, long b) {
|
||||
Trace.add("body");
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public void throwWide(int a) {
|
||||
thrown(a);
|
||||
}
|
||||
|
||||
public void throwObject(int a) {
|
||||
thrown(a);
|
||||
}
|
||||
|
||||
private static void thrown(int a) {
|
||||
Trace.add("body " + a);
|
||||
throw new IllegalStateException("boom " + a);
|
||||
}
|
||||
|
||||
public void throwingEntry(int a) {
|
||||
Trace.add("body " + a);
|
||||
}
|
||||
|
||||
public int caught(int a) {
|
||||
try {
|
||||
if (a < 0) {
|
||||
throw new IllegalArgumentException("neg");
|
||||
}
|
||||
return a;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Trace.add("caught " + e.getMessage());
|
||||
return -1;
|
||||
} finally {
|
||||
Trace.add("inner-finally");
|
||||
}
|
||||
}
|
||||
|
||||
public int carryThree(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int carryUninitialized(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int carryGuarded(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int carrySplitAtEnd(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int carryWideBeforeFrame(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int carryArrays(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
public int reused(int a) {
|
||||
return carried(a);
|
||||
}
|
||||
|
||||
private static int carried(int a) {
|
||||
Trace.add("body " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public static int risky(int a) {
|
||||
if (a < 0) {
|
||||
throw new IllegalArgumentException("neg");
|
||||
}
|
||||
return a * 3;
|
||||
}
|
||||
|
||||
public static Object asObject(Object value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Callee;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
|
||||
public class WrapTarget {
|
||||
|
||||
private final Callee callee = new Callee();
|
||||
|
||||
public int wrapped(int a) {
|
||||
Trace.add("body " + a);
|
||||
return a * 2;
|
||||
}
|
||||
|
||||
public int originalOperands(int a) {
|
||||
return this.callee.twice(a);
|
||||
}
|
||||
|
||||
public int shifted(int a) {
|
||||
final int local = a + 1;
|
||||
final String label = "L" + local;
|
||||
return this.callee.twice(local) + label.length();
|
||||
}
|
||||
}
|
||||
+504
@@ -0,0 +1,504 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration.mixin;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Callee;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Entity;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Render;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.CALLEE;
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.TWICE;
|
||||
|
||||
@Mixin(value = CallTarget.class, remap = false)
|
||||
public class MixinCallTarget {
|
||||
|
||||
private static final String WIDE = CALLEE + "wide(JD)J";
|
||||
private static final String EXPLODE = CALLEE + "explode(I)I";
|
||||
private static final String MAYBE_BOOM = CALLEE + "maybeBoom(I)V";
|
||||
private static final String DO_RENDER = "Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Render;" + "doRender(Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Entity;DDDFF)V";
|
||||
private static final long QUEUED = -1L;
|
||||
|
||||
@Surround(method = "staticTarget", at = @At(value = "INVOKE", target = CALLEE + "twiceStatic(I)I"), id = "staticTarget")
|
||||
private static void surround$enterStaticTarget(int a) {
|
||||
@Surround.Carry int doubled = a * 2;
|
||||
Trace.add("enterStaticTarget " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("staticTarget")
|
||||
private static void surround$exitStaticTarget(int a, @Surround.Carry int doubled) {
|
||||
Trace.add("exitStaticTarget " + a + " " + doubled);
|
||||
}
|
||||
|
||||
@Surround(method = "interfaceCall", at = @At(value = "INVOKE", target = "Ljava/util/function/IntUnaryOperator;applyAsInt(I)I"), id = "interface")
|
||||
private void surround$enterInterface(IntUnaryOperator op, int a) {
|
||||
Trace.add("enterInterface " + a + " " + (op instanceof Callee));
|
||||
}
|
||||
|
||||
@Surround.Finally("interface")
|
||||
private void surround$exitInterface(Object op) {
|
||||
Trace.add("exitInterface " + (op instanceof Callee));
|
||||
}
|
||||
|
||||
@Surround(method = "privateCall", at = @At(value = "INVOKE", target = "Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget;priv(I)I"), id = "private")
|
||||
private void surround$enterPrivate(CallTarget self, int a) {
|
||||
Trace.add("enterPrivate " + (self == (Object) this) + " " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("private")
|
||||
private void surround$exitPrivate(CallTarget self, int a) {
|
||||
Trace.add("exitPrivate " + (self == (Object) this) + " " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "all", at = @At(value = "INVOKE", target = TWICE), id = "all", require = 3)
|
||||
private void surround$enterAll(Callee callee, int a) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterAll " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("all")
|
||||
private void surround$exitAll(Callee callee, int a, @Surround.Carry long token) {
|
||||
Trace.add("exitAll " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "sliced", at = @At(value = "INVOKE", target = TWICE), slice = @Slice(from = @At(value = "INVOKE", target = CALLEE + "marker()V")), id = "sliced", require = 1, allow = 1)
|
||||
private void surround$enterSliced(Callee callee, int a) {
|
||||
Trace.add("enterSliced " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("sliced")
|
||||
private void surround$exitSliced(Callee callee, int a) {
|
||||
Trace.add("exitSliced " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "throwingEntry", at = @At(value = "INVOKE", target = TWICE), id = "throwingEntry")
|
||||
private void surround$enterThrowingEntry(Callee callee, int a) {
|
||||
Trace.add("enterThrowingEntry " + a);
|
||||
if (a > 0) {
|
||||
throw new IllegalArgumentException("entry");
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Catch("throwingEntry")
|
||||
private void surround$caughtThrowingEntry(Throwable error) {
|
||||
Trace.add("caughtThrowingEntry");
|
||||
}
|
||||
|
||||
@Surround.Finally("throwingEntry")
|
||||
private void surround$exitThrowingEntry() {
|
||||
Trace.add("exitThrowingEntry");
|
||||
}
|
||||
|
||||
@Inject(method = "composed", at = @At(value = "INVOKE", target = TWICE))
|
||||
private void surround$before(int a, CallbackInfoReturnable<Integer> cir) {
|
||||
Trace.add("before " + a);
|
||||
}
|
||||
|
||||
@Inject(method = "composed", at = @At(value = "INVOKE", target = TWICE, shift = At.Shift.AFTER))
|
||||
private void surround$after(int a, CallbackInfoReturnable<Integer> cir) {
|
||||
Trace.add("after " + a);
|
||||
}
|
||||
|
||||
@ModifyArg(method = "composed", at = @At(value = "INVOKE", target = TWICE))
|
||||
private int surround$plusHundred(int a) {
|
||||
Trace.add("modifyArg " + a);
|
||||
return a + 100;
|
||||
}
|
||||
|
||||
@Surround(method = "composed", at = @At(value = "INVOKE", target = TWICE), id = "composed")
|
||||
private void surround$enterComposed(Callee callee, int a) {
|
||||
Trace.add("enterComposed " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("composed")
|
||||
private void surround$exitComposed(Callee callee, int a) {
|
||||
Trace.add("exitComposed " + a);
|
||||
}
|
||||
|
||||
@Redirect(method = "render", at = @At(value = "INVOKE", target = DO_RENDER))
|
||||
private void surround$redirectDoRender(Render render, Entity entity, double x, double y, double z, float yaw, float partial) {
|
||||
Trace.add("redirect " + entity.name);
|
||||
render.doRender(entity, x, y, z, yaw, partial);
|
||||
}
|
||||
|
||||
@Surround(method = "render", at = @At(value = "INVOKE", target = DO_RENDER), id = "render", require = 1)
|
||||
private void surround$enterDoRender(Render render, Entity entity, double x, double y, double z, float yaw, float partial) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterDoRender " + entity.name + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Catch("render")
|
||||
private void surround$caughtDoRender(Throwable error, Render render, Entity entity, @Surround.Carry long token) {
|
||||
Trace.add("caughtDoRender " + entity.name + " " + error.getMessage() + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("render")
|
||||
private void surround$exitDoRender(Render render, Entity entity, @Surround.Carry long token) {
|
||||
Trace.add("exitDoRender " + entity.name + " " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "byName", at = @At(value = "INVOKE", target = TWICE), id = "byName")
|
||||
private void surround$enterByName(@Surround.Local(name = "second") int second) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterByName " + second + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("byName")
|
||||
private void surround$exitByName(Callee callee, int first, @Surround.Local(name = "second") int second, @Surround.Carry long token) {
|
||||
Trace.add("exitByName " + first + " " + second + " " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "wideLocals", at = @At(value = "INVOKE", target = WIDE), id = "wideLocals")
|
||||
private void surround$enterWideLocals(Callee callee, long a, double b, @Surround.Local long big, @Surround.Local double half) {
|
||||
Trace.add("enterWideLocals " + big + " " + half);
|
||||
}
|
||||
|
||||
@Surround.Finally("wideLocals")
|
||||
private void surround$exitWideLocals(@Surround.Local(index = 4) double half, @Surround.Local(index = 2) long big) {
|
||||
Trace.add("exitWideLocals " + half + " " + big);
|
||||
}
|
||||
|
||||
@Surround(method = "valueBelow", at = @At(value = "INVOKE", target = EXPLODE), id = "valueBelow")
|
||||
private void surround$enterValueBelow(Callee callee, int a) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterValueBelow " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Catch(value = "valueBelow", handle = true)
|
||||
private int surround$handleValueBelow(IllegalStateException error, Callee callee, int a, @Surround.Carry long token) {
|
||||
Trace.add("handleValueBelow " + a + " " + token);
|
||||
return 7;
|
||||
}
|
||||
|
||||
@Surround.Finally("valueBelow")
|
||||
private void surround$exitValueBelow(Callee callee, int a, @Surround.Carry long token) {
|
||||
Trace.add("exitValueBelow " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "valueSkip", at = @At(value = "INVOKE", target = TWICE), id = "valueSkip")
|
||||
private void surround$enterValueSkip(Callee callee, int a) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterValueSkip " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Skipped("valueSkip")
|
||||
private int surround$skippedValueSkip(Callee callee, int a, @Surround.Carry long token) {
|
||||
Trace.add("skippedValueSkip " + a + " " + token);
|
||||
return a * 100;
|
||||
}
|
||||
|
||||
@Surround.Finally("valueSkip")
|
||||
private void surround$exitValueSkip(@Surround.Carry long token) {
|
||||
Trace.add("exitValueSkip " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "observed", at = @At(value = "INVOKE", target = MAYBE_BOOM), id = "observed")
|
||||
private void surround$enterObserved(Callee callee, int a) {
|
||||
Trace.add("enterObserved " + a);
|
||||
}
|
||||
|
||||
@Surround.Return("observed")
|
||||
private void surround$observe(Callee callee, int a) {
|
||||
Trace.add("observe " + a);
|
||||
}
|
||||
|
||||
@Surround.Catch(value = "observed", handle = true)
|
||||
private void surround$handleObserved(IllegalStateException error, Callee callee, int a) {
|
||||
Trace.add("handleObserved " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("observed")
|
||||
private void surround$exitObserved(Callee callee, int a) {
|
||||
Trace.add("exitObserved " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "returnAndHandle", at = @At(value = "INVOKE", target = EXPLODE), id = "returnAndHandle")
|
||||
private void surround$enterReturnAndHandle(Callee callee, int a) {
|
||||
Trace.add("enterReturnAndHandle " + a);
|
||||
}
|
||||
|
||||
@Surround.Return("returnAndHandle")
|
||||
private int surround$returnReturnAndHandle(int result) {
|
||||
Trace.add("returnReturnAndHandle " + result);
|
||||
return result * 10;
|
||||
}
|
||||
|
||||
@Surround.Catch(value = "returnAndHandle", handle = true)
|
||||
private int surround$handleReturnAndHandle(IllegalStateException error) {
|
||||
Trace.add("handleReturnAndHandle");
|
||||
return 7;
|
||||
}
|
||||
|
||||
@Surround.Finally("returnAndHandle")
|
||||
private void surround$exitReturnAndHandle() {
|
||||
Trace.add("exitReturnAndHandle");
|
||||
}
|
||||
|
||||
@Surround(method = "skipFinallyCatch", at = @At(value = "INVOKE", target = EXPLODE), id = "sfc")
|
||||
private void surround$enterSfc(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a <= 0;
|
||||
Trace.add("enterSfc " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("sfc")
|
||||
private int surround$skippedSfc(Callee callee, int a) {
|
||||
Trace.add("skippedSfc " + a);
|
||||
if (a == 0) {
|
||||
throw new IllegalStateException("zero");
|
||||
}
|
||||
return -a;
|
||||
}
|
||||
|
||||
@Surround.Return("sfc")
|
||||
private int surround$returnSfc(int result, Callee callee, int a) {
|
||||
Trace.add("returnSfc " + result);
|
||||
return result + 1000;
|
||||
}
|
||||
|
||||
@Surround.Catch("sfc")
|
||||
private void surround$caughtSfc(IllegalStateException error, Callee callee, int a) {
|
||||
Trace.add("caughtSfc " + error.getMessage());
|
||||
}
|
||||
|
||||
@Surround.Finally("sfc")
|
||||
private void surround$exitSfc(Callee callee, int a) {
|
||||
Trace.add("exitSfc " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "nestedSkip", at = @At(value = "INVOKE", target = TWICE), id = "nestedInner")
|
||||
private void surround$enterNestedInner(Callee callee, int a) {
|
||||
Trace.add("enterNestedInner " + a);
|
||||
}
|
||||
|
||||
@Surround.Return("nestedInner")
|
||||
private int surround$returnNestedInner(int result) {
|
||||
Trace.add("returnNestedInner " + result);
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
@Surround.Finally("nestedInner")
|
||||
private void surround$exitNestedInner(Callee callee, int a) {
|
||||
Trace.add("exitNestedInner " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "nestedSkip", at = @At(value = "INVOKE", target = TWICE), id = "nestedOuter")
|
||||
private void surround$enterNestedOuter(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterNestedOuter " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("nestedOuter")
|
||||
private int surround$skippedNestedOuter() {
|
||||
Trace.add("skippedNestedOuter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Surround.Return("nestedOuter")
|
||||
private int surround$returnNestedOuter(int result) {
|
||||
Trace.add("returnNestedOuter " + result);
|
||||
return result * 10;
|
||||
}
|
||||
|
||||
@Surround.Finally("nestedOuter")
|
||||
private void surround$exitNestedOuter(Callee callee, int a) {
|
||||
Trace.add("exitNestedOuter " + a);
|
||||
}
|
||||
|
||||
@Redirect(method = "redirectedSkip", at = @At(value = "INVOKE", target = TWICE))
|
||||
private int surround$redirectSkip(Callee callee, int a) {
|
||||
Trace.add("redirectSkip " + a);
|
||||
return callee.twice(a) + 1;
|
||||
}
|
||||
|
||||
@Surround(method = "redirectedSkip", at = @At(value = "INVOKE", target = TWICE), id = "redirectedSkip")
|
||||
private void surround$enterRedirectedSkip(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterRedirectedSkip " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("redirectedSkip")
|
||||
private int surround$skippedRedirectedSkip(Callee callee, int a) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Surround.Finally("redirectedSkip")
|
||||
private void surround$exitRedirectedSkip(Callee callee, int a) {
|
||||
Trace.add("exitRedirectedSkip " + a);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static long surround$part(int a) {
|
||||
Trace.add("part " + a);
|
||||
return a < 0 ? QUEUED : a;
|
||||
}
|
||||
|
||||
@Surround(method = "helperSkip", at = @At(value = "INVOKE", target = TWICE), id = "helper")
|
||||
private void surround$enterHelper(Callee callee, int a) {
|
||||
@Surround.Carry final long part = surround$part(a);
|
||||
@Surround.Skip final boolean queued = part == QUEUED;
|
||||
Trace.add("enterHelper " + part);
|
||||
}
|
||||
|
||||
@Surround.Skipped("helper")
|
||||
private int surround$skippedHelper(Callee callee, int a, @Surround.Carry long part) {
|
||||
Trace.add("skippedHelper");
|
||||
return (int) part;
|
||||
}
|
||||
|
||||
@Surround.Finally("helper")
|
||||
private void surround$exitHelper(@Surround.Carry long part) {
|
||||
Trace.add("exitHelper " + part);
|
||||
}
|
||||
|
||||
@Surround(method = "wideSkip", at = @At(value = "INVOKE", target = WIDE), id = "wideSkip")
|
||||
private void surround$enterWideSkip(Callee callee, long a, double b) {
|
||||
@Surround.Carry double halved = b / 2;
|
||||
@Surround.Skip int skip = a < 0 ? 1 : 0;
|
||||
Trace.add("enterWideSkip " + a + " " + b);
|
||||
}
|
||||
|
||||
@Surround.Skipped("wideSkip")
|
||||
private long surround$skippedWideSkip(Callee callee, long a, @Surround.Carry double halved) {
|
||||
Trace.add("skippedWideSkip " + a + " " + halved);
|
||||
return a * 10L;
|
||||
}
|
||||
|
||||
@Surround.Return("wideSkip")
|
||||
private long surround$returnWideSkip(long result, Callee callee, long a, double b, @Surround.Carry double halved) {
|
||||
Trace.add("returnWideSkip " + result + " " + halved);
|
||||
return result + 100L;
|
||||
}
|
||||
|
||||
@Surround.Finally("wideSkip")
|
||||
private void surround$exitWideSkip(Callee callee, long a, double b) {
|
||||
Trace.add("exitWideSkip " + a + " " + b);
|
||||
}
|
||||
|
||||
@Surround(method = "entryOnlySkip", at = @At(value = "INVOKE", target = CALLEE + "render(I)V"), id = "entryOnlySkip")
|
||||
private void surround$enterOnlySkip(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a > 0;
|
||||
Trace.add("enterOnlySkip " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "entryOnlyWhole", id = "entryOnlyWhole")
|
||||
private void surround$enterOnlyWhole() {
|
||||
Trace.add("enterOnlyWhole");
|
||||
}
|
||||
|
||||
@Surround(method = "skipBelowRestored", at = @At(value = "INVOKE", target = TWICE), id = "skipBelowRestored")
|
||||
private void surround$enterSkipBelowRestored(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
try {
|
||||
if (a > 100) {
|
||||
throw new IllegalArgumentException("big");
|
||||
}
|
||||
Trace.add("enterSkipBelowRestored " + a);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Trace.add("entryCaughtSkipBelowRestored " + a);
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Skipped("skipBelowRestored")
|
||||
private int surround$skippedSkipBelowRestored(Callee callee, int a) {
|
||||
Trace.add("skippedSkipBelowRestored " + a);
|
||||
return a * 10;
|
||||
}
|
||||
|
||||
@Surround.Finally("skipBelowRestored")
|
||||
private void surround$exitSkipBelowRestored(Callee callee, int a) {
|
||||
Trace.add("exitSkipBelowRestored " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "innerSkipOuterReturn", at = @At(value = "INVOKE", target = TWICE), id = "innerSkip")
|
||||
private void surround$enterInnerSkip(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterInnerSkip " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("innerSkip")
|
||||
private int surround$skippedInnerSkip(Callee callee, int a) {
|
||||
Trace.add("skippedInnerSkip " + a);
|
||||
return a * 100;
|
||||
}
|
||||
|
||||
@Surround.Finally("innerSkip")
|
||||
private void surround$exitInnerSkip(Callee callee, int a) {
|
||||
Trace.add("exitInnerSkip " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "innerSkipOuterReturn", at = @At(value = "INVOKE", target = TWICE), id = "outerReturn")
|
||||
private void surround$enterOuterReturn(Callee callee, int a) {
|
||||
Trace.add("enterOuterReturn " + a);
|
||||
}
|
||||
|
||||
@Surround.Return("outerReturn")
|
||||
private int surround$returnOuterReturn(int result) {
|
||||
Trace.add("returnOuterReturn " + result);
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
@Surround.Finally("outerReturn")
|
||||
private void surround$exitOuterReturn(Callee callee, int a) {
|
||||
Trace.add("exitOuterReturn " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "skipInsideNew", at = @At(value = "INVOKE", target = TWICE), id = "skipInsideNew")
|
||||
private void surround$enterSkipInsideNew(Callee callee, int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterSkipInsideNew " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("skipInsideNew")
|
||||
private int surround$skippedSkipInsideNew(Callee callee, int a) {
|
||||
Trace.add("skippedSkipInsideNew " + a);
|
||||
return a * 10;
|
||||
}
|
||||
|
||||
@Surround.Catch("skipInsideNew")
|
||||
private void surround$caughtSkipInsideNew(Throwable error) {
|
||||
Trace.add("caughtSkipInsideNew");
|
||||
}
|
||||
|
||||
@Surround.Finally("skipInsideNew")
|
||||
private void surround$exitSkipInsideNew(Callee callee, int a) {
|
||||
Trace.add("exitSkipInsideNew " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "movedBody", id = "movedWhole")
|
||||
private void surround$enterMovedWhole(int a) {
|
||||
Trace.add("enterMovedWhole " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("movedWhole")
|
||||
private void surround$exitMovedWhole(int a) {
|
||||
Trace.add("exitMovedWhole " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "movedBody", at = @At(value = "INVOKE", target = TWICE), id = "movedCall")
|
||||
private void surround$enterMovedCall(Callee callee, int b, @Surround.Local(argsOnly = true) int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterMovedCall " + b + " " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("movedCall")
|
||||
private int surround$skippedMovedCall(Callee callee, int b, @Surround.Local(argsOnly = true) int a) {
|
||||
Trace.add("skippedMovedCall " + b + " " + a);
|
||||
return b * 10;
|
||||
}
|
||||
|
||||
@Surround.Finally("movedCall")
|
||||
private void surround$exitMovedCall(Callee callee, int b, @Surround.Local(argsOnly = true) int a) {
|
||||
Trace.add("exitMovedCall " + b + " " + a);
|
||||
}
|
||||
}
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration.mixin;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.WholeTarget;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = WholeTarget.class, remap = false)
|
||||
public class MixinWholeTarget {
|
||||
|
||||
@Surround(method = "compute(I)I", id = "compute")
|
||||
private void surround$enter(int a) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enter " + a + " " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("compute")
|
||||
private void surround$exit(@Surround.Carry long token) {
|
||||
Trace.add("exit " + token);
|
||||
}
|
||||
|
||||
@Inject(method = "compute(I)I", at = @At("HEAD"))
|
||||
private void surround$head(int a, CallbackInfoReturnable<Integer> cir) {
|
||||
Trace.add("head " + a);
|
||||
}
|
||||
|
||||
@Inject(method = "compute(I)I", at = @At("RETURN"))
|
||||
private void surround$tail(int a, CallbackInfoReturnable<Integer> cir) {
|
||||
Trace.add("tail " + cir.getReturnValue());
|
||||
}
|
||||
|
||||
@Surround(method = "boom()V", id = "boom")
|
||||
private void surround$enterBoom() {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterBoom " + token);
|
||||
}
|
||||
|
||||
@Surround.Catch("boom")
|
||||
private void surround$caughtBoomBroad(Throwable e, @Surround.Carry long token) {
|
||||
Trace.add("caughtBroad " + token + " " + e.getMessage());
|
||||
}
|
||||
|
||||
@Surround.Catch("boom")
|
||||
private void surround$caughtBoom(IllegalStateException e, @Surround.Carry long token) {
|
||||
Trace.add("caught " + token + " " + e.getMessage());
|
||||
}
|
||||
|
||||
@Surround.Finally("boom")
|
||||
private void surround$exitBoom(@Surround.Carry long token) {
|
||||
Trace.add("exitBoom " + token);
|
||||
}
|
||||
|
||||
@Inject(method = "boom()V", at = @At("HEAD"))
|
||||
private void surround$boomHead(CallbackInfo ci) {
|
||||
Trace.add("boomHead");
|
||||
}
|
||||
|
||||
@Surround(method = {"alpha()V", "beta()V"}, id = "both")
|
||||
private void surround$enterBoth() {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enterBoth " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("both")
|
||||
private void surround$exitBoth(@Surround.Carry long token) {
|
||||
Trace.add("exitBoth " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "lockedInstance(I)I", id = "instance")
|
||||
private void surround$enterInstance(int a) {
|
||||
Trace.add("enterInstance " + Thread.holdsLock(this));
|
||||
}
|
||||
|
||||
@Surround.Finally("instance")
|
||||
private void surround$exitInstance() {
|
||||
Trace.add("exitInstance " + Thread.holdsLock(this));
|
||||
}
|
||||
|
||||
@Surround(method = "run()V", id = "run")
|
||||
private void surround$enterRun() {
|
||||
Trace.add("enter");
|
||||
}
|
||||
|
||||
@Unique
|
||||
@Surround.Finally("run")
|
||||
private void exit() {
|
||||
Trace.add("handler exit");
|
||||
}
|
||||
|
||||
@Surround(method = "priority(I)I", id = "priority")
|
||||
private void surround$enterLow(int a) {
|
||||
Trace.add("enterLow");
|
||||
}
|
||||
|
||||
@Surround.Return("priority")
|
||||
private int surround$returnLow(int result) {
|
||||
Trace.add("returnLow " + result);
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
@Surround.Finally("priority")
|
||||
private void surround$exitLow() {
|
||||
Trace.add("exitLow");
|
||||
}
|
||||
|
||||
@Surround(method = "branching(I)I", id = "branching")
|
||||
private void surround$enterBranching(int a) {
|
||||
@Surround.Carry String sign = a < 0 ? "negative" : "positive";
|
||||
Trace.add("enterBranching " + sign);
|
||||
}
|
||||
|
||||
@Surround.Finally("branching")
|
||||
private void surround$exitBranching(int a, @Surround.Carry String sign) {
|
||||
Trace.add("exitBranching " + sign);
|
||||
}
|
||||
|
||||
@Surround(method = "skippedVoid(I)V", id = "skippedVoid")
|
||||
private void surround$enterSkippedVoid(int a) {
|
||||
@Surround.Skip boolean skip = a < 0;
|
||||
Trace.add("enterSkippedVoid " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("skippedVoid")
|
||||
private void surround$exitSkippedVoid() {
|
||||
Trace.add("exitSkippedVoid");
|
||||
}
|
||||
|
||||
@Surround(method = "reentrant(I)I", id = "reentrant")
|
||||
private void surround$enterReentrant(int a) {
|
||||
@Surround.Carry int direct = 0;
|
||||
@Surround.Skip boolean skip = false;
|
||||
if (!WholeTarget.guard) {
|
||||
WholeTarget.guard = true;
|
||||
direct = ((WholeTarget) (Object) this).reentrant(a + 10);
|
||||
WholeTarget.guard = false;
|
||||
skip = true;
|
||||
}
|
||||
Trace.add("enterReentrant " + a + " " + skip);
|
||||
}
|
||||
|
||||
@Surround.Skipped("reentrant")
|
||||
private int surround$skippedReentrant(int a, @Surround.Carry int direct) {
|
||||
return direct * 2;
|
||||
}
|
||||
|
||||
@Surround.Finally("reentrant")
|
||||
private void surround$exitReentrant(int a) {
|
||||
Trace.add("exitReentrant " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "skipAndReturn(Ljava/lang/String;)Ljava/lang/String;", id = "skipAndReturn")
|
||||
private void surround$enterSkipAndReturn(String a) {
|
||||
@Surround.Skip boolean skip = a.isEmpty();
|
||||
Trace.add("enterSkipAndReturn " + a);
|
||||
}
|
||||
|
||||
@Surround.Skipped("skipAndReturn")
|
||||
private String surround$skippedSkipAndReturn(String a) {
|
||||
Trace.add("skippedSkipAndReturn");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Surround.Return("skipAndReturn")
|
||||
private String surround$returnSkipAndReturn(String result, String a) {
|
||||
Trace.add("returnSkipAndReturn " + result);
|
||||
if (result.equals("x!")) {
|
||||
throw new IllegalStateException("return");
|
||||
}
|
||||
return result + "?";
|
||||
}
|
||||
|
||||
@Surround.Catch("skipAndReturn")
|
||||
private void surround$caughtSkipAndReturn(Throwable e) {
|
||||
Trace.add("caughtSkipAndReturn " + e.getMessage());
|
||||
}
|
||||
|
||||
@Surround.Finally("skipAndReturn")
|
||||
private void surround$exitSkipAndReturn(String a) {
|
||||
Trace.add("exitSkipAndReturn " + a);
|
||||
}
|
||||
|
||||
@Surround(method = "entryCatches(I)I", id = "entryCatches")
|
||||
private static void surround$enterEntryCatches(int a) {
|
||||
@Surround.Carry final boolean direct = !WholeTarget.guard;
|
||||
if (direct) {
|
||||
WholeTarget.guard = true;
|
||||
try {
|
||||
WholeTarget.entryCatches(a);
|
||||
} catch (Throwable t) {
|
||||
WholeTarget.guard = false;
|
||||
Trace.add("entryRethrow " + t.getMessage());
|
||||
throw t;
|
||||
}
|
||||
Trace.add("enterEntryCatches " + a);
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Finally("entryCatches")
|
||||
private static void surround$exitEntryCatches(@Surround.Carry boolean direct) {
|
||||
if (direct) {
|
||||
WholeTarget.guard = false;
|
||||
}
|
||||
Trace.add("exitEntryCatches " + direct);
|
||||
}
|
||||
|
||||
@Surround(method = "wideBody(DJ)D", id = "wideBody")
|
||||
private static void surround$enterWideBody(double a, long b) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enter " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("wideBody")
|
||||
private static void surround$exitWideBody(@Surround.Carry long token) {
|
||||
Trace.add("exit " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "throwingEntry(I)V", id = "throwingEntry")
|
||||
private void surround$enterThrowingEntry(int a) {
|
||||
Trace.add("enterThrows " + a);
|
||||
if (a > 0) {
|
||||
throw new IllegalArgumentException("entry failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Finally("throwingEntry")
|
||||
private void surround$exitThrowingEntry() {
|
||||
Trace.add("exitNothing");
|
||||
}
|
||||
|
||||
@Surround(method = "throwingFinally(II)I", id = "throwingFinally")
|
||||
private void surround$enterThrowingFinally(int a, int b) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enter " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("throwingFinally")
|
||||
private void surround$exitThrowingFinally(@Surround.Carry long token) {
|
||||
Trace.add("exitThrows " + token);
|
||||
throw new UnsupportedOperationException("finally failed");
|
||||
}
|
||||
|
||||
@Surround(method = "caught(I)I", id = "caught")
|
||||
private void surround$enterCaught(int a) {
|
||||
@Surround.Carry long token = Trace.nextToken();
|
||||
Trace.add("enter " + token);
|
||||
}
|
||||
|
||||
@Surround.Finally("caught")
|
||||
private void surround$exitCaught(@Surround.Carry long token) {
|
||||
Trace.add("exit " + token);
|
||||
}
|
||||
|
||||
@Surround(method = "carryThree(I)I", id = "carryThree")
|
||||
private void surround$enterCarryThree(int a) {
|
||||
@Surround.Carry int count = a + 1;
|
||||
@Surround.Carry long stamp = 100L + a;
|
||||
@Surround.Carry String label = "L" + a;
|
||||
Trace.add("enter " + count + " " + stamp + " " + label);
|
||||
}
|
||||
|
||||
@Surround.Finally("carryThree")
|
||||
private void surround$exitCarryThree(@Surround.Carry String label, @Surround.Carry long stamp, @Surround.Carry int count) {
|
||||
Trace.add("exit " + count + " " + stamp + " " + label);
|
||||
}
|
||||
|
||||
@Surround(method = "carryUninitialized(I)I", id = "carryUninitialized")
|
||||
private void surround$enterCarryUninitialized(int a) {
|
||||
@Surround.Carry String label = new String(a > 0 ? "pos" : "neg");
|
||||
Trace.add("enter " + label);
|
||||
}
|
||||
|
||||
@Surround.Finally("carryUninitialized")
|
||||
private void surround$exitCarryUninitialized(@Surround.Carry String label) {
|
||||
Trace.add("exit " + label);
|
||||
}
|
||||
|
||||
@Surround(method = "carryGuarded(I)I", id = "carryGuarded")
|
||||
private void surround$enterCarryGuarded(int a) {
|
||||
@Surround.Carry int state = 0;
|
||||
try {
|
||||
state = WholeTarget.risky(a);
|
||||
} catch (IllegalArgumentException e) {
|
||||
state = -1;
|
||||
Trace.add("recovered");
|
||||
}
|
||||
Trace.add("enter " + state);
|
||||
}
|
||||
|
||||
@Surround.Finally("carryGuarded")
|
||||
private void surround$exitCarryGuarded(@Surround.Carry int flag) {
|
||||
Trace.add("exit " + flag);
|
||||
}
|
||||
|
||||
@Surround(method = "carrySplitAtEnd(I)I", id = "carrySplitAtEnd")
|
||||
private void surround$enterCarrySplitAtEnd(int a) {
|
||||
@Surround.Carry int flag;
|
||||
if (a > 0) {
|
||||
flag = 1;
|
||||
} else {
|
||||
flag = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Finally("carrySplitAtEnd")
|
||||
private void surround$exitCarrySplitAtEnd(@Surround.Carry int flag) {
|
||||
Trace.add("exit " + flag);
|
||||
}
|
||||
|
||||
@Surround(method = "carryWideBeforeFrame(I)I", id = "carryWideBeforeFrame")
|
||||
private void surround$enterCarryWideBeforeFrame(int a) {
|
||||
@Surround.Carry long stamp = a;
|
||||
@Surround.Carry int count = a + 1;
|
||||
if (a > 0) {
|
||||
Trace.add("positive");
|
||||
}
|
||||
}
|
||||
|
||||
@Surround.Finally("carryWideBeforeFrame")
|
||||
private void surround$exitCarryWideBeforeFrame(@Surround.Carry long stamp, @Surround.Carry int count) {
|
||||
Trace.add("exit " + stamp + " " + count);
|
||||
}
|
||||
|
||||
@Surround(method = "carryArrays(I)I", id = "carryArrays")
|
||||
private void surround$enterCarryArrays(int a) {
|
||||
@Surround.Carry("elements") int[] elements = {a};
|
||||
String @Surround.Carry("names") [] names = {"n" + a};
|
||||
}
|
||||
|
||||
@Surround.Finally("carryArrays")
|
||||
private void surround$exitCarryArrays(@Surround.Carry("names") String[] names, @Surround.Carry("elements") int[] elements) {
|
||||
Trace.add("exit " + names[0] + " " + elements[0]);
|
||||
}
|
||||
|
||||
@Surround(method = "reused(I)I", id = "reused")
|
||||
private void surround$enterReused(int a) {
|
||||
{
|
||||
int tmp = a + 5;
|
||||
Trace.add("tmp " + tmp);
|
||||
}
|
||||
@Surround.Carry long prev = a + 7;
|
||||
Trace.add("enter " + prev);
|
||||
}
|
||||
|
||||
@Surround.Finally("reused")
|
||||
private void surround$exitPrev(@Surround.Carry long prev) {
|
||||
Trace.add("exit " + prev);
|
||||
}
|
||||
|
||||
@Surround(method = "throwWide(I)V", id = "throwWide")
|
||||
private void surround$enterThrowWide(int a) {
|
||||
@Surround.Carry long first = a + 1L;
|
||||
String scratch = "s" + a;
|
||||
@Surround.Carry double second = a / 2.0d;
|
||||
@Surround.Carry int third = a;
|
||||
Trace.add("enter " + first + " " + second + " " + third + " " + scratch);
|
||||
}
|
||||
|
||||
@Surround.Catch("throwWide")
|
||||
private void surround$caughtThrowWide(Throwable e, @Surround.Carry long first, @Surround.Carry double second, @Surround.Carry int third) {
|
||||
Trace.add("caught " + first + " " + second + " " + third);
|
||||
}
|
||||
|
||||
@Surround.Finally("throwWide")
|
||||
private void surround$exitThrowWide(@Surround.Carry double second, @Surround.Carry int third, @Surround.Carry long first) {
|
||||
Trace.add("exit " + first + " " + second + " " + third);
|
||||
}
|
||||
|
||||
@Surround(method = "throwObject(I)V", id = "throwObject")
|
||||
private void surround$enterThrowObject(int a) {
|
||||
@Surround.Carry Object prev = WholeTarget.asObject("o" + a);
|
||||
Trace.add("enter " + prev);
|
||||
}
|
||||
|
||||
@Surround.Catch("throwObject")
|
||||
private void surround$caughtThrowObject(IllegalStateException e, @Surround.Carry String prev) {
|
||||
Trace.add("caught " + prev.length() + " " + prev);
|
||||
throw new UnsupportedOperationException("replaced", e);
|
||||
}
|
||||
|
||||
@Surround.Finally("throwObject")
|
||||
private void surround$exitThrowObject(@Surround.Carry String prev) {
|
||||
Trace.add("exit " + prev.length() + " " + prev);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration.mixin;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.WholeTarget;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(value = WholeTarget.class, priority = 1100, remap = false)
|
||||
public class MixinWholeTargetHigh {
|
||||
|
||||
@Surround(method = "priority(I)I")
|
||||
private void surround$enterHigh(int a) {
|
||||
Trace.add("enterHigh");
|
||||
}
|
||||
|
||||
@Surround.Return
|
||||
private int surround$returnHigh(int result) {
|
||||
Trace.add("returnHigh " + result);
|
||||
return result * 10;
|
||||
}
|
||||
|
||||
@Surround.Finally
|
||||
private void surround$exitHigh() {
|
||||
Trace.add("exitHigh");
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.integration.mixinextras;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.Callee;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.integration.WrapTarget;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.targets.Trace;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Share;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalIntRef;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import static com.gtnewhorizons.angelica.experimental.surround.integration.CallTarget.TWICE;
|
||||
|
||||
@Mixin(value = WrapTarget.class, remap = false)
|
||||
public class MixinWrapTarget {
|
||||
|
||||
@Surround(method = "wrapped", id = "wrapped")
|
||||
private void surround$enterWrapped(int a) {
|
||||
Trace.add("entry " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("wrapped")
|
||||
private void surround$exitWrapped() {
|
||||
Trace.add("finally");
|
||||
}
|
||||
|
||||
@WrapMethod(method = "wrapped")
|
||||
private int wrapWrapped(int a, Operation<Integer> original) {
|
||||
Trace.add("wrap-before " + a);
|
||||
final int result = original.call(a);
|
||||
Trace.add("wrap-after " + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@WrapOperation(method = "originalOperands", at = @At(value = "INVOKE", target = TWICE))
|
||||
private int wrapOperation$shiftArgument(Callee callee, int a, Operation<Integer> original) {
|
||||
Trace.add("wrap " + a);
|
||||
return original.call(callee, a + 100);
|
||||
}
|
||||
|
||||
@Surround(method = "originalOperands", at = @At(value = "INVOKE", target = TWICE), id = "originalOperands")
|
||||
private void surround$enterOriginalOperands(Callee callee, int a) {
|
||||
Trace.add("enter " + a);
|
||||
}
|
||||
|
||||
@Surround.Finally("originalOperands")
|
||||
private void surround$exitOriginalOperands(Callee callee, int a) {
|
||||
Trace.add("exit " + a);
|
||||
}
|
||||
|
||||
@WrapMethod(method = "shifted")
|
||||
private int wrapShifted(int a, Operation<Integer> original, @Share("seen") LocalIntRef seen) {
|
||||
seen.set(a);
|
||||
Trace.add("wrap " + a);
|
||||
return original.call(a);
|
||||
}
|
||||
|
||||
@Surround(method = "shifted", at = @At(value = "INVOKE", target = TWICE), id = "shifted")
|
||||
private void surround$enterShifted(Callee callee, int value, @Surround.Local(ordinal = 1) int local, @Surround.Local String label) {
|
||||
Trace.add("enterShifted " + value + " " + local + " " + label);
|
||||
}
|
||||
|
||||
@Surround.Finally("shifted")
|
||||
private void surround$exitShifted(@Surround.Local(name = "local") int local, @Surround.Local String label) {
|
||||
Trace.add("exitShifted " + local + " " + label);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.experimental.surround.targets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
public final class Trace {
|
||||
|
||||
private static final List<String> EVENTS = new ArrayList<>();
|
||||
private static long counter;
|
||||
|
||||
private Trace() {
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
EVENTS.clear();
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
public static void add(String event) {
|
||||
EVENTS.add(event);
|
||||
}
|
||||
|
||||
public static void assertEvents(String... expected) {
|
||||
assertArrayEquals(expected, EVENTS.toArray(new String[EVENTS.size()]), EVENTS.toString());
|
||||
}
|
||||
|
||||
public static long nextToken() {
|
||||
return ++counter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// class version 52.0 (52)
|
||||
// access flags 0x21
|
||||
public class com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget {
|
||||
|
||||
|
||||
// access flags 0x1
|
||||
public valueBelow(I)I
|
||||
TRYCATCHBLOCK L0 L1 L2 java/lang/IllegalStateException
|
||||
TRYCATCHBLOCK L3 L4 L5 null
|
||||
TRYCATCHBLOCK L0 L1 L5 null
|
||||
L6
|
||||
ILOAD 1
|
||||
ALOAD 0
|
||||
GETFIELD com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.callee : Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;
|
||||
ILOAD 1
|
||||
ISTORE 3
|
||||
ASTORE 2
|
||||
ISTORE 4
|
||||
L7
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.nextToken ()J
|
||||
LSTORE 5
|
||||
L8
|
||||
ILOAD 3
|
||||
LLOAD 5
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.md$jvmdowngrader$concat$surround$enterValueBelow$1$3f (IJ)Ljava/lang/String;
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.add (Ljava/lang/String;)V
|
||||
L9
|
||||
ILOAD 4
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
L0
|
||||
INVOKEVIRTUAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee.explode (I)I
|
||||
L1
|
||||
ALOAD 0
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
LLOAD 5
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$exitValueBelow (Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;IJ)V
|
||||
GOTO L10
|
||||
L2
|
||||
FRAME FULL [com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget I com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee I I J] [java/lang/IllegalStateException]
|
||||
ASTORE 7
|
||||
ILOAD 4
|
||||
L3
|
||||
ALOAD 0
|
||||
ALOAD 7
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
LLOAD 5
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$handleValueBelow (Ljava/lang/IllegalStateException;Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;IJ)I
|
||||
L4
|
||||
ALOAD 0
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
LLOAD 5
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$exitValueBelow (Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;IJ)V
|
||||
GOTO L10
|
||||
L5
|
||||
FRAME SAME1 java/lang/Throwable
|
||||
ASTORE 7
|
||||
ALOAD 0
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
LLOAD 5
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$exitValueBelow (Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;IJ)V
|
||||
ALOAD 7
|
||||
ATHROW
|
||||
L10
|
||||
FRAME FULL [com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget I com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee I I J] [I I]
|
||||
BIPUSH 10
|
||||
IMUL
|
||||
IADD
|
||||
IRETURN
|
||||
L11
|
||||
LOCALVARIABLE this Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget; L6 L11 0
|
||||
LOCALVARIABLE a I L6 L11 1
|
||||
MAXSTACK = 7
|
||||
MAXLOCALS = 8
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// class version 52.0 (52)
|
||||
// access flags 0x21
|
||||
public class com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget {
|
||||
|
||||
|
||||
// access flags 0x1
|
||||
public valueSkip(I)I
|
||||
TRYCATCHBLOCK L0 L1 L2 null
|
||||
L3
|
||||
ALOAD 0
|
||||
GETFIELD com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.callee : Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;
|
||||
ILOAD 1
|
||||
ISTORE 3
|
||||
ASTORE 2
|
||||
L4
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.nextToken ()J
|
||||
LSTORE 4
|
||||
L5
|
||||
ILOAD 3
|
||||
IFGE L6
|
||||
ICONST_1
|
||||
GOTO L7
|
||||
L6
|
||||
FRAME APPEND [com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee I J]
|
||||
ICONST_0
|
||||
L7
|
||||
FRAME SAME1 I
|
||||
ISTORE 6
|
||||
L8
|
||||
ILOAD 3
|
||||
LLOAD 4
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.md$jvmdowngrader$concat$surround$enterValueSkip$1$50 (IJ)Ljava/lang/String;
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.add (Ljava/lang/String;)V
|
||||
L9
|
||||
ILOAD 6
|
||||
IFEQ L10
|
||||
L0
|
||||
ALOAD 0
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
LLOAD 4
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$skippedValueSkip (Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee;IJ)I
|
||||
GOTO L1
|
||||
L10
|
||||
FRAME APPEND [I]
|
||||
ALOAD 2
|
||||
ILOAD 3
|
||||
INVOKEVIRTUAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget$Callee.twice (I)I
|
||||
L1
|
||||
FRAME SAME1 I
|
||||
ALOAD 0
|
||||
LLOAD 4
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$exitValueSkip (J)V
|
||||
GOTO L11
|
||||
L2
|
||||
FRAME SAME1 java/lang/Throwable
|
||||
ASTORE 7
|
||||
ALOAD 0
|
||||
LLOAD 4
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/CallTarget.surround$exitValueSkip (J)V
|
||||
ALOAD 7
|
||||
ATHROW
|
||||
L11
|
||||
FRAME SAME1 I
|
||||
IRETURN
|
||||
L12
|
||||
LOCALVARIABLE this Lcom/gtnewhorizons/angelica/experimental/surround/integration/CallTarget; L3 L12 0
|
||||
LOCALVARIABLE a I L3 L12 1
|
||||
MAXSTACK = 5
|
||||
MAXLOCALS = 8
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// class version 52.0 (52)
|
||||
// access flags 0x21
|
||||
public class com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget {
|
||||
|
||||
|
||||
// access flags 0x1
|
||||
public skipAndReturn(Ljava/lang/String;)Ljava/lang/String;
|
||||
TRYCATCHBLOCK L0 L1 L2 java/lang/Throwable
|
||||
TRYCATCHBLOCK L3 L4 L5 null
|
||||
TRYCATCHBLOCK L1 L6 L5 null
|
||||
TRYCATCHBLOCK L0 L1 L5 null
|
||||
L7
|
||||
ALOAD 1
|
||||
INVOKEVIRTUAL java/lang/String.isEmpty ()Z
|
||||
ISTORE 2
|
||||
L8
|
||||
ALOAD 1
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.md$jvmdowngrader$concat$surround$enterSkipAndReturn$1 (Ljava/lang/String;)Ljava/lang/String;
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.add (Ljava/lang/String;)V
|
||||
L9
|
||||
ILOAD 2
|
||||
IFEQ L10
|
||||
L0
|
||||
ALOAD 0
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$skippedSkipAndReturn (Ljava/lang/String;)Ljava/lang/String;
|
||||
GOTO L6
|
||||
L10
|
||||
FRAME APPEND [I]
|
||||
ALOAD 0
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.skipAndReturn$surround (Ljava/lang/String;)Ljava/lang/String;
|
||||
L1
|
||||
ASTORE 4
|
||||
ALOAD 0
|
||||
ALOAD 4
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$returnSkipAndReturn (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
||||
L6
|
||||
FRAME SAME1 java/lang/String
|
||||
ALOAD 0
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$exitSkipAndReturn (Ljava/lang/String;)V
|
||||
GOTO L11
|
||||
L2
|
||||
FRAME SAME1 java/lang/Throwable
|
||||
ASTORE 3
|
||||
L3
|
||||
ALOAD 0
|
||||
ALOAD 3
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$caughtSkipAndReturn (Ljava/lang/Throwable;)V
|
||||
L4
|
||||
ALOAD 0
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$exitSkipAndReturn (Ljava/lang/String;)V
|
||||
ALOAD 3
|
||||
ATHROW
|
||||
L5
|
||||
FRAME SAME1 java/lang/Throwable
|
||||
ASTORE 3
|
||||
ALOAD 0
|
||||
ALOAD 1
|
||||
INVOKESPECIAL com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.surround$exitSkipAndReturn (Ljava/lang/String;)V
|
||||
ALOAD 3
|
||||
ATHROW
|
||||
L11
|
||||
FRAME SAME1 java/lang/String
|
||||
ARETURN
|
||||
L12
|
||||
LOCALVARIABLE this Lcom/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget; L7 L12 0
|
||||
LOCALVARIABLE a Ljava/lang/String; L7 L12 1
|
||||
LOCALVARIABLE skip Z L8 L9 2
|
||||
LOCALVARIABLE surround$error Ljava/lang/Throwable; L2 L12 3
|
||||
LOCALVARIABLE surround$result Ljava/lang/String; L1 L6 4
|
||||
MAXSTACK = 3
|
||||
MAXLOCALS = 5
|
||||
|
||||
// access flags 0x2
|
||||
private skipAndReturn$surround(Ljava/lang/String;)Ljava/lang/String;
|
||||
L0
|
||||
ALOAD 1
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.jvmdowngrader$concat$skipAndReturn$1 (Ljava/lang/String;)Ljava/lang/String;
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/targets/Trace.add (Ljava/lang/String;)V
|
||||
L1
|
||||
ALOAD 1
|
||||
INVOKESTATIC com/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget.jvmdowngrader$concat$skipAndReturn$2 (Ljava/lang/String;)Ljava/lang/String;
|
||||
ARETURN
|
||||
L2
|
||||
LOCALVARIABLE this Lcom/gtnewhorizons/angelica/experimental/surround/integration/WholeTarget; L0 L2 0
|
||||
LOCALVARIABLE a Ljava/lang/String; L0 L2 1
|
||||
MAXSTACK = 1
|
||||
MAXLOCALS = 2
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "com.gtnewhorizons.angelica.experimental.surround.integration.mixinextras",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinWrapTarget"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "com.gtnewhorizons.angelica.experimental.surround.integration.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"MixinCallTarget",
|
||||
"MixinWholeTarget",
|
||||
"MixinWholeTargetHigh"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
extensions.getByType<JavaPluginExtension>().apply {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
}
|
||||
maven {
|
||||
name = "Minecraft Libraries"
|
||||
url = uri("https://libraries.minecraft.net")
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
dependencies {
|
||||
add("shadowImplementation", project(":experimental-surround-runtime")) { isTransitive = false }
|
||||
add("mixinAnnotationProcessor", project(":experimental-surround-ap"))
|
||||
}
|
||||
|
||||
tasks.named<ShadowJar>("shadowJar") {
|
||||
minimize {
|
||||
exclude(project(":experimental-surround-runtime"))
|
||||
}
|
||||
relocate("com.gtnewhorizons.angelica.experimental.surround", "com.gtnewhorizons.angelica.experimental.surround")
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
include("experimental-surround-runtime", "experimental-surround-ap")
|
||||
project(":experimental-surround-runtime").projectDir = file("runtime")
|
||||
project(":experimental-surround-ap").projectDir = file("ap")
|
||||
@@ -67,6 +67,22 @@ artifacts {
|
||||
}
|
||||
}
|
||||
|
||||
// LWJGL2-API stubs for test JVMs that strip the lwjgl-2 jars (glsmSdlTest, glesTest).
|
||||
val lwjgl2StubsSourceSet = sourceSets.create("lwjgl2Stubs") {
|
||||
java.srcDir("src/lwjgl2Stubs/java")
|
||||
}
|
||||
|
||||
val lwjgl2Stubs by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(lwjgl2Stubs.name, lwjgl2StubsSourceSet.java.classesDirectory) {
|
||||
builtBy(tasks.named("lwjgl2StubsClasses"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Mojang"
|
||||
@@ -111,6 +127,7 @@ val spirvTestRuntime by configurations.creating {
|
||||
dependencies {
|
||||
// MC compile stubs
|
||||
compileOnly(sourceSets["stubs"].output)
|
||||
"lwjgl2StubsCompileOnly"(libs.lwjgl3.opengl)
|
||||
|
||||
// LWJGL
|
||||
compileOnly(libs.lwjgl2)
|
||||
@@ -158,6 +175,7 @@ dependencies {
|
||||
testFixturesApi(platform(libs.junit.bom))
|
||||
testFixturesApi(libs.junit.jupiter.api)
|
||||
testFixturesApi(libs.lwjgl2)
|
||||
testFixturesCompileOnly(libs.asm.tree.test)
|
||||
|
||||
// Test
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
@@ -183,6 +201,7 @@ dependencies {
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeFiles>().configureEach {
|
||||
outputs.cacheIf { true }
|
||||
downgradeTo.set(JavaVersion.VERSION_1_8)
|
||||
multiReleaseOriginal.set(false)
|
||||
multiReleaseVersions.set(emptySet())
|
||||
@@ -256,6 +275,13 @@ fun Test.configureGlsmJava8() {
|
||||
}
|
||||
}
|
||||
|
||||
val mutableStaticsAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "mutable-statics-agent"
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.testutil.MutableStatics")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags = GL_TASK_TAGS.values.toSet() + "lwjgl3"
|
||||
@@ -264,6 +290,29 @@ tasks.test {
|
||||
excludeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
}
|
||||
configureGlsmJava8()
|
||||
javaAgent(files(mutableStaticsAgentJar))
|
||||
}
|
||||
|
||||
val redirectAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "redirect-agent"
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent")
|
||||
}
|
||||
}
|
||||
|
||||
val redirectAgent by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
val mutableStaticsAgent by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(redirectAgent.name, redirectAgentJar)
|
||||
add(mutableStaticsAgent.name, mutableStaticsAgentJar)
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
@@ -274,6 +323,7 @@ val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
includeTags = setOf(tag)
|
||||
}
|
||||
configureGlsmJava8()
|
||||
javaAgent(files(redirectAgentJar))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,6 +352,8 @@ val verifyTestsRan by tasks.registering {
|
||||
}
|
||||
tasks.check { dependsOn(verifyTestsRan) }
|
||||
|
||||
val unpackLwjgl3Natives = registerLwjgl3NativesUnpack()
|
||||
|
||||
val spirvTest by tasks.registering(Test::class) {
|
||||
description = "Runs SpirvShaderTranslator* tests against native shaderc + spvc on Java 21."
|
||||
group = "verification"
|
||||
@@ -320,6 +372,7 @@ val spirvTest by tasks.registering(Test::class) {
|
||||
filter {
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator*Test")
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.ffp.FFPUniformBlockSpirvLayoutTest")
|
||||
includeTestsMatching("com.gtnewhorizons.angelica.glsm.shader.SpirvCompilerDiskCacheTest")
|
||||
}
|
||||
|
||||
// libshaderc.so / libspirv-cross.so are extracted by extractNatives3.
|
||||
@@ -329,6 +382,7 @@ val spirvTest by tasks.registering(Test::class) {
|
||||
"-Djava.library.path=${extractNatives3.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}",
|
||||
"--enable-native-access=ALL-UNNAMED"
|
||||
)
|
||||
loadLwjgl3NativesFrom(unpackLwjgl3Natives)
|
||||
|
||||
testLogging {
|
||||
showStandardStreams = true
|
||||
|
||||
File renamed without changes.
+19
@@ -1,5 +1,7 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ContextCapabilities {
|
||||
|
||||
public boolean OpenGL30 = true;
|
||||
@@ -21,11 +23,28 @@ public class ContextCapabilities {
|
||||
public boolean GL_ARB_sampler_objects = true;
|
||||
public boolean GL_ARB_shader_image_load_store = true;
|
||||
public boolean GL_ARB_shader_storage_buffer_object = true;
|
||||
public boolean GL_ARB_stencil_texturing = false;
|
||||
public boolean GL_ARB_tessellation_shader = false;
|
||||
public boolean GL_ARB_texture_storage = false;
|
||||
|
||||
public boolean GL_EXT_direct_state_access = false;
|
||||
public boolean GL_EXT_gpu_shader4 = true;
|
||||
public boolean GL_EXT_shader_image_load_store = true;
|
||||
|
||||
public boolean GL_NVX_gpu_memory_info = false;
|
||||
|
||||
public ContextCapabilities() {
|
||||
if ("lwjgl3".equals(System.getProperty("glsm.stubs.capabilities"))) copyFromCurrentContext();
|
||||
}
|
||||
|
||||
private void copyFromCurrentContext() {
|
||||
final GLCapabilities caps = GL.getCapabilities();
|
||||
for (Field field : ContextCapabilities.class.getFields()) {
|
||||
try {
|
||||
field.setBoolean(this, GLCapabilities.class.getField(field.getName()).getBoolean(caps));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException("LWJGL3 GLCapabilities has no " + field.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,13 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public final class GLContext {
|
||||
|
||||
private static ContextCapabilities capabilities;
|
||||
|
||||
private GLContext() {}
|
||||
|
||||
public static ContextCapabilities getCapabilities() {
|
||||
if (capabilities == null) capabilities = new ContextCapabilities();
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,11 @@ public final class SystemProperties {
|
||||
|
||||
// Backend selection
|
||||
public static final String KEY_USE_SDL_GPU = "angelica.sdlgpu.enable";
|
||||
public static final boolean USE_SDL_GPU = Boolean.getBoolean(KEY_USE_SDL_GPU);
|
||||
public static final Boolean SDL_GPU_OVERRIDE = parseBooleanOverride(KEY_USE_SDL_GPU);
|
||||
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
|
||||
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
public static final boolean DISABLE_LTW_WORKAROUND = Boolean.getBoolean("angelica.disableLtwWorkaround");
|
||||
public static final boolean EMULATE_CLIP_DISTANCES = Boolean.getBoolean("angelica.emulateClipDistances");
|
||||
|
||||
// Redirector
|
||||
public static final UnmappedGLMode UNMAPPED_GL = parseEnum("angelica.unmappedGL", UnmappedGLMode.WARN, UnmappedGLMode.WARN, UnmappedGLMode.class);
|
||||
@@ -28,7 +29,8 @@ public final class SystemProperties {
|
||||
|
||||
// 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 String SDL_GPU_DRIVER_OVERRIDE = parseStringOverride(KEY_SDL_GPU_DRIVER);
|
||||
public static final String MOLTENVK_DIR = System.getProperty("angelica.moltenvk.dir", "angelica" + File.separator + "natives" + File.separator + "moltenvk");
|
||||
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;
|
||||
@@ -44,6 +46,8 @@ public final class SystemProperties {
|
||||
public static final Boolean TRACY_FINE_ZONES_OVERRIDE = parseBooleanOverride("angelica.tracy.fineZones");
|
||||
public static final String TRACY_DIR = System.getProperty("angelica.tracy.dir", "angelica" + File.separator + "natives" + File.separator + "tracy");
|
||||
public static final Integer TRACY_MAX_SRC_LOCS_OVERRIDE = Integer.getInteger("angelica.tracy.maxSrcLocs");
|
||||
public static final Boolean TRACY_FRAME_IMAGES_OVERRIDE = parseBooleanOverride("angelica.tracy.frameImages");
|
||||
public static final Integer TRACY_FRAME_IMAGE_RATE_OVERRIDE = Integer.getInteger("angelica.tracy.frameImageRate");
|
||||
|
||||
// Profiling
|
||||
public static final String PROFILE_OPTS = System.getProperty("angelica.profile.opts", "event=wall,interval=5ms,alloc=512k,lock=10ms");
|
||||
@@ -61,9 +65,25 @@ public final class SystemProperties {
|
||||
public static final long FLYBY_TIME_OF_DAY = Math.floorMod(Long.getLong("angelica.flyby.timeOfDay", 6000L), 24000L);
|
||||
public static final String FLYBY_COMMANDS = System.getProperty("angelica.flyby.commands", "");
|
||||
public static final String FLYBY_ORIGIN = System.getProperty("angelica.flyby.origin", "");
|
||||
public static final Integer FLYBY_DIMENSION = Integer.getInteger("angelica.flyby.dimension");
|
||||
public static final String FLYBY_BIOME = System.getProperty("angelica.flyby.biome", "");
|
||||
public static final boolean FLYBY_SURVIVAL = Boolean.getBoolean("angelica.flyby.survival");
|
||||
public static final FlybyPacing FLYBY_PACING = parseEnum("angelica.flyby.pacing", FlybyPacing.UNCAPPED, FlybyPacing.UNCAPPED, FlybyPacing.class);
|
||||
public static final FlybyWeather FLYBY_WEATHER = parseEnum("angelica.flyby.weather", FlybyWeather.CLEAR, FlybyWeather.CLEAR, FlybyWeather.class);
|
||||
public static final FlybyCamera FLYBY_CAMERA = parseEnum("angelica.flyby.camera", FlybyCamera.FIRST_PERSON, FlybyCamera.FIRST_PERSON, FlybyCamera.class);
|
||||
public static final boolean FLYBY_JFR = Boolean.getBoolean("angelica.flyby.jfr");
|
||||
public static final float FLYBY_PITCH = parseFloat("angelica.flyby.pitch");
|
||||
public static final int FLYBY_SCREENSHOTS = Integer.getInteger("angelica.flyby.screenshots", 0);
|
||||
public static final Boolean FLYBY_DEBUG_HUD = parseBooleanOverride("angelica.flyby.debugHud");
|
||||
public static final String FLYBY_DEBUG_PROFILE = System.getProperty("angelica.flyby.debugProfile", "");
|
||||
public static final String FLYBY_DEBUG_ENTRIES = System.getProperty("angelica.flyby.debugEntries", "");
|
||||
public static final Boolean FLYBY_DEBUG_FLAT = parseBooleanOverride("angelica.flyby.debugFlat");
|
||||
public static final Boolean FLYBY_HUD_CACHING = parseBooleanOverride("angelica.flyby.hudCaching");
|
||||
public static final Boolean FLYBY_FPS_GRAPH = parseBooleanOverride("angelica.flyby.fpsGraph");
|
||||
public static final boolean FLYBY_DEBUG_DUMP = Boolean.getBoolean("angelica.flyby.debugDump");
|
||||
public static final String FLYBY_CRASH_TEST = System.getProperty("angelica.flyby.crashTest", "");
|
||||
public static final boolean FLYBY_HEAP_CHECK = Boolean.getBoolean("angelica.flyby.heapCheck");
|
||||
public static final int FLYBY_VISITS = Integer.getInteger("angelica.flyby.visits", 1);
|
||||
|
||||
// Debug
|
||||
public static final boolean LWJGL_DEBUG = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
@@ -127,6 +147,12 @@ public final class SystemProperties {
|
||||
CONFIGURED
|
||||
}
|
||||
|
||||
public enum FlybyCamera {
|
||||
FIRST_PERSON,
|
||||
THIRD_PERSON_BACK,
|
||||
THIRD_PERSON_FRONT
|
||||
}
|
||||
|
||||
public enum FlybyWeather {
|
||||
CLEAR,
|
||||
RAIN,
|
||||
@@ -154,7 +180,12 @@ public final class SystemProperties {
|
||||
|
||||
private static Boolean parseBooleanOverride(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
return raw == null ? null : Boolean.parseBoolean(raw);
|
||||
return raw == null || raw.isEmpty() ? null : Boolean.parseBoolean(raw);
|
||||
}
|
||||
|
||||
private static String parseStringOverride(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
return raw == null || raw.isEmpty() ? null : raw;
|
||||
}
|
||||
|
||||
private static double parseDouble(String key) {
|
||||
@@ -167,6 +198,16 @@ public final class SystemProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private static float parseFloat(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return Float.NaN;
|
||||
try {
|
||||
return Float.parseFloat(raw);
|
||||
} catch (NumberFormatException e) {
|
||||
return Float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDeobf() {
|
||||
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("fml.deobfuscatedEnvironment"));
|
||||
}
|
||||
|
||||
@@ -25,11 +25,21 @@ public final class CaptureGate {
|
||||
public static final boolean FORCED = SystemProperties.DEBUG_MARKERS || SystemProperties.LWJGL_DEBUG;
|
||||
public static final boolean TOOL_ATTACHED = detectCaptureTool();
|
||||
public static boolean markersThisFrame = true;
|
||||
public static boolean tracyConnected;
|
||||
private static boolean toolAttached = TOOL_ATTACHED;
|
||||
|
||||
private CaptureGate() {}
|
||||
|
||||
public static void refresh() {
|
||||
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, Tracy.isConnected());
|
||||
tracyConnected = Tracy.isConnected();
|
||||
markersThisFrame = gateValue(FORCED, toolAttached, tracyConnected);
|
||||
}
|
||||
|
||||
static void probeGlTool() {
|
||||
if (RenderSystem.hasExtension("GL_EXT_debug_tool")) {
|
||||
toolAttached = true;
|
||||
LOGGER.info("Capture tool detected via GL_EXT_debug_tool; instrumentation enabled");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
|
||||
@@ -37,7 +47,7 @@ public final class CaptureGate {
|
||||
}
|
||||
|
||||
public static boolean enabledAtStartup() {
|
||||
return FORCED || TOOL_ATTACHED;
|
||||
return FORCED || toolAttached;
|
||||
}
|
||||
|
||||
private static boolean detectCaptureTool() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
|
||||
public final class ClipPlaneBooleanState extends BooleanStateStack {
|
||||
@@ -13,12 +14,22 @@ public final class ClipPlaneBooleanState extends BooleanStateStack {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean forwardsToBackend() {
|
||||
return !RenderSystem.isClipDistanceEmulated();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabledChanged() {
|
||||
final int before = owner.clipPlaneEnabledMask;
|
||||
if (isEnabled()) {
|
||||
owner.clipPlaneEnabledMask |= (1 << index);
|
||||
} else {
|
||||
owner.clipPlaneEnabledMask &= ~(1 << index);
|
||||
}
|
||||
owner.clipPlaneGeneration++;
|
||||
if ((before == 0) != (owner.clipPlaneEnabledMask == 0) && RenderSystem.isClipDistanceEmulated() && GLSMHooks.CLIP_PLANES_IN_USE.hasListeners()) {
|
||||
GLSMHooks.CLIP_PLANES_IN_USE.post(GLSMHooks.clipPlanesInUseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ 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.ShaderDiskCache;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderIndex;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
@@ -139,12 +141,20 @@ public class CompatShaderTransformer {
|
||||
if (cached != null) {
|
||||
result = cached;
|
||||
} else {
|
||||
try {
|
||||
result = transformInternal(source, isFragment);
|
||||
final ShaderDiskCache.Key dk = ShaderDiskCache.isEnabled() ? diskKey(source, isFragment) : null;
|
||||
final String disk = dk != null ? ShaderDiskCache.getString(dk) : null;
|
||||
if (disk != null) {
|
||||
result = disk;
|
||||
cache.put(key, result);
|
||||
} catch (Exception e) {
|
||||
GLStateManager.LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
|
||||
result = fixupVersion(source);
|
||||
} else {
|
||||
try {
|
||||
result = transformInternal(source, isFragment);
|
||||
cache.put(key, result);
|
||||
if (dk != null) ShaderDiskCache.putString(dk, result);
|
||||
} catch (Exception e) {
|
||||
GLStateManager.LOGGER.warn("CompatShaderTransformer: AST transformation failed, falling back to version fixup only", e);
|
||||
result = fixupVersion(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,6 +174,14 @@ public class CompatShaderTransformer {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int minGlslVersion() {
|
||||
return RENDER_BACKEND != null ? RENDER_BACKEND.getMinGLSLVersion() : 330;
|
||||
}
|
||||
|
||||
static ShaderDiskCache.Key diskKey(String source, boolean isFragment) {
|
||||
return ShaderDiskCache.key("compat").str(source).b(isFragment).i(minGlslVersion());
|
||||
}
|
||||
|
||||
public static boolean isCoreShader(String source) {
|
||||
final Matcher vm = VERSION_PATTERN.matcher(source);
|
||||
if (vm.find()) {
|
||||
@@ -186,7 +204,7 @@ public class CompatShaderTransformer {
|
||||
declaredVersion = Integer.parseInt(versionMatcher.group(1));
|
||||
}
|
||||
|
||||
final int targetVersion = Math.max(declaredVersion, RENDER_BACKEND != null ? RENDER_BACKEND.getMinGLSLVersion() : 330);
|
||||
final int targetVersion = Math.max(declaredVersion, minGlslVersion());
|
||||
|
||||
// Pre-parse reserved word renaming - prevents ANTLR parse failures
|
||||
source = GlslTransformUtils.replaceTexture(source);
|
||||
@@ -439,12 +457,7 @@ public class CompatShaderTransformer {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
|
||||
declarators.add(decl.typeless_declaration());
|
||||
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
|
||||
declarators.addAll(list.typeless_declaration());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext d : declarators) {
|
||||
for (GLSLParser.Typeless_declarationContext d : ShaderIndex.declarators((GLSLParser.Init_declarator_listContext) decl.getParent())) {
|
||||
if (d.IDENTIFIER() == null) continue;
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
|
||||
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FragmentKey;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Uniforms;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LightStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
@@ -66,7 +68,13 @@ public class CompatUniformManager {
|
||||
static final int LOC_IRIS_MODELVIEW_INVERSE = LOC_MAT_BASE + MAT_FIELDS; // 48
|
||||
static final int LOC_IRIS_PROJECTION_INVERSE = LOC_IRIS_MODELVIEW_INVERSE + 1; // 49
|
||||
|
||||
static final int LOC_COUNT = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
|
||||
static final int LOC_FOG_MODE = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
|
||||
static final int LOC_FOG_PARAMS = LOC_FOG_MODE + 1; // 51
|
||||
static final int LOC_FOG_DISTANCE_MODE = LOC_FOG_PARAMS + 1; // 52
|
||||
|
||||
static final int LOC_MODELVIEW_PROJECTION = LOC_FOG_DISTANCE_MODE + 1; // 53
|
||||
|
||||
static final int LOC_COUNT = LOC_MODELVIEW_PROJECTION + 1; // 54
|
||||
|
||||
private static final String[] LIGHT_FIELD_NAMES = {
|
||||
"ambient", "diffuse", "specular", "position", "halfVector",
|
||||
@@ -84,6 +92,7 @@ public class CompatUniformManager {
|
||||
UNIFORM_NAMES[LOC_MODELVIEW] = "angelica_ModelViewMatrix";
|
||||
UNIFORM_NAMES[LOC_MODELVIEW_INVERSE] = "angelica_ModelViewMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_PROJECTION] = "angelica_ProjectionMatrix";
|
||||
UNIFORM_NAMES[LOC_MODELVIEW_PROJECTION] = "angelica_ModelViewProjectionMatrix";
|
||||
UNIFORM_NAMES[LOC_PROJECTION_INVERSE] = "angelica_ProjectionMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_NORMAL] = "angelica_NormalMatrix";
|
||||
UNIFORM_NAMES[LOC_LIGHTMAP_TEXTURE_MATRIX] = "angelica_LightmapTextureMatrix";
|
||||
@@ -98,6 +107,9 @@ public class CompatUniformManager {
|
||||
UNIFORM_NAMES[LOC_IRIS_TEXTURE_MATRIX] = "iris_TextureMatrix";
|
||||
UNIFORM_NAMES[LOC_IRIS_MODELVIEW_INVERSE] = "iris_ModelViewMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_IRIS_PROJECTION_INVERSE] = "iris_ProjectionMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_FOG_MODE] = "angelica_FogMode";
|
||||
UNIFORM_NAMES[LOC_FOG_PARAMS] = "angelica_FogParams";
|
||||
UNIFORM_NAMES[LOC_FOG_DISTANCE_MODE] = "angelica_FogDistanceMode";
|
||||
UNIFORM_NAMES[LOC_ALPHA_TEST_REF] = "angelica_currentAlphaTest";
|
||||
UNIFORM_NAMES[LOC_SCENE_COLOR] = "angelica_SceneColor";
|
||||
UNIFORM_NAMES[LOC_CLIP_PLANES] = "angelica_ClipPlane[0]";
|
||||
@@ -120,7 +132,14 @@ public class CompatUniformManager {
|
||||
GLContextState owner;
|
||||
int lastMvGen = -1, lastMvLinearGen = -1, lastProjGen = -1, lastTexMatGen = -1;
|
||||
int lastFragmentGen = -1, lastLightingGen = -1, lastClipPlaneGen = -1;
|
||||
ProgramUniforms(int[] locs) { this.locs = locs; }
|
||||
int lastFogMode = -1, lastFogDistanceMode = -1;
|
||||
final boolean lights;
|
||||
ProgramUniforms(int[] locs) {
|
||||
this.locs = locs;
|
||||
boolean any = false;
|
||||
for (int i = LOC_LIGHT_BASE; i < LOC_MAT_BASE; i++) any |= locs[i] != -1;
|
||||
this.lights = any;
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable NIO buffers for upload
|
||||
@@ -186,6 +205,7 @@ public class CompatUniformManager {
|
||||
pu.owner = glCtx;
|
||||
pu.lastMvGen = pu.lastMvLinearGen = pu.lastProjGen = pu.lastTexMatGen = -1;
|
||||
pu.lastFragmentGen = pu.lastLightingGen = pu.lastClipPlaneGen = -1;
|
||||
pu.lastFogMode = pu.lastFogDistanceMode = -1;
|
||||
}
|
||||
|
||||
// Matrix uniforms — skip if this program's storage already holds the current generation
|
||||
@@ -212,12 +232,15 @@ public class CompatUniformManager {
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
pu.lastFogMode = uploadIfChanged(locs[LOC_FOG_MODE], FragmentKey.activeFogMode(glCtx), pu.lastFogMode);
|
||||
pu.lastFogDistanceMode = uploadIfChanged(locs[LOC_FOG_DISTANCE_MODE], glCtx.fogState.getFogDistanceMode(), pu.lastFogDistanceMode);
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = glCtx.lightingGeneration;
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
uploadLightSources(locs);
|
||||
if (pu.lights) uploadLightSources(locs);
|
||||
uploadMaterial(locs);
|
||||
}
|
||||
|
||||
@@ -226,7 +249,7 @@ public class CompatUniformManager {
|
||||
final int cpGen = glCtx.clipPlaneGeneration;
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
uploadClipPlanes(locs, glCtx.clipPlaneEnabledMask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,8 +257,8 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
final Matrix4f mv = GLSMMatrices.getModelViewMatrix();
|
||||
final Matrix4f proj = GLSMMatrices.getProjectionMatrix();
|
||||
|
||||
if (mvChanged) {
|
||||
// ModelView
|
||||
@@ -262,6 +285,11 @@ public class CompatUniformManager {
|
||||
}
|
||||
}
|
||||
|
||||
if ((mvChanged || projChanged) && locs[LOC_MODELVIEW_PROJECTION] != -1) {
|
||||
Uniforms.modelViewProjection(proj, mv, scratchMatrix).get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
|
||||
if (mvLinearChanged) {
|
||||
// Normal Matrix (inverse-transpose of upper-left 3x3 of ModelView)
|
||||
if (locs[LOC_NORMAL] != -1 || locs[LOC_IRIS_NORMAL] != -1) {
|
||||
@@ -312,21 +340,26 @@ public class CompatUniformManager {
|
||||
|
||||
// angelica_LightmapTextureMatrix — actual GLSM texture unit 1 matrix (set by enableLightmap())
|
||||
if (locs[LOC_LIGHTMAP_TEXTURE_MATRIX] != -1) {
|
||||
GLStateManager.getTextures().getTextureUnitMatrix(1).get(mat4Buf);
|
||||
GLSMTextures.getTextures().getTextureUnitMatrix(1).get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_LIGHTMAP_TEXTURE_MATRIX], false, mat4Buf);
|
||||
}
|
||||
|
||||
// iris_TextureMatrix — texture unit 0 matrix (animated by glints, etc.)
|
||||
if (locs[LOC_IRIS_TEXTURE_MATRIX] != -1) {
|
||||
GLStateManager.getTextures().getTextureUnitMatrix(0).get(mat4Buf);
|
||||
GLSMTextures.getTextures().getTextureUnitMatrix(0).get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_TEXTURE_MATRIX], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int uploadIfChanged(int location, int value, int last) {
|
||||
if (location != -1 && value != last) RENDER_BACKEND.uniform1i(location, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void uploadFragmentUniforms(int[] locs) {
|
||||
// Fog uniforms
|
||||
FogStateStack fog = GLStateManager.getFogState();
|
||||
FogStateStack fog = GLSMFog.getFogState();
|
||||
if (locs[LOC_FOG_DENSITY] != -1) {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_DENSITY], Math.max(0.0f, fog.getDensity()));
|
||||
}
|
||||
@@ -336,6 +369,9 @@ public class CompatUniformManager {
|
||||
if (locs[LOC_FOG_END] != -1) {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_END], fog.getEnd());
|
||||
}
|
||||
if (locs[LOC_FOG_PARAMS] != -1) {
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_FOG_PARAMS], fog.linearScale(), fog.linearBias(), fog.expScale(), fog.exp2Scale());
|
||||
}
|
||||
if (locs[LOC_FOG_COLOR] != -1) {
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put((float) fog.getFogColor().x).put((float) fog.getFogColor().y).put((float) fog.getFogColor().z).put(fog.getFogAlpha());
|
||||
@@ -347,10 +383,10 @@ public class CompatUniformManager {
|
||||
// Return -1.0f for ALWAYS or disabled so `alpha <= ref` never discards.
|
||||
if (locs[LOC_ALPHA_TEST_REF] != -1) {
|
||||
final float ref;
|
||||
if (!GLStateManager.getAlphaTest().isEnabled() || GLStateManager.getAlphaState().getFunction() == GL11.GL_ALWAYS) {
|
||||
if (!GLSMDepthStencilAlpha.getAlphaTest().isEnabled() || GLSMDepthStencilAlpha.getAlphaState().getFunction() == GL11.GL_ALWAYS) {
|
||||
ref = -1.0f;
|
||||
} else {
|
||||
ref = GLStateManager.getAlphaState().getReference();
|
||||
ref = GLSMDepthStencilAlpha.getAlphaState().getReference();
|
||||
}
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_ALPHA_TEST_REF], ref);
|
||||
}
|
||||
@@ -360,8 +396,8 @@ public class CompatUniformManager {
|
||||
* Scene color = emission + ambient * lightModel.ambient, alpha = diffuse.a Same computation as {@code Uniforms.uploadLightingPreComputed()}.
|
||||
*/
|
||||
private static void uploadSceneColor(int[] locs) {
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
final LightModelState lm = GLStateManager.getLightModel();
|
||||
final MaterialState mat = GLSMLighting.getFrontMaterial();
|
||||
final LightModelState lm = GLSMLighting.getLightModel();
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(mat.emission.x + mat.ambient.x * lm.ambient.x)
|
||||
.put(mat.emission.y + mat.ambient.y * lm.ambient.y)
|
||||
@@ -372,9 +408,7 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
private static void uploadLightSources(int[] locs) {
|
||||
// Early exit: if the first light field isn't present, no lighting uniforms exist
|
||||
if (locs[LOC_LIGHT_BASE] == -1) return;
|
||||
final LightStateStack[] lights = GLStateManager.getLightDataStates();
|
||||
final LightStateStack[] lights = GLSMLighting.getLightDataStates();
|
||||
for (int li = 0; li < 2; li++) {
|
||||
final int base = LOC_LIGHT_BASE + li * LIGHT_FIELDS;
|
||||
final LightState light = lights[li];
|
||||
@@ -421,7 +455,7 @@ public class CompatUniformManager {
|
||||
private static void uploadMaterial(int[] locs) {
|
||||
// Early exit: if the first material field isn't present, no material uniforms exist
|
||||
if (locs[LOC_MAT_BASE] == -1) return;
|
||||
final MaterialState mat = GLStateManager.getFrontMaterial();
|
||||
final MaterialState mat = GLSMLighting.getFrontMaterial();
|
||||
if (locs[LOC_MAT_BASE + MF_EMISSION] != -1)
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_MAT_BASE + MF_EMISSION], mat.emission.x, mat.emission.y, mat.emission.z, mat.emission.w);
|
||||
if (locs[LOC_MAT_BASE + MF_AMBIENT] != -1)
|
||||
@@ -434,16 +468,14 @@ public class CompatUniformManager {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_MAT_BASE + MF_SHININESS], mat.shininess);
|
||||
}
|
||||
|
||||
private static void uploadClipPlanes(int[] locs) {
|
||||
private static void uploadClipPlanes(int[] locs, int enabledMask) {
|
||||
if (locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], GLStateManager.anyClipPlaneEnabled() ? 1 : 0);
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], enabledMask != 0 ? 1 : 0);
|
||||
}
|
||||
if (locs[LOC_CLIP_PLANES] != -1) {
|
||||
final ClipPlaneState cps = GLStateManager.getClipPlaneState();
|
||||
final ClipPlaneState cps = GLSMRasterizer.getClipPlaneState();
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
cps.putEffectivePlanes(enabledMask, clipPlaneBuf);
|
||||
clipPlaneBuf.flip();
|
||||
RENDER_BACKEND.uniform4(locs[LOC_CLIP_PLANES], clipPlaneBuf);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
/*
|
||||
* Ported from Mesa's util_idalloc (src/util/u_idalloc.c).
|
||||
* Ported from Mesa's util_idalloc and util_idalloc_sparse (src/util/u_idalloc.c).
|
||||
*
|
||||
* Original copyright and license:
|
||||
*
|
||||
@@ -32,17 +32,15 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bitmap ID allocator for display list IDs.
|
||||
*
|
||||
* <p>Bitmap-based: each bit in the {@code data} array represents one ID.
|
||||
* Bit set = allocated, bit clear = free. ID 0 is reserved (never returned).
|
||||
* Sparse bitmap ID allocator for display list IDs; ID 0 is reserved (never returned).
|
||||
*/
|
||||
public class DisplayListIDAllocator {
|
||||
|
||||
private int[] data;
|
||||
private int numElements; // number of uint32 words allocated
|
||||
private int numSetElements; // highest word index with any bit set + 1
|
||||
private int lowestFreeIdx; // optimization: skip fully-allocated words
|
||||
private static final int SEGMENT_SHIFT = 22;
|
||||
private static final int SEGMENT_MASK = (1 << SEGMENT_SHIFT) - 1;
|
||||
private static final int WORDS_PER_SEGMENT = (1 << SEGMENT_SHIFT) / 32;
|
||||
|
||||
private final Segment[] segments = new Segment[512];
|
||||
|
||||
public DisplayListIDAllocator() {
|
||||
this(32); // 1024 IDs initially
|
||||
@@ -50,138 +48,191 @@ public class DisplayListIDAllocator {
|
||||
|
||||
public DisplayListIDAllocator(int initialWords) {
|
||||
if (initialWords < 1) initialWords = 1;
|
||||
this.data = new int[initialWords];
|
||||
this.numElements = initialWords;
|
||||
this.numSetElements = 0;
|
||||
this.lowestFreeIdx = 0;
|
||||
final Segment first = new Segment(initialWords);
|
||||
// Reserve ID 0 (never returned), matching Mesa's skip_zero pattern
|
||||
data[0] = 1;
|
||||
numSetElements = 1;
|
||||
first.data[0] = 1;
|
||||
first.numSetElements = 1;
|
||||
segments[0] = first;
|
||||
}
|
||||
|
||||
private Segment segment(int index) {
|
||||
Segment s = segments[index];
|
||||
if (s == null) {
|
||||
s = new Segment(1);
|
||||
segments[index] = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public synchronized int allocRange(int count) {
|
||||
if (count <= 0) throw new IllegalArgumentException("count must be > 0");
|
||||
if (count == 1) return allocSingle();
|
||||
return allocMultiple(count);
|
||||
}
|
||||
|
||||
private int allocSingle() {
|
||||
for (int i = lowestFreeIdx; i < numElements; i++) {
|
||||
if (data[i] == 0xFFFFFFFF) continue;
|
||||
|
||||
int bit = Integer.numberOfTrailingZeros(~data[i]);
|
||||
data[i] |= 1 << bit;
|
||||
lowestFreeIdx = i;
|
||||
numSetElements = Math.max(numSetElements, i + 1);
|
||||
return i * 32 + bit;
|
||||
}
|
||||
|
||||
// No slots available, grow. Record old end, then mark first bit there.
|
||||
int oldNumElements = numElements;
|
||||
resize(Math.max(numElements, 1) * 2);
|
||||
lowestFreeIdx = oldNumElements;
|
||||
data[oldNumElements] |= 1;
|
||||
numSetElements = Math.max(numSetElements, oldNumElements + 1);
|
||||
return oldNumElements * 32;
|
||||
}
|
||||
|
||||
private int allocMultiple(int num) {
|
||||
int numWords = divRoundUp(num, 32);
|
||||
int base = findFreeBlock(lowestFreeIdx);
|
||||
boolean found = false;
|
||||
|
||||
while (!found) {
|
||||
int i;
|
||||
for (i = base; i < numElements && i - base < numWords && data[i] == 0; i++)
|
||||
;
|
||||
|
||||
if (i - base == numWords) {
|
||||
found = true;
|
||||
} else if (i == numElements) {
|
||||
// Hit end — base is valid: data[base..end] are zero, resize extends the run into newly zeroed words.
|
||||
resize(numElements * 2 + numWords);
|
||||
found = true;
|
||||
} else {
|
||||
// Non-zero word at i, skip past it
|
||||
base = i + 1;
|
||||
if (count == 1) {
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
final Segment s = segment(i);
|
||||
if (s.lowestFreeIdx < WORDS_PER_SEGMENT) return (i << SEGMENT_SHIFT) | s.allocSingle();
|
||||
}
|
||||
throw new IllegalStateException("all display list IDs are in use");
|
||||
}
|
||||
|
||||
// Mark bits as used
|
||||
int fullWords = num / 32;
|
||||
int remainder = num % 32;
|
||||
for (int i = base; i < base + fullWords; i++) {
|
||||
data[i] = 0xFFFFFFFF;
|
||||
final int numWords = Segment.divRoundUp(count, 32);
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
final Segment s = segment(i);
|
||||
if (s.lowestFreeIdx + numWords > WORDS_PER_SEGMENT) continue;
|
||||
final int base = s.allocMultiple(count);
|
||||
if (base + count <= 1 << SEGMENT_SHIFT) return (i << SEGMENT_SHIFT) | base;
|
||||
for (int j = 0; j < count; j++) s.free(base + j);
|
||||
}
|
||||
if (remainder != 0) {
|
||||
data[base + fullWords] |= (1 << remainder) - 1;
|
||||
}
|
||||
|
||||
if (lowestFreeIdx == base) {
|
||||
lowestFreeIdx = base + num / 32;
|
||||
}
|
||||
numSetElements = Math.max(numSetElements, base + numWords);
|
||||
|
||||
return base * 32;
|
||||
throw new IllegalStateException("no free consecutive range of " + count + " display list IDs");
|
||||
}
|
||||
|
||||
private int findFreeBlock(int start) {
|
||||
for (int i = start; i < numElements; i++) {
|
||||
if (data[i] == 0) return i;
|
||||
}
|
||||
return numElements;
|
||||
public synchronized void reserve(int id) {
|
||||
if (id <= 0) return;
|
||||
segment(id >>> SEGMENT_SHIFT).reserve(id & SEGMENT_MASK);
|
||||
}
|
||||
|
||||
public synchronized void free(int id) {
|
||||
if (id <= 0) return; // ID 0 is reserved
|
||||
int idx = id / 32;
|
||||
if (idx >= numElements) return;
|
||||
|
||||
lowestFreeIdx = Math.min(idx, lowestFreeIdx);
|
||||
data[idx] &= ~(1 << (id % 32));
|
||||
|
||||
if (numSetElements == idx + 1) {
|
||||
while (numSetElements > 0 && data[numSetElements - 1] == 0) {
|
||||
numSetElements--;
|
||||
}
|
||||
}
|
||||
final Segment s = segments[id >>> SEGMENT_SHIFT];
|
||||
if (s != null) s.free(id & SEGMENT_MASK);
|
||||
}
|
||||
|
||||
public synchronized void freeRange(int base, int count) {
|
||||
if (count <= 0 || base <= 0) return;
|
||||
|
||||
int startIdx = base / 32;
|
||||
if (startIdx >= numElements) return;
|
||||
|
||||
int end = base + count;
|
||||
for (int id = base; id < end; id++) {
|
||||
int idx = id / 32;
|
||||
if (idx >= numElements) break;
|
||||
data[idx] &= ~(1 << (id % 32));
|
||||
}
|
||||
|
||||
lowestFreeIdx = Math.min(startIdx, lowestFreeIdx);
|
||||
while (numSetElements > 0 && data[numSetElements - 1] == 0) {
|
||||
numSetElements--;
|
||||
final long last = Math.min((long) base + count - 1, Integer.MAX_VALUE);
|
||||
long id = base;
|
||||
while (id <= last) {
|
||||
final int index = (int) (id >>> SEGMENT_SHIFT);
|
||||
final Segment s = segments[index];
|
||||
if (s == null || (id & SEGMENT_MASK) >= (long) s.numElements * 32) {
|
||||
id = (long) (index + 1) << SEGMENT_SHIFT;
|
||||
continue;
|
||||
}
|
||||
s.free((int) id & SEGMENT_MASK);
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean isAllocated(int id) {
|
||||
if (id <= 0) return false;
|
||||
int idx = id / 32;
|
||||
if (idx >= numSetElements) return false;
|
||||
return (data[idx] & (1 << (id % 32))) != 0;
|
||||
final Segment s = segments[id >>> SEGMENT_SHIFT];
|
||||
return s != null && s.isAllocated(id & SEGMENT_MASK);
|
||||
}
|
||||
|
||||
private void resize(int newNumElements) {
|
||||
if (newNumElements <= numElements) return;
|
||||
int[] newData = new int[newNumElements];
|
||||
System.arraycopy(data, 0, newData, 0, numElements);
|
||||
data = newData;
|
||||
numElements = newNumElements;
|
||||
}
|
||||
private static final class Segment {
|
||||
private int[] data;
|
||||
private int numElements; // number of uint32 words allocated
|
||||
private int numSetElements; // highest word index with any bit set + 1
|
||||
private int lowestFreeIdx; // optimization: skip fully-allocated words
|
||||
|
||||
private static int divRoundUp(int a, int b) {
|
||||
return (a + b - 1) / b;
|
||||
Segment(int initialWords) {
|
||||
this.data = new int[initialWords];
|
||||
this.numElements = initialWords;
|
||||
}
|
||||
|
||||
int allocSingle() {
|
||||
for (int i = lowestFreeIdx; i < numElements; i++) {
|
||||
if (data[i] == 0xFFFFFFFF) continue;
|
||||
|
||||
int bit = Integer.numberOfTrailingZeros(~data[i]);
|
||||
data[i] |= 1 << bit;
|
||||
lowestFreeIdx = i;
|
||||
numSetElements = Math.max(numSetElements, i + 1);
|
||||
return i * 32 + bit;
|
||||
}
|
||||
|
||||
// No slots available, grow. Record old end, then mark first bit there.
|
||||
int oldNumElements = numElements;
|
||||
resize(Math.max(numElements, 1) * 2);
|
||||
lowestFreeIdx = oldNumElements;
|
||||
data[oldNumElements] |= 1;
|
||||
numSetElements = Math.max(numSetElements, oldNumElements + 1);
|
||||
return oldNumElements * 32;
|
||||
}
|
||||
|
||||
int allocMultiple(int num) {
|
||||
int numWords = divRoundUp(num, 32);
|
||||
int base = findFreeBlock(lowestFreeIdx);
|
||||
boolean found = false;
|
||||
|
||||
while (!found) {
|
||||
int i;
|
||||
for (i = base; i < numElements && i - base < numWords && data[i] == 0; i++)
|
||||
;
|
||||
|
||||
if (i - base == numWords) {
|
||||
found = true;
|
||||
} else if (i == numElements) {
|
||||
// Hit end — base is valid: data[base..end] are zero, resize extends the run into newly zeroed words.
|
||||
resize(numElements * 2 + numWords);
|
||||
found = true;
|
||||
} else {
|
||||
// Non-zero word at i, skip past it
|
||||
base = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark bits as used
|
||||
int fullWords = num / 32;
|
||||
int remainder = num % 32;
|
||||
for (int i = base; i < base + fullWords; i++) {
|
||||
data[i] = 0xFFFFFFFF;
|
||||
}
|
||||
if (remainder != 0) {
|
||||
data[base + fullWords] |= (1 << remainder) - 1;
|
||||
}
|
||||
|
||||
if (lowestFreeIdx == base) {
|
||||
lowestFreeIdx = base + num / 32;
|
||||
}
|
||||
numSetElements = Math.max(numSetElements, base + numWords);
|
||||
|
||||
return base * 32;
|
||||
}
|
||||
|
||||
int findFreeBlock(int start) {
|
||||
for (int i = start; i < numElements; i++) {
|
||||
if (data[i] == 0) return i;
|
||||
}
|
||||
return numElements;
|
||||
}
|
||||
|
||||
void free(int id) {
|
||||
int idx = id / 32;
|
||||
if (idx >= numElements) return;
|
||||
|
||||
lowestFreeIdx = Math.min(idx, lowestFreeIdx);
|
||||
data[idx] &= ~(1 << (id % 32));
|
||||
|
||||
if (numSetElements == idx + 1) {
|
||||
while (numSetElements > 0 && data[numSetElements - 1] == 0) {
|
||||
numSetElements--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAllocated(int id) {
|
||||
int idx = id / 32;
|
||||
if (idx >= numSetElements) return false;
|
||||
return (data[idx] & (1 << (id % 32))) != 0;
|
||||
}
|
||||
|
||||
void reserve(int id) {
|
||||
final int idx = id / 32;
|
||||
if (idx >= numElements) resize((idx + 1) * 2);
|
||||
data[idx] |= 1 << (id % 32);
|
||||
numSetElements = Math.max(numSetElements, idx + 1);
|
||||
}
|
||||
|
||||
private void resize(int newNumElements) {
|
||||
if (newNumElements <= numElements) return;
|
||||
int[] newData = new int[newNumElements];
|
||||
System.arraycopy(data, 0, newData, 0, numElements);
|
||||
data = newData;
|
||||
numElements = newNumElements;
|
||||
}
|
||||
|
||||
private static int divRoundUp(int a, int b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public class DisplayListManager {
|
||||
private static int glListMode = 0;
|
||||
private static int glListId = -1;
|
||||
private static int recordedStatePushes;
|
||||
private static final DisplayListCommand POP_STATE = GLStateManager::popState;
|
||||
private static final DisplayListCommand POP_STATE = GLSMAttribStack::popState;
|
||||
private static CommandRecorder currentRecorder = null; // Command recorder (null when not recording)
|
||||
private static volatile Thread recordingThread = null; // Thread that started recording (for thread-safety)
|
||||
private static List<AccumulatedDraw> accumulatedDraws = null; // Accumulates quad draws for batching
|
||||
@@ -237,7 +237,7 @@ public class DisplayListManager {
|
||||
currentRecorder.writeMultMatrix(pending);
|
||||
}
|
||||
if (glListMode == GL11.GL_COMPILE_AND_EXECUTE) {
|
||||
GLStateManager.applyMultMatrix(pending);
|
||||
GLSMMatrices.applyMultMatrix(pending);
|
||||
}
|
||||
|
||||
|
||||
@@ -722,7 +722,7 @@ public class DisplayListManager {
|
||||
|
||||
/**
|
||||
* Add an immediate mode draw to the current display list compilation.
|
||||
* Called by GLStateManager.glEnd() when immediate mode geometry is ready.
|
||||
* Called by GLSMDraws.glEnd() when immediate mode geometry is ready.
|
||||
*
|
||||
* <p>This method captures the current transform and command position, creating
|
||||
* an AccumulatedDraw that will be interleaved correctly with other commands
|
||||
@@ -781,7 +781,7 @@ public class DisplayListManager {
|
||||
|
||||
/**
|
||||
* Update the relative transform with a matrix multiplication during display list compilation.
|
||||
* Called by GLStateManager.glMultMatrix().
|
||||
* Called by GLSMMatrices.glMultMatrix().
|
||||
*
|
||||
* <p>In normal mode: accumulates transforms, emits collapsed MultMatrix at barriers.
|
||||
* <p>In debug mode: emits MultMatrix immediately (no collapsing).
|
||||
@@ -833,7 +833,7 @@ public class DisplayListManager {
|
||||
|
||||
/**
|
||||
* Reset the relative transform to identity during display list compilation.
|
||||
* Called by GLStateManager.glLoadMatrix() - after loading an absolute matrix,
|
||||
* Called by GLSMMatrices.glLoadMatrix() - after loading an absolute matrix,
|
||||
* subsequent transforms are relative to that loaded matrix (i.e., start from identity).
|
||||
*/
|
||||
static void resetRelativeTransform() {
|
||||
@@ -1025,7 +1025,7 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
final DisplayListCommand[] complexObjects = commandBuffer.getComplexObjects();
|
||||
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches);
|
||||
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches, GLStateManager.ctx());
|
||||
currentRecorder = null;
|
||||
} catch (Exception e) {
|
||||
GLStateManager.LOGGER.error("Encountered a fatal issue while trying to compile a display list.");
|
||||
@@ -1046,9 +1046,12 @@ public class DisplayListManager {
|
||||
}
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
|
||||
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
|
||||
previous.delete();
|
||||
synchronized (GLSMDraws.displayListIdAllocator) {
|
||||
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
|
||||
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
|
||||
previous.delete();
|
||||
}
|
||||
GLSMDraws.displayListIdAllocator.reserve(glListId);
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -1147,7 +1150,7 @@ public class DisplayListManager {
|
||||
}
|
||||
if (pendingDraw.drawMode != tessellator.drawMode
|
||||
|| pendingDraw.format != tessellator.getVertexFormat()
|
||||
|| GLStateManager.isContinuousDraw(tessellator.drawMode)
|
||||
|| GLSMDraws.isContinuousDraw(tessellator.drawMode)
|
||||
) {
|
||||
drawBarrier();
|
||||
createNewDraw(tessellator, copyLast);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class ExitState {
|
||||
public static final int POLYGON_OFFSET_ENABLE_WRITTEN = 1;
|
||||
public static final int POLYGON_OFFSET_VALUES_WRITTEN = 2;
|
||||
final Values entry = new Values();
|
||||
final Values value = new Values();
|
||||
final MaterialState frontMaterial = new MaterialState(GL11.GL_FRONT);
|
||||
final MaterialState backMaterial = new MaterialState(GL11.GL_BACK);
|
||||
long writes, dependencies;
|
||||
boolean valid;
|
||||
int textureDeleteCount;
|
||||
|
||||
public void clear() {
|
||||
valid = false;
|
||||
writes = dependencies = 0;
|
||||
}
|
||||
|
||||
static final class Values {
|
||||
final Color4 color = new Color4();
|
||||
final Vector3f normal = new Vector3f();
|
||||
final Vector4f texCoord = new Vector4f();
|
||||
final Vector4f shaderColor = new Vector4f();
|
||||
final Vector4f overlayColor = new Vector4f();
|
||||
final BlendState blend = new BlendState();
|
||||
final AlphaState alpha = new AlphaState();
|
||||
final ColorMask colorMask = new ColorMask();
|
||||
final Matrix4f textureMatrix = new Matrix4f();
|
||||
long enabled;
|
||||
int activeUnit, clientActiveUnit, matrixMode, texture, depthFunction, cullFace, frontFace, shadeModel;
|
||||
int materialFace, materialParameter;
|
||||
boolean depthMask;
|
||||
float lightmapX, lightmapY, offsetFactor, offsetUnits, offsetClamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Records the GL state a block of rendering code leaves behind, so a later run of the same block can be skipped and
|
||||
* its exit state applied instead.
|
||||
*/
|
||||
final class ExitStateCapture {
|
||||
// Fields 0..12 are the capabilities, in CAPS order. FRONT_* and BACK_* are four contiguous fields each, ordered like
|
||||
// vector(): apply() and finish() address them as 15L << FRONT_AMBIENT and (field - FRONT_AMBIENT) & 3.
|
||||
static final int BLEND = 0, ALPHA = 1, DEPTH = 2, CULL = 3, LIGHTING = 4, NORMALIZE = 5, RESCALE = 6, LIGHT0 = 7, LIGHT1 = 8, TEXTURE0 = 9, TEXTURE1 = 10, OFFSET = 11, COLOR_MATERIAL = 12, COLOR = 13, NORMAL = 14, TEXCOORD = 15,
|
||||
LIGHTMAP = 16, SHADER_COLOR = 17, BLEND_FUNC = 18, ALPHA_FUNC = 19, DEPTH_FUNC = 20, DEPTH_MASK = 21, COLOR_MASK = 22, CULL_FACE = 23, FRONT_FACE = 24, SHADE_MODEL = 25, ACTIVE_UNIT = 26, TEXTURE_BINDING = 27, MATRIX_MODE = 28,
|
||||
TEXTURE_MATRIX = 29, OFFSET_VALUES = 30, COLOR_MATERIAL_SETTINGS = 31, FRONT_AMBIENT = 32, FRONT_DIFFUSE = 33, FRONT_SPECULAR = 34, FRONT_EMISSION = 35, BACK_AMBIENT = 36, BACK_DIFFUSE = 37, BACK_SPECULAR = 38,
|
||||
BACK_EMISSION = 39, OVERLAY_COLOR = 40, CLIENT_ACTIVE_UNIT = 41;
|
||||
private static final int FIELD_COUNT = 42;
|
||||
private static final long IMPLICIT_INPUTS = bits(ACTIVE_UNIT, MATRIX_MODE);
|
||||
private static final long MATERIAL_BAKE_INPUTS = bits(COLOR, COLOR_MATERIAL_SETTINGS);
|
||||
private static final long DRAW_INPUTS = bits(CULL, TEXTURE0, TEXTURE1, COLOR, LIGHTMAP, OVERLAY_COLOR, CULL_FACE, FRONT_FACE, TEXTURE_BINDING);
|
||||
private static final long VERTEX_INPUTS = bits(NORMAL, TEXCOORD);
|
||||
private static final long MATERIAL_INPUTS = bits(BLEND, ALPHA, DEPTH, BLEND_FUNC, ALPHA_FUNC, DEPTH_FUNC, DEPTH_MASK, COLOR_MASK);
|
||||
private static final int[] CAPS = {GL11.GL_BLEND, GL11.GL_ALPHA_TEST, GL11.GL_DEPTH_TEST, GL11.GL_CULL_FACE, GL11.GL_LIGHTING, GL11.GL_NORMALIZE, GL12.GL_RESCALE_NORMAL, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_TEXTURE_2D,
|
||||
GL11.GL_TEXTURE_2D, GL11.GL_POLYGON_OFFSET_FILL, GL11.GL_COLOR_MATERIAL};
|
||||
|
||||
private static final AtomicInteger openCaptures = new AtomicInteger();
|
||||
|
||||
private final GLContextState context;
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final long[] attribWrites = new long[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
private final long[] matrixWrites = new long[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final boolean[] matrixInherited = new boolean[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final ExitState.Values current = new ExitState.Values();
|
||||
private final Reference2LongOpenHashMap<StateSet> restoredFieldsBySet = new Reference2LongOpenHashMap<>();
|
||||
private ExitState result;
|
||||
private int suspended, attribDepth, modelViewDepth, textureDepth, entryOverrides;
|
||||
private int filterTexture, filterMin, filterMag;
|
||||
private float filterAniso;
|
||||
private boolean filterOperation;
|
||||
private boolean textureInherited;
|
||||
|
||||
ExitStateCapture(GLContextState context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
static boolean anyOpen() {
|
||||
return openCaptures.get() != 0;
|
||||
}
|
||||
|
||||
private static boolean blendWritesReachVanilla() {
|
||||
final DeferredBlendHandler handler = GLSMHooks.blendHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isBlendLocked();
|
||||
}
|
||||
|
||||
private static boolean alphaWritesReachVanilla() {
|
||||
final DeferredAlphaHandler handler = GLSMHooks.alphaHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isAlphaTestLocked();
|
||||
}
|
||||
|
||||
private static boolean depthColorWritesReachVanilla() {
|
||||
final DeferredDepthColorHandler handler = GLSMHooks.depthColorHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isDepthColorLocked();
|
||||
}
|
||||
|
||||
private static int overridesInControl() {
|
||||
return (blendWritesReachVanilla() ? 0 : 1) | (alphaWritesReachVanilla() ? 0 : 2) | (depthColorWritesReachVanilla() ? 0 : 4);
|
||||
}
|
||||
|
||||
private static boolean same(int field, ExitState.Values a, ExitState.Values b) {
|
||||
return switch (field) {
|
||||
case COLOR -> a.color.sameAs(b.color);
|
||||
case NORMAL -> a.normal.equals(b.normal);
|
||||
case TEXCOORD -> a.texCoord.equals(b.texCoord);
|
||||
case LIGHTMAP -> a.lightmapX == b.lightmapX && a.lightmapY == b.lightmapY;
|
||||
case OVERLAY_COLOR -> a.overlayColor.equals(b.overlayColor);
|
||||
case BLEND_FUNC -> a.blend.sameAs(b.blend);
|
||||
case ALPHA_FUNC -> a.alpha.sameAs(b.alpha);
|
||||
case DEPTH_FUNC -> a.depthFunction == b.depthFunction;
|
||||
case DEPTH_MASK -> a.depthMask == b.depthMask;
|
||||
case COLOR_MASK -> a.colorMask.sameAs(b.colorMask);
|
||||
case CULL_FACE -> a.cullFace == b.cullFace;
|
||||
case FRONT_FACE -> a.frontFace == b.frontFace;
|
||||
case ACTIVE_UNIT -> a.activeUnit == b.activeUnit;
|
||||
case TEXTURE_BINDING -> a.texture == b.texture;
|
||||
case MATRIX_MODE -> a.matrixMode == b.matrixMode;
|
||||
case TEXTURE_MATRIX -> a.textureMatrix.equals(b.textureMatrix);
|
||||
case COLOR_MATERIAL_SETTINGS -> a.materialFace == b.materialFace && a.materialParameter == b.materialParameter;
|
||||
default -> throw new IllegalArgumentException("Unsupported renderer state dependency " + field);
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector4f vector(MaterialState state, int component) {
|
||||
return switch (component) {
|
||||
case 0 -> state.ambient;
|
||||
case 1 -> state.diffuse;
|
||||
case 2 -> state.specular;
|
||||
default -> state.emission;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean has(ExitState state, int field) {
|
||||
return has(state.writes, field);
|
||||
}
|
||||
|
||||
private static boolean has(long fields, int field) {
|
||||
return (fields & (1L << field)) != 0;
|
||||
}
|
||||
|
||||
private static long bits(int... fields) {
|
||||
long bits = 0;
|
||||
for (int field : fields) bits |= 1L << field;
|
||||
return bits;
|
||||
}
|
||||
|
||||
boolean active() {
|
||||
return result != null && result.valid && suspended == 0 && !context.poppingAttributes && !BatchStateGuard.isSuspended();
|
||||
}
|
||||
|
||||
void suspend() {
|
||||
suspended++;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
if (suspended == 0) throw new IllegalStateException("Exit capture suspension underflow");
|
||||
suspended--;
|
||||
}
|
||||
|
||||
void begin(ExitState out) {
|
||||
out.clear();
|
||||
if (result != null) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
result = out;
|
||||
openCaptures.incrementAndGet();
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
out.valid = suspended == 0 && !BatchStateGuard.isSuspended() && GLStateManager.isCachingEnabled() && !DisplayListManager.isRecording() && context.savedFilterTextureId == 0;
|
||||
if (!out.valid) return;
|
||||
out.dependencies = IMPLICIT_INPUTS;
|
||||
capture(out.entry, IMPLICIT_INPUTS | DRAW_INPUTS | MATERIAL_INPUTS | MATERIAL_BAKE_INPUTS | VERTEX_INPUTS | (1L << TEXTURE_MATRIX));
|
||||
textureInherited = true;
|
||||
modelView.set(context.modelViewMatrix);
|
||||
attribDepth = context.attribDepth;
|
||||
modelViewDepth = GLSMMatrices.getMatrixStackDepth(context.modelViewMatrix);
|
||||
textureDepth = GLSMMatrices.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
entryOverrides = overridesInControl();
|
||||
}
|
||||
|
||||
boolean finish(ExitState out) {
|
||||
if (result != out) {
|
||||
out.clear();
|
||||
return false;
|
||||
}
|
||||
if (textureInherited && has(out, TEXTURE_MATRIX)) out.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
result = null;
|
||||
openCaptures.decrementAndGet();
|
||||
if (out.valid) out.valid = suspended == 0 && filterTexture == 0 && stacksMatchEntry() && overridesInControl() == entryOverrides;
|
||||
if (out.valid) {
|
||||
capture(out.value, out.writes);
|
||||
if ((out.writes & (15L << FRONT_AMBIENT)) != 0) out.frontMaterial.set(context.frontMaterial);
|
||||
if ((out.writes & (15L << BACK_AMBIENT)) != 0) out.backMaterial.set(context.backMaterial);
|
||||
out.textureDeleteCount = TextureInfoCache.INSTANCE.deleteCount();
|
||||
}
|
||||
filterTexture = 0;
|
||||
return out.valid;
|
||||
}
|
||||
|
||||
private boolean stacksMatchEntry() {
|
||||
return context.attribDepth == attribDepth && GLSMMatrices.getMatrixStackDepth(context.modelViewMatrix) == modelViewDepth
|
||||
&& GLSMMatrices.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0)) == textureDepth && modelView.equals(context.modelViewMatrix);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
if (result != null) {
|
||||
result.clear();
|
||||
openCaptures.decrementAndGet();
|
||||
}
|
||||
result = null;
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void reject() {
|
||||
if (active()) result.valid = false;
|
||||
}
|
||||
|
||||
// Callers record before their cache's unchanged-value early return
|
||||
void write(int field) {
|
||||
if (active()) record(field);
|
||||
}
|
||||
|
||||
void writeBlend() {
|
||||
if (active() && blendWritesReachVanilla()) record(ExitStateCapture.BLEND_FUNC);
|
||||
}
|
||||
|
||||
void writeAlpha() {
|
||||
if (active() && alphaWritesReachVanilla()) record(ExitStateCapture.ALPHA_FUNC);
|
||||
}
|
||||
|
||||
void writeDepthColor(int field) {
|
||||
if (active() && depthColorWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
private void record(int field) {
|
||||
result.writes |= 1L << field;
|
||||
}
|
||||
|
||||
void requireDrawState(boolean materialFromState) {
|
||||
if (!active()) return;
|
||||
long fields = (materialFromState ? DRAW_INPUTS | MATERIAL_INPUTS : DRAW_INPUTS) & ~(result.writes | result.dependencies);
|
||||
if ((fields & (1L << BLEND_FUNC)) != 0 && !context.blendMode.isEffectivelyEnabled()) fields &= ~(1L << BLEND_FUNC);
|
||||
if ((fields & (1L << ALPHA_FUNC)) != 0 && !context.alphaTest.isEffectivelyEnabled()) fields &= ~(1L << ALPHA_FUNC);
|
||||
result.dependencies |= fields;
|
||||
if (textureInherited) result.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
}
|
||||
|
||||
void requireMaterialBakeInputs() {
|
||||
if (active()) result.dependencies |= MATERIAL_BAKE_INPUTS & ~result.writes;
|
||||
}
|
||||
|
||||
void requireVertexDefaults(boolean normal, boolean texCoord, boolean color, boolean lightmap) {
|
||||
if (!active()) return;
|
||||
final long fields = (normal ? 1L << NORMAL : 0) | (texCoord ? 1L << TEXCOORD : 0) | (color ? 1L << COLOR : 0) | (lightmap ? 1L << LIGHTMAP : 0);
|
||||
result.dependencies |= fields & ~result.writes;
|
||||
}
|
||||
|
||||
boolean beginTextureFilter(boolean nearest) {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (!nearest || filterTexture != 0 || context.savedFilterTextureId <= 0 || context.activeTextureUnit.getValue() != 0 || context.savedFilterMin < 0 || context.savedFilterMag < 0 || !(context.savedFilterAniso >= 1)) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
filterTexture = context.savedFilterTextureId;
|
||||
filterMin = context.savedFilterMin;
|
||||
filterMag = context.savedFilterMag;
|
||||
filterAniso = context.savedFilterAniso;
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterSave() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || context.activeTextureUnit.getValue() != 0 || context.textures.getTextureUnitBindings(0).getBinding() != filterTexture || filterDiffersFrom(GL11.GL_NEAREST, GL11.GL_NEAREST, 1))) reject();
|
||||
}
|
||||
|
||||
boolean beginTextureFilterRestore() {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (savedFilterDiffers()) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterRestore() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || filterDiffersFrom(filterMin, filterMag, filterAniso))) reject();
|
||||
filterTexture = 0;
|
||||
}
|
||||
|
||||
private boolean filterOperationBlocked() {
|
||||
if (filterOperation) {
|
||||
failTextureFilter();
|
||||
return true;
|
||||
}
|
||||
return !active();
|
||||
}
|
||||
|
||||
private void startFilterOperation() {
|
||||
filterOperation = true;
|
||||
suspend();
|
||||
}
|
||||
|
||||
private void endFilterOperation() {
|
||||
resume();
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void failTextureFilter() {
|
||||
if (result != null) result.valid = false;
|
||||
}
|
||||
|
||||
void requireTextureFilter(int texture, boolean nearest) {
|
||||
if (filterTexture != 0 && texture == filterTexture && !nearest && active()) reject();
|
||||
}
|
||||
|
||||
private boolean savedFilterDiffers() {
|
||||
return filterTexture == 0 || context.savedFilterTextureId != filterTexture || context.savedFilterMin != filterMin || context.savedFilterMag != filterMag || context.savedFilterAniso != filterAniso;
|
||||
}
|
||||
|
||||
private boolean filterDiffersFrom(int min, int mag, float aniso) {
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(filterTexture);
|
||||
return info == null || info.getMinFilter() != min || info.getMagFilter() != mag || info.getMaxAnisotropy() != aniso;
|
||||
}
|
||||
|
||||
boolean lightingWritten() {
|
||||
return active() && has(result, LIGHTING);
|
||||
}
|
||||
|
||||
int polygonOffsetWrites() {
|
||||
if (!active()) return 0;
|
||||
return (has(result, OFFSET) ? ExitState.POLYGON_OFFSET_ENABLE_WRITTEN : 0) | (has(result, OFFSET_VALUES) ? ExitState.POLYGON_OFFSET_VALUES_WRITTEN : 0);
|
||||
}
|
||||
|
||||
void capability(int cap, int unit) {
|
||||
if (!active()) return;
|
||||
int field = -1;
|
||||
for (int i = 0; i < CAPS.length; i++) {
|
||||
if (CAPS[i] == cap) {
|
||||
field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (field == TEXTURE0) field = unit == 0 ? TEXTURE0 : unit == 1 ? TEXTURE1 : -1;
|
||||
if (field < 0) result.valid = false;
|
||||
else if (field == BLEND ? blendWritesReachVanilla() : field != ALPHA || alphaWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
void material(int face, int parameter) {
|
||||
if (!active()) return;
|
||||
int first = switch (parameter) {
|
||||
case GL11.GL_AMBIENT, GL11.GL_AMBIENT_AND_DIFFUSE -> 0;
|
||||
case GL11.GL_DIFFUSE -> 1;
|
||||
case GL11.GL_SPECULAR -> 2;
|
||||
case GL11.GL_EMISSION -> 3;
|
||||
default -> -1;
|
||||
};
|
||||
if (first < 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (face != GL11.GL_BACK) {
|
||||
record(FRONT_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(FRONT_DIFFUSE);
|
||||
}
|
||||
if (face != GL11.GL_FRONT) {
|
||||
record(BACK_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(BACK_DIFFUSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes and pops check result rather than active()
|
||||
void pushAttrib() {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth < attribWrites.length) attribWrites[context.attribDepth] = result.writes;
|
||||
else result.valid = false;
|
||||
}
|
||||
|
||||
void popAttrib(StateSet set) {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth <= attribDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
final long restored = restoredFields(set);
|
||||
result.writes = (result.writes & ~restored) | (attribWrites[context.attribDepth - 1] & restored);
|
||||
}
|
||||
|
||||
private long restoredFields(StateSet set) {
|
||||
long restored = restoredFieldsBySet.getOrDefault(set, -1L);
|
||||
if (restored >= 0) return restored;
|
||||
restored = 0;
|
||||
for (int field = 0; field < FIELD_COUNT; field++) {
|
||||
final CowStateStack<?> stack = stack(field);
|
||||
if (stack != null && set.contains(stack.stackId())) restored |= 1L << field;
|
||||
}
|
||||
restoredFieldsBySet.put(set, restored);
|
||||
return restored;
|
||||
}
|
||||
|
||||
private CowStateStack<?> stack(int field) {
|
||||
return switch (field) {
|
||||
case BLEND -> context.blendMode;
|
||||
case ALPHA -> context.alphaTest;
|
||||
case DEPTH -> context.depthTest;
|
||||
case CULL -> context.cullState;
|
||||
case LIGHTING -> context.lightingState;
|
||||
case NORMALIZE -> context.normalizeState;
|
||||
case RESCALE -> context.rescaleNormalState;
|
||||
case LIGHT0, LIGHT1 -> context.lightStates[field - LIGHT0];
|
||||
case TEXTURE0, TEXTURE1 -> context.textures.getTextureUnitStates(field - TEXTURE0);
|
||||
case OFFSET -> context.polygonOffsetFillState;
|
||||
case COLOR_MATERIAL -> context.colorMaterial;
|
||||
case COLOR -> context.color;
|
||||
case NORMAL -> context.ffp.normalStack;
|
||||
case TEXCOORD -> context.ffp.texCoordStack;
|
||||
case BLEND_FUNC -> context.blendState;
|
||||
case ALPHA_FUNC -> context.alphaState;
|
||||
case DEPTH_FUNC, DEPTH_MASK -> context.depthState;
|
||||
case COLOR_MASK -> context.colorMask;
|
||||
case CULL_FACE, FRONT_FACE, OFFSET_VALUES -> context.polygonState;
|
||||
case SHADE_MODEL -> context.shadeModelState;
|
||||
case ACTIVE_UNIT -> context.activeTextureUnit;
|
||||
case TEXTURE_BINDING -> context.textures.getTextureUnitBindings(0);
|
||||
case MATRIX_MODE -> context.matrixMode;
|
||||
// Face and parameter are restored together (GL_LIGHTING_BIT and the batch set hold both).
|
||||
case COLOR_MATERIAL_SETTINGS -> context.colorMaterialFace;
|
||||
case FRONT_AMBIENT, FRONT_DIFFUSE, FRONT_SPECULAR, FRONT_EMISSION -> context.frontMaterial;
|
||||
case BACK_AMBIENT, BACK_DIFFUSE, BACK_SPECULAR, BACK_EMISSION -> context.backMaterial;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
void matrixChange(boolean absolute) {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if ((absolute || mode != GL11.GL_MODELVIEW) && active()) recordMatrixChange(mode, context.activeTextureUnit.getValue(), absolute);
|
||||
}
|
||||
|
||||
void matrixChange(int mode, int unit, boolean absolute) {
|
||||
if (active()) recordMatrixChange(mode, unit, absolute);
|
||||
}
|
||||
|
||||
private void recordMatrixChange(int mode, int unit, boolean absolute) {
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
if (absolute) result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (mode != GL11.GL_TEXTURE || unit != 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
record(TEXTURE_MATRIX);
|
||||
if (absolute) textureInherited = false;
|
||||
}
|
||||
|
||||
void pushMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if (mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0) {
|
||||
final int depth = GLSMMatrices.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
if (depth < matrixWrites.length) {
|
||||
matrixWrites[depth] = result.writes;
|
||||
matrixInherited[depth] = textureInherited;
|
||||
} else {
|
||||
result.valid = false;
|
||||
}
|
||||
} else if (mode != GL11.GL_MODELVIEW) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void popMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
final boolean texture = mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0;
|
||||
final Matrix4fStack matrix;
|
||||
final int entryDepth;
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
matrix = context.modelViewMatrix;
|
||||
entryDepth = modelViewDepth;
|
||||
} else if (texture) {
|
||||
matrix = context.textures.getTextureUnitMatrix(0);
|
||||
entryDepth = textureDepth;
|
||||
} else {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
final int depth = GLSMMatrices.getMatrixStackDepth(matrix);
|
||||
if (depth <= entryDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (texture) {
|
||||
final long bit = 1L << TEXTURE_MATRIX;
|
||||
result.writes = (result.writes & ~bit) | (matrixWrites[depth - 1] & bit);
|
||||
textureInherited = matrixInherited[depth - 1];
|
||||
}
|
||||
}
|
||||
|
||||
private void capture(ExitState.Values out, long fields) {
|
||||
out.enabled = 0;
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (has(fields, field) && ((BooleanStateStack) Objects.requireNonNull(stack(field))).isEffectivelyEnabled()) out.enabled |= 1L << field;
|
||||
}
|
||||
if (has(fields, COLOR)) out.color.set(context.color);
|
||||
if (has(fields, NORMAL)) out.normal.set(context.ffp.currentNormal);
|
||||
if (has(fields, TEXCOORD)) out.texCoord.set(context.ffp.currentTexCoords[0]);
|
||||
if (has(fields, LIGHTMAP)) {
|
||||
out.lightmapX = context.lastBrightnessX;
|
||||
out.lightmapY = context.lastBrightnessY;
|
||||
}
|
||||
if (has(fields, SHADER_COLOR)) out.shaderColor.set(context.shaderColorR, context.shaderColorG, context.shaderColorB, context.shaderColorA);
|
||||
if (has(fields, OVERLAY_COLOR)) out.overlayColor.set(context.overlayR, context.overlayG, context.overlayB, context.overlayA);
|
||||
if (has(fields, BLEND_FUNC)) context.blendState.readEffective(out.blend);
|
||||
if (has(fields, ALPHA_FUNC)) context.alphaState.readEffective(out.alpha);
|
||||
if (has(fields, COLOR_MASK)) context.colorMask.readEffective(out.colorMask);
|
||||
if (has(fields, DEPTH_FUNC)) out.depthFunction = context.depthState.getFunc();
|
||||
if (has(fields, DEPTH_MASK)) out.depthMask = context.depthState.isEffectiveMaskEnabled();
|
||||
if (has(fields, CULL_FACE)) out.cullFace = context.polygonState.getCullFaceMode();
|
||||
if (has(fields, FRONT_FACE)) out.frontFace = context.polygonState.getFrontFace();
|
||||
if (has(fields, OFFSET_VALUES)) {
|
||||
out.offsetFactor = context.polygonState.getOffsetFactor();
|
||||
out.offsetUnits = context.polygonState.getOffsetUnits();
|
||||
out.offsetClamp = context.polygonState.getOffsetClamp();
|
||||
}
|
||||
if (has(fields, SHADE_MODEL)) out.shadeModel = context.shadeModelState.getValue();
|
||||
if (has(fields, ACTIVE_UNIT)) out.activeUnit = context.activeTextureUnit.getValue();
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) out.clientActiveUnit = context.clientActiveTextureUnit;
|
||||
if (has(fields, MATRIX_MODE)) out.matrixMode = context.matrixMode.getMode();
|
||||
if (has(fields, TEXTURE_BINDING)) out.texture = context.textures.getTextureUnitBindings(0).getBinding();
|
||||
if (has(fields, TEXTURE_MATRIX)) out.textureMatrix.set(context.textures.getTextureUnitMatrix(0));
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) {
|
||||
out.materialFace = context.colorMaterialFace.getValue();
|
||||
out.materialParameter = context.colorMaterialParameter.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
boolean canApply(ExitState in) {
|
||||
if (!in.valid || result != null || !GLStateManager.isCachingEnabled() || DisplayListManager.isRecording() || context.savedFilterTextureId != 0) return false;
|
||||
if (in.textureDeleteCount != TextureInfoCache.INSTANCE.deleteCount()) return false;
|
||||
capture(current, in.dependencies);
|
||||
if (((current.enabled ^ in.entry.enabled) & in.dependencies & ((1L << CAPS.length) - 1)) != 0) return false;
|
||||
long remaining = in.dependencies & -(1L << CAPS.length);
|
||||
while (remaining != 0) {
|
||||
final int field = Long.numberOfTrailingZeros(remaining);
|
||||
if (!same(field, in.entry, current)) return false;
|
||||
remaining &= remaining - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void apply(ExitState state) {
|
||||
if (!state.valid) throw new IllegalStateException("Invalid renderer exit state");
|
||||
if (result != null) throw new IllegalStateException("Exit state applied while a capture is open");
|
||||
final ExitState.Values in = state.value;
|
||||
final long fields = state.writes;
|
||||
final int liveUnit = context.activeTextureUnit.getValue();
|
||||
// Capabilities go first so the glColor4f below bakes into the material only if color material is on at exit.
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
if (field == TEXTURE0 || field == TEXTURE1) GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + field - TEXTURE0);
|
||||
// glEnable would bake the live color, not the exit color, into the material.
|
||||
if (field == COLOR_MATERIAL) {
|
||||
context.colorMaterial.setEnabled((in.enabled & (1L << field)) != 0);
|
||||
} else if ((in.enabled & (1L << field)) != 0) GLSMEnable.glEnable(CAPS[field]);
|
||||
else GLSMEnable.glDisable(CAPS[field]);
|
||||
}
|
||||
if (has(fields, COLOR)) GLSMCurrentAttribs.glColor4f(in.color.getRed(), in.color.getGreen(), in.color.getBlue(), in.color.getAlpha());
|
||||
if (has(fields, NORMAL)) GLSMCurrentAttribs.glNormal3f(in.normal.x, in.normal.y, in.normal.z);
|
||||
if (has(fields, TEXCOORD)) GLSMCurrentAttribs.glTexCoord4f(in.texCoord.x, in.texCoord.y, in.texCoord.z, in.texCoord.w);
|
||||
if (has(fields, LIGHTMAP)) GLSMCurrentAttribs.setLightmapTextureCoords(GL13.GL_TEXTURE1, in.lightmapX, in.lightmapY);
|
||||
if (has(fields, SHADER_COLOR)) GLSMCurrentAttribs.setShaderColor(in.shaderColor.x, in.shaderColor.y, in.shaderColor.z, in.shaderColor.w);
|
||||
if (has(fields, OVERLAY_COLOR)) GLSMCurrentAttribs.setOverlayColor(in.overlayColor.x, in.overlayColor.y, in.overlayColor.z, in.overlayColor.w);
|
||||
if (has(fields, BLEND_FUNC)) GLSMBlend.tryBlendFuncSeparate(in.blend.getSrcRgb(), in.blend.getDstRgb(), in.blend.getSrcAlpha(), in.blend.getDstAlpha());
|
||||
if (has(fields, ALPHA_FUNC)) GLSMDepthStencilAlpha.glAlphaFunc(in.alpha.getFunction(), in.alpha.getReference());
|
||||
if (has(fields, DEPTH_FUNC)) GLSMDepthStencilAlpha.glDepthFunc(in.depthFunction);
|
||||
if (has(fields, DEPTH_MASK)) GLSMDepthStencilAlpha.glDepthMask(in.depthMask);
|
||||
if (has(fields, COLOR_MASK)) GLSMBlend.glColorMask(in.colorMask.red, in.colorMask.green, in.colorMask.blue, in.colorMask.alpha);
|
||||
if (has(fields, CULL_FACE)) GLSMRasterizer.glCullFace(in.cullFace);
|
||||
if (has(fields, FRONT_FACE)) GLSMRasterizer.glFrontFace(in.frontFace);
|
||||
if (has(fields, SHADE_MODEL)) GLSMRasterizer.glShadeModel(in.shadeModel);
|
||||
if (has(fields, OFFSET_VALUES)) GLSMRasterizer.glPolygonOffsetClamp(in.offsetFactor, in.offsetUnits, in.offsetClamp);
|
||||
if (has(fields, TEXTURE_BINDING)) {
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, in.texture);
|
||||
}
|
||||
if (has(fields, TEXTURE_MATRIX)) GLSMMatrices.setTextureMatrix(0, in.textureMatrix);
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) GLSMLighting.glColorMaterial(in.materialFace, in.materialParameter);
|
||||
boolean materialChanged = false;
|
||||
for (int field = FRONT_AMBIENT; field <= BACK_EMISSION; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
final boolean front = field < BACK_AMBIENT;
|
||||
final int component = (field - FRONT_AMBIENT) & 3;
|
||||
final Vector4f source = vector(front ? state.frontMaterial : state.backMaterial, component);
|
||||
final var target = front ? context.frontMaterial : context.backMaterial;
|
||||
if (vector(target, component).equals(source)) continue;
|
||||
target.beforeModify();
|
||||
vector(target, component).set(source);
|
||||
materialChanged = true;
|
||||
}
|
||||
if (materialChanged) context.lightingGeneration++;
|
||||
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + (has(fields, ACTIVE_UNIT) ? in.activeUnit : liveUnit));
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) GLSMTextures.glClientActiveTexture(GL13.GL_TEXTURE0 + in.clientActiveUnit);
|
||||
if (has(fields, MATRIX_MODE)) GLSMMatrices.glMatrixMode(in.matrixMode);
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,10 @@ public class Feature {
|
||||
*/
|
||||
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; 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));
|
||||
collection.add(GLSMTextures.getTextures().getTexGenSStates(i));
|
||||
collection.add(GLSMTextures.getTextures().getTexGenTStates(i));
|
||||
collection.add(GLSMTextures.getTextures().getTexGenRStates(i));
|
||||
collection.add(GLSMTextures.getTextures().getTexGenQStates(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,23 +79,24 @@ public class Feature {
|
||||
final Int2ObjectMap<Set<IStateStack<?>>> attribToFeatures = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.getAlphaTest()
|
||||
, GLStateManager.getAlphaState()
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getBlendState()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getDitherState()
|
||||
, GLStateManager.getDrawBuffer()
|
||||
, GLStateManager.getIndexLogicOpState()
|
||||
, GLStateManager.getLogicOpMode()
|
||||
, GLStateManager.getColorMask()
|
||||
, GLStateManager.getClearColor()
|
||||
GLSMDepthStencilAlpha.getAlphaTest()
|
||||
, GLSMDepthStencilAlpha.getAlphaState()
|
||||
, GLSMBlend.getBlendMode()
|
||||
, GLSMBlend.getBlendState()
|
||||
, GLSMBlend.getColorLogicOpState()
|
||||
, GLSMRasterizer.getDitherState()
|
||||
, GLSMFramebuffer.getDrawBuffer()
|
||||
, GLSMBlend.getIndexLogicOpState()
|
||||
, GLSMBlend.getLogicOpMode()
|
||||
, GLSMBlend.getColorMask()
|
||||
, GLSMBlend.getClearColor()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
|
||||
GLStateManager.getColor()
|
||||
, GLStateManager.getSecondaryColor() // Current secondary color
|
||||
GLSMCurrentAttribs.getColor()
|
||||
, GLSMCurrentAttribs.getSecondaryColor() // Current secondary color
|
||||
, ShaderManager.getNormalStack() // Current normal vector
|
||||
, ShaderManager.getTexCoordStack() // Current texture coordinates
|
||||
, GLSMRasterizer.getEdgeFlagState()
|
||||
// Current color index
|
||||
// Current raster position
|
||||
// GL_CURRENT_RASTER_POSITION_VALID flag
|
||||
@@ -105,79 +106,82 @@ public class Feature {
|
||||
// GL_EDGE_FLAG flag
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_DEPTH_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.getDepthTest()
|
||||
, GLStateManager.getDepthState()
|
||||
GLSMDepthStencilAlpha.getDepthTest()
|
||||
, GLSMDepthStencilAlpha.getDepthState()
|
||||
// Depth buffer clear value
|
||||
// GL_DEPTH_WRITEMASK enable bit
|
||||
));
|
||||
|
||||
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.getAlphaTest()
|
||||
, GLStateManager.getAutoNormalState()
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorSumState()
|
||||
, 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()
|
||||
GLSMDepthStencilAlpha.getAlphaTest()
|
||||
, GLSMRasterizer.getAutoNormalState()
|
||||
, GLSMBlend.getBlendMode()
|
||||
, GLSMBlend.getColorLogicOpState()
|
||||
, GLSMLighting.getColorMaterial()
|
||||
, GLSMCurrentAttribs.getColorSumState()
|
||||
, GLSMRasterizer.getCullState()
|
||||
, GLSMDepthStencilAlpha.getDepthTest()
|
||||
, GLSMRasterizer.getDitherState()
|
||||
, GLSMFog.getFogMode()
|
||||
, GLSMBlend.getIndexLogicOpState()
|
||||
, GLSMLighting.getLightStates()[0] // GL_LIGHT0
|
||||
, GLSMLighting.getLightStates()[1] // GL_LIGHT1
|
||||
, GLSMLighting.getLightStates()[2] // GL_LIGHT2
|
||||
, GLSMLighting.getLightStates()[3] // GL_LIGHT3
|
||||
, GLSMLighting.getLightStates()[4] // GL_LIGHT4
|
||||
, GLSMLighting.getLightStates()[5] // GL_LIGHT5
|
||||
, GLSMLighting.getLightStates()[6] // GL_LIGHT6
|
||||
, GLSMLighting.getLightStates()[7] // GL_LIGHT7
|
||||
, GLSMLighting.getLightingState()
|
||||
, GLSMRasterizer.getLineSmoothState()
|
||||
, GLSMRasterizer.getLineStippleState()
|
||||
, GLSMRasterizer.getMap1Color4State()
|
||||
, GLSMRasterizer.getMap1IndexState()
|
||||
, GLSMRasterizer.getMap1NormalState()
|
||||
, GLSMRasterizer.getMap1TextureCoord1State()
|
||||
, GLSMRasterizer.getMap1TextureCoord2State()
|
||||
, GLSMRasterizer.getMap1TextureCoord3State()
|
||||
, GLSMRasterizer.getMap1TextureCoord4State()
|
||||
, GLSMRasterizer.getMap1Vertex3State()
|
||||
, GLSMRasterizer.getMap1Vertex4State()
|
||||
, GLSMRasterizer.getMap2Color4State()
|
||||
, GLSMRasterizer.getMap2IndexState()
|
||||
, GLSMRasterizer.getMap2NormalState()
|
||||
, GLSMRasterizer.getMap2TextureCoord1State()
|
||||
, GLSMRasterizer.getMap2TextureCoord2State()
|
||||
, GLSMRasterizer.getMap2TextureCoord3State()
|
||||
, GLSMRasterizer.getMap2TextureCoord4State()
|
||||
, GLSMRasterizer.getMap2Vertex3State()
|
||||
, GLSMRasterizer.getMap2Vertex4State()
|
||||
, GLSMRasterizer.getMultisampleState()
|
||||
, GLSMLighting.getNormalizeState()
|
||||
, GLSMRasterizer.getPointSmoothState()
|
||||
, GLSMRasterizer.getPolygonOffsetPointState()
|
||||
, GLSMRasterizer.getPolygonOffsetLineState()
|
||||
, GLSMRasterizer.getPolygonOffsetFillState()
|
||||
, GLSMRasterizer.getPointSpriteState()
|
||||
, GLSMPrograms.getVertexProgramTwoSideState()
|
||||
, GLSMRasterizer.getPolygonSmoothState()
|
||||
, GLSMRasterizer.getPolygonStippleState()
|
||||
, GLSMLighting.getRescaleNormalState()
|
||||
, GLSMRasterizer.getSampleAlphaToCoverageState()
|
||||
, GLSMRasterizer.getSampleAlphaToOneState()
|
||||
, GLSMRasterizer.getSampleCoverageState()
|
||||
, GLSMRasterizer.getScissorTest()
|
||||
, GLSMDepthStencilAlpha.getStencilTest()
|
||||
));
|
||||
|
||||
// Enable bits for the user-definable clipping planes
|
||||
for(int i = 0; i < GLStateManager.getClipPlaneStates().length; i++) {
|
||||
enableBits.add(GLStateManager.getClipPlaneStates()[i]);
|
||||
for(int i = 0; i < GLSMRasterizer.getClipPlaneStates().length; i++) {
|
||||
enableBits.add(GLSMRasterizer.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.getTextures().getTexture1DStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTexture3DStates(i));
|
||||
enableBits.add(GLSMTextures.getTextures().getTexture1DStates(i));
|
||||
enableBits.add(GLSMTextures.getTextures().getTextureUnitStates(i));
|
||||
enableBits.add(GLSMTextures.getTextures().getTexture3DStates(i));
|
||||
enableBits.add(GLSMTextures.getTextures().getTextureCubeMapStates(i));
|
||||
}
|
||||
|
||||
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
@@ -185,30 +189,30 @@ public class Feature {
|
||||
|
||||
attribToFeatures.put(GL11.GL_ENABLE_BIT, enableBits);
|
||||
attribToFeatures.put(GL11.GL_EVAL_BIT, ImmutableSet.of(
|
||||
GLStateManager.getAutoNormalState()
|
||||
, GLStateManager.getMap1Color4State()
|
||||
, GLStateManager.getMap1IndexState()
|
||||
, GLStateManager.getMap1NormalState()
|
||||
, GLStateManager.getMap1TextureCoord1State()
|
||||
, GLStateManager.getMap1TextureCoord2State()
|
||||
, GLStateManager.getMap1TextureCoord3State()
|
||||
, GLStateManager.getMap1TextureCoord4State()
|
||||
, GLStateManager.getMap1Vertex3State()
|
||||
, GLStateManager.getMap1Vertex4State()
|
||||
, GLStateManager.getMap2Color4State()
|
||||
, GLStateManager.getMap2IndexState()
|
||||
, GLStateManager.getMap2NormalState()
|
||||
, GLStateManager.getMap2TextureCoord1State()
|
||||
, GLStateManager.getMap2TextureCoord2State()
|
||||
, GLStateManager.getMap2TextureCoord3State()
|
||||
, GLStateManager.getMap2TextureCoord4State()
|
||||
, GLStateManager.getMap2Vertex3State()
|
||||
, GLStateManager.getMap2Vertex4State()
|
||||
GLSMRasterizer.getAutoNormalState()
|
||||
, GLSMRasterizer.getMap1Color4State()
|
||||
, GLSMRasterizer.getMap1IndexState()
|
||||
, GLSMRasterizer.getMap1NormalState()
|
||||
, GLSMRasterizer.getMap1TextureCoord1State()
|
||||
, GLSMRasterizer.getMap1TextureCoord2State()
|
||||
, GLSMRasterizer.getMap1TextureCoord3State()
|
||||
, GLSMRasterizer.getMap1TextureCoord4State()
|
||||
, GLSMRasterizer.getMap1Vertex3State()
|
||||
, GLSMRasterizer.getMap1Vertex4State()
|
||||
, GLSMRasterizer.getMap2Color4State()
|
||||
, GLSMRasterizer.getMap2IndexState()
|
||||
, GLSMRasterizer.getMap2NormalState()
|
||||
, GLSMRasterizer.getMap2TextureCoord1State()
|
||||
, GLSMRasterizer.getMap2TextureCoord2State()
|
||||
, GLSMRasterizer.getMap2TextureCoord3State()
|
||||
, GLSMRasterizer.getMap2TextureCoord4State()
|
||||
, GLSMRasterizer.getMap2Vertex3State()
|
||||
, GLSMRasterizer.getMap2Vertex4State()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
|
||||
GLStateManager.getFogMode()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getFogState()
|
||||
GLSMFog.getFogMode()
|
||||
, GLSMCurrentAttribs.getColorSumState()
|
||||
, GLSMFog.getFogState()
|
||||
// ^^ Fog density
|
||||
// ^^ Linear fog start
|
||||
// ^^ Linear fog end
|
||||
@@ -216,60 +220,54 @@ public class Feature {
|
||||
// ^^ GL_FOG_MODE value
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_HINT_BIT, ImmutableSet.of(
|
||||
// GL_PERSPECTIVE_CORRECTION_HINT setting
|
||||
// GL_POINT_SMOOTH_HINT setting
|
||||
// GL_LINE_SMOOTH_HINT setting
|
||||
// GL_POLYGON_SMOOTH_HINT setting
|
||||
// GL_FOG_HINT setting
|
||||
// GL_GENERATE_MIPMAP_HINT setting
|
||||
// GL_TEXTURE_COMPRESSION_HINT setting
|
||||
GLSMRasterizer.getHintState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of(
|
||||
GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorMaterialFace()
|
||||
, GLStateManager.getColorMaterialParameter()
|
||||
, GLStateManager.getLightModel()
|
||||
, GLStateManager.getLightingState()
|
||||
GLSMLighting.getColorMaterial()
|
||||
, GLSMLighting.getColorMaterialFace()
|
||||
, GLSMLighting.getColorMaterialParameter()
|
||||
, GLSMLighting.getLightModel()
|
||||
, GLSMLighting.getLightingState()
|
||||
// Enable bit for each light
|
||||
, 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
|
||||
, GLSMLighting.getLightStates()[0] // GL_LIGHT0
|
||||
, GLSMLighting.getLightStates()[1] // GL_LIGHT1
|
||||
, GLSMLighting.getLightStates()[2] // GL_LIGHT2
|
||||
, GLSMLighting.getLightStates()[3] // GL_LIGHT3
|
||||
, GLSMLighting.getLightStates()[4] // GL_LIGHT4
|
||||
, GLSMLighting.getLightStates()[5] // GL_LIGHT5
|
||||
, GLSMLighting.getLightStates()[6] // GL_LIGHT6
|
||||
, GLSMLighting.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.getLightDataStates()[0]
|
||||
, GLStateManager.getLightDataStates()[1]
|
||||
, GLStateManager.getLightDataStates()[2]
|
||||
, GLStateManager.getLightDataStates()[3]
|
||||
, GLStateManager.getLightDataStates()[4]
|
||||
, GLStateManager.getLightDataStates()[5]
|
||||
, GLStateManager.getLightDataStates()[6]
|
||||
, GLStateManager.getLightDataStates()[7]
|
||||
, GLSMLighting.getLightDataStates()[0]
|
||||
, GLSMLighting.getLightDataStates()[1]
|
||||
, GLSMLighting.getLightDataStates()[2]
|
||||
, GLSMLighting.getLightDataStates()[3]
|
||||
, GLSMLighting.getLightDataStates()[4]
|
||||
, GLSMLighting.getLightDataStates()[5]
|
||||
, GLSMLighting.getLightDataStates()[6]
|
||||
, GLSMLighting.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.getFrontMaterial()
|
||||
, GLStateManager.getBackMaterial()
|
||||
, GLStateManager.getShadeModelState()
|
||||
, GLSMLighting.getFrontMaterial()
|
||||
, GLSMLighting.getBackMaterial()
|
||||
, GLSMRasterizer.getShadeModelState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LINE_BIT, ImmutableSet.of(
|
||||
GLStateManager.getLineSmoothState()
|
||||
, GLStateManager.getLineStippleState()
|
||||
, GLStateManager.getLineState()
|
||||
GLSMRasterizer.getLineSmoothState()
|
||||
, GLSMRasterizer.getLineStippleState()
|
||||
, GLSMRasterizer.getLineState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIST_BIT, ImmutableSet.of(
|
||||
// GL_LIST_BASE setting
|
||||
));
|
||||
attribToFeatures.put(GL13.GL_MULTISAMPLE_BIT, ImmutableSet.of(
|
||||
GLStateManager.getMultisampleState()
|
||||
, GLStateManager.getSampleAlphaToCoverageState()
|
||||
, GLStateManager.getSampleAlphaToOneState()
|
||||
, GLStateManager.getSampleCoverageState()
|
||||
GLSMRasterizer.getMultisampleState()
|
||||
, GLSMRasterizer.getSampleAlphaToCoverageState()
|
||||
, GLSMRasterizer.getSampleAlphaToOneState()
|
||||
, GLSMRasterizer.getSampleCoverageState()
|
||||
// GL_SAMPLE_COVERAGE_VALUE value
|
||||
// GL_SAMPLE_COVERAGE_INVERT value
|
||||
));
|
||||
@@ -285,31 +283,32 @@ public class Feature {
|
||||
// GL_READ_BUFFER setting
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
|
||||
GLStateManager.getPointSmoothState()
|
||||
, GLStateManager.getPointState()
|
||||
GLSMRasterizer.getPointSmoothState()
|
||||
, GLSMRasterizer.getPointSpriteState()
|
||||
, GLSMRasterizer.getPointState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
|
||||
GLStateManager.getCullState()
|
||||
, GLStateManager.getPolygonSmoothState()
|
||||
, GLStateManager.getPolygonStippleState()
|
||||
, GLStateManager.getPolygonOffsetFillState()
|
||||
, GLStateManager.getPolygonOffsetLineState()
|
||||
, GLStateManager.getPolygonOffsetPointState()
|
||||
, GLStateManager.getPolygonState()
|
||||
GLSMRasterizer.getCullState()
|
||||
, GLSMRasterizer.getPolygonSmoothState()
|
||||
, GLSMRasterizer.getPolygonStippleState()
|
||||
, GLSMRasterizer.getPolygonOffsetFillState()
|
||||
, GLSMRasterizer.getPolygonOffsetLineState()
|
||||
, GLSMRasterizer.getPolygonOffsetPointState()
|
||||
, GLSMRasterizer.getPolygonState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_STIPPLE_BIT, ImmutableSet.of(
|
||||
// Polygon stipple pattern
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_SCISSOR_BIT, ImmutableSet.of(
|
||||
GLStateManager.getScissorTest()
|
||||
, GLStateManager.getScissorState()
|
||||
GLSMRasterizer.getScissorTest()
|
||||
, GLSMRasterizer.getScissorState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_STENCIL_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.getStencilTest()
|
||||
, GLStateManager.getStencilState()
|
||||
GLSMDepthStencilAlpha.getStencilTest()
|
||||
, GLSMDepthStencilAlpha.getStencilState()
|
||||
));
|
||||
final Set<IStateStack<?>> textureAttribs = new HashSet<>(ImmutableSet.of(
|
||||
GLStateManager.getActiveTextureUnitStack()
|
||||
GLSMTextures.getActiveTextureUnitStack()
|
||||
// GL_TEXTURE_ENV_MODE + GL_TEXTURE_ENV_COLOR — now per-unit in TexEnvState (added below)
|
||||
// Enable bits for the four texture coordinates
|
||||
|
||||
@@ -326,8 +325,12 @@ 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.getTextures().getTextureUnitBindings(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTextureUnitBindings(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTexEnvState(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTexture1DStates(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTextureUnitStates(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTexture3DStates(i));
|
||||
textureAttribs.add(GLSMTextures.getTextures().getTextureCubeMapStates(i));
|
||||
}
|
||||
|
||||
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
@@ -338,17 +341,17 @@ public class Feature {
|
||||
final Set<IStateStack<?>> transformAttribs = new HashSet<>(ImmutableSet.of(
|
||||
// Coefficients of the six clipping planes
|
||||
|
||||
GLStateManager.getMatrixMode()
|
||||
, GLStateManager.getNormalizeState()
|
||||
, GLStateManager.getRescaleNormalState()
|
||||
GLSMMatrices.getMatrixMode()
|
||||
, GLSMLighting.getNormalizeState()
|
||||
, GLSMLighting.getRescaleNormalState()
|
||||
));
|
||||
for(int i = 0; i < GLStateManager.getClipPlaneStates().length; i++) {
|
||||
transformAttribs.add(GLStateManager.getClipPlaneStates()[i]);
|
||||
for(int i = 0; i < GLSMRasterizer.getClipPlaneStates().length; i++) {
|
||||
transformAttribs.add(GLSMRasterizer.getClipPlaneStates()[i]);
|
||||
}
|
||||
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, transformAttribs);
|
||||
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
|
||||
|
||||
GLStateManager.getViewportState()
|
||||
GLSMRasterizer.getViewportState()
|
||||
));
|
||||
|
||||
for (Int2ObjectMap.Entry<Set<IStateStack<?>>> entry : attribToFeatures.int2ObjectEntrySet()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -120,7 +121,7 @@ public class FeedbackManager {
|
||||
final float ndcY = clipVec.y / w;
|
||||
final float ndcZ = clipVec.z / w;
|
||||
|
||||
final ViewportState vp = GLStateManager.getViewportState();
|
||||
final ViewportState vp = GLSMRasterizer.getViewportState();
|
||||
final float winX = vp.x + vp.width * (ndcX + 1.0f) * 0.5f;
|
||||
final float winY = vp.y + vp.height * (ndcY + 1.0f) * 0.5f;
|
||||
final float winZ = (float) ((vp.depthRangeFar - vp.depthRangeNear) * ndcZ + (vp.depthRangeFar + vp.depthRangeNear)) * 0.5f;
|
||||
@@ -141,6 +142,7 @@ public class FeedbackManager {
|
||||
public static void processDrawArrays(int mode, int first, int count) {
|
||||
computeMVP();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (!glCtx.vaos.isAttribEnabled(0)) return;
|
||||
final int stride = positionStride(glCtx);
|
||||
final long posOffset = positionOffset(glCtx);
|
||||
final int byteCount = count * stride;
|
||||
@@ -154,6 +156,7 @@ public class FeedbackManager {
|
||||
public static void processDrawElements(int mode, int indexCount, int type, long eboOffset) {
|
||||
computeMVP();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (!glCtx.vaos.isAttribEnabled(0)) return;
|
||||
final int stride = positionStride(glCtx);
|
||||
|
||||
final int elementSize = GLTypes.sizeBytes(type);
|
||||
@@ -210,6 +213,7 @@ public class FeedbackManager {
|
||||
private static void processDrawElementsImpl(int mode, int count, IndexReader reader) {
|
||||
computeMVP();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (!glCtx.vaos.isAttribEnabled(0)) return;
|
||||
final int stride = positionStride(glCtx);
|
||||
|
||||
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
|
||||
@@ -225,7 +229,7 @@ public class FeedbackManager {
|
||||
}
|
||||
|
||||
private static void computeMVP() {
|
||||
mvpMatrix.set(GLStateManager.getProjectionMatrix()).mul(GLStateManager.getModelViewMatrix());
|
||||
mvpMatrix.set(GLSMMatrices.getProjectionMatrix()).mul(GLSMMatrices.getModelViewMatrix());
|
||||
}
|
||||
|
||||
private static int computeMask(int type) {
|
||||
@@ -261,17 +265,25 @@ public class FeedbackManager {
|
||||
}
|
||||
|
||||
private static void readPositionBuffer(long offset, ByteBuffer destination, GLContextState glCtx) {
|
||||
final VAOManager.Attrib position = glCtx.vaos.attrib(0);
|
||||
if (position != null && position.clientPointer != null) {
|
||||
final ByteBuffer source = position.clientPointer.duplicate();
|
||||
source.position(source.position() + (int) offset);
|
||||
if (source.remaining() > destination.remaining()) source.limit(source.position() + destination.remaining());
|
||||
destination.duplicate().put(source);
|
||||
return;
|
||||
}
|
||||
final int positionVbo = glCtx.vaos.attribVBO(0);
|
||||
final int previousVbo = glCtx.boundVBO;
|
||||
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVbo);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVbo);
|
||||
}
|
||||
try {
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, offset, destination);
|
||||
} finally {
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, previousVbo);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, previousVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -21,6 +22,7 @@ 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.RetainedState;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
@@ -29,10 +31,11 @@ import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -46,6 +49,7 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
@@ -55,6 +59,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GLContextState {
|
||||
final ExitStateCapture exitCapture = new ExitStateCapture(this);
|
||||
private final List<IStateStack<?>> allStacksBuilder = new ArrayList<>();
|
||||
public IStateStack<?>[] allStacks;
|
||||
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
|
||||
@@ -102,11 +107,22 @@ public class GLContextState {
|
||||
public boolean dirtyNormalAttrib;
|
||||
public boolean dirtyTexCoordAttrib;
|
||||
public boolean dirtyLightmapAttrib = true;
|
||||
public long entityIds = InstancedAttribs.DEFAULT_ENTITY_IDS;
|
||||
public boolean dirtyEntityIdsAttrib = true;
|
||||
public boolean unit23TexCoordSetDuringDraw = false;
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
public final StateSet[] attribSets = new StateSet[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public RetainedState retainedOwner;
|
||||
public int attribDepth = 0;
|
||||
|
||||
public boolean savesState(int id) {
|
||||
for (int i = attribDepth - 1; i >= 0; i--) {
|
||||
if (attribSets[i].contains(id)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int batchStateGeneration;
|
||||
public final int[] savedBatchStateGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public CowStateStack<?>[] stackById;
|
||||
@@ -145,7 +161,7 @@ public class GLContextState {
|
||||
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
|
||||
public final Color4Stack clearColor = member(new Color4Stack(StackIdAllocator.nextId(), new Color4(0.0F, 0.0F, 0.0F, 0.0F)).restoreBit(StateSet.R_CLEAR_COLOR));
|
||||
public final ColorMaskStack colorMask = member(new ColorMaskStack(StackIdAllocator.nextId()));
|
||||
public final IntegerStateStack drawBuffer = member(new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER, StackIdAllocator.nextId()).restoreBit(StateSet.R_DRAW_BUFFER));
|
||||
public final IntegerStateStack drawBuffer = member(new IntegerStateStack(GLSMFramebuffer.DEFAULT_DRAW_BUFFER, StackIdAllocator.nextId()).restoreBit(StateSet.R_DRAW_BUFFER));
|
||||
public final IntegerStateStack logicOpMode = member(new IntegerStateStack(GL11.GL_COPY, StackIdAllocator.nextId()).restoreBit(StateSet.R_LOGIC_OP));
|
||||
public final BooleanStateStack cullState = track(new BooleanStateStack(GL11.GL_CULL_FACE));
|
||||
public final AlphaState restoredAlphaScratch = new AlphaState();
|
||||
@@ -159,6 +175,9 @@ public class GLContextState {
|
||||
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 pointSpriteState = track(new BooleanStateStack(GL20.GL_POINT_SPRITE, false, true));
|
||||
public final BooleanStateStack vertexProgramTwoSideState = track(new BooleanStateStack(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false, true));
|
||||
public final BooleanStateStack edgeFlagState = track(new BooleanStateStack(GL11.GL_EDGE_FLAG, true, 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));
|
||||
@@ -172,6 +191,7 @@ public class GLContextState {
|
||||
public final BooleanStateStack polygonOffsetFillState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL));
|
||||
public final LineStateStack lineState = member(new LineStateStack(StackIdAllocator.nextId()));
|
||||
public final PointStateStack pointState = member(new PointStateStack(StackIdAllocator.nextId()));
|
||||
public final IntegerStateStack hintState = member(new IntegerStateStack(0, StackIdAllocator.nextId()).restoreBit(StateSet.R_HINT));
|
||||
public final PolygonStateStack polygonState = member(new PolygonStateStack(StackIdAllocator.nextId()));
|
||||
public final StencilStateStack stencilState = member(new StencilStateStack(StackIdAllocator.nextId()));
|
||||
public int stencilBitMask = 0xFFFFFFFF;
|
||||
@@ -221,9 +241,16 @@ public class GLContextState {
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
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 final int[] uniformBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public final int[] storageBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public PixelStoreState pixelUnpackState = PixelStoreState.DEFAULT;
|
||||
public PixelStoreState pixelPackState = PixelStoreState.DEFAULT;
|
||||
public final int[] clientAttribSavedMask = new int[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final PixelStoreState[] clientAttribSavedPixelUnpack = new PixelStoreState[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedUnpackBuffer = new int[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedPackBuffer = new int[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
@@ -232,6 +259,7 @@ public class GLContextState {
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
public int drawFramebuffer = 0;
|
||||
public int readFramebuffer = 0;
|
||||
public final Int2ObjectOpenHashMap<int[]> framebufferTextures = new Int2ObjectOpenHashMap<>();
|
||||
public int texGenGeneration;
|
||||
public int defaultVAO;
|
||||
public final VAOManager vaos = new VAOManager();
|
||||
@@ -242,8 +270,12 @@ public class GLContextState {
|
||||
public int internalGlDepth;
|
||||
public int internalDrawDepth;
|
||||
public int savedFilterTextureId;
|
||||
public boolean wideLineEmulationActive = false;
|
||||
public int savedFilterMin = -1;
|
||||
public int savedFilterMag = -1;
|
||||
public float savedFilterAniso = -1.0f;
|
||||
public boolean lineQuadsActive = false;
|
||||
public boolean lineStippleActive = false;
|
||||
public int textureSupersamplingSamples = 1;
|
||||
public Instancing ffpInstancing = Instancing.NONE;
|
||||
public float lastBrightnessX;
|
||||
public float lastBrightnessY;
|
||||
|
||||
@@ -11,11 +11,8 @@ public final class GLESCaps {
|
||||
|
||||
private GLESCaps() {}
|
||||
|
||||
public static boolean isCapAllowed(int cap, boolean isGLES, boolean hasClipCullDistance) {
|
||||
public static boolean isCapAllowed(int cap, boolean isGLES) {
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,667 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowDepths;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowDispatch;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_ATTRIB_STACK_DEPTH;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_TEXTURE_UNITS;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.RETAINED_SLOT;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.primaryContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.warnOnce;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMAttribStack {
|
||||
|
||||
private GLSMAttribStack() {}
|
||||
|
||||
public static long attribPushes;
|
||||
public static long attribSlotsSaved;
|
||||
public static long attribValueRestores;
|
||||
public static long attribBackendCalls;
|
||||
public static long attribDiscards;
|
||||
|
||||
/** Register a state stack id as modified at the current depth (called from beforeModify). */
|
||||
public static void registerModifiedState(GLContextState c, int id) {
|
||||
if (c.attribDepth > 0) {
|
||||
final int depth = c.attribDepth - 1;
|
||||
c.modifiedIds[depth][c.modifiedCount[depth]++] = id;
|
||||
if (!c.poppingAttributes) {
|
||||
attribSlotsSaved++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int CLIENT_ATTRIB_STACK_DEPTH = 16;
|
||||
public static void glPushClientAttrib(int mask) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final int sp = glCtx.clientAttribStackPointer;
|
||||
if (sp < CLIENT_ATTRIB_STACK_DEPTH) {
|
||||
glCtx.clientAttribSavedMask[sp] = mask;
|
||||
if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) {
|
||||
glCtx.clientAttribSavedPixelPack[sp] = glCtx.pixelPackState;
|
||||
glCtx.clientAttribSavedPixelUnpack[sp] = glCtx.pixelUnpackState;
|
||||
glCtx.clientAttribSavedPackBuffer[sp] = glCtx.boundPixelPackBuffer;
|
||||
glCtx.clientAttribSavedUnpackBuffer[sp] = glCtx.boundPixelUnpackBuffer;
|
||||
}
|
||||
if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) {
|
||||
VAOManager.ClientArrays saved = glCtx.clientAttribSavedArrays[sp];
|
||||
if (saved == null) glCtx.clientAttribSavedArrays[sp] = saved = new VAOManager.ClientArrays();
|
||||
saved.boundVAO = glCtx.boundVAO;
|
||||
saved.boundVBO = glCtx.boundVBO;
|
||||
saved.boundEBO = glCtx.vaos.boundEBO;
|
||||
saved.clientActiveTextureUnit = glCtx.clientActiveTextureUnit;
|
||||
glCtx.vaos.saveClientArrays(saved);
|
||||
}
|
||||
glCtx.clientAttribStackPointer = sp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPopClientAttrib() {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.clientAttribStackPointer > 0) {
|
||||
final int sp = --glCtx.clientAttribStackPointer;
|
||||
final int mask = glCtx.clientAttribSavedMask[sp];
|
||||
if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) {
|
||||
final PixelStoreState pack = glCtx.clientAttribSavedPixelPack[sp];
|
||||
final PixelStoreState unpack = glCtx.clientAttribSavedPixelUnpack[sp];
|
||||
glCtx.clientAttribSavedPixelPack[sp] = null;
|
||||
glCtx.clientAttribSavedPixelUnpack[sp] = null;
|
||||
PixelStoreState.applyDiff(glCtx.pixelPackState, pack, true);
|
||||
glCtx.pixelPackState = pack;
|
||||
PixelStoreState.applyDiff(glCtx.pixelUnpackState, unpack, false);
|
||||
glCtx.pixelUnpackState = unpack;
|
||||
final int packBuffer = glCtx.clientAttribSavedPackBuffer[sp];
|
||||
if (glCtx.boundPixelPackBuffer != packBuffer) GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
|
||||
final int unpackBuffer = glCtx.clientAttribSavedUnpackBuffer[sp];
|
||||
if (glCtx.boundPixelUnpackBuffer != unpackBuffer) GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
|
||||
}
|
||||
if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) {
|
||||
restoreClientArrays(glCtx, glCtx.clientAttribSavedArrays[sp]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void restoreClientArrays(GLContextState glCtx, VAOManager.ClientArrays saved) {
|
||||
final VAOManager vaos = glCtx.vaos;
|
||||
if (!vaos.vaoMap.containsKey(saved.boundVAO)) return;
|
||||
GLSMVertexInput.bindVertexArrayInternal(glCtx, saved.boundVAO);
|
||||
glCtx.clientActiveTextureUnit = saved.clientActiveTextureUnit;
|
||||
if (vaos.isSavedVboLive(saved)) {
|
||||
final int changed = vaos.restoreClientArrays(saved);
|
||||
for (int pointers = changed & 0xFFFF; pointers != 0; pointers &= pointers - 1) {
|
||||
GLSMVertexInput.pointAttrib(glCtx, Integer.numberOfTrailingZeros(pointers));
|
||||
}
|
||||
for (int enables = changed >>> 16; enables != 0; enables &= enables - 1) {
|
||||
GLSMVertexInput.syncAttribEnable(glCtx, Integer.numberOfTrailingZeros(enables));
|
||||
}
|
||||
if (glCtx.boundVBO != saved.boundVBO) GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, saved.boundVBO);
|
||||
}
|
||||
if (vaos.boundEBO != saved.boundEBO && vaos.isSavedEboLive(saved)) GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, saved.boundEBO);
|
||||
}
|
||||
|
||||
public static void pushState(int mask) {
|
||||
pushStateLive(StateSet.forMask(mask));
|
||||
}
|
||||
|
||||
public static int pushState(StateSet set) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
final int token = ctx().attribDepth + DisplayListManager.virtualStateDepth(mode);
|
||||
if (token >= MAX_ATTRIB_STACK_DEPTH) {
|
||||
throw new IllegalStateException("Attrib stack overflow: max depth " + MAX_ATTRIB_STACK_DEPTH + " reached");
|
||||
}
|
||||
DisplayListManager.recordPushState(set);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
return pushStateLive(set);
|
||||
}
|
||||
|
||||
static int pushStateLive(StateSet set) {
|
||||
attribPushes++;
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.pushAttrib();
|
||||
if (glCtx.attribDepth >= MAX_ATTRIB_STACK_DEPTH) {
|
||||
throw new IllegalStateException("Attrib stack overflow: max depth " + MAX_ATTRIB_STACK_DEPTH + " reached");
|
||||
}
|
||||
final int depthBefore = glCtx.attribDepth;
|
||||
glCtx.attribSets[depthBefore] = set;
|
||||
glCtx.savedBatchStateGen[depthBefore] = glCtx.batchStateGeneration;
|
||||
|
||||
snapshotGenerations(glCtx, set.bump);
|
||||
|
||||
glCtx.modifiedCount[depthBefore] = 0;
|
||||
|
||||
if ((set.restore & StateSet.R_PROGRAM) != 0) {
|
||||
glCtx.programStack.setValue(glCtx.activeProgram);
|
||||
glCtx.programStack.push();
|
||||
attribSlotsSaved++;
|
||||
}
|
||||
|
||||
glCtx.attribDepth++;
|
||||
return depthBefore;
|
||||
}
|
||||
|
||||
private static void snapshotGenerations(GLContextState glCtx, int bump) {
|
||||
final int depth = glCtx.attribDepth;
|
||||
if ((bump & StateSet.B_LIGHTING) != 0) {
|
||||
glCtx.savedLightingGen[depth] = glCtx.lightingGeneration;
|
||||
}
|
||||
if ((bump & (StateSet.B_FRAG_COLORBUF | StateSet.B_FRAG_TEXTURE | StateSet.B_FRAG_FOG)) != 0) {
|
||||
glCtx.savedFragmentGen[depth] = glCtx.fragmentGeneration;
|
||||
}
|
||||
if ((bump & StateSet.B_TRANSFORM) != 0) {
|
||||
glCtx.savedMvGen[depth] = glCtx.mvGeneration;
|
||||
glCtx.savedMvLinearGen[depth] = glCtx.mvLinearGeneration;
|
||||
glCtx.savedProjGen[depth] = glCtx.projGeneration;
|
||||
glCtx.savedTexMatGen[depth] = glCtx.texMatrixGeneration;
|
||||
}
|
||||
if ((bump & StateSet.B_CURRENT) != 0) {
|
||||
glCtx.savedColorGen[depth] = glCtx.colorGeneration;
|
||||
glCtx.savedNormalGen[depth] = glCtx.ffp.normalGeneration;
|
||||
glCtx.savedTexCoordGen[depth] = glCtx.ffp.texCoordGeneration;
|
||||
}
|
||||
}
|
||||
|
||||
public static void popStateTo(int depth) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
while (glCtx.attribDepth + DisplayListManager.virtualStateDepth(mode) > depth) {
|
||||
if (mode == RecordMode.COMPILE && DisplayListManager.recordPopStateIfPending()) continue;
|
||||
if (mode == RecordMode.COMPILE_AND_EXECUTE) DisplayListManager.recordPopStateIfPending();
|
||||
popState();
|
||||
}
|
||||
}
|
||||
|
||||
public static void popState() {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.attribDepth > 0 && glCtx.savedBatchStateGen[glCtx.attribDepth - 1] != glCtx.batchStateGeneration) {
|
||||
beforeUncapturedStateChange();
|
||||
}
|
||||
final int poppedDepth = glCtx.attribDepth;
|
||||
final StateSet set = glCtx.attribSets[poppedDepth - 1];
|
||||
glCtx.exitCapture.popAttrib(set);
|
||||
final boolean inspectAlpha = GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()
|
||||
&& ((glCtx.alphaState.cowDepths().claimedAt(poppedDepth) && set.contains(glCtx.alphaState.stackId()))
|
||||
|| (glCtx.alphaTest.cowDepths().claimedAt(poppedDepth) && set.contains(glCtx.alphaTest.stackId())));
|
||||
final AlphaState effectiveAlpha = inspectAlpha ? glCtx.alphaState.readEffective(glCtx.restoredAlphaScratch) : null;
|
||||
final boolean effectiveEnabled = inspectAlpha && glCtx.alphaTest.isEffectivelyEnabled();
|
||||
final int effectiveFunction = inspectAlpha ? effectiveAlpha.getFunction() : 0;
|
||||
final float effectiveReference = inspectAlpha ? effectiveAlpha.getReference() : 0.0f;
|
||||
final boolean liveEnabled = inspectAlpha && glCtx.alphaTest.isEnabled();
|
||||
final int liveFunction = inspectAlpha ? glCtx.alphaState.getFunction() : 0;
|
||||
final float liveReference = inspectAlpha ? glCtx.alphaState.getReference() : 0.0f;
|
||||
final boolean prevPoppingAttributes = glCtx.poppingAttributes;
|
||||
glCtx.poppingAttributes = true;
|
||||
try {
|
||||
glCtx.attribDepth--;
|
||||
final int restore = set.restore;
|
||||
|
||||
final boolean blendSnapshotted = set.blendSnapshot
|
||||
&& (glCtx.blendState.cowDepths().claimedAt(poppedDepth) || glCtx.blendMode.cowDepths().claimedAt(poppedDepth))
|
||||
&& GLSMBlend.snapshotVanillaBlendFunc();
|
||||
|
||||
int changed = 0;
|
||||
long unitsChanged = 0L;
|
||||
|
||||
if ((restore & StateSet.R_PROGRAM) != 0) {
|
||||
glCtx.programStack.setValue(glCtx.activeProgram);
|
||||
if (glCtx.programStack.topSlotChanged()) changed |= StateSet.R_PROGRAM;
|
||||
glCtx.programStack.pop();
|
||||
}
|
||||
|
||||
final int depth = glCtx.attribDepth;
|
||||
final int count = glCtx.modifiedCount[depth];
|
||||
final int[] ids = glCtx.modifiedIds[depth];
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
final CowDepths[] depthsById = glCtx.depthsById;
|
||||
final int[] restoreBitById = glCtx.restoreBitById;
|
||||
final int[] restoreUnitById = glCtx.restoreUnitById;
|
||||
final byte[] kindById = glCtx.kindById;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int id = ids[i];
|
||||
final CowStateStack<?> cow = stackById[id];
|
||||
final byte kind = kindById[id];
|
||||
if (set.contains(id)) {
|
||||
final int bit = restoreBitById[id];
|
||||
if (bit != 0 && CowDispatch.topSlotChanged(kind, cow)) {
|
||||
changed |= bit;
|
||||
final int unit = restoreUnitById[id];
|
||||
if (unit >= 0) unitsChanged |= 1L << Math.min(unit, 63);
|
||||
}
|
||||
final int slot = depthsById[id].pop();
|
||||
if (slot >= 0) CowDispatch.restoreSlot(kind, cow, slot);
|
||||
if (bit == 0
|
||||
&& (kind == CowDispatch.BOOLEAN
|
||||
|| kind == CowDispatch.CLIP_PLANE_BOOLEAN
|
||||
|| kind == CowDispatch.TEXTURE_UNIT_BOOLEAN)) attribValueRestores++;
|
||||
} else {
|
||||
if (depthsById[id].discard(poppedDepth) >= 0) registerModifiedState(glCtx, id);
|
||||
attribDiscards++;
|
||||
}
|
||||
}
|
||||
glCtx.modifiedCount[depth] = 0;
|
||||
|
||||
glCtx.restoreChangedMask = changed;
|
||||
glCtx.restoreUnitChangedMask = unitsChanged;
|
||||
|
||||
applyRestoredState(set, blendSnapshotted);
|
||||
} finally {
|
||||
glCtx.poppingAttributes = prevPoppingAttributes;
|
||||
}
|
||||
if (inspectAlpha) {
|
||||
glCtx.alphaState.readEffective(glCtx.restoredAlphaScratch);
|
||||
if (effectiveEnabled != glCtx.alphaTest.isEffectivelyEnabled()
|
||||
|| effectiveFunction != effectiveAlpha.getFunction()
|
||||
|| Float.compare(effectiveReference, effectiveAlpha.getReference()) != 0
|
||||
|| liveEnabled != glCtx.alphaTest.isEnabled()
|
||||
|| liveFunction != glCtx.alphaState.getFunction()
|
||||
|| Float.compare(liveReference, glCtx.alphaState.getReference()) != 0) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void retainModifiedState(int depth, RetainedState out) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (depth < 0 || depth != glCtx.attribDepth - 1) {
|
||||
throw new IllegalStateException("retainModifiedState: depth " + depth + " is not the open push (attrib depth " + glCtx.attribDepth + ")");
|
||||
}
|
||||
glCtx.retainedOwner = out;
|
||||
final int count = glCtx.modifiedCount[depth];
|
||||
final int[] ids = glCtx.modifiedIds[depth];
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
out.ensureCapacity(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int id = ids[i];
|
||||
stackById[id].captureSlot(RETAINED_SLOT);
|
||||
out.ids[i] = id;
|
||||
}
|
||||
out.count = count;
|
||||
out.program = glCtx.activeProgram;
|
||||
out.programGen = GLSMPrograms.programLifetimeGeneration;
|
||||
}
|
||||
|
||||
public static void applyRetainedState(RetainedState in) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.retainedOwner != in) return;
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
final byte[] kindById = glCtx.kindById;
|
||||
final int[] ids = in.ids;
|
||||
final int count = in.count;
|
||||
int first = 0;
|
||||
while (first < count && !CowDispatch.slotChanged(kindById[ids[first]], stackById[ids[first]], RETAINED_SLOT)) first++;
|
||||
if (first < count) applyRetainedChanges(glCtx, in, first);
|
||||
if (in.program != glCtx.activeProgram && (in.program == 0 || in.programGen == GLSMPrograms.programLifetimeGeneration)) GLSMPrograms.glUseProgram(in.program);
|
||||
}
|
||||
|
||||
private static void applyRetainedChanges(GLContextState glCtx, RetainedState in, int first) {
|
||||
beforeUncapturedStateChange();
|
||||
final boolean blendSnapshotted = GLSMBlend.snapshotVanillaBlendFunc();
|
||||
final int alphaFuncId = glCtx.alphaState.stackId();
|
||||
final int alphaTestId = glCtx.alphaTest.stackId();
|
||||
boolean alphaChanged = false;
|
||||
final boolean prevPoppingAttributes = glCtx.poppingAttributes;
|
||||
glCtx.poppingAttributes = true;
|
||||
try {
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
final int[] restoreBitById = glCtx.restoreBitById;
|
||||
final int[] restoreUnitById = glCtx.restoreUnitById;
|
||||
final byte[] kindById = glCtx.kindById;
|
||||
int changed = 0;
|
||||
long unitsChanged = 0L;
|
||||
for (int i = first; i < in.count; i++) {
|
||||
final int id = in.ids[i];
|
||||
final CowStateStack<?> cow = stackById[id];
|
||||
final byte kind = kindById[id];
|
||||
if (!CowDispatch.slotChanged(kind, cow, RETAINED_SLOT)) continue;
|
||||
cow.beforeModify();
|
||||
final int bit = restoreBitById[id];
|
||||
if (bit != 0) {
|
||||
changed |= bit;
|
||||
final int unit = restoreUnitById[id];
|
||||
if (unit >= 0) unitsChanged |= 1L << Math.min(unit, 63);
|
||||
}
|
||||
CowDispatch.restoreSlot(kind, cow, RETAINED_SLOT);
|
||||
if (id == alphaFuncId || id == alphaTestId) alphaChanged = true;
|
||||
}
|
||||
glCtx.restoreChangedMask = changed;
|
||||
glCtx.restoreUnitChangedMask = unitsChanged;
|
||||
issueRestoredBackendState(glCtx, -1);
|
||||
glCtx.lightingGeneration++;
|
||||
glCtx.fragmentGeneration++;
|
||||
glCtx.colorGeneration++;
|
||||
glCtx.ffp.normalGeneration++;
|
||||
glCtx.ffp.texCoordGeneration++;
|
||||
glCtx.dirtyColorAttrib = true;
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
GLSMBlend.postVanillaBlendChangeIfMoved(blendSnapshotted);
|
||||
} finally {
|
||||
glCtx.poppingAttributes = prevPoppingAttributes;
|
||||
}
|
||||
if (alphaChanged && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDepthColorOverridden() {
|
||||
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
|
||||
return dch != null && dch.isOverrideHeld();
|
||||
}
|
||||
|
||||
public static void replayStateToBackend() {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (RENDER_BACKEND == null) return;
|
||||
|
||||
final List<BooleanStateStack> booleanStates = glCtx.allBooleanStates;
|
||||
for (int i = 0; i < booleanStates.size(); i++) {
|
||||
booleanStates.get(i).applyToBackend();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.depthFunc(glCtx.depthState.getFunc());
|
||||
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
|
||||
GLSMBlend.issueBlendFunc(glCtx.blendState);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
|
||||
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
|
||||
RENDER_BACKEND.clearColor(glCtx.clearColor.getRed(), glCtx.clearColor.getGreen(), glCtx.clearColor.getBlue(), glCtx.clearColor.getAlpha());
|
||||
RENDER_BACKEND.logicOp(glCtx.logicOpMode.getValue());
|
||||
|
||||
RENDER_BACKEND.stencilFuncSeparate(GL11.GL_FRONT, glCtx.stencilState.getFuncFront(), glCtx.stencilState.getRefFront(), glCtx.stencilState.getValueMaskFront());
|
||||
RENDER_BACKEND.stencilFuncSeparate(GL11.GL_BACK, glCtx.stencilState.getFuncBack(), glCtx.stencilState.getRefBack(), glCtx.stencilState.getValueMaskBack());
|
||||
RENDER_BACKEND.stencilOpSeparate(GL11.GL_FRONT, glCtx.stencilState.getFailOpFront(), glCtx.stencilState.getZFailOpFront(), glCtx.stencilState.getZPassOpFront());
|
||||
RENDER_BACKEND.stencilOpSeparate(GL11.GL_BACK, glCtx.stencilState.getFailOpBack(), glCtx.stencilState.getZFailOpBack(), glCtx.stencilState.getZPassOpBack());
|
||||
RENDER_BACKEND.stencilMaskSeparate(GL11.GL_FRONT, glCtx.stencilState.getWriteMaskFront());
|
||||
RENDER_BACKEND.stencilMaskSeparate(GL11.GL_BACK, glCtx.stencilState.getWriteMaskBack());
|
||||
RENDER_BACKEND.clearStencil(glCtx.stencilState.getClearValue());
|
||||
|
||||
RENDER_BACKEND.viewport(glCtx.viewportState.x, glCtx.viewportState.y, glCtx.viewportState.width, glCtx.viewportState.height);
|
||||
RENDER_BACKEND.depthRange(glCtx.viewportState.depthRangeNear, glCtx.viewportState.depthRangeFar);
|
||||
RENDER_BACKEND.scissor(glCtx.scissorState.x, glCtx.scissorState.y, glCtx.scissorState.width, glCtx.scissorState.height);
|
||||
RENDER_BACKEND.lineWidth(Math.clamp(glCtx.lineState.getWidth(), GLSMRasterizer.lineWidthMin, GLSMRasterizer.lineWidthMax));
|
||||
GLSMRasterizer.applyPointState(glCtx);
|
||||
GLSMRasterizer.applyHintState(glCtx);
|
||||
|
||||
RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, glCtx.polygonState.getFrontMode());
|
||||
applyPolygonOffset(glCtx);
|
||||
RENDER_BACKEND.cullFace(glCtx.polygonState.getCullFaceMode());
|
||||
RENDER_BACKEND.frontFace(glCtx.polygonState.getFrontFace());
|
||||
|
||||
final SamplerUnitArray samplerUnits = glCtx.samplerUnits;
|
||||
for (int i = 0; i <= samplerUnits.getHighestBoundUnit(); i++) {
|
||||
final int sampler = samplerUnits.get(i);
|
||||
if (sampler != 0) RENDER_BACKEND.bindSampler(i, sampler);
|
||||
}
|
||||
|
||||
for (int i = 0; i <= glCtx.maxBoundTextureUnit; i++) {
|
||||
final TextureBinding b = glCtx.textures.getTextureUnitBindings(i);
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
RENDER_BACKEND.bindTexture(b.getTarget() == 0 ? GL11.GL_TEXTURE_2D : b.getTarget(), b.getBinding());
|
||||
}
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + GLSMTextures.getActiveTextureUnit());
|
||||
if (glCtx.defaultVAO == 0 && primaryContext().defaultVAO != 0) glCtx.defaultVAO = RENDER_BACKEND.genVertexArrays();
|
||||
if (!glCtx.vaos.isInitialized()) glCtx.vaos.init(glCtx.defaultVAO);
|
||||
RENDER_BACKEND.bindVertexArray(glCtx.boundVAO != 0 ? glCtx.boundVAO : glCtx.defaultVAO);
|
||||
RENDER_BACKEND.useProgram(glCtx.activeProgram);
|
||||
glCtx.ffp.invalidateProgram();
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, glCtx.drawFramebuffer);
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_READ_FRAMEBUFFER, glCtx.readFramebuffer);
|
||||
glCtx.dirtyColorAttrib = true;
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
glCtx.dirtyLightmapAttrib = true;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
|
||||
private static void applyPolygonOffset(GLContextState glCtx) {
|
||||
final float clamp = glCtx.polygonState.getOffsetClamp();
|
||||
if (clamp == 0.0f) {
|
||||
RENDER_BACKEND.polygonOffset(glCtx.polygonState.getOffsetFactor(), glCtx.polygonState.getOffsetUnits());
|
||||
} else {
|
||||
RENDER_BACKEND.polygonOffsetClamp(glCtx.polygonState.getOffsetFactor(), glCtx.polygonState.getOffsetUnits(), clamp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void issueRestoredBackendState(GLContextState glCtx, int restore) {
|
||||
final int changed = glCtx.restoreChangedMask;
|
||||
if ((restore & StateSet.R_DEPTH) != 0 && (changed & StateSet.R_DEPTH) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.depthFunc(glCtx.depthState.getFunc());
|
||||
attribBackendCalls++;
|
||||
if (!isDepthColorOverridden()) {
|
||||
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_BLEND) != 0) {
|
||||
final BlendStateStack blend = glCtx.blendState;
|
||||
if ((changed & StateSet.R_BLEND) != 0 || blend.isFuncUnknown()) {
|
||||
attribValueRestores++;
|
||||
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
|
||||
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
|
||||
GLSMBlend.issueBlendFunc(blend);
|
||||
attribBackendCalls++;
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
if ((changed & StateSet.R_BLEND) != 0) {
|
||||
RENDER_BACKEND.blendEquationSeparate(blend.getEquationRgb(), blend.getEquationAlpha());
|
||||
attribBackendCalls++;
|
||||
RENDER_BACKEND.blendColor(
|
||||
blend.getBlendColorR(), blend.getBlendColorG(), blend.getBlendColorB(), blend.getBlendColorA());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((restore & StateSet.R_COLOR_MASK) != 0 && (changed & StateSet.R_COLOR_MASK) != 0 && !isDepthColorOverridden()) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_CLEAR_COLOR) != 0 && (changed & StateSet.R_CLEAR_COLOR) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.clearColor(glCtx.clearColor.getRed(), glCtx.clearColor.getGreen(), glCtx.clearColor.getBlue(), glCtx.clearColor.getAlpha());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_DRAW_BUFFER) != 0 && (changed & StateSet.R_DRAW_BUFFER) != 0 && glCtx.drawFramebuffer == 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.drawBuffer(glCtx.drawBuffer.getValue());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_LOGIC_OP) != 0 && (changed & StateSet.R_LOGIC_OP) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.logicOp(glCtx.logicOpMode.getValue());
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_STENCIL) != 0 && (changed & StateSet.R_STENCIL) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.stencilFuncSeparate(GL11.GL_FRONT, glCtx.stencilState.getFuncFront(), glCtx.stencilState.getRefFront(), glCtx.stencilState.getValueMaskFront());
|
||||
RENDER_BACKEND.stencilFuncSeparate(GL11.GL_BACK, glCtx.stencilState.getFuncBack(), glCtx.stencilState.getRefBack(), glCtx.stencilState.getValueMaskBack());
|
||||
RENDER_BACKEND.stencilOpSeparate(GL11.GL_FRONT, glCtx.stencilState.getFailOpFront(), glCtx.stencilState.getZFailOpFront(), glCtx.stencilState.getZPassOpFront());
|
||||
RENDER_BACKEND.stencilOpSeparate(GL11.GL_BACK, glCtx.stencilState.getFailOpBack(), glCtx.stencilState.getZFailOpBack(), glCtx.stencilState.getZPassOpBack());
|
||||
RENDER_BACKEND.stencilMaskSeparate(GL11.GL_FRONT, glCtx.stencilState.getWriteMaskFront());
|
||||
RENDER_BACKEND.stencilMaskSeparate(GL11.GL_BACK, glCtx.stencilState.getWriteMaskBack());
|
||||
RENDER_BACKEND.clearStencil(glCtx.stencilState.getClearValue());
|
||||
attribBackendCalls += 7;
|
||||
}
|
||||
if ((restore & StateSet.R_VIEWPORT) != 0 && (changed & StateSet.R_VIEWPORT) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.viewport(glCtx.viewportState.x, glCtx.viewportState.y, glCtx.viewportState.width, glCtx.viewportState.height);
|
||||
RENDER_BACKEND.depthRange(glCtx.viewportState.depthRangeNear, glCtx.viewportState.depthRangeFar);
|
||||
attribBackendCalls += 2;
|
||||
}
|
||||
if ((restore & StateSet.R_SCISSOR) != 0 && (changed & StateSet.R_SCISSOR) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.scissor(glCtx.scissorState.x, glCtx.scissorState.y, glCtx.scissorState.width, glCtx.scissorState.height);
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_LINE) != 0 && (changed & StateSet.R_LINE) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.lineWidth(Math.clamp(glCtx.lineState.getWidth(), GLSMRasterizer.lineWidthMin, GLSMRasterizer.lineWidthMax));
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_POINT) != 0 && (changed & StateSet.R_POINT) != 0) {
|
||||
attribValueRestores++;
|
||||
GLSMRasterizer.applyPointState(glCtx);
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_HINT) != 0 && (changed & StateSet.R_HINT) != 0) {
|
||||
attribValueRestores++;
|
||||
GLSMRasterizer.applyHintState(glCtx);
|
||||
attribBackendCalls++;
|
||||
}
|
||||
if ((restore & StateSet.R_POLYGON) != 0 && (changed & StateSet.R_POLYGON) != 0) {
|
||||
attribValueRestores++;
|
||||
RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, glCtx.polygonState.getFrontMode());
|
||||
applyPolygonOffset(glCtx);
|
||||
RENDER_BACKEND.cullFace(glCtx.polygonState.getCullFaceMode());
|
||||
RENDER_BACKEND.frontFace(glCtx.polygonState.getFrontFace());
|
||||
attribBackendCalls += 4;
|
||||
}
|
||||
final boolean textureBitSet = (restore & StateSet.R_TEXTURE) != 0 && (changed & StateSet.R_TEXTURE) != 0;
|
||||
final boolean activeUnitBitSet = (restore & StateSet.R_ACTIVE_UNIT) != 0;
|
||||
if (textureBitSet || activeUnitBitSet) {
|
||||
boolean unitTouched = false;
|
||||
if (textureBitSet) {
|
||||
final long unitsChanged = glCtx.restoreUnitChangedMask;
|
||||
for (int i = 0; i < MAX_TEXTURE_UNITS; i++) {
|
||||
if ((unitsChanged & (1L << Math.min(i, 63))) != 0) {
|
||||
final TextureBinding restored = glCtx.textures.getTextureUnitBindings(i);
|
||||
final int restoredTarget = restored.getTarget() != 0 ? restored.getTarget() : GL11.GL_TEXTURE_2D;
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
RENDER_BACKEND.bindTexture(restoredTarget, restored.getBinding());
|
||||
attribValueRestores++;
|
||||
attribBackendCalls += 2;
|
||||
unitTouched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unitTouched || (activeUnitBitSet && (changed & StateSet.R_ACTIVE_UNIT) != 0)) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + glCtx.activeTextureUnit.getValue());
|
||||
attribValueRestores++;
|
||||
attribBackendCalls++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After popping GLSM stacks, apply restored state to the GL driver.
|
||||
*/
|
||||
private static void applyRestoredState(StateSet set, boolean blendSnapshotted) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int restore = set.restore;
|
||||
final int changed = glCtx.restoreChangedMask;
|
||||
issueRestoredBackendState(glCtx, restore);
|
||||
|
||||
final int bump = set.bump;
|
||||
final int depth = glCtx.attribDepth;
|
||||
if ((bump & StateSet.B_LIGHTING) != 0 && glCtx.lightingGeneration != glCtx.savedLightingGen[depth]) glCtx.lightingGeneration++;
|
||||
if ((bump & StateSet.B_FRAG_FOG) != 0 && glCtx.fragmentGeneration != glCtx.savedFragmentGen[depth]) glCtx.fragmentGeneration++;
|
||||
if ((bump & StateSet.B_FRAG_COLORBUF) != 0 && glCtx.fragmentGeneration != glCtx.savedFragmentGen[depth]) glCtx.fragmentGeneration++;
|
||||
if ((bump & StateSet.B_FRAG_TEXTURE) != 0 && glCtx.fragmentGeneration != glCtx.savedFragmentGen[depth]) glCtx.fragmentGeneration++;
|
||||
if ((bump & StateSet.B_TRANSFORM) != 0) {
|
||||
if (glCtx.mvGeneration != glCtx.savedMvGen[depth]) glCtx.mvGeneration++;
|
||||
if (glCtx.mvLinearGeneration != glCtx.savedMvLinearGen[depth]) glCtx.mvLinearGeneration++;
|
||||
if (glCtx.projGeneration != glCtx.savedProjGen[depth]) glCtx.projGeneration++;
|
||||
if (glCtx.texMatrixGeneration != glCtx.savedTexMatGen[depth]) glCtx.texMatrixGeneration++;
|
||||
}
|
||||
if ((bump & StateSet.B_CURRENT) != 0) {
|
||||
if (glCtx.colorGeneration != glCtx.savedColorGen[depth]) {
|
||||
glCtx.colorGeneration++;
|
||||
glCtx.dirtyColorAttrib = true;
|
||||
}
|
||||
if (glCtx.ffp.normalGeneration != glCtx.savedNormalGen[depth]) {
|
||||
glCtx.ffp.normalGeneration++;
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
}
|
||||
if (glCtx.ffp.texCoordGeneration != glCtx.savedTexCoordGen[depth]) {
|
||||
glCtx.ffp.texCoordGeneration++;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
if ((restore & StateSet.R_SECONDARY_COLOR) != 0 && (changed & StateSet.R_SECONDARY_COLOR) != 0) glCtx.fragmentGeneration++;
|
||||
}
|
||||
|
||||
GLSMBlend.postVanillaBlendChangeIfMoved(blendSnapshotted);
|
||||
|
||||
if ((restore & StateSet.R_PROGRAM) != 0 && (changed & StateSet.R_PROGRAM) != 0) {
|
||||
GLSMPrograms.glUseProgram(glCtx.programStack.getValue());
|
||||
attribValueRestores++;
|
||||
attribBackendCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPushAttrib(int mask) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPushAttrib(mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
GLDebug.pushGroup("pushState");
|
||||
pushState(mask);
|
||||
}
|
||||
|
||||
public static void glPopAttrib() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPopAttrib();
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ctx().attribDepth == 0) {
|
||||
rejectExitCapture();
|
||||
warnOnce("popattrib-underflow", "glPopAttrib called with an empty attrib stack; ignoring");
|
||||
return;
|
||||
}
|
||||
GLDebug.popGroup();
|
||||
GLDebug.pushGroup("popState");
|
||||
try {
|
||||
popState();
|
||||
} finally {
|
||||
GLDebug.popGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPoppingAttributes() {
|
||||
return ctx().poppingAttributes;
|
||||
}
|
||||
|
||||
public static int getAttribDepth() {
|
||||
return ctx().attribDepth;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
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.IntegerStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMBlend {
|
||||
|
||||
private GLSMBlend() {}
|
||||
|
||||
public static void glBlendColor(float red, float green, float blue, float alpha) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordBlendColor(red, green, blue, alpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || red != glCtx.blendState.getBlendColorR() || green != glCtx.blendState.getBlendColorG() || blue != glCtx.blendState.getBlendColorB() || alpha != glCtx.blendState.getBlendColorA()) {
|
||||
mod(glCtx.blendState);
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setBlendColorR(red);
|
||||
glCtx.blendState.setBlendColorG(green);
|
||||
glCtx.blendState.setBlendColorB(blue);
|
||||
glCtx.blendState.setBlendColorA(alpha);
|
||||
RENDER_BACKEND.blendColor(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBlendFuncSeparatei(int buf, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.blendFuncSeparatei(buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
ctx().blendState.setFuncUnknownState();
|
||||
}
|
||||
|
||||
public static void enableBlend() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_BLEND);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableBlend(ctx());
|
||||
}
|
||||
|
||||
static void enableBlend(GLContextState glCtx) {
|
||||
final BooleanStateStack blend = glCtx.blendMode;
|
||||
final boolean wasEnabled = blend.isEffectivelyEnabled();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
glCtx.exitCapture.capability(GL11.GL_BLEND, -1);
|
||||
blend.beforeModify();
|
||||
bh.deferBlendModeToggle(true);
|
||||
} else {
|
||||
blend.enable();
|
||||
}
|
||||
if (!wasEnabled && blend.isEffectivelyEnabled()) postVanillaBlendChange();
|
||||
}
|
||||
|
||||
public static void disableBlend() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_BLEND);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableBlend(ctx());
|
||||
}
|
||||
|
||||
static void disableBlend(GLContextState glCtx) {
|
||||
final BooleanStateStack blend = glCtx.blendMode;
|
||||
final boolean wasEnabled = blend.isEffectivelyEnabled();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
glCtx.exitCapture.capability(GL11.GL_BLEND, -1);
|
||||
blend.beforeModify();
|
||||
bh.deferBlendModeToggle(false);
|
||||
} else {
|
||||
blend.disable();
|
||||
}
|
||||
if (wasEnabled && !blend.isEffectivelyEnabled()) postVanillaBlendChange();
|
||||
}
|
||||
|
||||
private static void postVanillaBlendChange() {
|
||||
if (GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) {
|
||||
GLSMHooks.VANILLA_BLEND_CHANGE.post(GLSMHooks.vanillaBlendChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean snapshotVanillaBlendFunc() {
|
||||
if (!GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) return false;
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.blendState.readEffective(glCtx.vanillaBlendBefore);
|
||||
glCtx.vanillaBlendEnabledBefore = glCtx.blendMode.isEffectivelyEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void postVanillaBlendChangeIfMoved(boolean snapshotted) {
|
||||
if (!snapshotted) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
final BlendState before = glCtx.vanillaBlendBefore;
|
||||
final BlendState after = glCtx.blendState.readEffective(glCtx.vanillaBlendAfter);
|
||||
if (glCtx.blendMode.isEffectivelyEnabled() != glCtx.vanillaBlendEnabledBefore
|
||||
|| after.getSrcRgb() != before.getSrcRgb()
|
||||
|| after.getDstRgb() != before.getDstRgb()
|
||||
|| after.getSrcAlpha() != before.getSrcAlpha()
|
||||
|| after.getDstAlpha() != before.getDstAlpha()) {
|
||||
postVanillaBlendChange();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEffectiveBlendEnabled() {
|
||||
return ctx().blendMode.isEffectivelyEnabled();
|
||||
}
|
||||
|
||||
public static BlendState getEffectiveBlendState(BlendState out) {
|
||||
return ctx().blendState.readEffective(out);
|
||||
}
|
||||
|
||||
public static ColorMask getEffectiveColorMask(ColorMask out) {
|
||||
return ctx().colorMask.readEffective(out);
|
||||
}
|
||||
|
||||
public static void glBlendFunc(int srcFactor, int dstFactor) {
|
||||
tryBlendFuncSeparate(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
}
|
||||
|
||||
static void issueBlendFunc(BlendStateStack blend) {
|
||||
if (GLSMConfig.hudCacheOverride) {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setHudCacheOverride(boolean enabled) {
|
||||
if (GLSMConfig.hudCacheOverride == enabled) return;
|
||||
GLSMConfig.hudCacheOverride = enabled;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) return;
|
||||
final BlendStateStack blend = ctx().blendState;
|
||||
issueBlendFunc(blend);
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
|
||||
public static void glBlendEquation(int mode) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glBlendEquation");
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getEquationRgb() != mode || glCtx.blendState.getEquationAlpha() != mode) {
|
||||
mod(glCtx.blendState);
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setEquationRgb(mode);
|
||||
glCtx.blendState.setEquationAlpha(mode);
|
||||
RENDER_BACKEND.blendEquation(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBlendEquationSeparate(int modeRGB, int modeAlpha) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glBlendEquationSeparate");
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getEquationRgb() != modeRGB || glCtx.blendState.getEquationAlpha() != modeAlpha) {
|
||||
mod(glCtx.blendState);
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setEquationRgb(modeRGB);
|
||||
glCtx.blendState.setEquationAlpha(modeAlpha);
|
||||
RENDER_BACKEND.blendEquationSeparate(modeRGB, modeAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static void tryBlendFuncSeparate(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.writeBlend();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean snapshotted = snapshotVanillaBlendFunc();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
mod(glCtx.blendState);
|
||||
bh.deferBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getSrcRgb() != srcRgb || glCtx.blendState.getDstRgb() != dstRgb || glCtx.blendState.getSrcAlpha() != srcAlpha || glCtx.blendState.getDstAlpha() != dstAlpha || glCtx.blendState.isFuncUnknown()) {
|
||||
mod(glCtx.blendState);
|
||||
glCtx.blendState.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
issueBlendFunc(glCtx.blendState);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
final boolean hudCache = GLSMConfig.hudCacheOverride;
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = hudCache ? GL11.GL_ONE : srcAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = hudCache ? GL11.GL_ONE_MINUS_SRC_ALPHA : dstAlpha;
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
}
|
||||
|
||||
public static void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
tryBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
public static void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.writeDepthColor(ExitStateCapture.COLOR_MASK);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColorMask(red, green, blue, alpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
|
||||
if (dch != null && dch.isDepthColorLocked()) {
|
||||
mod(glCtx.colorMask);
|
||||
dch.deferColorMask(red, green, blue, alpha);
|
||||
return;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || red != glCtx.colorMask.red || green != glCtx.colorMask.green || blue != glCtx.colorMask.blue || alpha != glCtx.colorMask.alpha) {
|
||||
mod(glCtx.colorMask);
|
||||
glCtx.colorMask.setAll(red, green, blue, alpha);
|
||||
RENDER_BACKEND.colorMask(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glClearColor(float red, float green, float blue, float alpha) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordClearColor(red, green, blue, alpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || red != glCtx.clearColor.getRed() || green != glCtx.clearColor.getGreen() || blue != glCtx.clearColor.getBlue() || alpha != glCtx.clearColor.getAlpha()) {
|
||||
mod(glCtx.clearColor);
|
||||
glCtx.clearColor.setRed(red);
|
||||
glCtx.clearColor.setGreen(green);
|
||||
glCtx.clearColor.setBlue(blue);
|
||||
glCtx.clearColor.setAlpha(alpha);
|
||||
RENDER_BACKEND.clearColor(red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLogicOp(int opcode) {
|
||||
rejectExitCapture();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLogicOp(opcode);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!isCachingEnabled() || ctx().logicOpMode.getValue() != opcode) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(ctx().logicOpMode).setValue(opcode);
|
||||
RENDER_BACKEND.logicOp(opcode);
|
||||
}
|
||||
|
||||
public static void defaultBlendFunc() {
|
||||
tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
public static BlendStateStack getBlendState() {
|
||||
return ctx().blendState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getBlendMode() {
|
||||
return ctx().blendMode;
|
||||
}
|
||||
|
||||
public static Color4Stack getClearColor() {
|
||||
return ctx().clearColor;
|
||||
}
|
||||
|
||||
public static ColorMaskStack getColorMask() {
|
||||
return ctx().colorMask;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getLogicOpMode() {
|
||||
return ctx().logicOpMode;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getColorLogicOpState() {
|
||||
return ctx().colorLogicOpState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getIndexLogicOpState() {
|
||||
return ctx().indexLogicOpState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,631 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.INDEXED_BUFFER_SLOTS;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.acquireDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.initConfig;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.releaseDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.requireContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMBuffers {
|
||||
|
||||
private GLSMBuffers() {}
|
||||
|
||||
private static final IntOpenHashSet writeMappedBuffers = new IntOpenHashSet();
|
||||
|
||||
public static void glDeleteBuffers(int buffer) {
|
||||
rejectExitCapture();
|
||||
if (buffer == 0) return;
|
||||
invalidateDeletedBuffer(buffer);
|
||||
RENDER_BACKEND.deleteBuffers(buffer);
|
||||
}
|
||||
|
||||
public static void glDeleteSync(long sync) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.deleteSync(sync);
|
||||
}
|
||||
|
||||
public static long glFenceSync(int condition, int flags) { rejectExitCapture(); return RENDER_BACKEND.fenceSync(condition, flags); }
|
||||
public static int glClientWaitSync(long sync, int flags, long timeout) { rejectExitCapture(); return RENDER_BACKEND.clientWaitSync(sync, flags, timeout); }
|
||||
public static void glWaitSync(long sync, int flags, long timeout) { rejectExitCapture(); RENDER_BACKEND.waitSync(sync, flags, timeout); }
|
||||
public static int glGetSynci(long sync, int pname, IntBuffer length) { rejectExitCapture(); return RENDER_BACKEND.getSynci(sync, pname, length); }
|
||||
|
||||
public static void glDeleteBuffers(IntBuffer buffers) {
|
||||
rejectExitCapture();
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) {
|
||||
invalidateDeletedBuffer(buffers.get(i));
|
||||
}
|
||||
RENDER_BACKEND.deleteBuffers(buffers);
|
||||
}
|
||||
|
||||
public static void glDeleteBuffers(int[] buffers) {
|
||||
for (int buffer : buffers) {
|
||||
glDeleteBuffers(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static long bufferDeletes;
|
||||
|
||||
private static void invalidateDeletedBuffer(int buffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (buffer == 0) return;
|
||||
bufferDeletes++;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
FfpExtendedAttribs.onDeleteBuffer(glCtx, buffer);
|
||||
if (glCtx.boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (glCtx.vaos.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (glCtx.boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
if (glCtx.boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
if (glCtx.boundCopyReadBuffer == buffer) glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0);
|
||||
if (glCtx.boundCopyWriteBuffer == buffer) glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0);
|
||||
for (Int2IntMap.Entry e : glCtx.boundOtherBuffers.int2IntEntrySet()) {
|
||||
if (e.getIntValue() != buffer) continue;
|
||||
e.setValue(0);
|
||||
RENDER_BACKEND.bindBuffer(e.getIntKey(), 0);
|
||||
}
|
||||
for (int i = 0; i < INDEXED_BUFFER_SLOTS; i++) {
|
||||
if (glCtx.uniformBufferBindings[i] == buffer) glCtx.uniformBufferBindings[i] = 0;
|
||||
if (glCtx.storageBufferBindings[i] == buffer) glCtx.storageBufferBindings[i] = 0;
|
||||
}
|
||||
writeMappedBuffers.remove(buffer);
|
||||
|
||||
glCtx.vaos.onDeleteBuffer(buffer, glCtx.clientAttribSavedArrays);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
/** glBindBufferBase/Range also set the generic binding point, so they share this. */
|
||||
private static void trackBufferBinding(GLContextState glCtx, int target, int buffer) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
glCtx.boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
glCtx.vaos.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
glCtx.boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
glCtx.boundPixelPackBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_READ_BUFFER) {
|
||||
glCtx.boundCopyReadBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_WRITE_BUFFER) {
|
||||
glCtx.boundCopyWriteBuffer = buffer;
|
||||
} else {
|
||||
glCtx.boundOtherBuffers.put(target, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBuffer(int target, int buffer) {
|
||||
rejectExitCapture();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean warnedUntrackedPixelBuffer;
|
||||
|
||||
private static void warnUntrackedPixelBuffer(int binding, String name) {
|
||||
if (warnedUntrackedPixelBuffer || initConfig == null || !initConfig.isLwjglDebug()) return;
|
||||
final int actual = RENDER_BACKEND.getInteger(binding);
|
||||
if (actual == 0) return;
|
||||
warnedUntrackedPixelBuffer = true;
|
||||
LOGGER.warn("{} is bound to {} but GLSM's cache says 0; a call site bypassed GLSMBuffers.glBindBuffer", name, actual, new Throwable());
|
||||
}
|
||||
|
||||
static void suspendPixelUnpackBuffer() {
|
||||
if (ctx().boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
} else {
|
||||
warnUntrackedPixelBuffer(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING, "GL_PIXEL_UNPACK_BUFFER");
|
||||
}
|
||||
}
|
||||
|
||||
static void restorePixelUnpackBuffer() {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, glCtx.boundPixelUnpackBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void suspendPixelPackBuffer() {
|
||||
if (ctx().boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
} else {
|
||||
warnUntrackedPixelBuffer(GL21.GL_PIXEL_PACK_BUFFER_BINDING, "GL_PIXEL_PACK_BUFFER");
|
||||
}
|
||||
}
|
||||
|
||||
static void restorePixelPackBuffer() {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, glCtx.boundPixelPackBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGenBuffers() {
|
||||
requireContext("glGenBuffers");
|
||||
return RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static void glGenBuffers(IntBuffer buffers) {
|
||||
requireContext("glGenBuffers");
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.genBuffers());
|
||||
}
|
||||
|
||||
public static void glGenBuffers(int[] buffers) {
|
||||
requireContext("glGenBuffers");
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static int glCreateBuffers() {
|
||||
requireContext("glCreateBuffers");
|
||||
return RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(IntBuffer buffers) {
|
||||
requireContext("glCreateBuffers");
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.createBuffers());
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(int[] buffers) {
|
||||
requireContext("glCreateBuffers");
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
private static final int MAP_PERSISTENT_BIT = 0x40;
|
||||
|
||||
private static int bufferForTarget(GLContextState glCtx, int target) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) return glCtx.boundVBO;
|
||||
if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) return glCtx.vaos.boundEBO;
|
||||
if (target == GL31.GL_COPY_READ_BUFFER) return glCtx.boundCopyReadBuffer;
|
||||
if (target == GL31.GL_COPY_WRITE_BUFFER) return glCtx.boundCopyWriteBuffer;
|
||||
if (target == GL21.GL_PIXEL_UNPACK_BUFFER) return glCtx.boundPixelUnpackBuffer;
|
||||
if (target == GL21.GL_PIXEL_PACK_BUFFER) return glCtx.boundPixelPackBuffer;
|
||||
return glCtx.boundOtherBuffers.get(target);
|
||||
}
|
||||
|
||||
private static void onBufferRespec(int target) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final int buffer = bufferForTarget(glCtx, target);
|
||||
if (buffer != 0) onNamedBufferRespec(glCtx, buffer);
|
||||
}
|
||||
|
||||
private static void onNamedBufferRespec(GLContextState glCtx, int buffer) {
|
||||
rejectExitCapture();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (!FfpExtendedAttribs.isEmpty()) FfpExtendedAttribs.onBufferRespecified(glCtx, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void onBufferMap(int target, int access) {
|
||||
if (access == GL15.GL_READ_ONLY) return;
|
||||
onBufferWriteMap(target);
|
||||
}
|
||||
|
||||
private static void onBufferMapRange(int target, int access) {
|
||||
if ((access & GL30.GL_MAP_WRITE_BIT) == 0) return;
|
||||
if ((access & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferWriteMap(target);
|
||||
}
|
||||
|
||||
private static void onBufferWriteMap(int target) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int buffer = bufferForTarget(glCtx, target);
|
||||
if (buffer == 0) return;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
writeMappedBuffers.add(buffer);
|
||||
if (!FfpExtendedAttribs.isEmpty()) FfpExtendedAttribs.onBufferRespecified(glCtx, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void onBufferUnmap(int target) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int buffer = bufferForTarget(glCtx, target);
|
||||
if (buffer == 0) return;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (writeMappedBuffers.remove(buffer) && !FfpExtendedAttribs.isEmpty()) FfpExtendedAttribs.onBufferRespecified(glCtx, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void onBufferStorage(int target, int flags) {
|
||||
rejectExitCapture();
|
||||
if ((flags & MAP_PERSISTENT_BIT) != 0) {
|
||||
if (!RENDER_BACKEND.isBufferImmutable(bufferForTarget(ctx(), target))) markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void markPersistentlyMapped(int target) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
FfpExtendedAttribs.markPersistent(glCtx, bufferForTarget(glCtx, target));
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, LongBuffer data, int usage) { glBufferData(target, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferData(int target, long[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferData(int target, double[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglBufferData(int target, long size, long data, int usage) {
|
||||
if (data == 0L) {
|
||||
glBufferData(target, size, usage);
|
||||
return;
|
||||
}
|
||||
glBufferData(target, MemoryUtilities.memByteBuffer(data, checkedSize(size)), usage);
|
||||
}
|
||||
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, LongBuffer data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, int[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, long[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, float[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferSubData(int target, long offset, double[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferSubData(target, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) {
|
||||
rejectExitCapture();
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) {
|
||||
rejectExitCapture();
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access, length, old_buffer);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) {
|
||||
rejectExitCapture();
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBufferRange(target, offset, length, access);
|
||||
if (buf != null) onBufferMapRange(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static long glMapBufferRangeAddress(int target, long offset, long length, int access) {
|
||||
rejectExitCapture();
|
||||
final long address = RENDER_BACKEND.mapBufferRangeAddress(target, offset, length, access);
|
||||
if (address != 0L) onBufferMapRange(target, access);
|
||||
return address;
|
||||
}
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { rejectExitCapture(); RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) {
|
||||
rejectExitCapture();
|
||||
onBufferUnmap(target);
|
||||
return RENDER_BACKEND.unmapBuffer(target);
|
||||
}
|
||||
|
||||
public static void glGetBufferSubData(int target, long offset, ByteBuffer data) { rejectExitCapture(); RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, ShortBuffer data) { rejectExitCapture(); RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, IntBuffer data) { rejectExitCapture(); RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, FloatBuffer data) { rejectExitCapture(); RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, DoubleBuffer data) { rejectExitCapture(); RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, LongBuffer data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glGetBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 1);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asShortBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, int[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asIntBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, long[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asLongBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, float[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asFloatBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glGetBufferSubData(int target, long offset, double[] data) {
|
||||
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
|
||||
glGetBufferSubData(target, offset, copy);
|
||||
copy.asDoubleBuffer().get(data);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void nglGetBufferSubData(int target, long offset, long size, long data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static int glGetBufferParameteri(int target, int pname) { rejectExitCapture(); return RENDER_BACKEND.getBufferParameteri(target, pname); }
|
||||
public static int glGetBufferParameter(int target, int pname) { return glGetBufferParameteri(target, pname); }
|
||||
public static void glGetBufferParameter(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, int[] params) { params[0] = glGetBufferParameteri(target, pname); }
|
||||
|
||||
public static void glBufferStorage(int target, long size, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, ShortBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, IntBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, FloatBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, DoubleBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, short[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, int[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, float[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glBufferStorage(int target, double[] data, int flags) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferStorage(target, copy, flags);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { onNamedBufferRespec(ctx(), buffer); RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { onNamedBufferRespec(ctx(), buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { onNamedBufferRespec(ctx(), buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, ShortBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, IntBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, LongBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, DoubleBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, int[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, long[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, float[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferData(int buffer, double[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferData(buffer, copy, usage);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { onNamedBufferRespec(ctx(), buffer); RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ShortBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, IntBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, LongBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, FloatBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, DoubleBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, int[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, long[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, float[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
public static void glNamedBufferSubData(int buffer, long offset, double[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glNamedBufferSubData(buffer, offset, copy);
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glClearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferData(target, internalformat, format, type, data); }
|
||||
public static boolean glIsBuffer(int buffer) { rejectExitCapture(); return RENDER_BACKEND.isBuffer(buffer); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { onBufferRespec(writeTarget); RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
|
||||
private static int checkedSize(long size) {
|
||||
if (size < 0 || size > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Buffer range does not fit a ByteBuffer wrapper: " + size);
|
||||
}
|
||||
return (int) size;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(short[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 1);
|
||||
bb.asShortBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(int[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
|
||||
bb.asIntBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(long[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
|
||||
bb.asLongBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(float[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
|
||||
bb.asFloatBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
private static ByteBuffer copyOf(double[] data) {
|
||||
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
|
||||
bb.asDoubleBuffer().put(data);
|
||||
return bb;
|
||||
}
|
||||
|
||||
public static void glBindBufferBase(int target, int index, int buffer) {
|
||||
rejectExitCapture();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
final GLContextState glCtx = ctx();
|
||||
trackBufferBinding(glCtx, target, buffer);
|
||||
trackIndexedBufferBinding(glCtx, target, index, buffer);
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBufferRange(int target, int index, int buffer, long offset, long size) {
|
||||
rejectExitCapture();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
final GLContextState glCtx = ctx();
|
||||
trackBufferBinding(glCtx, target, buffer);
|
||||
trackIndexedBufferBinding(glCtx, target, index, buffer);
|
||||
RENDER_BACKEND.bindBufferRange(target, index, buffer, offset, size);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] indexedBufferBindings(GLContextState glCtx, int target, int index) {
|
||||
if (index < 0 || index >= INDEXED_BUFFER_SLOTS) return null;
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) return glCtx.uniformBufferBindings;
|
||||
if (target == GL43.GL_SHADER_STORAGE_BUFFER) return glCtx.storageBufferBindings;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void trackIndexedBufferBinding(GLContextState glCtx, int target, int index, int buffer) {
|
||||
final int[] bindings = indexedBufferBindings(glCtx, target, index);
|
||||
if (bindings != null) bindings[index] = buffer;
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
if (isCachingEnabled()) {
|
||||
final int[] bindings = indexedBufferBindings(ctx(), target, index);
|
||||
if (bindings != null) return bindings[index];
|
||||
}
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
public static long nglMapBuffer(int target, int access) {
|
||||
rejectExitCapture();
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
|
||||
public static void glMemoryBarrier(int barriers) { rejectExitCapture(); RENDER_BACKEND.memoryBarrier(barriers); }
|
||||
|
||||
public static int getBoundVBO() {
|
||||
return ctx().boundVBO;
|
||||
}
|
||||
|
||||
public static int getBoundCopyReadBuffer() {
|
||||
return ctx().boundCopyReadBuffer;
|
||||
}
|
||||
|
||||
public static int getBoundCopyWriteBuffer() {
|
||||
return ctx().boundCopyWriteBuffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.Color4Stack;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorEntityIds;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isMainThread;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMCurrentAttribs {
|
||||
|
||||
private GLSMCurrentAttribs() {}
|
||||
|
||||
public static void setOverlayColor(float r, float g, float b, float a) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.OVERLAY_COLOR);
|
||||
if (r == glCtx.overlayR && g == glCtx.overlayG && b == glCtx.overlayB && a == glCtx.overlayA) return;
|
||||
glCtx.overlayR = r; glCtx.overlayG = g; glCtx.overlayB = b; glCtx.overlayA = a;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
|
||||
public static void setShaderColor(float r, float g, float b, float a) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.SHADER_COLOR);
|
||||
if (r == glCtx.shaderColorR && g == glCtx.shaderColorG && b == glCtx.shaderColorB && a == glCtx.shaderColorA) return;
|
||||
glCtx.shaderColorR = r; glCtx.shaderColorG = g; glCtx.shaderColorB = b; glCtx.shaderColorA = a;
|
||||
if (GLSMHooks.SHADER_COLOR_CHANGE.hasListeners()) {
|
||||
GLSMHooks.shaderColorChangeEvent.red = r;
|
||||
GLSMHooks.shaderColorChangeEvent.green = g;
|
||||
GLSMHooks.shaderColorChangeEvent.blue = b;
|
||||
GLSMHooks.shaderColorChangeEvent.alpha = a;
|
||||
GLSMHooks.SHADER_COLOR_CHANGE.post(GLSMHooks.shaderColorChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean consumeUnit23TexCoordSetDuringDraw(GLContextState glCtx) {
|
||||
boolean v = glCtx.unit23TexCoordSetDuringDraw;
|
||||
glCtx.unit23TexCoordSetDuringDraw = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
public static void forceAttribDefaultsDirty() {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
// has* bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
|
||||
// Skip the backend vertexAttrib call in either case
|
||||
public static void flushDeferredVertexAttribs(GLContextState glCtx, boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
final ShaderManager sm = glCtx.ffp;
|
||||
final boolean ffpWillHandle = sm.isActive() || (sm.isEnabled() && glCtx.activeProgram == 0);
|
||||
if (!hasColor && glCtx.dirtyColorAttrib && !ffpWillHandle) {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), glCtx.color.getRed(), glCtx.color.getGreen(), glCtx.color.getBlue(), glCtx.color.getAlpha());
|
||||
glCtx.dirtyColorAttrib = false;
|
||||
}
|
||||
if (!hasNormal && glCtx.dirtyNormalAttrib && !ffpWillHandle) {
|
||||
final var n = sm.currentNormal;
|
||||
RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z);
|
||||
glCtx.dirtyNormalAttrib = false;
|
||||
}
|
||||
if (!hasTexCoord && glCtx.dirtyTexCoordAttrib && !ffpWillHandle) {
|
||||
final var tc = sm.currentTexCoords[0];
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w);
|
||||
glCtx.dirtyTexCoordAttrib = false;
|
||||
}
|
||||
if (!hasLightmap && glCtx.dirtyLightmapAttrib && !ffpWillHandle) {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), glCtx.lastBrightnessX, glCtx.lastBrightnessY, 0.0f, 1.0f);
|
||||
glCtx.dirtyLightmapAttrib = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setEntityIds(int entity, int blockEntity, int item) {
|
||||
rejectExitCapture();
|
||||
if (!isMainThread()) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
final long ids = InstancedAttribs.packEntityInfo(entity, blockEntity, item);
|
||||
if (ids == glCtx.entityIds) return;
|
||||
TessellatorEntityIds.onIdsChanged(glCtx.entityIds, ids);
|
||||
glCtx.entityIds = ids;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
|
||||
public static void flushEntityIds(GLContextState glCtx) {
|
||||
if (!glCtx.dirtyEntityIdsAttrib || glCtx.activeProgram == 0) return;
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
if (handler == null || !handler.wantsEntityIds()) return;
|
||||
final long ids = glCtx.entityIds;
|
||||
RENDER_BACKEND.vertexAttrib4f(ImmediateExtendedAttribHandler.LOC_ENTITY_IDS,
|
||||
InstancedAttribs.normalizedId(ids, 0), InstancedAttribs.normalizedId(ids, 16), InstancedAttribs.normalizedId(ids, 32), 0.0f);
|
||||
glCtx.dirtyEntityIdsAttrib = false;
|
||||
}
|
||||
|
||||
public static void glNormal3b(byte nx, byte ny, byte nz) {
|
||||
final float fnx = b2f(nx), fny = b2f(ny), fnz = b2f(nz);
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setNormal(fnx, fny, fnz);
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setNormal(fnx, fny, fnz);
|
||||
return;
|
||||
}
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
}
|
||||
|
||||
public static void glNormal3d(double nx, double ny, double nz) {
|
||||
final float fnx = (float) nx, fny = (float) ny, fnz = (float) nz;
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setNormal(fnx, fny, fnz);
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setNormal(fnx, fny, fnz);
|
||||
return;
|
||||
}
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
}
|
||||
|
||||
public static void glNormal3f(float nx, float ny, float nz) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setNormal(nx, ny, nz);
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setNormal(nx, ny, nz);
|
||||
return;
|
||||
}
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
}
|
||||
|
||||
public static void glNormal3i(int nx, int ny, int nz) {
|
||||
final float fnx = nx / 2147483647.0f, fny = ny / 2147483647.0f, fnz = nz / 2147483647.0f;
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setNormal(fnx, fny, fnz);
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setNormal(fnx, fny, fnz);
|
||||
return;
|
||||
}
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
}
|
||||
|
||||
public static void glEdgeFlag(boolean flag) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStateCommand(() -> glEdgeFlag(flag));
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
}
|
||||
ctx().edgeFlagState.setEnabled(flag);
|
||||
}
|
||||
|
||||
public static void glColor4f(float red, float green, float blue, float alpha) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(red, green, blue, alpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
public static void glColor4d(double red, double green, double blue, double alpha) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor((float) red, (float) green, (float) blue, (float) alpha);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor((float) red, (float) green, (float) blue, (float) alpha);
|
||||
}
|
||||
|
||||
public static void glColor4b(byte red, byte green, byte blue, byte alpha) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(b2f(red), b2f(green), b2f(blue), b2f(alpha));
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(b2f(red), b2f(green), b2f(blue), b2f(alpha));
|
||||
}
|
||||
|
||||
public static void glColor4ub(byte red, byte green, byte blue, byte alpha) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(ub2f(red), ub2f(green), ub2f(blue), ub2f(alpha));
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(ub2f(red), ub2f(green), ub2f(blue), ub2f(alpha));
|
||||
}
|
||||
|
||||
public static void glColor3f(float red, float green, float blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(red, green, blue, 1.0F);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(red, green, blue, 1.0F);
|
||||
}
|
||||
|
||||
public static void glColor3d(double red, double green, double blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor((float) red, (float) green, (float) blue, 1.0F);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor((float) red, (float) green, (float) blue, 1.0F);
|
||||
}
|
||||
|
||||
public static void glColor3b(byte red, byte green, byte blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(b2f(red), b2f(green), b2f(blue), 1.0F);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(b2f(red), b2f(green), b2f(blue), 1.0F);
|
||||
}
|
||||
|
||||
public static void glColor3ub(byte red, byte green, byte blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColor(ub2f(red), ub2f(green), ub2f(blue), 1.0F);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeColor(ub2f(red), ub2f(green), ub2f(blue), 1.0F);
|
||||
}
|
||||
|
||||
private static float ub2f(byte b) {
|
||||
return (b & 0xFF) / 255.0F;
|
||||
}
|
||||
|
||||
private static float b2f(byte b) {
|
||||
return ((b - Byte.MIN_VALUE) & 0xFF) / 255.0F;
|
||||
}
|
||||
|
||||
private static boolean changeColor(float red, float green, float blue, float alpha) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.COLOR);
|
||||
// Helper function for glColor*
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || red != glCtx.color.getRed() || green != glCtx.color.getGreen() || blue != glCtx.color.getBlue() || alpha != glCtx.color.getAlpha()) {
|
||||
mod(glCtx.color);
|
||||
glCtx.color.setRed(red);
|
||||
glCtx.color.setGreen(green);
|
||||
glCtx.color.setBlue(blue);
|
||||
glCtx.color.setAlpha(alpha);
|
||||
glCtx.colorGeneration++;
|
||||
if (ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setColor(red, green, blue, alpha);
|
||||
}
|
||||
glCtx.dirtyColorAttrib = true;
|
||||
// Only set while an enchantment-glint span is active
|
||||
final GlintColorHandler glintHandler = GLSMHooks.glintColorHandler;
|
||||
if (glintHandler != null) {
|
||||
glintHandler.onColorChanged(red, green, blue, alpha);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3f(float red, float green, float blue) {
|
||||
rejectExitCapture();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordSecondaryColor(red, green, blue);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeSecondaryColor(red, green, blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3d(double red, double green, double blue) {
|
||||
glSecondaryColor3f((float) red, (float) green, (float) blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3b(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(b2f(red), b2f(green), b2f(blue));
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3ub(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(ub2f(red), ub2f(green), ub2f(blue));
|
||||
}
|
||||
|
||||
/** Helper for glSecondaryColor* - the color sum is emulated by the FFP shaders, nothing is sent to the driver. */
|
||||
private static void changeSecondaryColor(float red, float green, float blue) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || red != glCtx.secondaryColor.getRed() || green != glCtx.secondaryColor.getGreen() || blue != glCtx.secondaryColor.getBlue()) {
|
||||
if (glCtx.colorSumState.isEnabled()) beforeUncapturedStateChange();
|
||||
mod(glCtx.secondaryColor);
|
||||
glCtx.secondaryColor.setRed(red);
|
||||
glCtx.secondaryColor.setGreen(green);
|
||||
glCtx.secondaryColor.setBlue(blue);
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTexCoord1f(float s) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord(s, 0.0f);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord(s, 0.0f, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord1d(double s) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord((float) s, 0.0f);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord((float) s, 0.0f, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord2f(float s, float t) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord(s, t);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord(s, t, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord2d(double s, double t) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord((float) s, (float) t);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord((float) s, (float) t, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord3f(float s, float t, float r) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord(s, t); // Only track s,t for 2D textures
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord(s, t, r, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord3d(double s, double t, double r) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord((float) s, (float) t);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord((float) s, (float) t, (float) r, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord4f(float s, float t, float r, float q) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord(s, t);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord(s, t, r, q);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glTexCoord4d(double s, double t, double r, double q) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord((float) s, (float) t);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.ffp.setTexCoord((float) s, (float) t, (float) r, (float) q);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
|
||||
public static void glVertexAttrib2f(int index, float v0, float v1) {
|
||||
glVertexAttrib4f(index, v0, v1, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib2s(int index, short v0, short v1) {
|
||||
glVertexAttrib4f(index, v0, v1, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib3f(int index, float v0, float v1, float v2) {
|
||||
glVertexAttrib4f(index, v0, v1, v2, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordVertexAttrib(index, v0, v1, v2, v3);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void glMultiTexCoord2f(int target, float s, float t) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL13.GL_TEXTURE0) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setTexCoord(s, t);
|
||||
return;
|
||||
}
|
||||
glCtx.ffp.setTexCoord(s, t, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
} else if (target == GL13.GL_TEXTURE1) {
|
||||
if (ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setLightmapCoord(s, t);
|
||||
return;
|
||||
}
|
||||
setLightmapTextureCoords(target, s, t);
|
||||
} else {
|
||||
final int unit = target - GL13.GL_TEXTURE0;
|
||||
if (unit >= 2 && unit < 4) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
glCtx.unit23TexCoordSetDuringDraw = true;
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.ffp.setTexCoord(unit, s, t, 0.0f, 1.0f);
|
||||
} else {
|
||||
glCtx.exitCapture.reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glMultiTexCoord2d(int target, double s, double t) {
|
||||
glMultiTexCoord2f(target, (float) s, (float) t);
|
||||
}
|
||||
|
||||
public static void glMultiTexCoord2s(int target, short s, short t) {
|
||||
glMultiTexCoord2f(target, s, t);
|
||||
}
|
||||
|
||||
public static void setLightmapTextureCoords(int unit, float x, float y) {
|
||||
if (unit != GL13.GL_TEXTURE1) {
|
||||
rejectExitCapture();
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.LIGHTMAP);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStateCommand(() -> setLightmapTextureCoords(unit, x, y));
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
}
|
||||
glCtx.lastBrightnessX = x;
|
||||
glCtx.lastBrightnessY = y;
|
||||
glCtx.dirtyLightmapAttrib = true;
|
||||
if (GLSMHooks.LIGHTMAP_COORDS.hasListeners()) {
|
||||
GLSMHooks.lightmapCoordsEvent.x = x;
|
||||
GLSMHooks.lightmapCoordsEvent.y = y;
|
||||
GLSMHooks.LIGHTMAP_COORDS.post(GLSMHooks.lightmapCoordsEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getOverlayR() {
|
||||
return ctx().overlayR;
|
||||
}
|
||||
|
||||
public static float getOverlayG() {
|
||||
return ctx().overlayG;
|
||||
}
|
||||
|
||||
public static float getOverlayB() {
|
||||
return ctx().overlayB;
|
||||
}
|
||||
|
||||
public static float getOverlayA() {
|
||||
return ctx().overlayA;
|
||||
}
|
||||
|
||||
public static float getShaderColorR() {
|
||||
return ctx().shaderColorR;
|
||||
}
|
||||
|
||||
public static float getShaderColorG() {
|
||||
return ctx().shaderColorG;
|
||||
}
|
||||
|
||||
public static float getShaderColorB() {
|
||||
return ctx().shaderColorB;
|
||||
}
|
||||
|
||||
public static float getShaderColorA() {
|
||||
return ctx().shaderColorA;
|
||||
}
|
||||
|
||||
public static Color4Stack getColor() {
|
||||
return ctx().color;
|
||||
}
|
||||
|
||||
public static Color4Stack getSecondaryColor() {
|
||||
return ctx().secondaryColor;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getColorSumState() {
|
||||
return ctx().colorSumState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.DepthStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMDepthStencilAlpha {
|
||||
|
||||
private GLSMDepthStencilAlpha() {}
|
||||
|
||||
public static boolean isEffectiveDepthMaskEnabled() {
|
||||
return ctx().depthState.isEffectiveMaskEnabled();
|
||||
}
|
||||
|
||||
public static boolean isEffectiveAlphaTestEnabled() {
|
||||
return ctx().alphaTest.isEffectivelyEnabled();
|
||||
}
|
||||
|
||||
public static AlphaState getEffectiveAlphaState(AlphaState out) {
|
||||
return ctx().alphaState.readEffective(out);
|
||||
}
|
||||
|
||||
public static void glDepthFunc(int func) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.DEPTH_FUNC);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDepthFunc(func);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || func != glCtx.depthState.getFunc()) {
|
||||
mod(glCtx.depthState);
|
||||
glCtx.depthState.setFunc(func);
|
||||
RENDER_BACKEND.depthFunc(func);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDepthMask(boolean mask) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.writeDepthColor(ExitStateCapture.DEPTH_MASK);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDepthMask(mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
|
||||
if (dch != null && dch.isDepthColorLocked()) {
|
||||
mod(glCtx.depthState);
|
||||
dch.deferDepthEnable(mask);
|
||||
return;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || mask != glCtx.depthState.isEnabled()) {
|
||||
mod(glCtx.depthState);
|
||||
glCtx.depthState.setEnabled(mask);
|
||||
RENDER_BACKEND.depthMask(mask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glClearDepth(double depth) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordClearDepth(depth);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
depth = Math.min(1.0, Math.max(0.0, depth));
|
||||
mod(glCtx.depthState);
|
||||
if (isCachingEnabled()) glCtx.depthState.setClearValue(depth);
|
||||
RENDER_BACKEND.clearDepth(depth);
|
||||
}
|
||||
|
||||
public static void enableAlphaTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_ALPHA_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableAlphaTest(ctx());
|
||||
}
|
||||
|
||||
static void enableAlphaTest(GLContextState glCtx) {
|
||||
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
|
||||
if (ah != null && ah.isAlphaTestLocked()) {
|
||||
glCtx.exitCapture.capability(GL11.GL_ALPHA_TEST, -1);
|
||||
glCtx.alphaTest.beforeModify();
|
||||
if (ah.deferAlphaTestToggle(true) && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final boolean wasEnabled = glCtx.alphaTest.isEnabled();
|
||||
glCtx.alphaTest.enable();
|
||||
if (glCtx.alphaTest.isEnabled() != wasEnabled) {
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableAlphaTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_ALPHA_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableAlphaTest(ctx());
|
||||
}
|
||||
|
||||
static void disableAlphaTest(GLContextState glCtx) {
|
||||
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
|
||||
if (ah != null && ah.isAlphaTestLocked()) {
|
||||
glCtx.exitCapture.capability(GL11.GL_ALPHA_TEST, -1);
|
||||
glCtx.alphaTest.beforeModify();
|
||||
if (ah.deferAlphaTestToggle(false) && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final boolean wasEnabled = glCtx.alphaTest.isEnabled();
|
||||
glCtx.alphaTest.disable();
|
||||
if (glCtx.alphaTest.isEnabled() != wasEnabled) {
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glAlphaFunc(int function, float reference) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.writeAlpha();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordAlphaFunc(function, reference);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
|
||||
if (ah != null && ah.isAlphaTestLocked()) {
|
||||
mod(glCtx.alphaState);
|
||||
if (ah.deferAlphaFunc(function, reference) && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()
|
||||
&& (function != glCtx.alphaState.getFunction() || Float.compare(reference, glCtx.alphaState.getReference()) != 0)) {
|
||||
mod(glCtx.alphaState);
|
||||
glCtx.alphaState.setFunction(function);
|
||||
glCtx.alphaState.setReference(reference);
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableDepthTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_DEPTH_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableDepthTest(ctx());
|
||||
}
|
||||
|
||||
static void enableDepthTest(GLContextState glCtx) {
|
||||
glCtx.depthTest.enable();
|
||||
}
|
||||
|
||||
public static void disableDepthTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_DEPTH_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableDepthTest(ctx());
|
||||
}
|
||||
|
||||
static void disableDepthTest(GLContextState glCtx) {
|
||||
glCtx.depthTest.disable();
|
||||
}
|
||||
|
||||
public static void glDepthRange(double near, double far) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || glCtx.viewportState.depthRangeNear != near || glCtx.viewportState.depthRangeFar != far) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(glCtx.viewportState).setDepthRange(near, far);
|
||||
RENDER_BACKEND.depthRange(near, far);
|
||||
}
|
||||
|
||||
public static void glStencilFunc(int func, int ref, int mask) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilFunc(func, ref, mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getFuncFront() != func || glCtx.stencilState.getRefFront() != ref || glCtx.stencilState.getValueMaskFront() != clampedMask) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setFunc(func, ref, clampedMask);
|
||||
RENDER_BACKEND.stencilFunc(func, ref, clampedMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glStencilMask(int mask) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilMask(mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getWriteMaskFront() != clampedMask) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setWriteMask(clampedMask);
|
||||
RENDER_BACKEND.stencilMask(clampedMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glStencilOp(int fail, int zfail, int zpass) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilOp(fail, zfail, zpass);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getFailOpFront() != fail || glCtx.stencilState.getZFailOpFront() != zfail || glCtx.stencilState.getZPassOpFront() != zpass) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setOp(fail, zfail, zpass);
|
||||
RENDER_BACKEND.stencilOp(fail, zfail, zpass);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glClearStencil(int s) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordClearStencil(s);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getClearValue() != s) {
|
||||
mod(glCtx.stencilState).setClearValue(s);
|
||||
RENDER_BACKEND.clearStencil(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilFuncSeparate(face, func, ref, mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
if (!needsUpdate) {
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
needsUpdate = glCtx.stencilState.getFuncFront() != func || glCtx.stencilState.getRefFront() != ref || glCtx.stencilState.getValueMaskFront() != clampedMask;
|
||||
}
|
||||
if (!needsUpdate && (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK)) {
|
||||
needsUpdate = glCtx.stencilState.getFuncBack() != func || glCtx.stencilState.getRefBack() != ref || glCtx.stencilState.getValueMaskBack() != clampedMask;
|
||||
}
|
||||
}
|
||||
if (needsUpdate) {
|
||||
mod(glCtx.stencilState);
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setFuncFront(func);
|
||||
glCtx.stencilState.setRefFront(ref);
|
||||
glCtx.stencilState.setValueMaskFront(clampedMask);
|
||||
}
|
||||
if (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setFuncBack(func);
|
||||
glCtx.stencilState.setRefBack(ref);
|
||||
glCtx.stencilState.setValueMaskBack(clampedMask);
|
||||
}
|
||||
RENDER_BACKEND.stencilFuncSeparate(face, func, ref, clampedMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glStencilMaskSeparate(int face, int mask) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilMaskSeparate(face, mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
if (!needsUpdate) {
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
needsUpdate = glCtx.stencilState.getWriteMaskFront() != clampedMask;
|
||||
}
|
||||
if (!needsUpdate && (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK)) {
|
||||
needsUpdate = glCtx.stencilState.getWriteMaskBack() != clampedMask;
|
||||
}
|
||||
}
|
||||
if (needsUpdate) {
|
||||
mod(glCtx.stencilState);
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setWriteMaskFront(clampedMask);
|
||||
}
|
||||
if (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setWriteMaskBack(clampedMask);
|
||||
}
|
||||
RENDER_BACKEND.stencilMaskSeparate(face, clampedMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStencilOpSeparate(face, sfail, dpfail, dppass);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
if (!needsUpdate) {
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
needsUpdate = glCtx.stencilState.getFailOpFront() != sfail || glCtx.stencilState.getZFailOpFront() != dpfail || glCtx.stencilState.getZPassOpFront() != dppass;
|
||||
}
|
||||
if (!needsUpdate && (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK)) {
|
||||
needsUpdate = glCtx.stencilState.getFailOpBack() != sfail || glCtx.stencilState.getZFailOpBack() != dpfail || glCtx.stencilState.getZPassOpBack() != dppass;
|
||||
}
|
||||
}
|
||||
if (needsUpdate) {
|
||||
mod(glCtx.stencilState);
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setFailOpFront(sfail);
|
||||
glCtx.stencilState.setZFailOpFront(dpfail);
|
||||
glCtx.stencilState.setZPassOpFront(dppass);
|
||||
}
|
||||
if (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK) {
|
||||
glCtx.stencilState.setFailOpBack(sfail);
|
||||
glCtx.stencilState.setZFailOpBack(dpfail);
|
||||
glCtx.stencilState.setZPassOpBack(dppass);
|
||||
}
|
||||
RENDER_BACKEND.stencilOpSeparate(face, sfail, dpfail, dppass);
|
||||
}
|
||||
}
|
||||
|
||||
public static DepthStateStack getDepthState() {
|
||||
return ctx().depthState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getDepthTest() {
|
||||
return ctx().depthTest;
|
||||
}
|
||||
|
||||
public static AlphaStateStack getAlphaState() {
|
||||
return ctx().alphaState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getAlphaTest() {
|
||||
return ctx().alphaTest;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getStencilTest() {
|
||||
return ctx().stencilTest;
|
||||
}
|
||||
|
||||
public static StencilStateStack getStencilState() {
|
||||
return ctx().stencilState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.LineQuads;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.KHRDebugCallback;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.acquireDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.guardUnsupportedFFP;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.initConfig;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.releaseDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.warnOnce;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMDraws {
|
||||
|
||||
private GLSMDraws() {}
|
||||
|
||||
public static long drawCalls;
|
||||
|
||||
/**
|
||||
* Check if a display list exists (has been compiled and stored). Delegates to DisplayListManager.
|
||||
*
|
||||
* @param list The display list ID to check
|
||||
* @return true if the display list exists, false otherwise
|
||||
*/
|
||||
public static boolean displayListExists(int list) {
|
||||
return DisplayListManager.displayListExists(list);
|
||||
}
|
||||
|
||||
private static final String PIXELTRANSFER_MSG = "glPixelTransfer is not available in GL 3.3 core profile.";
|
||||
|
||||
public static void glPixelTransferf(int pname, float param) {
|
||||
guardUnsupportedFFP("glPixelTransfer", PIXELTRANSFER_MSG);
|
||||
}
|
||||
|
||||
public static void glPixelTransferi(int pname, int param) {
|
||||
guardUnsupportedFFP("glPixelTransfer", PIXELTRANSFER_MSG);
|
||||
}
|
||||
|
||||
private static final String RASTERPOS_MSG = "glRasterPos is not available in GL 3.3 core profile.";
|
||||
|
||||
public static void glRasterPos2f(float x, float y) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos2d(double x, double y) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos2i(int x, int y) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos3f(float x, float y, float z) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos3d(double x, double y, double z) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos3i(int x, int y, int z) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos4f(float x, float y, float z, float w) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos4d(double x, double y, double z, double w) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
public static void glRasterPos4i(int x, int y, int z, int w) { guardUnsupportedFFP("glRasterPos", RASTERPOS_MSG); }
|
||||
|
||||
public static void glBegin(int mode) {
|
||||
rejectExitCapture();
|
||||
ctx().unit23TexCoordSetDuringDraw = false;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
ImmediateModeRecorder.begin(mode);
|
||||
return;
|
||||
}
|
||||
ImmediateModeRecorder.beginLive(mode);
|
||||
}
|
||||
|
||||
public static void glEnd() {
|
||||
rejectExitCapture();
|
||||
final DirectTessellator result = ImmediateModeRecorder.end();
|
||||
if (result != null) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
if (DisplayListManager.isCompileAndExecute() && initConfig != null && initConfig.getDirectDrawer() != null) {
|
||||
final CommandRecorder recorder = DisplayListManager.pauseRecording();
|
||||
initConfig.getDirectDrawer().accept(result);
|
||||
DisplayListManager.resumeRecording(recorder);
|
||||
}
|
||||
DisplayListManager.addImmediateModeDraw(result);
|
||||
return;
|
||||
}
|
||||
if (initConfig != null && initConfig.getDirectDrawer() != null) {
|
||||
initConfig.getDirectDrawer().accept(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex2f(float x, float y) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex(x, y, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex2i(int x, int y) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex((float) x, (float) y, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex2d(double x, double y) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex((float) x, (float) y, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex3f(float x, float y, float z) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex3d(double x, double y, double z) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex((float) x, (float) y, (float) z);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertex3i(int x, int y, int z) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.vertex((float) x, (float) y, (float) z);
|
||||
}
|
||||
}
|
||||
|
||||
public static void preDraw(GLContextState glCtx, int drawMode) {
|
||||
prepareLineEmulation(glCtx, drawMode);
|
||||
glCtx.ffp.preDraw(glCtx);
|
||||
GLSMVertexInput.prepareClientArrays(glCtx);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_BYTE, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(glCtx, indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
}
|
||||
if (widensLineDraw(mode, indices.remaining())) {
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawElementsFromAttribs(mode, indices.remaining(), GL11.GL_UNSIGNED_BYTE, MemoryUtilities.memAddress(indices)));
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_INT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 2);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(glCtx, indices);
|
||||
return;
|
||||
}
|
||||
if (widensLineDraw(mode, indices.remaining())) {
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawElementsFromAttribs(mode, indices.remaining(), GL11.GL_UNSIGNED_INT, MemoryUtilities.memAddress(indices)));
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_SHORT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 1);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(glCtx, indices);
|
||||
return;
|
||||
}
|
||||
if (widensLineDraw(mode, indices.remaining())) {
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawElementsFromAttribs(mode, indices.remaining(), GL11.GL_UNSIGNED_SHORT, MemoryUtilities.memAddress(indices)));
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, count, type, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, count, type, indices);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(glCtx, count, type, indices);
|
||||
return;
|
||||
}
|
||||
if (widensLineDraw(mode, count)) {
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawElementsFromAttribs(mode, count, type, MemoryUtilities.memAddress(indices)));
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
// Core profile: a default VAO is generated at init and glBindVertexArray(0)
|
||||
// is redirected to it, so a VAO is always bound here -- no fallback branches.
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, glCtx.vaos.boundEBO);
|
||||
if (capture != null) {
|
||||
DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
}
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (widensLineDraw(mode, indices_count)) {
|
||||
drawLinesAsQuads(glCtx, lineElementsFromBuffer(glCtx, mode, indices_count, type, indices_buffer_offset));
|
||||
return;
|
||||
}
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBindIndexed(glCtx, mode, indices_count, type, indices_buffer_offset);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(glCtx, indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
rejectExitCapture();
|
||||
drawCalls += drawcount;
|
||||
RENDER_BACKEND.multiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
public static void glMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
rejectExitCapture();
|
||||
drawCalls += drawcount;
|
||||
RENDER_BACKEND.multiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTrianglesInstanced(glCtx, count, type, indices, primcount);
|
||||
return;
|
||||
}
|
||||
warnIfInstancedWideLines(mode);
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTrianglesInstanced(glCtx, first, count, primcount);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
RENDER_BACKEND.drawArraysInstanced(GL11.GL_TRIANGLE_STRIP, first, count & ~1, primcount);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
RENDER_BACKEND.drawArraysInstanced(GL11.GL_TRIANGLE_FAN, first, count, primcount);
|
||||
} else {
|
||||
warnIfInstancedWideLines(mode);
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (widensLineDraw(mode, count)) {
|
||||
drawLinesAsQuads(glCtx, lineElementsFromBuffer(glCtx, mode, count, type, indices));
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawRangeElements(mode, start, end, count, type, indices);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDrawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.drawRangeElementsBaseVertex(mode, start, end, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
public static void glMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (LineQuads.isLineMode(mode) && ffpWidensLineQuads()) {
|
||||
for (int i = firsts.position(); i < firsts.limit(); i++) {
|
||||
final int count = counts.get(counts.position() + i - firsts.position());
|
||||
if (LineQuads.segmentCount(mode, count) == 0) continue;
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawArraysFromAttribs(mode, firsts.get(i), count));
|
||||
}
|
||||
return;
|
||||
}
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.multiDrawArrays(mode, firsts, counts);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPrimitiveRestartIndex(int index) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.primitiveRestartIndex(index);
|
||||
}
|
||||
|
||||
public static void glDrawArrays(int mode, int first, int count) {
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final DirectTessellator result = ImmediateModeRecorder.processDrawArraysFromAttribs(mode, first, count);
|
||||
if (result != null) {
|
||||
DisplayListManager.addImmediateModeDraw(result);
|
||||
}
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
DisplayListManager.flushAll();
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawArrays(mode, first, count);
|
||||
return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (widensLineDraw(mode, count)) {
|
||||
drawLinesAsQuads(glCtx, ImmediateModeRecorder.processDrawArraysFromAttribs(mode, first, count));
|
||||
return;
|
||||
}
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBind(glCtx, mode, first, count);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(glCtx, first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
preDraw(glCtx, mode);
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
}
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glNewList(int list, int mode) {
|
||||
rejectExitCapture();
|
||||
DisplayListManager.glNewList(list, mode);
|
||||
}
|
||||
|
||||
public static void glEndList() {
|
||||
rejectExitCapture();
|
||||
DisplayListManager.glEndList();
|
||||
}
|
||||
|
||||
static final DisplayListIDAllocator displayListIdAllocator = new DisplayListIDAllocator();
|
||||
|
||||
public static int glGenLists(int range) {
|
||||
rejectExitCapture();
|
||||
return displayListIdAllocator.allocRange(range);
|
||||
}
|
||||
|
||||
public static boolean glIsList(int list) {
|
||||
rejectExitCapture();
|
||||
return displayListIdAllocator.isAllocated(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete display lists and free their VBO resources.
|
||||
*/
|
||||
public static void glDeleteLists(int list, int range) {
|
||||
rejectExitCapture();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
synchronized (displayListIdAllocator) {
|
||||
DisplayListManager.glDeleteLists(list, range);
|
||||
displayListIdAllocator.freeRange(list, range);
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a compiled display list from the cache. Used for executing nested display lists.
|
||||
*
|
||||
* @param list The display list ID
|
||||
* @return The CompiledDisplayList, or null if not found
|
||||
*/
|
||||
public static CompiledDisplayList getDisplayList(int list) {
|
||||
return DisplayListManager.getDisplayList(list);
|
||||
}
|
||||
|
||||
public static void glCallList(int list) {
|
||||
rejectExitCapture();
|
||||
if (!GLDebug.isActive()) {
|
||||
DisplayListManager.glCallList(list);
|
||||
return;
|
||||
}
|
||||
GLDebug.pushGroup("glCallList ", list);
|
||||
try {
|
||||
DisplayListManager.glCallList(list);
|
||||
} finally {
|
||||
GLDebug.popGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getListMode() {
|
||||
return DisplayListManager.getListMode();
|
||||
}
|
||||
|
||||
public static boolean ffpWidensLineQuads() {
|
||||
final GLContextState glCtx = ctx();
|
||||
return GLSMRasterizer.wideLineEmulationEnabled && glCtx.activeProgram == 0 && glCtx.lineState.getWidth() > 1.0f
|
||||
&& !DisplayListManager.isRecording();
|
||||
}
|
||||
|
||||
private static boolean widensLineDraw(int mode, int count) {
|
||||
return LineQuads.isLineMode(mode) && LineQuads.segmentCount(mode, count) > 0 && ffpWidensLineQuads();
|
||||
}
|
||||
|
||||
private static void drawLinesAsQuads(GLContextState glCtx, DirectTessellator lines) {
|
||||
if (lines == null) return;
|
||||
if (readsBufferObject(glCtx.vaos)) {
|
||||
warnOnce("line-readback", "Wide GL_LINES drawn from a buffer object; widening them reads the buffer back, which waits for the GPU");
|
||||
}
|
||||
final int vao = glCtx.boundVAO;
|
||||
final int vbo = glCtx.boundVBO;
|
||||
TessellatorStreamingDrawer.drawDirect(lines);
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
}
|
||||
|
||||
private static DirectTessellator lineElementsFromBuffer(GLContextState glCtx, int mode, int count, int type, long offset) {
|
||||
if (glCtx.vaos.boundEBO == 0) return null;
|
||||
final ByteBuffer indices = MemoryUtilities.memAlloc(count * GLTypes.sizeBytes(type));
|
||||
try {
|
||||
GLSMBuffers.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, indices);
|
||||
return ImmediateModeRecorder.processDrawElementsFromAttribs(mode, count, type, MemoryUtilities.memAddress0(indices));
|
||||
} finally {
|
||||
MemoryUtilities.memFree(indices);
|
||||
}
|
||||
}
|
||||
|
||||
private static void warnIfInstancedWideLines(int mode) {
|
||||
if (LineQuads.isLineMode(mode) && ffpWidensLineQuads()) {
|
||||
warnOnce("instanced-wide-lines", "Instanced wide GL_LINES are not widened and draw 1 px wide");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean readsBufferObject(VAOManager vaos) {
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final VAOManager.Attrib attrib = vaos.attrib(i);
|
||||
if (attrib != null && attrib.enabled && attrib.vboId != 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void prepareLineEmulation(GLContextState glCtx, int drawMode) {
|
||||
setLineStippleActive(glCtx, (LineQuads.isLineMode(drawMode) || glCtx.lineQuadsActive) && glCtx.lineStippleState.isEnabled());
|
||||
}
|
||||
|
||||
private static void setLineStippleActive(GLContextState glCtx, boolean active) {
|
||||
if (glCtx.lineStippleActive == active) return;
|
||||
glCtx.lineStippleActive = active;
|
||||
RENDER_BACKEND.provokingVertex(active ? GL32.GL_FIRST_VERTEX_CONVENTION : GL32.GL_LAST_VERTEX_CONVENTION);
|
||||
}
|
||||
|
||||
public static void glFlush() {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.flush();
|
||||
}
|
||||
|
||||
public static void glFinish() {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.finish();
|
||||
}
|
||||
|
||||
public static void glMultiDrawArraysIndirectCount(int mode, long indirect, long drawcount, int maxdrawcount, int stride) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.multiDrawArraysIndirectCount(mode, indirect, drawcount, maxdrawcount, stride);
|
||||
}
|
||||
|
||||
public static void glMultiDrawElementsIndirectCount(int mode, int type, long indirect, long drawcount, int maxdrawcount, int stride) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.multiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride);
|
||||
}
|
||||
|
||||
public static void glCallLists(IntBuffer lists) {
|
||||
while (lists.hasRemaining()) {
|
||||
final int listId = lists.get() + ctx().listBase;
|
||||
glCallList(listId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glCallLists(ShortBuffer lists) {
|
||||
while (lists.hasRemaining()) {
|
||||
final int listId = (lists.get() & 0xFFFF) + ctx().listBase;
|
||||
glCallList(listId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glCallLists(ByteBuffer lists) {
|
||||
while (lists.hasRemaining()) {
|
||||
final int listId = (lists.get() & 0xFF) + ctx().listBase;
|
||||
glCallList(listId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glListBase(int base) {
|
||||
rejectExitCapture();
|
||||
ctx().listBase = base;
|
||||
}
|
||||
public static void nglMultiDrawElementsBaseVertex(int mode, long count, int type, long indices, int primcount, long basevertex) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.multiDrawElementsBaseVertex(mode, count, type, indices, primcount, basevertex);
|
||||
}
|
||||
|
||||
public static void glObjectLabel(int identifier, int name, CharSequence label) { RENDER_BACKEND.objectLabel(identifier, name, label); }
|
||||
public static String glGetObjectLabel(int identifier, int name, int bufSize) { rejectExitCapture(); return RENDER_BACKEND.getObjectLabel(identifier, name); }
|
||||
public static void glPushDebugGroup(int source, int id, CharSequence message) { RENDER_BACKEND.pushDebugGroup(source, id, message); }
|
||||
public static void glPopDebugGroup() { RENDER_BACKEND.popDebugGroup(); }
|
||||
public static void glDebugMessageInsert(int source, int type, int id, int severity, CharSequence buf) { RENDER_BACKEND.debugMessageInsert(source, type, id, severity, buf); }
|
||||
public static void glDebugMessageControl(int source, int type, int severity, IntBuffer ids, boolean enabled) { RENDER_BACKEND.debugMessageControl(source, type, severity, ids, enabled); }
|
||||
|
||||
public static int glGetDebugMessageLog(int count, IntBuffer sources, IntBuffer types, IntBuffer ids, IntBuffer severities, IntBuffer lengths, ByteBuffer messageLog) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getDebugMessageLog(count, sources, types, ids, severities, lengths, messageLog);
|
||||
}
|
||||
|
||||
public static void registerDebugMessageListener(GLDebugMessageListener listener, long userParam) {
|
||||
RENDER_BACKEND.debugMessageCallback(listener, userParam);
|
||||
}
|
||||
|
||||
public static void glDebugMessageCallback(KHRDebugCallback callback) {
|
||||
RENDER_BACKEND.debugMessageCallback(GLDebug.adaptDebugCallback(callback), 0L);
|
||||
}
|
||||
|
||||
public static void glAccum(int op, float value) {
|
||||
guardUnsupportedFFP("glAccum", "The accumulation buffer is not available in GL 3.3 core profile.");
|
||||
}
|
||||
|
||||
public static void glFeedbackBuffer(int type, FloatBuffer buffer) {
|
||||
rejectExitCapture();
|
||||
FeedbackManager.glFeedbackBuffer(type, buffer);
|
||||
}
|
||||
|
||||
public static int glRenderMode(int mode) {
|
||||
rejectExitCapture();
|
||||
return FeedbackManager.glRenderMode(mode);
|
||||
}
|
||||
|
||||
public static void glPassThrough(float token) {
|
||||
rejectExitCapture();
|
||||
FeedbackManager.glPassThrough(token);
|
||||
}
|
||||
|
||||
private static boolean selectWarned = false;
|
||||
|
||||
public static void glSelectBuffer(IntBuffer buffer) {
|
||||
rejectExitCapture();
|
||||
if (!selectWarned) {
|
||||
LOGGER.warn("glSelectBuffer: selection mode not emulated");
|
||||
selectWarned = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glInitNames() { /* no-op: ignored outside GL_SELECT */ }
|
||||
|
||||
public static void glPushName(int name) { /* no-op */ }
|
||||
|
||||
public static void glPopName() { /* no-op */ }
|
||||
|
||||
public static void glLoadName(int name) { /* no-op */ }
|
||||
|
||||
/**
|
||||
* Draw modes that rely on the previous vertex data cannot be merged currently.
|
||||
* It is possible to merge them using Index Buffers, but currently unimplemented.
|
||||
*/
|
||||
public static boolean isContinuousDraw(int drawMode) {
|
||||
return drawMode == GL11.GL_TRIANGLE_STRIP || drawMode == GL11.GL_TRIANGLE_FAN
|
||||
|| drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP
|
||||
|| drawMode == GL11.GL_QUAD_STRIP || drawMode == GL11.GL_POLYGON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_CLIP_PLANES;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMEnable {
|
||||
|
||||
private GLSMEnable() {}
|
||||
|
||||
public static void glEnable(int cap) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(cap);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
glCtx.clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].enable();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cap) {
|
||||
case GL11.GL_ALPHA_TEST -> GLSMDepthStencilAlpha.enableAlphaTest(glCtx);
|
||||
case GL11.GL_AUTO_NORMAL -> glCtx.autoNormalState.enable();
|
||||
case GL11.GL_BLEND -> GLSMBlend.enableBlend(glCtx);
|
||||
case GL11.GL_COLOR_MATERIAL -> GLSMLighting.enableColorMaterial(glCtx);
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.enable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.enable();
|
||||
case GL11.GL_CULL_FACE -> GLSMRasterizer.enableCull(glCtx);
|
||||
case GL11.GL_DEPTH_TEST -> GLSMDepthStencilAlpha.enableDepthTest(glCtx);
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.enable();
|
||||
case GL11.GL_FOG -> GLSMFog.enableFog(glCtx);
|
||||
case GL11.GL_INDEX_LOGIC_OP -> glCtx.indexLogicOpState.enable();
|
||||
case GL11.GL_LIGHTING -> GLSMLighting.enableLighting(glCtx);
|
||||
case GL11.GL_LIGHT0 -> GLSMLighting.enableLight(glCtx, 0);
|
||||
case GL11.GL_LIGHT1 -> GLSMLighting.enableLight(glCtx, 1);
|
||||
case GL11.GL_LIGHT2 -> GLSMLighting.enableLight(glCtx, 2);
|
||||
case GL11.GL_LIGHT3 -> GLSMLighting.enableLight(glCtx, 3);
|
||||
case GL11.GL_LIGHT4 -> GLSMLighting.enableLight(glCtx, 4);
|
||||
case GL11.GL_LIGHT5 -> GLSMLighting.enableLight(glCtx, 5);
|
||||
case GL11.GL_LIGHT6 -> GLSMLighting.enableLight(glCtx, 6);
|
||||
case GL11.GL_LIGHT7 -> GLSMLighting.enableLight(glCtx, 7);
|
||||
case GL11.GL_LINE_SMOOTH -> glCtx.lineSmoothState.enable();
|
||||
case GL11.GL_LINE_STIPPLE -> glCtx.lineStippleState.enable();
|
||||
case GL11.GL_MAP1_COLOR_4 -> glCtx.map1Color4State.enable();
|
||||
case GL11.GL_MAP1_INDEX -> glCtx.map1IndexState.enable();
|
||||
case GL11.GL_MAP1_NORMAL -> glCtx.map1NormalState.enable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_1 -> glCtx.map1TextureCoord1State.enable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_2 -> glCtx.map1TextureCoord2State.enable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_3 -> glCtx.map1TextureCoord3State.enable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_4 -> glCtx.map1TextureCoord4State.enable();
|
||||
case GL11.GL_MAP1_VERTEX_3 -> glCtx.map1Vertex3State.enable();
|
||||
case GL11.GL_MAP1_VERTEX_4 -> glCtx.map1Vertex4State.enable();
|
||||
case GL11.GL_MAP2_COLOR_4 -> glCtx.map2Color4State.enable();
|
||||
case GL11.GL_MAP2_INDEX -> glCtx.map2IndexState.enable();
|
||||
case GL11.GL_MAP2_NORMAL -> glCtx.map2NormalState.enable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_1 -> glCtx.map2TextureCoord1State.enable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_2 -> glCtx.map2TextureCoord2State.enable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_3 -> glCtx.map2TextureCoord3State.enable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_4 -> glCtx.map2TextureCoord4State.enable();
|
||||
case GL11.GL_MAP2_VERTEX_3 -> glCtx.map2Vertex3State.enable();
|
||||
case GL11.GL_MAP2_VERTEX_4 -> glCtx.map2Vertex4State.enable();
|
||||
case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.enable();
|
||||
case GL11.GL_NORMALIZE -> glCtx.normalizeState.enable();
|
||||
case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.enable();
|
||||
case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.enable();
|
||||
case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.enable();
|
||||
case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.enable();
|
||||
case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.enable();
|
||||
case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.enable();
|
||||
case GL11.GL_POLYGON_SMOOTH -> glCtx.polygonSmoothState.enable();
|
||||
case GL11.GL_POLYGON_STIPPLE -> glCtx.polygonStippleState.enable();
|
||||
case GL12.GL_RESCALE_NORMAL -> GLSMLighting.enableRescaleNormal(glCtx);
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> glCtx.sampleAlphaToCoverageState.enable();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> glCtx.sampleAlphaToOneState.enable();
|
||||
case GL13.GL_SAMPLE_COVERAGE -> glCtx.sampleCoverageState.enable();
|
||||
case GL30.GL_RASTERIZER_DISCARD -> glCtx.rasterizerDiscard.enable();
|
||||
case GL11.GL_SCISSOR_TEST -> GLSMRasterizer.enableScissorTest(glCtx);
|
||||
case GL11.GL_STENCIL_TEST -> glCtx.stencilTest.enable();
|
||||
case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL11.GL_TEXTURE_2D -> GLSMTextures.enableTexture(glCtx);
|
||||
case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).enable();
|
||||
default -> {
|
||||
glCtx.exitCapture.reject();
|
||||
if (!GLSMVertexInput.setClientArrayCap(glCtx, cap, true)) RENDER_BACKEND.enable(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDisable(int cap) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(cap);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
glCtx.clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].disable();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cap) {
|
||||
case GL11.GL_ALPHA_TEST -> GLSMDepthStencilAlpha.disableAlphaTest(glCtx);
|
||||
case GL11.GL_AUTO_NORMAL -> glCtx.autoNormalState.disable();
|
||||
case GL11.GL_BLEND -> GLSMBlend.disableBlend(glCtx);
|
||||
case GL11.GL_COLOR_MATERIAL -> GLSMLighting.disableColorMaterial(glCtx);
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.disable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.disable();
|
||||
case GL11.GL_CULL_FACE -> GLSMRasterizer.disableCull(glCtx);
|
||||
case GL11.GL_DEPTH_TEST -> GLSMDepthStencilAlpha.disableDepthTest(glCtx);
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.disable();
|
||||
case GL11.GL_FOG -> GLSMFog.disableFog(glCtx);
|
||||
case GL11.GL_INDEX_LOGIC_OP -> glCtx.indexLogicOpState.disable();
|
||||
case GL11.GL_LIGHTING -> GLSMLighting.disableLighting(glCtx);
|
||||
case GL11.GL_LIGHT0 -> GLSMLighting.disableLight(glCtx, 0);
|
||||
case GL11.GL_LIGHT1 -> GLSMLighting.disableLight(glCtx, 1);
|
||||
case GL11.GL_LIGHT2 -> GLSMLighting.disableLight(glCtx, 2);
|
||||
case GL11.GL_LIGHT3 -> GLSMLighting.disableLight(glCtx, 3);
|
||||
case GL11.GL_LIGHT4 -> GLSMLighting.disableLight(glCtx, 4);
|
||||
case GL11.GL_LIGHT5 -> GLSMLighting.disableLight(glCtx, 5);
|
||||
case GL11.GL_LIGHT6 -> GLSMLighting.disableLight(glCtx, 6);
|
||||
case GL11.GL_LIGHT7 -> GLSMLighting.disableLight(glCtx, 7);
|
||||
case GL11.GL_LINE_SMOOTH -> glCtx.lineSmoothState.disable();
|
||||
case GL11.GL_LINE_STIPPLE -> glCtx.lineStippleState.disable();
|
||||
case GL11.GL_MAP1_COLOR_4 -> glCtx.map1Color4State.disable();
|
||||
case GL11.GL_MAP1_INDEX -> glCtx.map1IndexState.disable();
|
||||
case GL11.GL_MAP1_NORMAL -> glCtx.map1NormalState.disable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_1 -> glCtx.map1TextureCoord1State.disable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_2 -> glCtx.map1TextureCoord2State.disable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_3 -> glCtx.map1TextureCoord3State.disable();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_4 -> glCtx.map1TextureCoord4State.disable();
|
||||
case GL11.GL_MAP1_VERTEX_3 -> glCtx.map1Vertex3State.disable();
|
||||
case GL11.GL_MAP1_VERTEX_4 -> glCtx.map1Vertex4State.disable();
|
||||
case GL11.GL_MAP2_COLOR_4 -> glCtx.map2Color4State.disable();
|
||||
case GL11.GL_MAP2_INDEX -> glCtx.map2IndexState.disable();
|
||||
case GL11.GL_MAP2_NORMAL -> glCtx.map2NormalState.disable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_1 -> glCtx.map2TextureCoord1State.disable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_2 -> glCtx.map2TextureCoord2State.disable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_3 -> glCtx.map2TextureCoord3State.disable();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_4 -> glCtx.map2TextureCoord4State.disable();
|
||||
case GL11.GL_MAP2_VERTEX_3 -> glCtx.map2Vertex3State.disable();
|
||||
case GL11.GL_MAP2_VERTEX_4 -> glCtx.map2Vertex4State.disable();
|
||||
case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.disable();
|
||||
case GL11.GL_NORMALIZE -> glCtx.normalizeState.disable();
|
||||
case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.disable();
|
||||
case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.disable();
|
||||
case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.disable();
|
||||
case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.disable();
|
||||
case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.disable();
|
||||
case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.disable();
|
||||
case GL11.GL_POLYGON_SMOOTH -> glCtx.polygonSmoothState.disable();
|
||||
case GL11.GL_POLYGON_STIPPLE -> glCtx.polygonStippleState.disable();
|
||||
case GL12.GL_RESCALE_NORMAL -> GLSMLighting.disableRescaleNormal(glCtx);
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> glCtx.sampleAlphaToCoverageState.disable();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> glCtx.sampleAlphaToOneState.disable();
|
||||
case GL13.GL_SAMPLE_COVERAGE -> glCtx.sampleCoverageState.disable();
|
||||
case GL30.GL_RASTERIZER_DISCARD -> glCtx.rasterizerDiscard.disable();
|
||||
case GL11.GL_SCISSOR_TEST -> GLSMRasterizer.disableScissorTest(glCtx);
|
||||
case GL11.GL_STENCIL_TEST -> glCtx.stencilTest.disable();
|
||||
case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL11.GL_TEXTURE_2D -> GLSMTextures.disableTexture(glCtx);
|
||||
case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).disable();
|
||||
default -> {
|
||||
glCtx.exitCapture.reject();
|
||||
if (!GLSMVertexInput.setClientArrayCap(glCtx, cap, false)) RENDER_BACKEND.disable(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean glIsEnabled(int cap) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) {
|
||||
return RENDER_BACKEND.getBoolean(cap);
|
||||
}
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
return glCtx.clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].isEnabled();
|
||||
}
|
||||
|
||||
return switch (cap) {
|
||||
case GL11.GL_ALPHA_TEST -> glCtx.alphaTest.isEnabled();
|
||||
case GL11.GL_AUTO_NORMAL -> glCtx.autoNormalState.isEnabled();
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.isEnabled();
|
||||
case GL11.GL_FOG -> glCtx.fogMode.isEnabled();
|
||||
case GL11.GL_INDEX_LOGIC_OP -> glCtx.indexLogicOpState.isEnabled();
|
||||
case GL11.GL_LIGHTING -> glCtx.lightingState.isEnabled();
|
||||
case GL11.GL_LIGHT0 -> glCtx.lightStates[0].isEnabled();
|
||||
case GL11.GL_LIGHT1 -> glCtx.lightStates[1].isEnabled();
|
||||
case GL11.GL_LIGHT2 -> glCtx.lightStates[2].isEnabled();
|
||||
case GL11.GL_LIGHT3 -> glCtx.lightStates[3].isEnabled();
|
||||
case GL11.GL_LIGHT4 -> glCtx.lightStates[4].isEnabled();
|
||||
case GL11.GL_LIGHT5 -> glCtx.lightStates[5].isEnabled();
|
||||
case GL11.GL_LIGHT6 -> glCtx.lightStates[6].isEnabled();
|
||||
case GL11.GL_LIGHT7 -> glCtx.lightStates[7].isEnabled();
|
||||
case GL11.GL_LINE_SMOOTH -> glCtx.lineSmoothState.isEnabled();
|
||||
case GL11.GL_LINE_STIPPLE -> glCtx.lineStippleState.isEnabled();
|
||||
case GL11.GL_MAP1_COLOR_4 -> glCtx.map1Color4State.isEnabled();
|
||||
case GL11.GL_MAP1_INDEX -> glCtx.map1IndexState.isEnabled();
|
||||
case GL11.GL_MAP1_NORMAL -> glCtx.map1NormalState.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_1 -> glCtx.map1TextureCoord1State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_2 -> glCtx.map1TextureCoord2State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_3 -> glCtx.map1TextureCoord3State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_4 -> glCtx.map1TextureCoord4State.isEnabled();
|
||||
case GL11.GL_MAP1_VERTEX_3 -> glCtx.map1Vertex3State.isEnabled();
|
||||
case GL11.GL_MAP1_VERTEX_4 -> glCtx.map1Vertex4State.isEnabled();
|
||||
case GL11.GL_MAP2_COLOR_4 -> glCtx.map2Color4State.isEnabled();
|
||||
case GL11.GL_MAP2_INDEX -> glCtx.map2IndexState.isEnabled();
|
||||
case GL11.GL_MAP2_NORMAL -> glCtx.map2NormalState.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_1 -> glCtx.map2TextureCoord1State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_2 -> glCtx.map2TextureCoord2State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_3 -> glCtx.map2TextureCoord3State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_4 -> glCtx.map2TextureCoord4State.isEnabled();
|
||||
case GL11.GL_MAP2_VERTEX_3 -> glCtx.map2Vertex3State.isEnabled();
|
||||
case GL11.GL_MAP2_VERTEX_4 -> glCtx.map2Vertex4State.isEnabled();
|
||||
case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.isEnabled();
|
||||
case GL11.GL_NORMALIZE -> glCtx.normalizeState.isEnabled();
|
||||
case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.isEnabled();
|
||||
case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.isEnabled();
|
||||
case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.isEnabled();
|
||||
case GL11.GL_POLYGON_SMOOTH -> glCtx.polygonSmoothState.isEnabled();
|
||||
case GL11.GL_POLYGON_STIPPLE -> glCtx.polygonStippleState.isEnabled();
|
||||
case GL12.GL_RESCALE_NORMAL -> glCtx.rescaleNormalState.isEnabled();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> glCtx.sampleAlphaToCoverageState.isEnabled();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> glCtx.sampleAlphaToOneState.isEnabled();
|
||||
case GL13.GL_SAMPLE_COVERAGE -> glCtx.sampleCoverageState.isEnabled();
|
||||
case GL30.GL_RASTERIZER_DISCARD -> glCtx.rasterizerDiscard.isEnabled();
|
||||
case GL11.GL_SCISSOR_TEST -> glCtx.scissorTest.isEnabled();
|
||||
case GL11.GL_STENCIL_TEST -> glCtx.stencilTest.isEnabled();
|
||||
case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_2D -> glCtx.textures.getTextureUnitStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
default -> {
|
||||
final int conv = GLSMVertexInput.clientStateToConventional(glCtx, cap);
|
||||
yield conv >= 0 ? glCtx.vaos.isConventionalEnabled(conv) : LegacyQueries.desc(cap) == 0 && RENDER_BACKEND.getBoolean(cap);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void glEnablei(int cap, int index) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.enablei(cap, index);
|
||||
markIndexedCapUnknown(cap);
|
||||
}
|
||||
|
||||
public static void glDisablei(int cap, int index) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.disablei(cap, index);
|
||||
markIndexedCapUnknown(cap);
|
||||
}
|
||||
|
||||
private static void markIndexedCapUnknown(int cap) {
|
||||
if (cap == GL11.GL_BLEND) {
|
||||
ctx().blendMode.setUnknownState();
|
||||
} else if (cap == GL11.GL_SCISSOR_TEST) {
|
||||
ctx().scissorTest.setUnknownState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import org.joml.Vector3d;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.HAS_MULTIPLE_SET;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
|
||||
public final class GLSMFog {
|
||||
|
||||
private GLSMFog() {}
|
||||
|
||||
public static void enableFog() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_FOG);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableFog(ctx());
|
||||
}
|
||||
|
||||
static void enableFog(GLContextState glCtx) {
|
||||
glCtx.fogMode.enable();
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableFog() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_FOG);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableFog(ctx());
|
||||
}
|
||||
|
||||
static void disableFog(GLContextState glCtx) {
|
||||
glCtx.fogMode.disable();
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glFog(int pname, FloatBuffer param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordFog(pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (HAS_MULTIPLE_SET.contains(pname)) {
|
||||
if (pname == GL11.GL_FOG_COLOR && isCachingEnabled()) {
|
||||
final float red = param.get(0);
|
||||
final float green = param.get(1);
|
||||
final float blue = param.get(2);
|
||||
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState);
|
||||
glCtx.fogState.getFogColor().set(red, green, blue);
|
||||
glCtx.fogState.setFogAlpha(param.get(3));
|
||||
glCtx.fogState.getFogColorBuffer().clear();
|
||||
glCtx.fogState.getFogColorBuffer().put((FloatBuffer) param.position(0)).flip();
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
glFogf(pname, param.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3d getFogColor() {
|
||||
return ctx().fogState.getFogColor();
|
||||
}
|
||||
|
||||
public static void fogColor(float red, float green, float blue, float alpha) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || red != glCtx.fogState.getFogColor().x || green != glCtx.fogState.getFogColor().y || blue != glCtx.fogState.getFogColor().z || alpha != glCtx.fogState.getFogAlpha()) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState);
|
||||
glCtx.fogState.getFogColor().set(red, green, blue);
|
||||
glCtx.fogState.setFogAlpha(alpha);
|
||||
glCtx.fogState.getFogColorBuffer().clear();
|
||||
glCtx.fogState.getFogColorBuffer().put(red).put(green).put(blue).put(alpha).flip();
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glFogf(int pname, float param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordFogf(pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Update cached state (FFP shader reads from cache)
|
||||
if (isCachingEnabled()) {
|
||||
boolean changed = false;
|
||||
switch (pname) {
|
||||
case GL11.GL_FOG_DENSITY -> { if (!isCachingEnabled() || glCtx.fogState.getDensity() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setDensity(param); changed = true; } }
|
||||
case GL11.GL_FOG_START -> { if (!isCachingEnabled() || glCtx.fogState.getStart() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setStart(param); changed = true; } }
|
||||
case GL11.GL_FOG_END -> { if (!isCachingEnabled() || glCtx.fogState.getEnd() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setEnd(param); changed = true; } }
|
||||
case GL11.GL_FOG_MODE -> { if (!isCachingEnabled() || glCtx.fogState.getFogMode() != (int) param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setFogMode((int) param); changed = true; } }
|
||||
default -> {}
|
||||
}
|
||||
if (changed) {
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glFogi(int pname, int param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordFogi(pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Update cached state (FFP shader reads from cache)
|
||||
if (isCachingEnabled() && pname == GL11.GL_FOG_MODE && (!isCachingEnabled() || glCtx.fogState.getFogMode() != param)) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState).setFogMode(param);
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FogStateStack getFogState() {
|
||||
return ctx().fogState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getFogMode() {
|
||||
return ctx().fogMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MainThread;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.requireContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMFramebuffer {
|
||||
|
||||
private GLSMFramebuffer() {}
|
||||
|
||||
// This setting varies depending on driver, so it gets queried at runtime
|
||||
public static int DEFAULT_DRAW_BUFFER = GL11.GL_BACK;
|
||||
public static boolean framebufferCompletenessIsMeaningful() { return RENDER_BACKEND.framebufferCompletenessIsMeaningful(); }
|
||||
|
||||
static int drawFramebufferBits(GLContextState glCtx, boolean stencil) {
|
||||
final int sizePname = stencil ? GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE : GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
|
||||
if (glCtx.drawFramebuffer == 0) {
|
||||
return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, stencil ? GL11.GL_STENCIL : GL11.GL_DEPTH, sizePname);
|
||||
}
|
||||
final int attachment = stencil ? GL30.GL_STENCIL_ATTACHMENT : GL30.GL_DEPTH_ATTACHMENT;
|
||||
if (RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) == GL11.GL_NONE) {
|
||||
return 0;
|
||||
}
|
||||
return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, sizePname);
|
||||
}
|
||||
|
||||
static void queryDefaultDrawBuffer() {
|
||||
DEFAULT_DRAW_BUFFER = RENDER_BACKEND.getInteger(GL11.GL_DRAW_BUFFER);
|
||||
}
|
||||
|
||||
static void armFirstClear() {
|
||||
firstClearPending = true;
|
||||
}
|
||||
|
||||
public static void glDrawBuffer(int mode) {
|
||||
rejectExitCapture();
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDrawBuffer(mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!isCachingEnabled() || ctx().drawBuffer.getValue() != mode) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(ctx().drawBuffer).setValue(mode);
|
||||
RENDER_BACKEND.drawBuffer(mode);
|
||||
}
|
||||
|
||||
private static boolean firstClearPending;
|
||||
private static long firstClearBlockNanos;
|
||||
|
||||
public static void glClear(int mask) {
|
||||
rejectExitCapture();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordClear(mask);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ctx().rasterizerDiscard.isEnabled() || FeedbackManager.getRenderMode() != GL11.GL_RENDER) {
|
||||
return;
|
||||
}
|
||||
if (firstClearPending && Thread.currentThread() == MainThread && getDrawFramebuffer() == 0) {
|
||||
firstClearPending = false;
|
||||
final long start = System.nanoTime();
|
||||
RENDER_BACKEND.clear(mask);
|
||||
final long end = System.nanoTime();
|
||||
if (end - start > 0) {
|
||||
firstClearBlockNanos += end - start;
|
||||
RENDER_BACKEND.recordGate(end - start, end);
|
||||
}
|
||||
return;
|
||||
}
|
||||
RENDER_BACKEND.clear(mask);
|
||||
}
|
||||
|
||||
public static long takeFirstClearBlockNanos() {
|
||||
final long value = firstClearBlockNanos;
|
||||
firstClearBlockNanos = 0L;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
rejectExitCapture();
|
||||
type = GLSMTextures.remapPixelTypeForGLES(format, type);
|
||||
GLSMBuffers.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLSMBuffers.restorePixelPackBuffer();
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
rejectExitCapture();
|
||||
type = GLSMTextures.remapPixelTypeForGLES(format, type);
|
||||
GLSMBuffers.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLSMBuffers.restorePixelPackBuffer();
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
rejectExitCapture();
|
||||
type = GLSMTextures.remapPixelTypeForGLES(format, type);
|
||||
GLSMBuffers.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLSMBuffers.restorePixelPackBuffer();
|
||||
}
|
||||
|
||||
public static void glReadBuffer(int mode) {
|
||||
rejectExitCapture();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glReadBuffer");
|
||||
}
|
||||
RENDER_BACKEND.readBuffer(mode);
|
||||
}
|
||||
|
||||
public static void glDrawBuffers(int buffer) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDrawBuffers(1, buffer);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.drawBuffers(buffer);
|
||||
}
|
||||
|
||||
public static void glDrawBuffers(IntBuffer bufs) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDrawBuffers(bufs.remaining(), bufs);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.drawBuffers(bufs);
|
||||
}
|
||||
public static boolean glIsFramebuffer(int framebuffer) { rejectExitCapture(); return RENDER_BACKEND.isFramebuffer(framebuffer); }
|
||||
public static boolean glIsRenderbuffer(int renderbuffer) { rejectExitCapture(); return RENDER_BACKEND.isRenderbuffer(renderbuffer); }
|
||||
|
||||
public static void glBindFramebuffer(int target, int framebuffer) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL30.GL_FRAMEBUFFER) {
|
||||
if (glCtx.drawFramebuffer == framebuffer && glCtx.readFramebuffer == framebuffer) return;
|
||||
if (glCtx.drawFramebuffer != framebuffer) glCtx.drawFramebufferGeneration++;
|
||||
glCtx.drawFramebuffer = framebuffer;
|
||||
glCtx.readFramebuffer = framebuffer;
|
||||
} else if (target == GL30.GL_DRAW_FRAMEBUFFER) {
|
||||
if (glCtx.drawFramebuffer == framebuffer) return;
|
||||
glCtx.drawFramebufferGeneration++;
|
||||
glCtx.drawFramebuffer = framebuffer;
|
||||
} else if (target == GL30.GL_READ_FRAMEBUFFER) {
|
||||
if (glCtx.readFramebuffer == framebuffer) return;
|
||||
glCtx.readFramebuffer = framebuffer;
|
||||
}
|
||||
RENDER_BACKEND.bindFramebuffer(target, framebuffer);
|
||||
}
|
||||
|
||||
public static void glDeleteFramebuffers(int framebuffer) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.drawFramebuffer == framebuffer) { glCtx.drawFramebuffer = 0; glCtx.drawFramebufferGeneration++; }
|
||||
if (glCtx.readFramebuffer == framebuffer) glCtx.readFramebuffer = 0;
|
||||
final int[] slots = glCtx.framebufferTextures.remove(framebuffer);
|
||||
if (slots != null) {
|
||||
for (int slot = 0; slot < slots.length; slot++) GLSMTextureLifetime.setFramebufferSlot(slots, slot, 0);
|
||||
}
|
||||
RENDER_BACKEND.deleteFramebuffers(framebuffer);
|
||||
}
|
||||
|
||||
public static void glDeleteFramebuffers(IntBuffer framebuffers) {
|
||||
for (int i = framebuffers.position(); i < framebuffers.limit(); i++) glDeleteFramebuffers(framebuffers.get(i));
|
||||
}
|
||||
|
||||
public static void glDeleteFramebuffers(int[] framebuffers) {
|
||||
for (int framebuffer : framebuffers) glDeleteFramebuffers(framebuffer);
|
||||
}
|
||||
|
||||
public static int glGenFramebuffers() {
|
||||
requireContext("glGenFramebuffers");
|
||||
return RENDER_BACKEND.genFramebuffers();
|
||||
}
|
||||
|
||||
public static int glCheckFramebufferStatus(int target) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.checkFramebufferStatus(target);
|
||||
}
|
||||
|
||||
public static int glGetFramebufferAttachmentParameteri(int target, int attachment, int pname) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getFramebufferAttachmentParameteri(target, attachment, pname);
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(target, attachment, texture)) return;
|
||||
RENDER_BACKEND.framebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture1D(int target, int attachment, int textarget, int texture, int level) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(target, attachment, texture)) return;
|
||||
RENDER_BACKEND.framebufferTexture1D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture3D(int target, int attachment, int textarget, int texture, int level, int zoffset) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(target, attachment, texture)) return;
|
||||
RENDER_BACKEND.framebufferTexture3D(target, attachment, textarget, texture, level, zoffset);
|
||||
}
|
||||
|
||||
public static void glFramebufferTextureLayer(int target, int attachment, int texture, int level, int layer) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(target, attachment, texture)) return;
|
||||
RENDER_BACKEND.framebufferTextureLayer(target, attachment, texture, level, layer);
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture(int target, int attachment, int texture, int level) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(target, attachment, texture)) return;
|
||||
RENDER_BACKEND.framebufferTexture(target, attachment, texture, level);
|
||||
}
|
||||
|
||||
public static void glFramebufferTextureFace(int target, int attachment, int texture, int level, int face) {
|
||||
glFramebufferTexture2D(target, attachment, face, texture, level);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTexture(int framebuffer, int attachment, int texture, int level) {
|
||||
rejectExitCapture();
|
||||
if (!GLSMTextureLifetime.beginAttach(ctx(), framebuffer, attachment, texture)) return;
|
||||
RENDER_BACKEND.namedFramebufferTexture(framebuffer, attachment, texture, level);
|
||||
}
|
||||
|
||||
private static int swapDrawFramebuffer(int framebuffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int previous = glCtx.drawFramebuffer;
|
||||
glCtx.drawFramebuffer = framebuffer;
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
return previous;
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTextureLayer(int framebuffer, int attachment, int texture, int level, int layer) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferTextureLayer(GL30.GL_DRAW_FRAMEBUFFER, attachment, texture, level, layer);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferRenderbuffer(int framebuffer, int attachment, int renderbuffertarget, int renderbuffer) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferRenderbuffer(GL30.GL_DRAW_FRAMEBUFFER, attachment, renderbuffertarget, renderbuffer);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTexture1DEXT(int framebuffer, int attachment, int textarget, int texture, int level) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferTexture1D(GL30.GL_DRAW_FRAMEBUFFER, attachment, textarget, texture, level);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTexture2DEXT(int framebuffer, int attachment, int textarget, int texture, int level) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferTexture2D(GL30.GL_DRAW_FRAMEBUFFER, attachment, textarget, texture, level);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTexture3DEXT(int framebuffer, int attachment, int textarget, int texture, int level, int zoffset) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferTexture3D(GL30.GL_DRAW_FRAMEBUFFER, attachment, textarget, texture, level, zoffset);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTextureEXT(int framebuffer, int attachment, int texture, int level) {
|
||||
final int previous = swapDrawFramebuffer(framebuffer);
|
||||
glFramebufferTexture(GL30.GL_DRAW_FRAMEBUFFER, attachment, texture, level);
|
||||
swapDrawFramebuffer(previous);
|
||||
}
|
||||
|
||||
public static void glNamedFramebufferTextureFaceEXT(int framebuffer, int attachment, int texture, int level, int face) {
|
||||
glNamedFramebufferTexture2DEXT(framebuffer, attachment, face, texture, level);
|
||||
}
|
||||
|
||||
public static void attachTextureForReading(int attachment, int texture) {
|
||||
if (GLSMTextureLifetime.isDeferredDelete(texture)) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
GLSMTextureLifetime.trackFramebufferAttachment(glCtx, glCtx.readFramebuffer, attachment, texture);
|
||||
RENDER_BACKEND.framebufferTexture2D(GL30.GL_READ_FRAMEBUFFER, attachment, GL11.GL_TEXTURE_2D, texture, 0);
|
||||
}
|
||||
|
||||
public static int glGenRenderbuffers() {
|
||||
requireContext("glGenRenderbuffers");
|
||||
return RENDER_BACKEND.genRenderbuffers();
|
||||
}
|
||||
public static void glDeleteRenderbuffers(int renderbuffer) { rejectExitCapture(); RENDER_BACKEND.deleteRenderbuffers(renderbuffer); }
|
||||
public static void glBindRenderbuffer(int target, int renderbuffer) { rejectExitCapture(); RENDER_BACKEND.bindRenderbuffer(target, renderbuffer); }
|
||||
public static void glRenderbufferStorage(int target, int internalformat, int width, int height) { rejectExitCapture(); RENDER_BACKEND.renderbufferStorage(target, internalformat, width, height); }
|
||||
public static void glRenderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { rejectExitCapture(); RENDER_BACKEND.renderbufferStorageMultisample(target, samples, internalformat, width, height); }
|
||||
public static void glFramebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) {
|
||||
rejectExitCapture();
|
||||
GLSMTextureLifetime.beginAttach(target, attachment, 0);
|
||||
RENDER_BACKEND.framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
|
||||
}
|
||||
|
||||
public static void glBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
public static boolean isFramebufferEnabled() {
|
||||
return true;
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, long pixels) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, GLSMTextures.remapPixelTypeForGLES(format, type), pixels);
|
||||
}
|
||||
|
||||
public static ScreenCapture createScreenCapture(int width, int height) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.createScreenCapture(width, height);
|
||||
}
|
||||
|
||||
public static int getDrawFramebufferGeneration() {
|
||||
return ctx().drawFramebufferGeneration;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getDrawBuffer() {
|
||||
return ctx().drawBuffer;
|
||||
}
|
||||
|
||||
public static int getDrawFramebuffer() {
|
||||
return ctx().drawFramebuffer;
|
||||
}
|
||||
|
||||
public static int getReadFramebuffer() {
|
||||
return ctx().readFramebuffer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,781 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
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.MaterialStateStack;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
|
||||
public final class GLSMLighting {
|
||||
|
||||
private GLSMLighting() {}
|
||||
|
||||
public static void glGetMaterial(int face, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final MaterialStateStack state;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
state = glCtx.frontMaterial;
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
state = glCtx.backMaterial;
|
||||
} else {
|
||||
throw new RuntimeException("Invalid face parameter specified to glGetMaterial: " + face);
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_AMBIENT -> state.ambient.get(params.position(), params);
|
||||
case GL11.GL_DIFFUSE -> state.diffuse.get(params.position(), params);
|
||||
case GL11.GL_SPECULAR -> state.specular.get(params.position(), params);
|
||||
case GL11.GL_EMISSION -> state.emission.get(params.position(), params);
|
||||
case GL11.GL_SHININESS -> params.put(params.position(), state.shininess);
|
||||
case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(params.position(), params);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void putLightingInts(FloatBuffer values, int pname, boolean material, IntBuffer params) {
|
||||
final int count = switch (pname) {
|
||||
case GL11.GL_AMBIENT, GL11.GL_DIFFUSE, GL11.GL_SPECULAR -> 4;
|
||||
case GL11.GL_EMISSION -> material ? 4 : 0;
|
||||
case GL11.GL_COLOR_INDEXES -> material ? 3 : 0;
|
||||
case GL11.GL_SHININESS -> material ? 1 : 0;
|
||||
case GL11.GL_POSITION -> material ? 0 : 4;
|
||||
case GL11.GL_SPOT_DIRECTION -> material ? 0 : 3;
|
||||
case GL11.GL_SPOT_EXPONENT, GL11.GL_SPOT_CUTOFF, GL11.GL_CONSTANT_ATTENUATION, GL11.GL_LINEAR_ATTENUATION,
|
||||
GL11.GL_QUADRATIC_ATTENUATION -> material ? 0 : 1;
|
||||
default -> 0;
|
||||
};
|
||||
final boolean color = count == 4 && pname != GL11.GL_POSITION;
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float value = values.get(i);
|
||||
final int converted;
|
||||
if (color) {
|
||||
converted = LegacyQueries.floatToInt(value);
|
||||
} else if (material) {
|
||||
converted = LegacyQueries.lround(value);
|
||||
} else {
|
||||
converted = (int) value;
|
||||
}
|
||||
params.put(pos + i, converted);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetMaterial(int face, int pname, IntBuffer params) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
scratch.clear();
|
||||
glGetMaterial(face, pname, scratch);
|
||||
putLightingInts(scratch, pname, true, params);
|
||||
}
|
||||
|
||||
public static void glGetLight(int light, int pname, IntBuffer params) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
scratch.clear();
|
||||
glGetLight(light, pname, scratch);
|
||||
putLightingInts(scratch, pname, false, params);
|
||||
}
|
||||
|
||||
public static void glGetLight(int light, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final LightStateStack state = ctx().lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
case GL11.GL_AMBIENT -> state.ambient.get(params.position(), params);
|
||||
case GL11.GL_DIFFUSE -> state.diffuse.get(params.position(), params);
|
||||
case GL11.GL_SPECULAR -> state.specular.get(params.position(), params);
|
||||
case GL11.GL_POSITION -> state.position.get(params.position(), params);
|
||||
case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(params.position(), params);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(params.position(), state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(params.position(), state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(params.position(), state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(params.position(), state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(params.position(), state.quadraticAttenuation);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float i2f(int i) {return ((i - Integer.MIN_VALUE) & 0xFFFFFFFFL) / 4294967295.0F;}
|
||||
|
||||
public static void enableLighting() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_LIGHTING);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableLighting(ctx());
|
||||
}
|
||||
|
||||
static void enableLighting(GLContextState glCtx) {
|
||||
glCtx.lightingState.enable();
|
||||
}
|
||||
|
||||
public static void enableLight(int light) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_LIGHT0 + light);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableLight(ctx(), light);
|
||||
}
|
||||
|
||||
static void enableLight(GLContextState glCtx, int light) {
|
||||
if (light >= VertexKey.FFP_LIGHT_COUNT && (warnedFfpLightMask & (1 << light)) == 0 && ShaderManager.isEnabled()) {
|
||||
warnedFfpLightMask |= (1 << light);
|
||||
LOGGER.warn("GL_LIGHT{} enabled, but FFP shader emulation implements only GL_LIGHT0..{}", light, VertexKey.FFP_LIGHT_COUNT - 1, new Throwable("call site"));
|
||||
}
|
||||
glCtx.lightStates[light].enable();
|
||||
}
|
||||
|
||||
private static int warnedFfpLightMask = 0;
|
||||
|
||||
public static void enableColorMaterial() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_COLOR_MATERIAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableColorMaterial(ctx());
|
||||
}
|
||||
|
||||
static void enableColorMaterial(GLContextState glCtx) {
|
||||
glCtx.colorMaterial.enable();
|
||||
bakeCurrentColorIntoTrackedMaterial(glCtx);
|
||||
}
|
||||
|
||||
public static void disableColorMaterial() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_COLOR_MATERIAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableColorMaterial(ctx());
|
||||
}
|
||||
|
||||
static void disableColorMaterial(GLContextState glCtx) {
|
||||
bakeCurrentColorIntoTrackedMaterial(glCtx);
|
||||
glCtx.colorMaterial.disable();
|
||||
}
|
||||
|
||||
private static void bakeCurrentColorIntoTrackedMaterial(GLContextState glCtx) {
|
||||
glCtx.exitCapture.requireMaterialBakeInputs();
|
||||
final float r = glCtx.color.getRed();
|
||||
final float g = glCtx.color.getGreen();
|
||||
final float b = glCtx.color.getBlue();
|
||||
final float a = glCtx.color.getAlpha();
|
||||
final int face = glCtx.colorMaterialFace.getValue();
|
||||
final int param = glCtx.colorMaterialParameter.getValue();
|
||||
if (face == GL11.GL_FRONT || face == GL11.GL_FRONT_AND_BACK) {
|
||||
bakeMaterialColor(glCtx, glCtx.frontMaterial, param, r, g, b, a);
|
||||
}
|
||||
if (face == GL11.GL_BACK || face == GL11.GL_FRONT_AND_BACK) {
|
||||
bakeMaterialColor(glCtx, glCtx.backMaterial, param, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean materialVectorDiffers(Vector4f vec, float r, float g, float b, float a) {
|
||||
return vec.x != r || vec.y != g || vec.z != b || vec.w != a;
|
||||
}
|
||||
|
||||
private static void bakeMaterialColor(GLContextState glCtx, MaterialStateStack material, int param, float r, float g, float b, float a) {
|
||||
glCtx.exitCapture.material(material.face, param);
|
||||
final boolean changed = switch (param) {
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> materialVectorDiffers(material.ambient, r, g, b, a) || materialVectorDiffers(material.diffuse, r, g, b, a);
|
||||
case GL11.GL_AMBIENT -> materialVectorDiffers(material.ambient, r, g, b, a);
|
||||
case GL11.GL_DIFFUSE -> materialVectorDiffers(material.diffuse, r, g, b, a);
|
||||
case GL11.GL_SPECULAR -> materialVectorDiffers(material.specular, r, g, b, a);
|
||||
case GL11.GL_EMISSION -> materialVectorDiffers(material.emission, r, g, b, a);
|
||||
default -> false;
|
||||
};
|
||||
if (!changed) return;
|
||||
mod(material);
|
||||
switch (param) {
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
material.ambient.set(r, g, b, a);
|
||||
material.diffuse.set(r, g, b, a);
|
||||
}
|
||||
case GL11.GL_AMBIENT -> material.ambient.set(r, g, b, a);
|
||||
case GL11.GL_DIFFUSE -> material.diffuse.set(r, g, b, a);
|
||||
case GL11.GL_SPECULAR -> material.specular.set(r, g, b, a);
|
||||
case GL11.GL_EMISSION -> material.emission.set(r, g, b, a);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableLighting() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_LIGHTING);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableLighting(ctx());
|
||||
}
|
||||
|
||||
static void disableLighting(GLContextState glCtx) {
|
||||
glCtx.lightingState.disable();
|
||||
}
|
||||
|
||||
public static void disableLight(int light) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_LIGHT0 + light);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableLight(ctx(), light);
|
||||
}
|
||||
|
||||
static void disableLight(GLContextState glCtx, int light) {
|
||||
glCtx.lightStates[light].disable();
|
||||
}
|
||||
|
||||
public static void enableRescaleNormal() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL12.GL_RESCALE_NORMAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableRescaleNormal(ctx());
|
||||
}
|
||||
|
||||
static void enableRescaleNormal(GLContextState glCtx) {
|
||||
glCtx.rescaleNormalState.enable();
|
||||
}
|
||||
|
||||
public static void disableRescaleNormal() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL12.GL_RESCALE_NORMAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableRescaleNormal(ctx());
|
||||
}
|
||||
|
||||
static void disableRescaleNormal(GLContextState glCtx) {
|
||||
glCtx.rescaleNormalState.disable();
|
||||
}
|
||||
|
||||
private static boolean glMaterialFront(int pname, FloatBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
mod(glCtx.frontMaterial);
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> glCtx.frontMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> glCtx.frontMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> glCtx.frontMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> glCtx.frontMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> glCtx.frontMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
final boolean a = glCtx.frontMaterial.setAmbient(params);
|
||||
final boolean d = glCtx.frontMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> glCtx.frontMaterial.setColorIndexes(params);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean glMaterialBack(int pname, FloatBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
mod(glCtx.backMaterial);
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> glCtx.backMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> glCtx.backMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> glCtx.backMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> glCtx.backMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> glCtx.backMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
final boolean a = glCtx.backMaterial.setAmbient(params);
|
||||
final boolean d = glCtx.backMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> glCtx.backMaterial.setColorIndexes(params);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean glMaterialFront(int pname, IntBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
mod(glCtx.frontMaterial);
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> glCtx.frontMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> glCtx.frontMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> glCtx.frontMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> glCtx.frontMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> glCtx.frontMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
final boolean a = glCtx.frontMaterial.setAmbient(params);
|
||||
final boolean d = glCtx.frontMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> glCtx.frontMaterial.setColorIndexes(params);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean glMaterialBack(int pname, IntBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
mod(glCtx.backMaterial);
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> glCtx.backMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> glCtx.backMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> glCtx.backMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> glCtx.backMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> glCtx.backMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
final boolean a = glCtx.backMaterial.setAmbient(params);
|
||||
final boolean d = glCtx.backMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> glCtx.backMaterial.setColorIndexes(params);
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static void glMaterial(int face, int pname, FloatBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.material(face, pname);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordMaterial(face, pname, params);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
changed = glMaterialFront(pname, params);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
changed = glMaterialBack(pname, params);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
final boolean f = glMaterialFront(pname, params);
|
||||
final boolean b = glMaterialBack(pname, params);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMaterial(int face, int pname, IntBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.material(face, pname);
|
||||
// For IntBuffer version, we need to convert to float for recording
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
// Convert int params to float for recording
|
||||
final float[] floatParams = new float[params.remaining()];
|
||||
params.mark();
|
||||
for (int i = 0; i < floatParams.length; i++) {
|
||||
floatParams[i] = i2f(params.get());
|
||||
}
|
||||
params.reset();
|
||||
DisplayListManager.recordMaterial(face, pname, FloatBuffer.wrap(floatParams));
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
changed = glMaterialFront(pname, params);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
changed = glMaterialBack(pname, params);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
final boolean f = glMaterialFront(pname, params);
|
||||
final boolean b = glMaterialBack(pname, params);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMaterialf(int face, int pname, float val) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordMaterialf(face, pname, val);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pname != GL11.GL_SHININESS) {
|
||||
// it is only valid to call glMaterialf for the GL_SHININESS parameter
|
||||
return;
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
mod(glCtx.frontMaterial);
|
||||
changed = glCtx.frontMaterial.setShininess(val);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
mod(glCtx.backMaterial);
|
||||
changed = glCtx.backMaterial.setShininess(val);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
mod(glCtx.frontMaterial);
|
||||
mod(glCtx.backMaterial);
|
||||
final boolean f = glCtx.frontMaterial.setShininess(val);
|
||||
final boolean b = glCtx.backMaterial.setShininess(val);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMateriali(int face, int pname, int val) {
|
||||
// This will end up no-opping if pname != GL_SHININESS, it is invalid to call this with another pname
|
||||
// Command recording happens in glMaterialf
|
||||
glMaterialf(face, pname, (float) val);
|
||||
}
|
||||
|
||||
public static void glLight(int light, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLight(light, pname, params);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = glCtx.lightDataStates[light - GL11.GL_LIGHT0];
|
||||
mod(lightState);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_AMBIENT -> lightState.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> lightState.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> lightState.setSpecular(params);
|
||||
case GL11.GL_POSITION -> lightState.setPosition(params);
|
||||
case GL11.GL_SPOT_DIRECTION -> lightState.setSpotDirection(params);
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(params);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(params);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(params);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(params);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(params);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLight(int light, int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
// For IntBuffer version, we need to convert to float for recording
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
// Convert int params to float for recording
|
||||
final float[] floatParams = new float[params.remaining()];
|
||||
params.mark();
|
||||
for (int i = 0; i < floatParams.length; i++) {
|
||||
floatParams[i] = i2f(params.get());
|
||||
}
|
||||
params.reset();
|
||||
DisplayListManager.recordLight(light, pname, FloatBuffer.wrap(floatParams));
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = glCtx.lightDataStates[light - GL11.GL_LIGHT0];
|
||||
mod(lightState);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_AMBIENT -> lightState.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> lightState.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> lightState.setSpecular(params);
|
||||
case GL11.GL_POSITION -> lightState.setPosition(params);
|
||||
case GL11.GL_SPOT_DIRECTION -> lightState.setSpotDirection(params);
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(params);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(params);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(params);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(params);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(params);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightf(int light, int pname, float param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLightf(light, pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = glCtx.lightDataStates[light - GL11.GL_LIGHT0];
|
||||
mod(lightState);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(param);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(param);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(param);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(param);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(param);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLighti(int light, int pname, int param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLighti(light, pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = glCtx.lightDataStates[light - GL11.GL_LIGHT0];
|
||||
mod(lightState);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(param);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(param);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(param);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(param);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(param);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModel(int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLightModel(pname, params);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mod(glCtx.lightModel);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> glCtx.lightModel.setAmbient(params);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.setLocalViewer(params);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.setTwoSide(params);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModel(int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
// For IntBuffer version, we need to convert to float for recording
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
// Convert int params to float for recording
|
||||
final float[] floatParams = new float[params.remaining()];
|
||||
params.mark();
|
||||
for (int i = 0; i < floatParams.length; i++) {
|
||||
floatParams[i] = i2f(params.get());
|
||||
}
|
||||
params.reset();
|
||||
DisplayListManager.recordLightModel(pname, FloatBuffer.wrap(floatParams));
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mod(glCtx.lightModel);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> glCtx.lightModel.setAmbient(params);
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> glCtx.lightModel.setColorControl(params);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.setLocalViewer(params);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.setTwoSide(params);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModelf(int pname, float param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLightModelf(pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCachingEnabled()) {
|
||||
mod(glCtx.lightModel);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.setLocalViewer(param);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.setTwoSide(param);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLightModeli(int pname, int param) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLightModeli(pname, param);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCachingEnabled()) {
|
||||
mod(glCtx.lightModel);
|
||||
final boolean changed = switch (pname) {
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> glCtx.lightModel.setColorControl(param);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.setLocalViewer(param);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.setTwoSide(param);
|
||||
default -> false;
|
||||
};
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glColorMaterial(int face, int mode) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.COLOR_MATERIAL_SETTINGS);
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordColorMaterial(face, mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.colorMaterialFace.getValue() != face || glCtx.colorMaterialParameter.getValue() != mode) {
|
||||
mod(glCtx.colorMaterialFace).setValue(face);
|
||||
mod(glCtx.colorMaterialParameter).setValue(mode);
|
||||
glCtx.lightingGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getLightingGeneration() {
|
||||
return ctx().lightingGeneration;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getLightingState() {
|
||||
return ctx().lightingState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getRescaleNormalState() {
|
||||
return ctx().rescaleNormalState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getNormalizeState() {
|
||||
return ctx().normalizeState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack[] getLightStates() {
|
||||
return ctx().lightStates;
|
||||
}
|
||||
|
||||
public static LightStateStack[] getLightDataStates() {
|
||||
return ctx().lightDataStates;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getColorMaterial() {
|
||||
return ctx().colorMaterial;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getColorMaterialFace() {
|
||||
return ctx().colorMaterialFace;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getColorMaterialParameter() {
|
||||
return ctx().colorMaterialParameter;
|
||||
}
|
||||
|
||||
public static LightModelStateStack getLightModel() {
|
||||
return ctx().lightModel;
|
||||
}
|
||||
|
||||
public static MaterialStateStack getFrontMaterial() {
|
||||
return ctx().frontMaterial;
|
||||
}
|
||||
|
||||
public static MaterialStateStack getBackMaterial() {
|
||||
return ctx().backMaterial;
|
||||
}
|
||||
|
||||
public static void captureLighting(LightingSnapshot out) {
|
||||
final GLContextState glCtx = ctx();
|
||||
for (int i = 0; i < out.lights.length; i++) {
|
||||
out.lightEnabled[i] = glCtx.lightStates[i].isEnabled();
|
||||
out.lights[i].set(glCtx.lightDataStates[i]);
|
||||
}
|
||||
out.lightModel.set(glCtx.lightModel);
|
||||
out.frontMaterial.set(glCtx.frontMaterial);
|
||||
out.backMaterial.set(glCtx.backMaterial);
|
||||
out.colorMaterial = glCtx.colorMaterial.isEnabled();
|
||||
out.colorMaterialFace = glCtx.colorMaterialFace.getValue();
|
||||
out.colorMaterialParameter = glCtx.colorMaterialParameter.getValue();
|
||||
out.normalize = glCtx.normalizeState.isEnabled();
|
||||
out.rescaleNormal = glCtx.rescaleNormalState.isEnabled();
|
||||
}
|
||||
|
||||
public static void applyLighting(LightingSnapshot in) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < in.lights.length; i++) {
|
||||
glCtx.lightStates[i].setEnabled(in.lightEnabled[i]);
|
||||
if (!glCtx.lightDataStates[i].sameAs(in.lights[i])) {
|
||||
mod(glCtx.lightDataStates[i]).set(in.lights[i]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!glCtx.lightModel.sameAs(in.lightModel)) {
|
||||
mod(glCtx.lightModel).set(in.lightModel);
|
||||
changed = true;
|
||||
}
|
||||
if (!glCtx.frontMaterial.sameAs(in.frontMaterial)) {
|
||||
mod(glCtx.frontMaterial).set(in.frontMaterial);
|
||||
changed = true;
|
||||
}
|
||||
if (!glCtx.backMaterial.sameAs(in.backMaterial)) {
|
||||
mod(glCtx.backMaterial).set(in.backMaterial);
|
||||
changed = true;
|
||||
}
|
||||
if (glCtx.colorMaterialFace.getValue() != in.colorMaterialFace || glCtx.colorMaterialParameter.getValue() != in.colorMaterialParameter) {
|
||||
mod(glCtx.colorMaterialFace).setValue(in.colorMaterialFace);
|
||||
mod(glCtx.colorMaterialParameter).setValue(in.colorMaterialParameter);
|
||||
changed = true;
|
||||
}
|
||||
// Direct set: glEnable(GL_COLOR_MATERIAL) would bake the current color over the captured material.
|
||||
glCtx.colorMaterial.setEnabled(in.colorMaterial);
|
||||
glCtx.normalizeState.setEnabled(in.normalize);
|
||||
glCtx.rescaleNormalState.setEnabled(in.rescaleNormal);
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import lombok.SneakyThrows;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.glu.GLU;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.initConfig;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
|
||||
public final class GLSMMatrices {
|
||||
|
||||
private GLSMMatrices() {}
|
||||
|
||||
private static void beforeMatrixChange(GLContextState c) {
|
||||
beforeMatrixChange(c, false);
|
||||
}
|
||||
|
||||
private static void beforeMatrixChange(GLContextState c, boolean absolute) {
|
||||
c.exitCapture.matrixChange(absolute);
|
||||
if (isUncapturedMatrix(c)) beforeUncapturedStateChange();
|
||||
}
|
||||
|
||||
private static boolean isUncapturedMatrix(GLContextState c) {
|
||||
final int mode = c.matrixMode.getMode();
|
||||
return mode == GL11.GL_PROJECTION || (mode == GL11.GL_TEXTURE && c.activeTextureUnit.getValue() != 0);
|
||||
}
|
||||
|
||||
private static final MethodHandle MAT4_STACK_CURR_DEPTH;
|
||||
|
||||
static {
|
||||
try {
|
||||
final Field curr = Matrix4fStack.class.getDeclaredField("curr");
|
||||
curr.setAccessible(true);
|
||||
MAT4_STACK_CURR_DEPTH = MethodHandles.lookup().unreflectGetter(curr);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static int getMatrixStackDepth(Matrix4fStack stack) {
|
||||
// JOML's curr is 0-based (0 = no pushes), but OpenGL starts at 1 (base matrix counts)
|
||||
return (int) MAT4_STACK_CURR_DEPTH.invokeExact(stack) + 1;
|
||||
}
|
||||
|
||||
public static void glMatrixMode(int mode) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.MATRIX_MODE);
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordMatrixMode(mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mod(glCtx.matrixMode).setMode(mode);
|
||||
}
|
||||
|
||||
public static void glLoadMatrix(FloatBuffer m) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx, true);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
final Matrix4f matrix = new Matrix4f().set(m);
|
||||
m.rewind();
|
||||
DisplayListManager.recordLoadMatrix(matrix);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).set(m);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLoadMatrix(DoubleBuffer m) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx, true);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
// Convert double buffer to float buffer for recording
|
||||
glCtx.conversionMatrix4d.set(m);
|
||||
m.rewind();
|
||||
final Matrix4f floatMatrix = new Matrix4f();
|
||||
floatMatrix.set(glCtx.conversionMatrix4d);
|
||||
DisplayListManager.recordLoadMatrix(floatMatrix);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
glCtx.conversionMatrix4d.set(m);
|
||||
matrixStack(glCtx).set(glCtx.conversionMatrix4d);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setModelViewMatrix(Matrix4fc m) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.matrixChange(GL11.GL_MODELVIEW, 0, true);
|
||||
if (isCachingEnabled()) {
|
||||
glCtx.modelViewMatrix.set(m);
|
||||
glCtx.mvGeneration++;
|
||||
glCtx.mvLinearGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTextureMatrix(int unit, Matrix4fc m) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.matrixChange(GL11.GL_TEXTURE, unit, true);
|
||||
if (unit != 0) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) {
|
||||
glCtx.textures.getTextureUnitMatrix(unit).set(m);
|
||||
glCtx.texMatrixGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setProjectionMatrix(Matrix4fc m) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (isCachingEnabled()) {
|
||||
glCtx.projectionMatrix.set(m);
|
||||
glCtx.projGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static Matrix4fStack getMatrixStack() {
|
||||
return matrixStack(ctx());
|
||||
}
|
||||
|
||||
private static Matrix4fStack matrixStack(GLContextState glCtx) {
|
||||
switch (glCtx.matrixMode.getMode()) {
|
||||
case GL11.GL_MODELVIEW -> {
|
||||
return glCtx.modelViewMatrix;
|
||||
}
|
||||
case GL11.GL_PROJECTION -> {
|
||||
return glCtx.projectionMatrix;
|
||||
}
|
||||
case GL11.GL_TEXTURE -> {
|
||||
return glCtx.textures.getTextureUnitMatrix(glCtx.activeTextureUnit.getValue());
|
||||
}
|
||||
default -> throw new IllegalStateException("Unknown matrix mode: " + glCtx.matrixMode.getMode());
|
||||
}
|
||||
}
|
||||
|
||||
/** Bump the generation counter for the currently active matrix mode. */
|
||||
private static void bumpMatrixGeneration(GLContextState glCtx) {
|
||||
bumpMatrixGeneration(glCtx, true);
|
||||
}
|
||||
|
||||
private static void bumpMatrixGeneration(GLContextState glCtx, boolean linearChanged) {
|
||||
switch (glCtx.matrixMode.getMode()) {
|
||||
case GL11.GL_MODELVIEW -> {
|
||||
glCtx.mvGeneration++;
|
||||
if (linearChanged) glCtx.mvLinearGeneration++;
|
||||
}
|
||||
case GL11.GL_PROJECTION -> glCtx.projGeneration++;
|
||||
case GL11.GL_TEXTURE -> glCtx.texMatrixGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLoadIdentity() {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx, true);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLoadIdentity();
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).identity();
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTranslatef(float x, float y, float z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation(x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).translate(x, y, z);
|
||||
bumpMatrixGeneration(glCtx, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTranslated(double x, double y, double z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation((float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).translate((float) x, (float) y, (float) z);
|
||||
bumpMatrixGeneration(glCtx, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glScalef(float x, float y, float z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale(x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).scale(x, y, z);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glScaled(double x, double y, double z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale((float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).scale((float) x, (float) y, (float) z);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glMultMatrix(FloatBuffer floatBuffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
glCtx.multMatrix.set(floatBuffer);
|
||||
final int currentMode = glCtx.matrixMode.getMode();
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransform(glCtx.multMatrix);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).mul(glCtx.multMatrix);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyMultMatrix(Matrix4f multMatrix) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).mul(multMatrix);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glMultMatrix(DoubleBuffer matrix) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
glCtx.conversionMatrix4d.set(matrix);
|
||||
glCtx.conversionMatrix4f.set(glCtx.conversionMatrix4d);
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransform(glCtx.conversionMatrix4f);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).mul(glCtx.conversionMatrix4f);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glRotatef(float angle, float x, float y, float z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
final float lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0f) return;
|
||||
|
||||
// Fast way to normalize rotation
|
||||
final float scalar = 1.0f / (float) Math.sqrt(lenSq);
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
// Convert deg to rad
|
||||
angle = (float) Math.toRadians(angle);
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixRotation(angle, x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).rotate(angle, x, y, z);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glRotated(double angle, double x, double y, double z) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
final double lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0) return;
|
||||
|
||||
// Fast way to normalize rotation
|
||||
final double scalar = 1.0 / Math.sqrt(lenSq);
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
// Convert deg to rad
|
||||
angle = Math.toRadians(angle);
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixRotation((float) angle, (float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).rotate((float) angle, (float) x, (float) y, (float) z);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformOrtho(left, right, bottom, top, zNear, zFar);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).ortho((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
final GLContextState glCtx = ctx();
|
||||
beforeMatrixChange(glCtx);
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformFrustum(left, right, bottom, top, zNear, zFar);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
matrixStack(glCtx).frustum((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
bumpMatrixGeneration(glCtx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPushMatrix() {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.pushMatrix();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPushMatrix(); // Handles flush + relativeTransform stack
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Only track stack on main thread (splash thread has separate GL context)
|
||||
if (isCachingEnabled()) {
|
||||
try {
|
||||
matrixStack(glCtx).pushMatrix();
|
||||
} catch (IllegalStateException ignored) {
|
||||
glCtx.exitCapture.reject();
|
||||
if (initConfig != null && initConfig.isLwjglDebug()) LOGGER.warn("Matrix stack overflow ", new Throwable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPopMatrix() {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.popMatrix();
|
||||
if (isUncapturedMatrix(glCtx)) beforeUncapturedStateChange();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPopMatrix(); // Handles flush + relativeTransform stack + lastRecordedTransform sync
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Only track stack on main thread (splash thread has separate GL context)
|
||||
if (isCachingEnabled()) {
|
||||
try {
|
||||
matrixStack(glCtx).popMatrix();
|
||||
bumpMatrixGeneration(glCtx);
|
||||
} catch (IllegalStateException ignored) {
|
||||
glCtx.exitCapture.reject();
|
||||
if (initConfig != null && initConfig.isLwjglDebug()) LOGGER.warn("Matrix stack underflow ", new Throwable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void gluPerspective(float fovy, float aspect, float zNear, float zFar) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.gluMatrix.identity().perspective((float) Math.toRadians(fovy), aspect, zNear, zFar);
|
||||
glCtx.gluMatrix.get(0, glCtx.gluBuffer);
|
||||
glMultMatrix(glCtx.gluBuffer);
|
||||
}
|
||||
|
||||
public static void gluLookAt(float eyex, float eyey, float eyez, float centerx, float centery, float centerz, float upx, float upy, float upz) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.gluMatrix.identity().lookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);
|
||||
glCtx.gluMatrix.get(0, glCtx.gluBuffer);
|
||||
glMultMatrix(glCtx.gluBuffer);
|
||||
}
|
||||
|
||||
public static void gluOrtho2D(float left, float right, float bottom, float top) {
|
||||
glOrtho(left, right, bottom, top, -1.0, 1.0);
|
||||
}
|
||||
|
||||
public static void gluPickMatrix(float x, float y, float deltaX, float deltaY, IntBuffer viewport) {
|
||||
rejectExitCapture();
|
||||
if (deltaX <= 0 || deltaY <= 0) return;
|
||||
glTranslatef(
|
||||
(viewport.get(viewport.position() + 2) - 2 * (x - viewport.get(viewport.position() + 0))) / deltaX,
|
||||
(viewport.get(viewport.position() + 3) - 2 * (y - viewport.get(viewport.position() + 1))) / deltaY,
|
||||
0);
|
||||
glScalef(viewport.get(viewport.position() + 2) / deltaX, viewport.get(viewport.position() + 3) / deltaY, 1.0f);
|
||||
}
|
||||
|
||||
public static int gluBuild2DMipmaps(int target, int components, int width, int height, int format, int type, ByteBuffer data) {
|
||||
if (width < 1 || height < 1) return GLU.GLU_INVALID_VALUE;
|
||||
GLSMTextures.glTexImage2D(target, 0, components, width, height, 0, format, type, data);
|
||||
GLSMTextures.glGenerateMipmap(target);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static String gluErrorString(int errorCode) {
|
||||
return switch (errorCode) {
|
||||
case GLU.GLU_INVALID_ENUM -> "Invalid enum (glu)";
|
||||
case GLU.GLU_INVALID_VALUE -> "Invalid value (glu)";
|
||||
case GLU.GLU_OUT_OF_MEMORY -> "Out of memory (glu)";
|
||||
case GL11.GL_NO_ERROR -> "No error";
|
||||
case GL11.GL_INVALID_ENUM -> "Invalid enum";
|
||||
case GL11.GL_INVALID_VALUE -> "Invalid value";
|
||||
case GL11.GL_INVALID_OPERATION -> "Invalid operation";
|
||||
case GL11.GL_OUT_OF_MEMORY -> "Out of memory";
|
||||
default -> "Unknown error: " + errorCode;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean gluUnProject(float winx, float winy, float winz, FloatBuffer modelMatrix, FloatBuffer projMatrix, IntBuffer viewport, FloatBuffer obj_pos) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.gluMatrix.set(projMatrix).mul(glCtx.gluMatrix2.set(modelMatrix));
|
||||
if (Math.abs(glCtx.gluMatrix.determinant()) < 1.0e-10f) return false;
|
||||
glCtx.gluMatrix.invert(glCtx.gluMatrix);
|
||||
|
||||
float ndcX = (winx - viewport.get(viewport.position())) / viewport.get(viewport.position() + 2) * 2.0f - 1.0f;
|
||||
float ndcY = (winy - viewport.get(viewport.position() + 1)) / viewport.get(viewport.position() + 3) * 2.0f - 1.0f;
|
||||
float ndcZ = winz * 2.0f - 1.0f;
|
||||
|
||||
glCtx.gluVec4.set(ndcX, ndcY, ndcZ, 1.0f);
|
||||
glCtx.gluMatrix.transform(glCtx.gluVec4);
|
||||
if (glCtx.gluVec4.w == 0.0f) return false;
|
||||
|
||||
float invW = 1.0f / glCtx.gluVec4.w;
|
||||
obj_pos.put(obj_pos.position(), glCtx.gluVec4.x * invW);
|
||||
obj_pos.put(obj_pos.position() + 1, glCtx.gluVec4.y * invW);
|
||||
obj_pos.put(obj_pos.position() + 2, glCtx.gluVec4.z * invW);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getMvGeneration() {
|
||||
return ctx().mvGeneration;
|
||||
}
|
||||
|
||||
public static int getMvLinearGeneration() {
|
||||
return ctx().mvLinearGeneration;
|
||||
}
|
||||
|
||||
public static int getProjGeneration() {
|
||||
return ctx().projGeneration;
|
||||
}
|
||||
|
||||
public static int getTexMatrixGeneration() {
|
||||
return ctx().texMatrixGeneration;
|
||||
}
|
||||
|
||||
public static MatrixModeStack getMatrixMode() {
|
||||
return ctx().matrixMode;
|
||||
}
|
||||
|
||||
public static Matrix4fStack getModelViewMatrix() {
|
||||
return ctx().modelViewMatrix;
|
||||
}
|
||||
|
||||
public static Matrix4fStack getProjectionMatrix() {
|
||||
return ctx().projectionMatrix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,613 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.Pointer;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ProgramBinaryCache;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import org.lwjgl.opengl.ARBShaderObjects;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.initConfig;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.requireContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.warnOnce;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMPrograms {
|
||||
|
||||
private GLSMPrograms() {}
|
||||
|
||||
public static long programSwitches;
|
||||
|
||||
private static final String TEXTURE = "texture";
|
||||
private static final String TEXTURE_RENAMED = "gtexture";
|
||||
public static void setFfpInstancing(Instancing mode) {
|
||||
ctx().ffpInstancing = mode;
|
||||
}
|
||||
|
||||
public static void glUseProgram(int program) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordUseProgram(program);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final ShaderManager ffp = glCtx.ffp;
|
||||
if (program == 0 && ffp.isEnabled()) {
|
||||
final int prev = glCtx.activeProgram;
|
||||
final boolean changed = prev != 0;
|
||||
if (changed) glCtx.programGeneration++;
|
||||
glCtx.activeProgram = 0; // Track that FFP was requested
|
||||
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = 0;
|
||||
GLSMHooks.programChangeEvent.postBind = false;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
ffp.activate();
|
||||
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = 0;
|
||||
GLSMHooks.programChangeEvent.postBind = true;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-zero program or FFP emulation not enabled
|
||||
if (program == 0) {
|
||||
warnOnce("useprogram-zero-ffp-disabled",
|
||||
"[FFP] glUseProgram(0) reached the driver because emulation is disabled");
|
||||
}
|
||||
if (ffp.isActive()) {
|
||||
ffp.deactivate();
|
||||
}
|
||||
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || program != glCtx.activeProgram) {
|
||||
if (Tracy.ENABLED) programSwitches++;
|
||||
glCtx.programGeneration++;
|
||||
final int prev = glCtx.activeProgram;
|
||||
glCtx.activeProgram = program;
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = program;
|
||||
GLSMHooks.programChangeEvent.postBind = false;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
if (initConfig != null && initConfig.isLwjglDebug()) {
|
||||
final String programName = GLDebug.getObjectLabel(KHRDebug.GL_PROGRAM, program);
|
||||
GLDebug.debugMessage("Activating Program - " + program + ":" + programName);
|
||||
}
|
||||
RENDER_BACKEND.useProgram(program);
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = program;
|
||||
GLSMHooks.programChangeEvent.postBind = true;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glShaderSource(int shader, CharSequence source) {
|
||||
rejectExitCapture();
|
||||
String src = source.toString();
|
||||
// Always rename reserved words for the target GLSL version (e.g. 'sampler' at 460)
|
||||
src = GlslTransformUtils.renameReservedWords(src, RENDER_BACKEND.getMinGLSLVersion());
|
||||
if (ShaderManager.isEnabled()) {
|
||||
final int shaderType = RENDER_BACKEND.getShaderi(shader, GL20.GL_SHADER_TYPE);
|
||||
src = CompatShaderTransformer.transform(src, shaderType, shaderType == GL20.GL_FRAGMENT_SHADER);
|
||||
}
|
||||
RENDER_BACKEND.shaderSource(shader, src);
|
||||
}
|
||||
|
||||
public static void nglShaderSource(int shader, int count, long strings, long lengths) {
|
||||
rejectExitCapture();
|
||||
if (count <= 0) return;
|
||||
final CharSequence source;
|
||||
if (count == 1) {
|
||||
source = decodeShaderSourceString(strings, lengths, 0);
|
||||
} else {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) sb.append(decodeShaderSourceString(strings, lengths, i));
|
||||
source = sb.toString();
|
||||
}
|
||||
glShaderSource(shader, source);
|
||||
}
|
||||
|
||||
private static String decodeShaderSourceString(long strings, long lengths, int i) {
|
||||
final long strAddr = MemoryUtilities.memGetAddress(strings + (long) i * Pointer.POINTER_SIZE);
|
||||
final int len = lengths == MemoryUtilities.NULL ? -1 : MemoryUtilities.memGetInt(lengths + (long) i * 4);
|
||||
return len < 0 ? MemoryUtilities.memUTF8(strAddr) : MemoryUtilities.memUTF8(strAddr, len);
|
||||
}
|
||||
|
||||
public static void glShaderSource(int shader, ByteBuffer source) {
|
||||
final byte[] bytes = new byte[source.remaining()];
|
||||
final int pos = source.position();
|
||||
source.get(bytes);
|
||||
source.position(pos);
|
||||
glShaderSource(shader, new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static void glShaderSource(int shader, CharSequence[] sources) {
|
||||
int totalLen = 0;
|
||||
for (CharSequence s : sources) totalLen += s.length();
|
||||
final StringBuilder sb = new StringBuilder(totalLen);
|
||||
for (CharSequence s : sources) sb.append(s);
|
||||
glShaderSource(shader, sb.toString());
|
||||
}
|
||||
|
||||
public static void glSpecializeShader(int shader, CharSequence entryPoint, IntBuffer constantIndex, IntBuffer constantValue) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.specializeShader(shader, entryPoint, constantIndex, constantValue);
|
||||
}
|
||||
|
||||
public static int glGetUniformBlockIndex(int program, CharSequence name) { rejectExitCapture(); return RENDER_BACKEND.getUniformBlockIndex(program, name); }
|
||||
public static void glUniformBlockBinding(int program, int blockIndex, int binding) { rejectExitCapture(); RENDER_BACKEND.uniformBlockBinding(program, blockIndex, binding); }
|
||||
public static void glBindFragDataLocation(int program, int colorNumber, CharSequence name) { rejectExitCapture(); RENDER_BACKEND.bindFragDataLocation(program, colorNumber, name); }
|
||||
public static void glUniform3(int location, float[] values) { rejectExitCapture(); RENDER_BACKEND.uniform3fv(location, values); }
|
||||
public static void glUniform4(int location, float[] values) { rejectExitCapture(); RENDER_BACKEND.uniform4fv(location, values); }
|
||||
|
||||
public static void glLinkProgram(int program) {
|
||||
rejectExitCapture();
|
||||
if (ShaderManager.isEnabled()) {
|
||||
generateVertexShaderIfNeeded(program);
|
||||
}
|
||||
if (ProgramBinaryCache.isEnabled()) {
|
||||
ProgramBinaryCache.markRetrievable(program);
|
||||
}
|
||||
RENDER_BACKEND.linkProgram(program);
|
||||
if (ShaderManager.isEnabled() && RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
LOGGER.warn("Program {} failed to link: {}", program, RENDER_BACKEND.getProgramInfoLog(program));
|
||||
}
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
|
||||
public static void glProgramParameteri(int program, int pname, int value) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.programParameteri(program, pname, value);
|
||||
}
|
||||
|
||||
public static void glGetProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getProgramBinary(program, length, binaryFormat, binary);
|
||||
}
|
||||
|
||||
public static void glProgramBinary(int program, int binaryFormat, ByteBuffer binary) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.programBinary(program, binaryFormat, binary);
|
||||
if (RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_TRUE) {
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a program has a fragment shader but no vertex shader, generate a passthrough vertex shader from the fragment's in declarations and attach it.
|
||||
*/
|
||||
private static void generateVertexShaderIfNeeded(int program) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int shaderCount = RENDER_BACKEND.getProgrami(program, GL20.GL_ATTACHED_SHADERS);
|
||||
if (shaderCount == 0 || shaderCount > glCtx.shaderBuf.capacity()) return;
|
||||
|
||||
glCtx.shaderBuf.clear().limit(shaderCount);
|
||||
RENDER_BACKEND.getAttachedShaders(program, null, glCtx.shaderBuf);
|
||||
|
||||
int fragmentShader = 0;
|
||||
boolean hasVertex = false;
|
||||
for (int i = 0; i < shaderCount; i++) {
|
||||
final int shader = glCtx.shaderBuf.get(i);
|
||||
final int type = RENDER_BACKEND.getShaderi(shader, GL20.GL_SHADER_TYPE);
|
||||
if (type == GL20.GL_VERTEX_SHADER) {
|
||||
hasVertex = true;
|
||||
break;
|
||||
} else if (type == GL20.GL_FRAGMENT_SHADER) {
|
||||
fragmentShader = shader;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasVertex || fragmentShader == 0) return;
|
||||
|
||||
// Fragment-only program - generate a passthrough vertex shader
|
||||
final String fragSource = RENDER_BACKEND.getShaderSource(fragmentShader, 65536);
|
||||
String vertSource = CompatShaderTransformer.generatePassthroughVertexShader(fragSource);
|
||||
if (RenderSystem.isGLES()) {
|
||||
vertSource = CompatShaderTransformer.toGLES(vertSource, GL20.GL_VERTEX_SHADER, false);
|
||||
}
|
||||
|
||||
final int vertShader = RENDER_BACKEND.createShader(GL20.GL_VERTEX_SHADER);
|
||||
RENDER_BACKEND.shaderSource(vertShader, vertSource);
|
||||
RENDER_BACKEND.compileShader(vertShader);
|
||||
|
||||
if (RENDER_BACKEND.getShaderi(vertShader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||
final String log = RENDER_BACKEND.getShaderInfoLog(vertShader, 8192);
|
||||
LOGGER.warn("CompatShaderTransformer: Generated passthrough vertex shader failed to compile:\n{}\nSource:\n{}", log, vertSource);
|
||||
RENDER_BACKEND.deleteShader(vertShader);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug("CompatShaderTransformer: Generated passthrough vertex shader for fragment-only program {}", program);
|
||||
RENDER_BACKEND.attachShader(program, vertShader);
|
||||
RENDER_BACKEND.deleteShader(vertShader);
|
||||
}
|
||||
|
||||
@Getter
|
||||
static long programLifetimeGeneration;
|
||||
private static final IntOpenHashSet programsPendingDeletion = new IntOpenHashSet();
|
||||
|
||||
public static boolean isProgramPendingDeletion(int program) {
|
||||
return programsPendingDeletion.contains(program);
|
||||
}
|
||||
|
||||
public static void glDeleteProgram(int program) {
|
||||
rejectExitCapture();
|
||||
if (program == 0) return;
|
||||
CompatUniformManager.onDeleteProgram(program);
|
||||
|
||||
// GL defers deletion of a bound program until unbind; activeProgram stays set so the
|
||||
// cache matches GL_CURRENT_PROGRAM and the next glUseProgram is not skipped as redundant.
|
||||
if (GLSMHooks.PROGRAM_DELETE.hasListeners()) {
|
||||
GLSMHooks.programDeleteEvent.program = program;
|
||||
GLSMHooks.PROGRAM_DELETE.post(GLSMHooks.programDeleteEvent);
|
||||
}
|
||||
RENDER_BACKEND.deleteProgram(program);
|
||||
programsPendingDeletion.add(program);
|
||||
programsPendingDeletion.removeIf((int id) -> !RENDER_BACKEND.isProgram(id));
|
||||
programLifetimeGeneration++;
|
||||
}
|
||||
|
||||
public static int glCreateShader(int type) {
|
||||
requireContext("glCreateShader");
|
||||
return RENDER_BACKEND.createShader(type);
|
||||
}
|
||||
|
||||
public static void glCompileShader(int shader) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.compileShader(shader);
|
||||
}
|
||||
|
||||
public static int glCreateProgram() {
|
||||
requireContext("glCreateProgram");
|
||||
final int program = RENDER_BACKEND.createProgram();
|
||||
programsPendingDeletion.remove(program);
|
||||
programLifetimeGeneration++;
|
||||
return program;
|
||||
}
|
||||
|
||||
public static void glAttachShader(int program, int shader) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.attachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void glDetachShader(int program, int shader) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.detachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void glDeleteShader(int shader) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.deleteShader(shader);
|
||||
}
|
||||
|
||||
public static void glBindAttribLocation(int program, int index, CharSequence name) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.bindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
public static void glValidateProgram(int program) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.validateProgram(program);
|
||||
}
|
||||
|
||||
public static int glGetShaderi(int shader, int pname) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderi(shader, pname);
|
||||
}
|
||||
|
||||
public static String glGetShaderInfoLog(int shader, int maxLength) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader, maxLength);
|
||||
}
|
||||
|
||||
public static String glGetShaderInfoLog(int shader) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader);
|
||||
}
|
||||
|
||||
public static int glGetProgrami(int program, int pname) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgrami(program, pname);
|
||||
}
|
||||
|
||||
public static String glGetProgramInfoLog(int program, int maxLength) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgramInfoLog(program, maxLength);
|
||||
}
|
||||
|
||||
public static String glGetProgramInfoLog(int program) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgramInfoLog(program);
|
||||
}
|
||||
|
||||
public static boolean glIsProgram(int obj) {
|
||||
rejectExitCapture();
|
||||
final boolean valid = RENDER_BACKEND.isProgram(obj);
|
||||
if (!valid) programsPendingDeletion.remove(obj);
|
||||
return valid;
|
||||
}
|
||||
|
||||
public static boolean glIsShader(int obj) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.isShader(obj);
|
||||
}
|
||||
public static void glDispatchCompute(int x, int y, int z) { rejectExitCapture(); RENDER_BACKEND.dispatchCompute(x, y, z); }
|
||||
|
||||
public static int glGetUniformLocation(int program, CharSequence name) {
|
||||
rejectExitCapture();
|
||||
int loc = RENDER_BACKEND.getUniformLocation(program, name);
|
||||
if (loc == -1 && TEXTURE.contentEquals(name)) {
|
||||
loc = RENDER_BACKEND.getUniformLocation(program, TEXTURE_RENAMED);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
public static int glGetUniformLocation(int program, ByteBuffer name) {
|
||||
rejectExitCapture();
|
||||
int loc = RENDER_BACKEND.getUniformLocation(program, name);
|
||||
if (loc == -1 && isTextureBuffer(name)) {
|
||||
loc = RENDER_BACKEND.getUniformLocation(program, TEXTURE_RENAMED);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
public static int glGetAttribLocation(int program, CharSequence name) {
|
||||
rejectExitCapture();
|
||||
int loc = RENDER_BACKEND.getAttribLocation(program, name);
|
||||
if (loc == -1 && TEXTURE.contentEquals(name)) {
|
||||
loc = RENDER_BACKEND.getAttribLocation(program, TEXTURE_RENAMED);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
public static int glGetAttribLocation(int program, ByteBuffer name) {
|
||||
rejectExitCapture();
|
||||
int loc = RENDER_BACKEND.getAttribLocation(program, name);
|
||||
if (loc == -1 && isTextureBuffer(name)) {
|
||||
loc = RENDER_BACKEND.getAttribLocation(program, TEXTURE_RENAMED);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private static final ByteBuffer TEXTURE_BYTES = ByteBuffer.wrap(TEXTURE.getBytes(StandardCharsets.US_ASCII));
|
||||
|
||||
private static boolean isTextureBuffer(ByteBuffer buf) {
|
||||
final int len = buf.remaining();
|
||||
if (len != 7 && len != 8) return false;
|
||||
// I believe the driver accepts null terminated or not
|
||||
if (len == 8 && buf.get(buf.position() + 7) != 0) return false;
|
||||
return buf.slice(buf.position(), 7).equals(TEXTURE_BYTES);
|
||||
}
|
||||
|
||||
public static void glDeleteObjectARB(int obj) {
|
||||
rejectExitCapture();
|
||||
if (RENDER_BACKEND.isShader(obj)) {
|
||||
RENDER_BACKEND.deleteShader(obj);
|
||||
} else {
|
||||
glDeleteProgram(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetHandleARB(int pname) {
|
||||
rejectExitCapture();
|
||||
return pname == ARBShaderObjects.GL_PROGRAM_OBJECT_ARB ? GLSMQueries.glGetInteger(GL20.GL_CURRENT_PROGRAM) : RENDER_BACKEND.getInteger(pname);
|
||||
}
|
||||
|
||||
public static void glGetObjectParameterARB(int obj, int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
final var backend = RENDER_BACKEND;
|
||||
if (backend.isShader(obj)) {
|
||||
params.put(params.position(), backend.getShaderi(obj, pname));
|
||||
} else {
|
||||
backend.getProgramiv(obj, pname, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetObjectParameterARB(int obj, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final var backend = RENDER_BACKEND;
|
||||
params.put(params.position(), (float) (backend.isShader(obj) ? backend.getShaderi(obj, pname) : backend.getProgrami(obj, pname)));
|
||||
}
|
||||
|
||||
public static int glGetObjectParameteriARB(int obj, int pname) {
|
||||
rejectExitCapture();
|
||||
final var backend = RENDER_BACKEND;
|
||||
return backend.isShader(obj) ? backend.getShaderi(obj, pname) : backend.getProgrami(obj, pname);
|
||||
}
|
||||
|
||||
public static String glGetInfoLogARB(int obj, int maxLength) {
|
||||
rejectExitCapture();
|
||||
final var backend = RENDER_BACKEND;
|
||||
return backend.isShader(obj) ? backend.getShaderInfoLog(obj, maxLength) : backend.getProgramInfoLog(obj, maxLength);
|
||||
}
|
||||
|
||||
public static void glGetInfoLogARB(int obj, IntBuffer length, ByteBuffer infoLog) {
|
||||
rejectExitCapture();
|
||||
final var backend = RENDER_BACKEND;
|
||||
if (backend.isShader(obj)) {
|
||||
backend.getShaderInfoLog(obj, length, infoLog);
|
||||
} else {
|
||||
backend.getProgramInfoLog(obj, length, infoLog);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glUniform1f(int location, float v0) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1f(location, v0);
|
||||
}
|
||||
|
||||
public static void glUniform2f(int location, float v0, float v1) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2f(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void glUniform3f(int location, float v0, float v1, float v2) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void glUniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void glUniform1i(int location, int v0) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1i(location, v0);
|
||||
}
|
||||
|
||||
public static void glUniform2i(int location, int v0, int v1) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2i(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void glUniform3i(int location, int v0, int v1, int v2) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3i(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void glUniform4i(int location, int v0, int v1, int v2, int v3) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void glUniform1(int location, FloatBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1fv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform1(int location, IntBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, FloatBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform2(int location, IntBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, FloatBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform3(int location, IntBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, FloatBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4(location, values);
|
||||
}
|
||||
|
||||
public static void glUniform4(int location, IntBuffer values) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4iv(location, values);
|
||||
}
|
||||
|
||||
public static void glUniformMatrix2(int location, boolean transpose, FloatBuffer matrices) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix2(location, transpose, matrices);
|
||||
}
|
||||
|
||||
public static void glUniformMatrix3(int location, boolean transpose, FloatBuffer matrices) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix3(location, transpose, matrices);
|
||||
}
|
||||
|
||||
public static void glUniformMatrix4(int location, boolean transpose, FloatBuffer matrices) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrices);
|
||||
}
|
||||
|
||||
public static void glGetActiveUniform(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getActiveUniform(program, index, length, size, type, name);
|
||||
}
|
||||
|
||||
public static void glGetActiveAttrib(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getActiveAttrib(program, index, length, size, type, name);
|
||||
}
|
||||
|
||||
public static String glGetActiveAttrib(int program, int index, int maxLength, IntBuffer sizeType) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getActiveAttrib(program, index, maxLength, sizeType);
|
||||
}
|
||||
|
||||
public static void glGetAttachedShaders(int program, IntBuffer count, IntBuffer shaders) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getAttachedShaders(program, count, shaders);
|
||||
}
|
||||
|
||||
public static String glGetShaderSource(int shader, int maxLength) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderSource(shader, maxLength);
|
||||
}
|
||||
|
||||
public static void glGetShaderSource(int shader, IntBuffer length, ByteBuffer source) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getShaderSource(shader, length, source);
|
||||
}
|
||||
|
||||
public static void glGetUniform(int program, int location, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getUniformfv(program, location, params);
|
||||
}
|
||||
|
||||
public static void glGetUniform(int program, int location, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.getUniformiv(program, location, params);
|
||||
}
|
||||
|
||||
public static int getProgramGeneration() {
|
||||
return ctx().programGeneration;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getProgramStack() {
|
||||
return ctx().programStack;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getVertexProgramTwoSideState() {
|
||||
return ctx().vertexProgramTwoSideState;
|
||||
}
|
||||
|
||||
public static int getActiveProgram() {
|
||||
return ctx().activeProgram;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,594 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.HAS_MULTIPLE_SET;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_ATTRIB_STACK_DEPTH;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_CLIP_PLANES;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_LEGACY_TEXTURE_UNITS;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_MODELVIEW_STACK_DEPTH;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_PROJECTION_STACK_DEPTH;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_TEXTURE_COORDS;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_TEXTURE_STACK_DEPTH;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.initConfig;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.requireContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMQueries {
|
||||
|
||||
private GLSMQueries() {}
|
||||
|
||||
public static boolean glGetBoolean(int pname) {
|
||||
rejectExitCapture();
|
||||
return glGetBoolean(ctx(), pname);
|
||||
}
|
||||
|
||||
static boolean glGetBoolean(GLContextState glCtx, int pname) {
|
||||
if (!isCachingEnabled()) {
|
||||
return RENDER_BACKEND.getBoolean(pname);
|
||||
}
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (pname >= GL11.GL_CLIP_PLANE0 && pname < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
return glCtx.clipPlaneStates[pname - GL11.GL_CLIP_PLANE0].isEnabled();
|
||||
}
|
||||
|
||||
return switch (pname) {
|
||||
case GL11.GL_ALPHA_TEST -> glCtx.alphaTest.isEnabled();
|
||||
case GL11.GL_AUTO_NORMAL -> glCtx.autoNormalState.isEnabled();
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DEPTH_WRITEMASK -> glCtx.depthState.isEnabled();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.isEnabled();
|
||||
case GL11.GL_FOG -> glCtx.fogMode.isEnabled();
|
||||
case GL11.GL_INDEX_LOGIC_OP -> glCtx.indexLogicOpState.isEnabled();
|
||||
case GL11.GL_LIGHTING -> glCtx.lightingState.isEnabled();
|
||||
case GL11.GL_LIGHT0 -> glCtx.lightStates[0].isEnabled();
|
||||
case GL11.GL_LIGHT1 -> glCtx.lightStates[1].isEnabled();
|
||||
case GL11.GL_LIGHT2 -> glCtx.lightStates[2].isEnabled();
|
||||
case GL11.GL_LIGHT3 -> glCtx.lightStates[3].isEnabled();
|
||||
case GL11.GL_LIGHT4 -> glCtx.lightStates[4].isEnabled();
|
||||
case GL11.GL_LIGHT5 -> glCtx.lightStates[5].isEnabled();
|
||||
case GL11.GL_LIGHT6 -> glCtx.lightStates[6].isEnabled();
|
||||
case GL11.GL_LIGHT7 -> glCtx.lightStates[7].isEnabled();
|
||||
case GL11.GL_LINE_SMOOTH -> glCtx.lineSmoothState.isEnabled();
|
||||
case GL11.GL_LINE_STIPPLE -> glCtx.lineStippleState.isEnabled();
|
||||
case GL11.GL_MAP1_COLOR_4 -> glCtx.map1Color4State.isEnabled();
|
||||
case GL11.GL_MAP1_INDEX -> glCtx.map1IndexState.isEnabled();
|
||||
case GL11.GL_MAP1_NORMAL -> glCtx.map1NormalState.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_1 -> glCtx.map1TextureCoord1State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_2 -> glCtx.map1TextureCoord2State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_3 -> glCtx.map1TextureCoord3State.isEnabled();
|
||||
case GL11.GL_MAP1_TEXTURE_COORD_4 -> glCtx.map1TextureCoord4State.isEnabled();
|
||||
case GL11.GL_MAP1_VERTEX_3 -> glCtx.map1Vertex3State.isEnabled();
|
||||
case GL11.GL_MAP1_VERTEX_4 -> glCtx.map1Vertex4State.isEnabled();
|
||||
case GL11.GL_MAP2_COLOR_4 -> glCtx.map2Color4State.isEnabled();
|
||||
case GL11.GL_MAP2_INDEX -> glCtx.map2IndexState.isEnabled();
|
||||
case GL11.GL_MAP2_NORMAL -> glCtx.map2NormalState.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_1 -> glCtx.map2TextureCoord1State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_2 -> glCtx.map2TextureCoord2State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_3 -> glCtx.map2TextureCoord3State.isEnabled();
|
||||
case GL11.GL_MAP2_TEXTURE_COORD_4 -> glCtx.map2TextureCoord4State.isEnabled();
|
||||
case GL11.GL_MAP2_VERTEX_3 -> glCtx.map2Vertex3State.isEnabled();
|
||||
case GL11.GL_MAP2_VERTEX_4 -> glCtx.map2Vertex4State.isEnabled();
|
||||
case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.isEnabled();
|
||||
case GL11.GL_NORMALIZE -> glCtx.normalizeState.isEnabled();
|
||||
case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.isEnabled();
|
||||
case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.isEnabled();
|
||||
case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.isEnabled();
|
||||
case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.isEnabled();
|
||||
case GL11.GL_POLYGON_SMOOTH -> glCtx.polygonSmoothState.isEnabled();
|
||||
case GL11.GL_POLYGON_STIPPLE -> glCtx.polygonStippleState.isEnabled();
|
||||
case GL12.GL_RESCALE_NORMAL -> glCtx.rescaleNormalState.isEnabled();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> glCtx.sampleAlphaToCoverageState.isEnabled();
|
||||
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> glCtx.sampleAlphaToOneState.isEnabled();
|
||||
case GL13.GL_SAMPLE_COVERAGE -> glCtx.sampleCoverageState.isEnabled();
|
||||
case GL30.GL_RASTERIZER_DISCARD -> glCtx.rasterizerDiscard.isEnabled();
|
||||
case GL11.GL_SCISSOR_TEST -> glCtx.scissorTest.isEnabled();
|
||||
case GL11.GL_STENCIL_TEST -> glCtx.stencilTest.isEnabled();
|
||||
case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_2D -> glCtx.textures.getTextureUnitStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).isEnabled();
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.localViewer != 0.0f;
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.twoSide != 0.0f;
|
||||
case GL11.GL_EDGE_FLAG -> glCtx.edgeFlagState.isEnabled();
|
||||
default -> {
|
||||
final int conv = GLSMVertexInput.clientStateToConventional(glCtx, pname);
|
||||
if (conv >= 0) yield glCtx.vaos.isConventionalEnabled(conv);
|
||||
final int desc = LegacyQueries.desc(pname);
|
||||
yield desc != 0 ? LegacyQueries.getBoolean(glCtx, pname, desc) : RENDER_BACKEND.getBoolean(pname);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetBoolean(int pname, ByteBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.getBoolean(pname, params);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_COLOR_WRITEMASK -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, (byte) (glCtx.colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 1, (byte) (glCtx.colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 2, (byte) (glCtx.colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 3, (byte) (glCtx.colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params.put(params.position(), (byte) (glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
} else if (!LegacyQueries.getBooleanv(glCtx, pname, params)) {
|
||||
RENDER_BACKEND.getBoolean(pname, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetInteger(int pname) {
|
||||
rejectExitCapture();
|
||||
return glGetInteger(ctx(), pname);
|
||||
}
|
||||
|
||||
static int glGetInteger(GLContextState glCtx, int pname) {
|
||||
if (!isCachingEnabled()) {
|
||||
return RENDER_BACKEND.getInteger(pname);
|
||||
}
|
||||
|
||||
return switch (pname) {
|
||||
case GL11.GL_ALPHA_TEST_FUNC -> glCtx.alphaState.getFunction();
|
||||
case GL11.GL_DEPTH_FUNC -> glCtx.depthState.getFunc();
|
||||
case GL11.GL_LIST_BASE -> glCtx.listBase;
|
||||
case GL11.GL_LIST_MODE -> DisplayListManager.getListMode();
|
||||
case GL11.GL_MATRIX_MODE -> glCtx.matrixMode.getMode();
|
||||
case GL11.GL_SHADE_MODEL -> glCtx.shadeModelState.getValue();
|
||||
// GL_TEXTURE_2D makes no sense here, but some mod still queries it...
|
||||
case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> GLSMTextures.getBoundTextureForServerState();
|
||||
case GL13.GL_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + GLSMTextures.getActiveTextureUnitForServerState();
|
||||
case GL13.GL_CLIENT_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + glCtx.clientActiveTextureUnit;
|
||||
case GL11.GL_COLOR_MATERIAL_FACE -> glCtx.colorMaterialFace.getValue();
|
||||
case GL11.GL_COLOR_MATERIAL_PARAMETER -> glCtx.colorMaterialParameter.getValue();
|
||||
case GL11.GL_MODELVIEW_STACK_DEPTH -> GLSMMatrices.getMatrixStackDepth(glCtx.modelViewMatrix);
|
||||
case GL11.GL_PROJECTION_STACK_DEPTH -> GLSMMatrices.getMatrixStackDepth(glCtx.projectionMatrix);
|
||||
case GL11.GL_DEPTH_WRITEMASK -> glCtx.depthState.isEnabled() ? GL11.GL_TRUE : GL11.GL_FALSE;
|
||||
case GL11.GL_CULL_FACE_MODE -> glCtx.polygonState.getCullFaceMode();
|
||||
case GL11.GL_FRONT_FACE -> glCtx.polygonState.getFrontFace();
|
||||
case GL11.GL_POLYGON_MODE -> glCtx.polygonState.getFrontMode();
|
||||
case GL11.GL_UNPACK_ALIGNMENT -> glCtx.pixelUnpackState.alignment();
|
||||
case GL11.GL_UNPACK_ROW_LENGTH -> glCtx.pixelUnpackState.rowLength();
|
||||
case GL11.GL_UNPACK_SKIP_ROWS -> glCtx.pixelUnpackState.skipRows();
|
||||
case GL11.GL_UNPACK_SKIP_PIXELS -> glCtx.pixelUnpackState.skipPixels();
|
||||
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> glCtx.lightModel.colorControl;
|
||||
|
||||
case GL14.GL_BLEND_DST_ALPHA -> glCtx.blendState.getDstAlpha();
|
||||
case GL11.GL_BLEND_DST, GL14.GL_BLEND_DST_RGB -> glCtx.blendState.getDstRgb();
|
||||
case GL14.GL_BLEND_SRC_ALPHA -> glCtx.blendState.getSrcAlpha();
|
||||
case GL11.GL_BLEND_SRC, GL14.GL_BLEND_SRC_RGB -> glCtx.blendState.getSrcRgb();
|
||||
case GL14.GL_BLEND_EQUATION -> glCtx.blendState.getEquationRgb();
|
||||
case GL20.GL_BLEND_EQUATION_ALPHA -> glCtx.blendState.getEquationAlpha();
|
||||
|
||||
case GL11.GL_LOGIC_OP_MODE -> glCtx.logicOpMode.getValue();
|
||||
case GL11.GL_DRAW_BUFFER -> glCtx.drawBuffer.getValue();
|
||||
|
||||
case GL11.GL_STENCIL_FUNC -> glCtx.stencilState.getFuncFront();
|
||||
case GL11.GL_STENCIL_REF -> glCtx.stencilState.getRefFront();
|
||||
case GL11.GL_STENCIL_VALUE_MASK -> glCtx.stencilState.getValueMaskFront();
|
||||
case GL11.GL_STENCIL_FAIL -> glCtx.stencilState.getFailOpFront();
|
||||
case GL11.GL_STENCIL_PASS_DEPTH_FAIL -> glCtx.stencilState.getZFailOpFront();
|
||||
case GL11.GL_STENCIL_PASS_DEPTH_PASS -> glCtx.stencilState.getZPassOpFront();
|
||||
case GL11.GL_STENCIL_WRITEMASK -> glCtx.stencilState.getWriteMaskFront();
|
||||
case GL11.GL_STENCIL_CLEAR_VALUE -> glCtx.stencilState.getClearValue();
|
||||
|
||||
case GL15.GL_ARRAY_BUFFER_BINDING -> glCtx.boundVBO;
|
||||
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> glCtx.vaos.boundEBO;
|
||||
case GL21.GL_PIXEL_UNPACK_BUFFER_BINDING -> glCtx.boundPixelUnpackBuffer;
|
||||
case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> glCtx.boundPixelPackBuffer;
|
||||
|
||||
case GL20.GL_CURRENT_PROGRAM -> glCtx.activeProgram;
|
||||
|
||||
case GL30.GL_VERTEX_ARRAY_BINDING -> glCtx.boundVAO;
|
||||
|
||||
case GL30.GL_DRAW_FRAMEBUFFER_BINDING -> glCtx.drawFramebuffer;
|
||||
case GL30.GL_READ_FRAMEBUFFER_BINDING -> glCtx.readFramebuffer;
|
||||
|
||||
case GL33.GL_SAMPLER_BINDING -> glCtx.samplerUnits.get(glCtx.activeTextureUnit.getValue());
|
||||
|
||||
default -> switch (pname) {
|
||||
case GL11.GL_FOG_MODE -> glCtx.fogState.getFogMode();
|
||||
case GL11.GL_LINE_STIPPLE_PATTERN -> glCtx.lineState.getStipplePattern() & 0xFFFF;
|
||||
case GL11.GL_LINE_STIPPLE_REPEAT -> glCtx.lineState.getStippleFactor();
|
||||
case GL11.GL_LIST_INDEX -> DisplayListManager.isRecording() ? Math.max(DisplayListManager.getRecordingListId(), 0) : 0;
|
||||
case GL11.GL_ATTRIB_STACK_DEPTH -> glCtx.attribDepth;
|
||||
case GL11.GL_CLIENT_ATTRIB_STACK_DEPTH -> glCtx.clientAttribStackPointer;
|
||||
case GL11.GL_TEXTURE_STACK_DEPTH -> GLSMMatrices.getMatrixStackDepth(glCtx.textures.getTextureUnitMatrix(glCtx.activeTextureUnit.getValue()));
|
||||
case GL11.GL_MAX_ATTRIB_STACK_DEPTH -> MAX_ATTRIB_STACK_DEPTH;
|
||||
case GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH -> GLSMAttribStack.CLIENT_ATTRIB_STACK_DEPTH;
|
||||
case GL11.GL_MAX_MODELVIEW_STACK_DEPTH -> MAX_MODELVIEW_STACK_DEPTH;
|
||||
case GL11.GL_MAX_PROJECTION_STACK_DEPTH -> MAX_PROJECTION_STACK_DEPTH;
|
||||
case GL11.GL_MAX_TEXTURE_STACK_DEPTH -> MAX_TEXTURE_STACK_DEPTH;
|
||||
case GL13.GL_MAX_TEXTURE_UNITS -> MAX_LEGACY_TEXTURE_UNITS;
|
||||
case GL11.GL_MAX_LIGHTS -> glCtx.lightStates.length;
|
||||
case GL11.GL_STENCIL_BITS -> GLSMFramebuffer.drawFramebufferBits(glCtx, true);
|
||||
case GL11.GL_DEPTH_BITS -> GLSMFramebuffer.drawFramebufferBits(glCtx, false);
|
||||
case GL20.GL_MAX_TEXTURE_COORDS -> MAX_TEXTURE_COORDS;
|
||||
case GL20.GL_POINT_SPRITE_COORD_ORIGIN -> glCtx.pointState.getSpriteCoordOrigin();
|
||||
case GL11.GL_RENDER_MODE -> FeedbackManager.getRenderMode();
|
||||
default -> {
|
||||
final int shift = GLSMRasterizer.hintShift(pname);
|
||||
if (shift >= 0) yield GLSMRasterizer.hintMode(glCtx.hintState.getValue(), shift);
|
||||
final int key = GLSMVertexInput.clientArrayField(glCtx, pname);
|
||||
if (key >= 0 && (key & ~0xFF) != GLSMVertexInput.FIELD_POINTER) yield GLSMVertexInput.clientArrayParam(glCtx, key);
|
||||
final int desc = LegacyQueries.desc(pname);
|
||||
yield desc != 0 ? LegacyQueries.getInteger(glCtx, pname, desc) : RENDER_BACKEND.getInteger(pname);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetInteger(int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_VIEWPORT -> ctx().viewportState.get(params);
|
||||
case GL11.GL_SCISSOR_BOX -> ctx().scissorState.get(params);
|
||||
case GL11.GL_POLYGON_MODE -> {
|
||||
final PolygonState polygon = ctx().polygonState;
|
||||
final int pos = params.position();
|
||||
params.put(pos, polygon.getFrontMode());
|
||||
params.put(pos + 1, polygon.getBackMode());
|
||||
}
|
||||
default -> {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params.put(params.position(), glGetInteger(glCtx, pname));
|
||||
} else if (!LegacyQueries.getIntegerv(glCtx, pname, params)) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetFloat(int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.getFloat(pname, params);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_MODELVIEW_MATRIX -> glCtx.modelViewMatrix.get(params.position(), params);
|
||||
case GL11.GL_PROJECTION_MATRIX -> glCtx.projectionMatrix.get(params.position(), params);
|
||||
case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(GLSMTextures.getActiveTextureUnit()).get(params.position(), params);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> glCtx.clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> glCtx.color.get(params);
|
||||
case GL14.GL_CURRENT_SECONDARY_COLOR -> glCtx.secondaryColor.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) glCtx.viewportState.depthRangeNear);
|
||||
params.put(pos + 1, (float) glCtx.viewportState.depthRangeFar);
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, glCtx.blendState.getBlendColorR());
|
||||
params.put(pos + 1, glCtx.blendState.getBlendColorG());
|
||||
params.put(pos + 2, glCtx.blendState.getBlendColorB());
|
||||
params.put(pos + 3, glCtx.blendState.getBlendColorA());
|
||||
}
|
||||
case GL11.GL_CURRENT_NORMAL -> {
|
||||
final Vector3f normal = glCtx.ffp.currentNormal;
|
||||
final int pos = params.position();
|
||||
params.put(pos, normal.x);
|
||||
params.put(pos + 1, normal.y);
|
||||
params.put(pos + 2, normal.z);
|
||||
}
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> {
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
final int pos = params.position();
|
||||
if (unit == 1) {
|
||||
params.put(pos, glCtx.lastBrightnessX);
|
||||
params.put(pos + 1, glCtx.lastBrightnessY);
|
||||
params.put(pos + 2, 0.0f);
|
||||
params.put(pos + 3, 1.0f);
|
||||
} else if (unit >= 0 && unit < glCtx.ffp.currentTexCoords.length) {
|
||||
final Vector4f tc = glCtx.ffp.currentTexCoords[unit];
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
} else {
|
||||
params.put(pos, 0.0f);
|
||||
params.put(pos + 1, 0.0f);
|
||||
params.put(pos + 2, 0.0f);
|
||||
params.put(pos + 3, 1.0f);
|
||||
}
|
||||
}
|
||||
case GL11.GL_FOG_COLOR -> {
|
||||
final FloatBuffer fogBuf = glCtx.fogState.getFogColorBuffer();
|
||||
final int pos = params.position();
|
||||
params.put(pos, fogBuf.get(0));
|
||||
params.put(pos + 1, fogBuf.get(1));
|
||||
params.put(pos + 2, fogBuf.get(2));
|
||||
params.put(pos + 3, fogBuf.get(3));
|
||||
}
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> glCtx.lightModel.ambient.get(params.position(), params);
|
||||
case GL14.GL_POINT_DISTANCE_ATTENUATION -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, glCtx.pointState.getAttenuationConstant());
|
||||
params.put(pos + 1, glCtx.pointState.getAttenuationLinear());
|
||||
params.put(pos + 2, glCtx.pointState.getAttenuationQuadratic());
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params.put(params.position(), glGetFloat(glCtx, pname));
|
||||
} else {
|
||||
RENDER_BACKEND.getFloat(pname, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float glGetFloat(int pname) {
|
||||
rejectExitCapture();
|
||||
return glGetFloat(ctx(), pname);
|
||||
}
|
||||
|
||||
static float glGetFloat(GLContextState glCtx, int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_ALPHA_TEST_REF -> glCtx.alphaState.getReference();
|
||||
case GL11.GL_FOG_DENSITY -> glCtx.fogState.getDensity();
|
||||
case GL11.GL_FOG_START -> glCtx.fogState.getStart();
|
||||
case GL11.GL_FOG_END -> glCtx.fogState.getEnd();
|
||||
case GL11.GL_DEPTH_CLEAR_VALUE -> (float) glCtx.depthState.getClearValue();
|
||||
case GL11.GL_LINE_WIDTH -> glCtx.lineState.getWidth();
|
||||
case GL11.GL_POINT_SIZE -> glCtx.pointState.getSize();
|
||||
case GL14.GL_POINT_SIZE_MIN -> glCtx.pointState.getMinSize();
|
||||
case GL14.GL_POINT_SIZE_MAX -> Float.isNaN(glCtx.pointState.getMaxSize()) ? GLSMRasterizer.maxPointSize : glCtx.pointState.getMaxSize();
|
||||
case GL14.GL_POINT_FADE_THRESHOLD_SIZE -> glCtx.pointState.getFadeThreshold();
|
||||
default -> {
|
||||
final int desc = isCachingEnabled() ? LegacyQueries.desc(pname) : 0;
|
||||
yield desc != 0 ? LegacyQueries.getFloat(glCtx, pname, desc) : RENDER_BACKEND.getFloat(pname);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetFloat(int pname, float[] params) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
final FloatBuffer buf;
|
||||
if (params.length <= scratch.capacity()) {
|
||||
buf = scratch;
|
||||
buf.clear();
|
||||
buf.limit(params.length);
|
||||
} else {
|
||||
buf = BufferUtils.createFloatBuffer(params.length);
|
||||
}
|
||||
glGetFloat(pname, buf);
|
||||
buf.position(0);
|
||||
buf.get(params);
|
||||
}
|
||||
|
||||
public static double glGetDouble(int pname) {
|
||||
rejectExitCapture();
|
||||
return switch (pname) {
|
||||
case GL11.GL_DEPTH_CLEAR_VALUE -> ctx().depthState.getClearValue();
|
||||
default -> glGetFloat(pname);
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetDouble(int pname, DoubleBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.getDouble(pname, params);
|
||||
return;
|
||||
}
|
||||
|
||||
final int pos = params.position();
|
||||
switch (pname) {
|
||||
case GL11.GL_DEPTH_CLEAR_VALUE -> params.put(pos, glCtx.depthState.getClearValue());
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
params.put(pos, glCtx.viewportState.depthRangeNear);
|
||||
params.put(pos + 1, glCtx.viewportState.depthRangeFar);
|
||||
}
|
||||
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> widenFromFloat(pname, params, 16);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE, GL11.GL_CURRENT_COLOR, GL11.GL_CURRENT_TEXTURE_COORDS, GL11.GL_FOG_COLOR,
|
||||
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR -> widenFromFloat(pname, params, 4);
|
||||
case GL11.GL_CURRENT_NORMAL, GL14.GL_POINT_DISTANCE_ATTENUATION -> widenFromFloat(pname, params, 3);
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params.put(pos, glGetDouble(pname));
|
||||
} else {
|
||||
RENDER_BACKEND.getDouble(pname, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetDouble(int pname, double[] params) {
|
||||
final DoubleBuffer scratch = ctx().queryScratchDouble;
|
||||
final DoubleBuffer buf;
|
||||
if (params.length <= scratch.capacity()) {
|
||||
buf = scratch;
|
||||
buf.clear();
|
||||
buf.limit(params.length);
|
||||
} else {
|
||||
buf = BufferUtils.createDoubleBuffer(params.length);
|
||||
}
|
||||
glGetDouble(pname, buf);
|
||||
buf.position(0);
|
||||
buf.get(params);
|
||||
}
|
||||
|
||||
private static void widenFromFloat(int pname, DoubleBuffer params, int count) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
scratch.clear();
|
||||
glGetFloat(pname, scratch);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
params.put(pos + i, scratch.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetError() {
|
||||
rejectExitCapture();
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
if (initConfig.isNoErrorChecks()) return 0;
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
public static void checkGLError() {
|
||||
final int error = glGetError();
|
||||
if (error != GL11.GL_NO_ERROR) GLErrorReporter.reportError(error);
|
||||
}
|
||||
|
||||
public static String glGetString(int pname) {
|
||||
rejectExitCapture();
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getString(pname);
|
||||
}
|
||||
|
||||
public static String glGetStringi(int name, int index) {
|
||||
rejectExitCapture();
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getStringi(name, index);
|
||||
}
|
||||
public static boolean glIsQuery(int query) { rejectExitCapture(); return RENDER_BACKEND.isQuery(query); }
|
||||
|
||||
public static void glGenQueries(IntBuffer ids) {
|
||||
requireContext("glGenQueries");
|
||||
RENDER_BACKEND.genQueries(ids);
|
||||
}
|
||||
public static int glGenQueries() {
|
||||
requireContext("glGenQueries");
|
||||
return RENDER_BACKEND.genQueries();
|
||||
}
|
||||
public static void glDeleteQueries(int id) { rejectExitCapture(); RENDER_BACKEND.deleteQueries(id); }
|
||||
public static void glBeginQuery(int target, int id) { rejectExitCapture(); RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { rejectExitCapture(); RENDER_BACKEND.endQuery(target); }
|
||||
public static void glGetQueryObjectui(int id, int pname, IntBuffer params) { rejectExitCapture(); RENDER_BACKEND.getQueryObjectui(id, pname, params); }
|
||||
public static int glGetQueryObjecti(int id, int pname) { rejectExitCapture(); return RENDER_BACKEND.getQueryObjecti(id, pname); }
|
||||
public static void glQueryCounter(int id, int target) { rejectExitCapture(); RENDER_BACKEND.queryCounter(id, target); }
|
||||
public static long glGetQueryObjectui64(int id, int pname) { rejectExitCapture(); return RENDER_BACKEND.getQueryObjectui64(id, pname); }
|
||||
|
||||
public static void glGetInteger(int pname, int[] params) {
|
||||
rejectExitCapture();
|
||||
if (!isCachingEnabled() || params.length == 0) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_VIEWPORT -> {
|
||||
if (params.length < 4) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
} else {
|
||||
ctx().viewportState.get(params);
|
||||
}
|
||||
}
|
||||
case GL11.GL_SCISSOR_BOX -> {
|
||||
if (params.length < 4) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
} else {
|
||||
ctx().scissorState.get(params);
|
||||
}
|
||||
}
|
||||
case GL11.GL_POLYGON_MODE -> {
|
||||
final PolygonState polygon = ctx().polygonState;
|
||||
params[0] = polygon.getFrontMode();
|
||||
if (params.length > 1) params[1] = polygon.getBackMode();
|
||||
}
|
||||
default -> {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
params[0] = glGetInteger(glCtx, pname);
|
||||
} else if (!LegacyQueries.getIntegerv(glCtx, pname, params)) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int indexedIntegerOrBackend(int pname, int index) {
|
||||
if (pname == GL31.GL_UNIFORM_BUFFER_BINDING) return GLSMBuffers.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index);
|
||||
if (pname == GL43.GL_SHADER_STORAGE_BUFFER_BINDING) return GLSMBuffers.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, index);
|
||||
final ImageUnitBinding binding = !isCachingEnabled() ? null : ctx().imageUnits.get(index);
|
||||
if (binding == null) return RENDER_BACKEND.getIntegerIndexed(pname, index);
|
||||
return switch (pname) {
|
||||
case GL42.GL_IMAGE_BINDING_NAME -> binding.getTexture();
|
||||
case GL42.GL_IMAGE_BINDING_LEVEL -> binding.getLevel();
|
||||
case GL42.GL_IMAGE_BINDING_LAYERED -> binding.isLayered() ? GL11.GL_TRUE : GL11.GL_FALSE;
|
||||
case GL42.GL_IMAGE_BINDING_LAYER -> binding.getLayer();
|
||||
case GL42.GL_IMAGE_BINDING_ACCESS -> binding.getAccess();
|
||||
case GL42.GL_IMAGE_BINDING_FORMAT -> binding.getFormat();
|
||||
default -> RENDER_BACKEND.getIntegerIndexed(pname, index);
|
||||
};
|
||||
}
|
||||
|
||||
public static int glGetInteger(int pname, int index) {
|
||||
rejectExitCapture();
|
||||
return indexedIntegerOrBackend(pname, index);
|
||||
}
|
||||
|
||||
public static int glGetIntegeri(int pname, int index) {
|
||||
rejectExitCapture();
|
||||
return indexedIntegerOrBackend(pname, index);
|
||||
}
|
||||
|
||||
public static void glGetIntegeri(int pname, int index, IntBuffer params) {
|
||||
glGetIntegeri_v(pname, index, params);
|
||||
}
|
||||
|
||||
public static void glGetIntegeri_v(int pname, int index, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
params.put(params.position(), indexedIntegerOrBackend(pname, index));
|
||||
}
|
||||
|
||||
public static void glGetBooleani_v(int pname, int index, ByteBuffer params) {
|
||||
rejectExitCapture();
|
||||
params.put(params.position(), (byte) (indexedIntegerOrBackend(pname, index) != 0 ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
|
||||
public static void glGetBooleani(int pname, int index, ByteBuffer params) {
|
||||
glGetBooleani_v(pname, index, params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.IntegerStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LineStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.MAX_CLIP_PLANES;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.guardUnsupportedFFP;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.mod;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMRasterizer {
|
||||
|
||||
private GLSMRasterizer() {}
|
||||
|
||||
static float maxPointSize;
|
||||
|
||||
// Line width range queried at init from GL_ALIASED_LINE_WIDTH_RANGE
|
||||
static float lineWidthMin = 1.0f;
|
||||
static float lineWidthMax = 1.0f;
|
||||
// The driver cannot draw wide lines (Mesa forward-compat, SDL_GPU), so FFP widens them from LineQuads in the vertex shader
|
||||
public static boolean wideLineEmulationEnabled = false;
|
||||
|
||||
public static void glGetClipPlane(int plane, DoubleBuffer equation) {
|
||||
rejectExitCapture();
|
||||
final int index = plane - GL11.GL_CLIP_PLANE0;
|
||||
if (index < 0 || index >= MAX_CLIP_PLANES) return;
|
||||
ctx().clipPlaneState.getEyePlane(index, equation);
|
||||
}
|
||||
|
||||
static void probeLineWidthRange() {
|
||||
final FloatBuffer lwRange = BufferUtils.createFloatBuffer(16);
|
||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_LINE_WIDTH_RANGE, lwRange);
|
||||
lineWidthMin = lwRange.get(0);
|
||||
lineWidthMax = lwRange.get(1);
|
||||
final float queriedMax = lineWidthMax;
|
||||
// Probe whether the driver actually accepts the queried max (Mesa forward-compat rejects > 1.0)
|
||||
if (lineWidthMax > 1.0f) {
|
||||
while (RENDER_BACKEND.getError() != GL11.GL_NO_ERROR) {}
|
||||
RENDER_BACKEND.lineWidth(lineWidthMax);
|
||||
if (RENDER_BACKEND.getError() != GL11.GL_NO_ERROR) {
|
||||
// Binary search for the actual accepted max between 1.0 and queriedMax
|
||||
float lo = 1.0f, hi = queriedMax;
|
||||
while (hi - lo > 0.01f) {
|
||||
final float mid = (lo + hi) * 0.5f;
|
||||
while (RENDER_BACKEND.getError() != GL11.GL_NO_ERROR) {}
|
||||
RENDER_BACKEND.lineWidth(mid);
|
||||
if (RENDER_BACKEND.getError() == GL11.GL_NO_ERROR) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
lineWidthMax = lo;
|
||||
}
|
||||
RENDER_BACKEND.lineWidth(1.0f);
|
||||
while (RENDER_BACKEND.getError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
wideLineEmulationEnabled = lineWidthMax <= 1.0f;
|
||||
if (wideLineEmulationEnabled) {
|
||||
LOGGER.info("GL line width: aliased range [{}, {}], probe at {} rejected, native [{}, {}], line quads widen ({}, {}]", lineWidthMin, queriedMax, queriedMax, lineWidthMin, lineWidthMax, lineWidthMax, queriedMax);
|
||||
} else {
|
||||
LOGGER.info("GL line width: aliased range [{}, {}], probe at {} accepted, native [{}, {}]", lineWidthMin, queriedMax, queriedMax, lineWidthMin, lineWidthMax);
|
||||
}
|
||||
}
|
||||
|
||||
static void probePointSizeRange(GLContextState glCtx) {
|
||||
final FloatBuffer range = glCtx.queryScratch;
|
||||
range.clear();
|
||||
float smooth = 0.0f;
|
||||
if (!RenderSystem.isGLES()) {
|
||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
smooth = range.get(1);
|
||||
}
|
||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||
maxPointSize = Math.max(smooth, range.get(1));
|
||||
}
|
||||
|
||||
public static void enableScissorTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_SCISSOR_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableScissorTest(ctx());
|
||||
}
|
||||
|
||||
static void enableScissorTest(GLContextState glCtx) {
|
||||
glCtx.scissorTest.enable();
|
||||
}
|
||||
|
||||
public static void disableScissorTest() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_SCISSOR_TEST);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableScissorTest(ctx());
|
||||
}
|
||||
|
||||
static void disableScissorTest(GLContextState glCtx) {
|
||||
glCtx.scissorTest.disable();
|
||||
}
|
||||
|
||||
public static void glPointParameterf(int pname, float param) {
|
||||
pointParameter(pname, param, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public static void glPointParameteri(int pname, int param) {
|
||||
pointParameter(pname, param, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public static void glPointParameter(int pname, FloatBuffer params) {
|
||||
final int pos = params.position();
|
||||
final boolean attenuation = pname == GL14.GL_POINT_DISTANCE_ATTENUATION;
|
||||
pointParameter(pname, params.get(pos), attenuation ? params.get(pos + 1) : 0.0f, attenuation ? params.get(pos + 2) : 0.0f);
|
||||
}
|
||||
|
||||
public static void glPointParameter(int pname, IntBuffer params) {
|
||||
final int pos = params.position();
|
||||
final boolean attenuation = pname == GL14.GL_POINT_DISTANCE_ATTENUATION;
|
||||
pointParameter(pname, params.get(pos), attenuation ? params.get(pos + 1) : 0.0f, attenuation ? params.get(pos + 2) : 0.0f);
|
||||
}
|
||||
|
||||
private static void pointParameter(int pname, float v0, float v1, float v2) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final PointStateStack point = glCtx.pointState;
|
||||
switch (pname) {
|
||||
case GL14.GL_POINT_SIZE_MIN -> {
|
||||
if (!(v0 < 0.0f) && point.getMinSize() != v0) mod(point).setMinSize(v0);
|
||||
}
|
||||
case GL14.GL_POINT_SIZE_MAX -> {
|
||||
if (!(v0 < 0.0f) && point.getMaxSize() != v0) mod(point).setMaxSize(v0);
|
||||
}
|
||||
case GL14.GL_POINT_FADE_THRESHOLD_SIZE -> {
|
||||
if (!(v0 < 0.0f) && point.getFadeThreshold() != v0) {
|
||||
mod(point).setFadeThreshold(v0);
|
||||
RENDER_BACKEND.pointParameterf(pname, v0);
|
||||
}
|
||||
}
|
||||
case GL14.GL_POINT_DISTANCE_ATTENUATION -> {
|
||||
if (point.getAttenuationConstant() == v0 && point.getAttenuationLinear() == v1 && point.getAttenuationQuadratic() == v2) return;
|
||||
mod(point);
|
||||
point.setAttenuationConstant(v0);
|
||||
point.setAttenuationLinear(v1);
|
||||
point.setAttenuationQuadratic(v2);
|
||||
}
|
||||
case GL20.GL_POINT_SPRITE_COORD_ORIGIN -> {
|
||||
final int origin = (int) v0;
|
||||
if ((origin == GL20.GL_LOWER_LEFT || origin == GL20.GL_UPPER_LEFT) && point.getSpriteCoordOrigin() != origin) {
|
||||
mod(point).setSpriteCoordOrigin(origin);
|
||||
RENDER_BACKEND.pointParameteri(pname, origin);
|
||||
}
|
||||
}
|
||||
default -> RENDER_BACKEND.pointParameterf(pname, v0);
|
||||
}
|
||||
}
|
||||
|
||||
static void applyPointState(GLContextState glCtx) {
|
||||
final PointStateStack point = glCtx.pointState;
|
||||
RENDER_BACKEND.pointSize(point.getSize());
|
||||
RENDER_BACKEND.pointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, point.getFadeThreshold());
|
||||
RENDER_BACKEND.pointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, point.getSpriteCoordOrigin());
|
||||
}
|
||||
|
||||
private static boolean isLegacyHint(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_FOG_HINT, GL14.GL_GENERATE_MIPMAP_HINT -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
static int hintShift(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_PERSPECTIVE_CORRECTION_HINT -> 0;
|
||||
case GL11.GL_POINT_SMOOTH_HINT -> 2;
|
||||
case GL11.GL_LINE_SMOOTH_HINT -> 4;
|
||||
case GL11.GL_POLYGON_SMOOTH_HINT -> 6;
|
||||
case GL11.GL_FOG_HINT -> 8;
|
||||
case GL13.GL_TEXTURE_COMPRESSION_HINT -> 10;
|
||||
case GL14.GL_GENERATE_MIPMAP_HINT -> 12;
|
||||
case GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT -> 14;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int hintMode(int packed, int shift) {
|
||||
return switch (packed >>> shift & 3) {
|
||||
case 1 -> GL11.GL_FASTEST;
|
||||
case 2 -> GL11.GL_NICEST;
|
||||
default -> GL11.GL_DONT_CARE;
|
||||
};
|
||||
}
|
||||
|
||||
private static final int[] DRIVER_HINTS = {
|
||||
GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT, GL13.GL_TEXTURE_COMPRESSION_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT
|
||||
};
|
||||
|
||||
static void applyHintState(GLContextState glCtx) {
|
||||
final int packed = glCtx.hintState.getValue();
|
||||
for (final int target : DRIVER_HINTS) {
|
||||
RENDER_BACKEND.hint(target, hintMode(packed, hintShift(target)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableCull() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordEnable(GL11.GL_CULL_FACE);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableCull(ctx());
|
||||
}
|
||||
|
||||
static void enableCull(GLContextState glCtx) {
|
||||
glCtx.cullState.enable();
|
||||
}
|
||||
|
||||
public static void disableCull() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_CULL_FACE);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
disableCull(ctx());
|
||||
}
|
||||
|
||||
static void disableCull(GLContextState glCtx) {
|
||||
glCtx.cullState.disable();
|
||||
}
|
||||
|
||||
public static void glShadeModel(int mode) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.SHADE_MODEL);
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordShadeModel(mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final int oldValue = glCtx.shadeModelState.getValue();
|
||||
final boolean needsUpdate = !caching || oldValue != mode;
|
||||
|
||||
if (needsUpdate) {
|
||||
mod(glCtx.shadeModelState).setValue(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glViewport(int x, int y, int width, int height) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordViewport(x, y, width, height);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.viewport(x, y, width, height);
|
||||
// Only update cached state when caching is enabled
|
||||
if (isCachingEnabled()) {
|
||||
mod(ctx().viewportState).setViewPort(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLineWidth(float width) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLineWidth(width);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.lineState.getWidth() != width) {
|
||||
mod(glCtx.lineState).setWidth(width);
|
||||
RENDER_BACKEND.lineWidth(Math.clamp(width, lineWidthMin, lineWidthMax));
|
||||
}
|
||||
}
|
||||
|
||||
public static void glCullFace(int mode) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.CULL_FACE);
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordCullFace(mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.polygonState.getCullFaceMode() != mode) {
|
||||
mod(glCtx.polygonState).setCullFaceMode(mode);
|
||||
RENDER_BACKEND.cullFace(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glFrontFace(int mode) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.FRONT_FACE);
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordFrontFace(mode);
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.polygonState.getFrontFace() != mode) {
|
||||
mod(glCtx.polygonState).setFrontFace(mode);
|
||||
RENDER_BACKEND.frontFace(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glHint(int target, int hint) {
|
||||
rejectExitCapture();
|
||||
if (hint != GL11.GL_NICEST && hint != GL11.GL_FASTEST && hint != GL11.GL_DONT_CARE) {
|
||||
return;
|
||||
}
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordHint(target, hint);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final int shift = hintShift(target);
|
||||
if (shift >= 0) {
|
||||
final IntegerStateStack hints = ctx().hintState;
|
||||
final int code = hint == GL11.GL_FASTEST ? 1 : hint == GL11.GL_NICEST ? 2 : 0;
|
||||
final int packed = hints.getValue() & ~(3 << shift) | code << shift;
|
||||
if (packed != hints.getValue()) mod(hints).setValue(packed);
|
||||
}
|
||||
if (!isLegacyHint(target)) {
|
||||
RENDER_BACKEND.hint(target, hint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glLineStipple(int factor, short pattern) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLineStipple(factor, pattern);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mod(glCtx.lineState);
|
||||
glCtx.lineState.setStippleFactor(factor);
|
||||
glCtx.lineState.setStipplePattern(pattern);
|
||||
}
|
||||
|
||||
public static void glPointSize(float size) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPointSize(size);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.pointState.getSize() != size) {
|
||||
mod(glCtx.pointState).setSize(size);
|
||||
RENDER_BACKEND.pointSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPolygonMode(int face, int polygonMode) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPolygonMode(face, polygonMode);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Track front/back separately in cache (Mesa compat semantics), but always issue GL_FRONT_AND_BACK to the driver (core profile constraint).
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean front = face != GL11.GL_BACK;
|
||||
final boolean back = face != GL11.GL_FRONT;
|
||||
final boolean needsUpdate = !caching
|
||||
|| (front && glCtx.polygonState.getFrontMode() != polygonMode)
|
||||
|| (back && glCtx.polygonState.getBackMode() != polygonMode);
|
||||
if (!needsUpdate) return;
|
||||
beforeUncapturedStateChange();
|
||||
if (caching) {
|
||||
mod(glCtx.polygonState);
|
||||
if (front) glCtx.polygonState.setFrontMode(polygonMode);
|
||||
if (back) glCtx.polygonState.setBackMode(polygonMode);
|
||||
}
|
||||
RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, polygonMode);
|
||||
}
|
||||
|
||||
public static void glPolygonOffset(float factor, float units) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.OFFSET_VALUES);
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPolygonOffset(factor, units);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
|
||||
if (!caching || glCtx.polygonState.getOffsetFactor() != factor || glCtx.polygonState.getOffsetUnits() != units || glCtx.polygonState.getOffsetClamp() != 0.0f) {
|
||||
mod(glCtx.polygonState);
|
||||
glCtx.polygonState.setOffsetFactor(factor);
|
||||
glCtx.polygonState.setOffsetUnits(units);
|
||||
glCtx.polygonState.setOffsetClamp(0.0f);
|
||||
RENDER_BACKEND.polygonOffset(factor, units);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPolygonOffsetClamp(float factor, float units, float clamp) {
|
||||
if (clamp == 0.0f) {
|
||||
glPolygonOffset(factor, units);
|
||||
return;
|
||||
}
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glPolygonOffsetClamp");
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.exitCapture.write(ExitStateCapture.OFFSET_VALUES);
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.polygonState.getOffsetFactor() != factor || glCtx.polygonState.getOffsetUnits() != units || glCtx.polygonState.getOffsetClamp() != clamp) {
|
||||
mod(glCtx.polygonState);
|
||||
glCtx.polygonState.setOffsetFactor(factor);
|
||||
glCtx.polygonState.setOffsetUnits(units);
|
||||
glCtx.polygonState.setOffsetClamp(clamp);
|
||||
RENDER_BACKEND.polygonOffsetClamp(factor, units, clamp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glScissor(int x, int y, int width, int height) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordScissor(x, y, width, height);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.scissor(x, y, width, height);
|
||||
if (isCachingEnabled()) {
|
||||
mod(ctx().scissorState).setScissor(x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glClipPlane(int plane, DoubleBuffer equation) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
final int pos = equation.position();
|
||||
DisplayListManager.recordClipPlane(plane, equation.get(pos), equation.get(pos + 1), equation.get(pos + 2), equation.get(pos + 3));
|
||||
return;
|
||||
}
|
||||
final int index = plane - GL11.GL_CLIP_PLANE0;
|
||||
if (index < 0 || index >= MAX_CLIP_PLANES) return;
|
||||
final int pos = equation.position();
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.clipPlaneState.setPlane(index, equation.get(pos), equation.get(pos + 1), equation.get(pos + 2), equation.get(pos + 3), glCtx.modelViewMatrix);
|
||||
glCtx.clipPlaneGeneration++;
|
||||
}
|
||||
|
||||
/** Returns true if any GL_CLIP_PLANE0..7 is currently enabled. */
|
||||
public static boolean anyClipPlaneEnabled() {
|
||||
return anyClipPlaneEnabled(ctx());
|
||||
}
|
||||
|
||||
public static boolean anyClipPlaneEnabled(GLContextState glCtx) {
|
||||
return glCtx.clipPlaneEnabledMask != 0;
|
||||
}
|
||||
|
||||
public static boolean clipEmulationActive() {
|
||||
return RenderSystem.isClipDistanceEmulated() && anyClipPlaneEnabled();
|
||||
}
|
||||
|
||||
public static boolean clipEmulationActive(GLContextState glCtx) {
|
||||
return RenderSystem.isClipDistanceEmulated() && anyClipPlaneEnabled(glCtx);
|
||||
}
|
||||
|
||||
public static void glSampleCoverage(float value, boolean invert) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glSampleCoverage");
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.sampleCoverage(value, invert);
|
||||
}
|
||||
|
||||
public static void glPolygonStipple(ByteBuffer pattern) {
|
||||
guardUnsupportedFFP("glPolygonStipple", "glPolygonStipple is not available in GL 3.3 core profile.");
|
||||
}
|
||||
|
||||
public static IntegerStateStack getShadeModelState() {
|
||||
return ctx().shadeModelState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getScissorTest() {
|
||||
return ctx().scissorTest;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getCullState() {
|
||||
return ctx().cullState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getDitherState() {
|
||||
return ctx().ditherState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getLineSmoothState() {
|
||||
return ctx().lineSmoothState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getLineStippleState() {
|
||||
return ctx().lineStippleState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPointSmoothState() {
|
||||
return ctx().pointSmoothState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPolygonSmoothState() {
|
||||
return ctx().polygonSmoothState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPolygonStippleState() {
|
||||
return ctx().polygonStippleState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMultisampleState() {
|
||||
return ctx().multisampleState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getSampleAlphaToCoverageState() {
|
||||
return ctx().sampleAlphaToCoverageState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getSampleAlphaToOneState() {
|
||||
return ctx().sampleAlphaToOneState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getSampleCoverageState() {
|
||||
return ctx().sampleCoverageState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPolygonOffsetPointState() {
|
||||
return ctx().polygonOffsetPointState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPolygonOffsetLineState() {
|
||||
return ctx().polygonOffsetLineState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPolygonOffsetFillState() {
|
||||
return ctx().polygonOffsetFillState;
|
||||
}
|
||||
|
||||
public static LineStateStack getLineState() {
|
||||
return ctx().lineState;
|
||||
}
|
||||
|
||||
public static PointStateStack getPointState() {
|
||||
return ctx().pointState;
|
||||
}
|
||||
|
||||
public static IntegerStateStack getHintState() {
|
||||
return ctx().hintState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getPointSpriteState() {
|
||||
return ctx().pointSpriteState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getEdgeFlagState() {
|
||||
return ctx().edgeFlagState;
|
||||
}
|
||||
|
||||
public static PolygonStateStack getPolygonState() {
|
||||
return ctx().polygonState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getAutoNormalState() {
|
||||
return ctx().autoNormalState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1Color4State() {
|
||||
return ctx().map1Color4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1IndexState() {
|
||||
return ctx().map1IndexState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1NormalState() {
|
||||
return ctx().map1NormalState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1TextureCoord1State() {
|
||||
return ctx().map1TextureCoord1State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1TextureCoord2State() {
|
||||
return ctx().map1TextureCoord2State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1TextureCoord3State() {
|
||||
return ctx().map1TextureCoord3State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1TextureCoord4State() {
|
||||
return ctx().map1TextureCoord4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1Vertex3State() {
|
||||
return ctx().map1Vertex3State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap1Vertex4State() {
|
||||
return ctx().map1Vertex4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2Color4State() {
|
||||
return ctx().map2Color4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2IndexState() {
|
||||
return ctx().map2IndexState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2NormalState() {
|
||||
return ctx().map2NormalState;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2TextureCoord1State() {
|
||||
return ctx().map2TextureCoord1State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2TextureCoord2State() {
|
||||
return ctx().map2TextureCoord2State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2TextureCoord3State() {
|
||||
return ctx().map2TextureCoord3State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2TextureCoord4State() {
|
||||
return ctx().map2TextureCoord4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2Vertex3State() {
|
||||
return ctx().map2Vertex3State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getMap2Vertex4State() {
|
||||
return ctx().map2Vertex4State;
|
||||
}
|
||||
|
||||
public static BooleanStateStack[] getClipPlaneStates() {
|
||||
return ctx().clipPlaneStates;
|
||||
}
|
||||
|
||||
public static ClipPlaneState getClipPlaneState() {
|
||||
return ctx().clipPlaneState;
|
||||
}
|
||||
|
||||
public static ViewPortStateStack getViewportState() {
|
||||
return ctx().viewportState;
|
||||
}
|
||||
|
||||
public static ScissorStateStack getScissorState() {
|
||||
return ctx().scissorState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.beforeUncapturedStateChange;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.guardUnsupportedFFP;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.isCachingEnabled;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
|
||||
public final class GLSMTexEnvGen {
|
||||
|
||||
private GLSMTexEnvGen() {}
|
||||
|
||||
public static void glTexEnvi(int target, int pname, int param) {
|
||||
handleTexEnvScalar(target, pname, param);
|
||||
}
|
||||
|
||||
public static void glTexEnvf(int target, int pname, float param) {
|
||||
handleTexEnvScalar(target, pname, param);
|
||||
}
|
||||
|
||||
public static void glTexEnv(int target, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL11.GL_TEXTURE_ENV && pname == GL11.GL_TEXTURE_ENV_COLOR && params.remaining() >= 4) {
|
||||
beforeUncapturedStateChange();
|
||||
final int pos = params.position();
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
envState.beforeModify();
|
||||
envState.envColorR = params.get(pos);
|
||||
envState.envColorG = params.get(pos + 1);
|
||||
envState.envColorB = params.get(pos + 2);
|
||||
envState.envColorA = params.get(pos + 3);
|
||||
glCtx.fragmentGeneration++;
|
||||
return;
|
||||
}
|
||||
if (target == GL14.GL_TEXTURE_FILTER_CONTROL) {
|
||||
guardUnsupportedFFP(
|
||||
"glTexEnv",
|
||||
"LOD bias via buffer glTexEnv is not supported; use glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, value) instead.");
|
||||
return;
|
||||
}
|
||||
// Silently ignore other buffer glTexEnv calls (some mods pass combine params via buffers)
|
||||
}
|
||||
|
||||
public static void glTexEnv(int target, int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
if (target == GL11.GL_TEXTURE_ENV) {
|
||||
handleTexEnvScalar(target, pname, (float) params.get(params.position()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scalar glTexEnvi/glTexEnvf dispatch. glTexEnv is removed in core profile. GL_TEXTURE_FILTER_CONTROL (LOD bias) is remapped to glTexParameterf. All
|
||||
* GL_TEXTURE_ENV parameters including GL_COMBINE sub-params are tracked in per-unit TexEnvState.
|
||||
*/
|
||||
private static void handleTexEnvScalar(int target, int pname, float param) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL14.GL_TEXTURE_FILTER_CONTROL) {
|
||||
// LOD bias via legacy glTexEnv path - remap to core-profile glTexParameterf
|
||||
GLSMTextures.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, param);
|
||||
return;
|
||||
}
|
||||
if (target != GL11.GL_TEXTURE_ENV) return;
|
||||
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
envState.beforeModify();
|
||||
final int iparam = (int) param;
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_TEXTURE_ENV_MODE -> {
|
||||
envState.mode = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_COMBINE_RGB -> {
|
||||
envState.combineRgb = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_COMBINE_ALPHA -> {
|
||||
envState.combineAlpha = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE0_RGB -> {
|
||||
envState.sourceRgb[0] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE1_RGB -> {
|
||||
envState.sourceRgb[1] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE2_RGB -> {
|
||||
envState.sourceRgb[2] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE0_ALPHA -> {
|
||||
envState.sourceAlpha[0] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE1_ALPHA -> {
|
||||
envState.sourceAlpha[1] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_SOURCE2_ALPHA -> {
|
||||
envState.sourceAlpha[2] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND0_RGB -> {
|
||||
envState.operandRgb[0] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND1_RGB -> {
|
||||
envState.operandRgb[1] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND2_RGB -> {
|
||||
envState.operandRgb[2] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND0_ALPHA -> {
|
||||
envState.operandAlpha[0] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND1_ALPHA -> {
|
||||
envState.operandAlpha[1] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_OPERAND2_ALPHA -> {
|
||||
envState.operandAlpha[2] = iparam;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL13.GL_RGB_SCALE -> {
|
||||
envState.scaleRgb = param;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
case GL11.GL_ALPHA_SCALE -> {
|
||||
envState.scaleAlpha = param;
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
// Silently ignore unknown pname - other mods may pass unrecognized params
|
||||
}
|
||||
}
|
||||
|
||||
public static float glGetTexEnvf(int target, int pname) {
|
||||
return (float) glGetTexEnvi(target, pname);
|
||||
}
|
||||
|
||||
public static int glGetTexEnvi(int target, int pname) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target != GL11.GL_TEXTURE_ENV) return 0;
|
||||
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_ENV_MODE -> envState.mode;
|
||||
case GL13.GL_COMBINE_RGB -> envState.combineRgb;
|
||||
case GL13.GL_COMBINE_ALPHA -> envState.combineAlpha;
|
||||
case GL13.GL_SOURCE0_RGB -> envState.sourceRgb[0];
|
||||
case GL13.GL_SOURCE1_RGB -> envState.sourceRgb[1];
|
||||
case GL13.GL_SOURCE2_RGB -> envState.sourceRgb[2];
|
||||
case GL13.GL_SOURCE0_ALPHA -> envState.sourceAlpha[0];
|
||||
case GL13.GL_SOURCE1_ALPHA -> envState.sourceAlpha[1];
|
||||
case GL13.GL_SOURCE2_ALPHA -> envState.sourceAlpha[2];
|
||||
case GL13.GL_OPERAND0_RGB -> envState.operandRgb[0];
|
||||
case GL13.GL_OPERAND1_RGB -> envState.operandRgb[1];
|
||||
case GL13.GL_OPERAND2_RGB -> envState.operandRgb[2];
|
||||
case GL13.GL_OPERAND0_ALPHA -> envState.operandAlpha[0];
|
||||
case GL13.GL_OPERAND1_ALPHA -> envState.operandAlpha[1];
|
||||
case GL13.GL_OPERAND2_ALPHA -> envState.operandAlpha[2];
|
||||
case GL13.GL_RGB_SCALE -> (int) envState.scaleRgb;
|
||||
case GL11.GL_ALPHA_SCALE -> (int) envState.scaleAlpha;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetTexEnv(int target, int pname, IntBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target != GL11.GL_TEXTURE_ENV) return;
|
||||
if (pname == GL11.GL_TEXTURE_ENV_COLOR) {
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
params.put(params.position(), (int) (envState.envColorR * 2147483647.0f));
|
||||
params.put(params.position() + 1, (int) (envState.envColorG * 2147483647.0f));
|
||||
params.put(params.position() + 2, (int) (envState.envColorB * 2147483647.0f));
|
||||
params.put(params.position() + 3, (int) (envState.envColorA * 2147483647.0f));
|
||||
} else {
|
||||
params.put(params.position(), glGetTexEnvi(target, pname));
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetTexEnv(int target, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target != GL11.GL_TEXTURE_ENV) return;
|
||||
if (pname == GL11.GL_TEXTURE_ENV_COLOR) {
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
params.put(params.position(), envState.envColorR);
|
||||
params.put(params.position() + 1, envState.envColorG);
|
||||
params.put(params.position() + 2, envState.envColorB);
|
||||
params.put(params.position() + 3, envState.envColorA);
|
||||
} else {
|
||||
params.put(params.position(), (float) glGetTexEnvi(target, pname));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void glTexGeni(int coord, int pname, int param) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
glCtx.textures.getTexGenState(unit).setMode(coord, param);
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTexGenf(int coord, int pname, float param) {
|
||||
// Single-float form only valid for GL_TEXTURE_GEN_MODE
|
||||
glTexGeni(coord, pname, (int) param);
|
||||
}
|
||||
|
||||
public static void glTexGend(int coord, int pname, double param) {
|
||||
glTexGeni(coord, pname, (int) param);
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, FloatBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
final var texGenState = glCtx.textures.getTexGenState(unit);
|
||||
final int pos = params.position();
|
||||
switch (pname) {
|
||||
case GL11.GL_TEXTURE_GEN_MODE -> {
|
||||
texGenState.setMode(coord, (int) params.get(pos));
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
case GL11.GL_OBJECT_PLANE -> {
|
||||
glCtx.texGenTempPlane[0] = params.get(pos);
|
||||
glCtx.texGenTempPlane[1] = params.get(pos + 1);
|
||||
glCtx.texGenTempPlane[2] = params.get(pos + 2);
|
||||
glCtx.texGenTempPlane[3] = params.get(pos + 3);
|
||||
texGenState.setObjectPlane(coord, glCtx.texGenTempPlane);
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
case GL11.GL_EYE_PLANE -> {
|
||||
glCtx.texGenTempPlane[0] = params.get(pos);
|
||||
glCtx.texGenTempPlane[1] = params.get(pos + 1);
|
||||
glCtx.texGenTempPlane[2] = params.get(pos + 2);
|
||||
glCtx.texGenTempPlane[3] = params.get(pos + 3);
|
||||
texGenState.setEyePlane(coord, glCtx.texGenTempPlane, glCtx.modelViewMatrix);
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, DoubleBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
final int pos = params.position();
|
||||
glCtx.texGenTempPlane[0] = (float) params.get(pos);
|
||||
glCtx.texGenTempPlane[1] = (float) params.get(pos + 1);
|
||||
glCtx.texGenTempPlane[2] = (float) params.get(pos + 2);
|
||||
glCtx.texGenTempPlane[3] = (float) params.get(pos + 3);
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
final var texGenState = glCtx.textures.getTexGenState(unit);
|
||||
switch (pname) {
|
||||
case GL11.GL_TEXTURE_GEN_MODE -> {
|
||||
texGenState.setMode(coord, (int) params.get(pos));
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
case GL11.GL_OBJECT_PLANE -> {
|
||||
texGenState.setObjectPlane(coord, glCtx.texGenTempPlane);
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
case GL11.GL_EYE_PLANE -> {
|
||||
texGenState.setEyePlane(coord, glCtx.texGenTempPlane, glCtx.modelViewMatrix);
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, IntBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
glCtx.textures.getTexGenState(unit).setMode(coord, params.get(params.position()));
|
||||
glCtx.texGenGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetTexGeni(int coord, int pname) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
return glCtx.textures.getTexGenState(glCtx.activeTextureUnit.getValue()).getMode(coord);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static float glGetTexGenf(int coord, int pname) {
|
||||
return (float) glGetTexGeni(coord, pname);
|
||||
}
|
||||
|
||||
public static double glGetTexGend(int coord, int pname) {
|
||||
return glGetTexGeni(coord, pname);
|
||||
}
|
||||
|
||||
public static void glGetTexGen(int coord, int pname, IntBuffer params) {
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
params.put(params.position(), glGetTexGeni(coord, pname));
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetTexGen(int coord, int pname, FloatBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final var state = glCtx.textures.getTexGenState(glCtx.activeTextureUnit.getValue());
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
params.put(params.position(), (float) state.getMode(coord));
|
||||
} else if (pname == GL11.GL_OBJECT_PLANE) {
|
||||
final float[] plane = state.getObjectPlane(coord);
|
||||
params.put(params.position(), plane[0]);
|
||||
params.put(params.position() + 1, plane[1]);
|
||||
params.put(params.position() + 2, plane[2]);
|
||||
params.put(params.position() + 3, plane[3]);
|
||||
} else if (pname == GL11.GL_EYE_PLANE) {
|
||||
final float[] plane = state.getEyePlane(coord);
|
||||
params.put(params.position(), plane[0]);
|
||||
params.put(params.position() + 1, plane[1]);
|
||||
params.put(params.position() + 2, plane[2]);
|
||||
params.put(params.position() + 3, plane[3]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetTexGen(int coord, int pname, DoubleBuffer params) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final var state = glCtx.textures.getTexGenState(glCtx.activeTextureUnit.getValue());
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
params.put(params.position(), state.getMode(coord));
|
||||
} else if (pname == GL11.GL_OBJECT_PLANE) {
|
||||
final float[] plane = state.getObjectPlane(coord);
|
||||
params.put(params.position(), plane[0]);
|
||||
params.put(params.position() + 1, plane[1]);
|
||||
params.put(params.position() + 2, plane[2]);
|
||||
params.put(params.position() + 3, plane[3]);
|
||||
} else if (pname == GL11.GL_EYE_PLANE) {
|
||||
final float[] plane = state.getEyePlane(coord);
|
||||
params.put(params.position(), plane[0]);
|
||||
params.put(params.position() + 1, plane[1]);
|
||||
params.put(params.position() + 2, plane[2]);
|
||||
params.put(params.position() + 3, plane[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureTargets;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2LongMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2LongMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.acquireDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.drawableContextStates;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.primaryContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.releaseDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.splashComplete;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMTextureLifetime {
|
||||
|
||||
private GLSMTextureLifetime() {}
|
||||
|
||||
// Deferred texture deletion: names kept valid until glGenTextures recycles them.
|
||||
// Emulates Mesa/compat-profile behavior where delete-then-bind doesn't error.
|
||||
private static final Int2LongOpenHashMap deferredDeleteTextures = new Int2LongOpenHashMap();
|
||||
private static final long DEFERRED_ABSENT = Long.MIN_VALUE;
|
||||
private static final long DEFERRED_ATTACHED = -1;
|
||||
private static final long DEFERRED_RELEASED = -2;
|
||||
private static final int RELEASE_DELAY_FRAMES = 3;
|
||||
private static long frameIndex;
|
||||
private static long nextReleaseFrame = Long.MAX_VALUE;
|
||||
private static final Int2IntOpenHashMap non2DTextureTargets = new Int2IntOpenHashMap();
|
||||
private static final Int2IntOpenHashMap textureFramebufferRefs = new Int2IntOpenHashMap();
|
||||
private static final FloatBuffer TRANSPARENT_BLACK = BufferUtils.createFloatBuffer(4);
|
||||
private static final Tracy.ZoneId Z_TEX_RELEASE_STORAGE = Tracy.zoneId("texReleaseStorage", Tracy.COLOR_FFP);
|
||||
private static final Tracy.ZoneId Z_TEX_RESCUE_RESET = Tracy.zoneId("texRescueReset", Tracy.COLOR_FFP);
|
||||
private static final Tracy.ZoneId Z_TEX_FLUSH_DELETES = Tracy.zoneId("texFlushDeletes", Tracy.COLOR_FFP);
|
||||
static {
|
||||
deferredDeleteTextures.defaultReturnValue(DEFERRED_ABSENT);
|
||||
non2DTextureTargets.defaultReturnValue(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer deleting textures until it has been reclaimed.
|
||||
* Why you might ask? In compat profile if you bind a texture that doesn't exist, ie a deleted texture,
|
||||
* mesa will silently create it for you, in core this is an error. Why would you be binding a deleted texture?
|
||||
* Damned if I know, but vanilla and mods do it... they create one, delete it, and then bind it....
|
||||
* */
|
||||
static void deferDeleteTexture(int id) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (id == 0) return;
|
||||
|
||||
final int savedUnit = glCtx.activeTextureUnit.getValue();
|
||||
int activeUnit = savedUnit;
|
||||
|
||||
// Unbind from all units that have this texture
|
||||
for (int i = 0; i <= glCtx.maxBoundTextureUnit; i++) {
|
||||
final TextureBindingStack unitBindings = glCtx.textures.getTextureUnitBindings(i);
|
||||
if (unitBindings.getBinding() == id) {
|
||||
if (i != activeUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
activeUnit = i;
|
||||
}
|
||||
final int unitTarget = unitBindings.getTarget();
|
||||
RENDER_BACKEND.bindTexture(unitTarget == 0 ? GL11.GL_TEXTURE_2D : unitTarget, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i <= glCtx.maxBoundImageUnit; i++) {
|
||||
final ImageUnitBinding image = glCtx.imageUnits.get(i);
|
||||
if (image != null && image.getTexture() == id) {
|
||||
RENDER_BACKEND.bindImageTexture(i, 0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeUnit != savedUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + savedUnit);
|
||||
}
|
||||
|
||||
final TextureBindingStack savedBindings = glCtx.textures.getTextureUnitBindings(savedUnit);
|
||||
if (!is2D(savedBindings.getTarget()) && previous2D(savedBindings) == id) restoreUnitAfter2DBind(savedBindings, id, id);
|
||||
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
detachFromFramebuffer(glCtx, GL30.GL_DRAW_FRAMEBUFFER, glCtx.drawFramebuffer, id);
|
||||
if (glCtx.readFramebuffer != glCtx.drawFramebuffer) detachFromFramebuffer(glCtx, GL30.GL_READ_FRAMEBUFFER, glCtx.readFramebuffer, id);
|
||||
if (textureFramebufferRefs.get(id) > 0) deferredDeleteTextures.put(id, DEFERRED_ATTACHED);
|
||||
else scheduleRelease(id);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void scheduleRelease(int id) {
|
||||
deferredDeleteTextures.put(id, frameIndex);
|
||||
nextReleaseFrame = Math.min(nextReleaseFrame, frameIndex + RELEASE_DELAY_FRAMES);
|
||||
}
|
||||
|
||||
static final long BIND_REJECTED = Long.MIN_VALUE + 1;
|
||||
|
||||
// Rebinding a deferred-delete texture rescues it (Mesa/compat-profile behavior)
|
||||
static long claimForBind(int texture, int target) {
|
||||
if (texture == 0) return DEFERRED_ABSENT;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
final long state = deferredDeleteTextures.isEmpty() ? DEFERRED_ABSENT : deferredDeleteTextures.get(texture);
|
||||
if (state == DEFERRED_ABSENT) {
|
||||
recordTarget(texture, target);
|
||||
return DEFERRED_ABSENT;
|
||||
}
|
||||
return non2DTextureTargets.get(texture) == target ? deferredDeleteTextures.remove(texture) : BIND_REJECTED;
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
static void finishRescue(int target, int texture, long claim) {
|
||||
if (claim != DEFERRED_ABSENT) resetRescuedTexture(target, texture, claim);
|
||||
}
|
||||
|
||||
static int targetOf(int texture) {
|
||||
return non2DTextureTargets.get(texture);
|
||||
}
|
||||
|
||||
static boolean isDeferredDelete(int texture) {
|
||||
if (texture == 0 || deferredDeleteTextures.isEmpty()) return false;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
return deferredDeleteTextures.containsKey(texture);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void onFrameEnd() {
|
||||
frameIndex++;
|
||||
if (!splashComplete || frameIndex < nextReleaseFrame) return;
|
||||
nextReleaseFrame = Long.MAX_VALUE;
|
||||
for (Int2LongMap.Entry entry : Int2LongMaps.fastIterable(deferredDeleteTextures)) {
|
||||
final long since = entry.getLongValue();
|
||||
if (since < 0) continue;
|
||||
if (frameIndex - since < RELEASE_DELAY_FRAMES) {
|
||||
nextReleaseFrame = Math.min(nextReleaseFrame, since + RELEASE_DELAY_FRAMES);
|
||||
continue;
|
||||
}
|
||||
final int id = entry.getIntKey();
|
||||
if (non2DTextureTargets.get(id) == GL11.GL_TEXTURE_2D) releaseStorage2D(id);
|
||||
entry.setValue(DEFERRED_RELEASED);
|
||||
}
|
||||
}
|
||||
|
||||
public static int deferredTexturesAwaitingRelease() {
|
||||
int count = 0;
|
||||
final LongIterator it = deferredDeleteTextures.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.nextLong() != DEFERRED_RELEASED) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void releaseStorage2D(int id) {
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TEX_RELEASE_STORAGE);
|
||||
try {
|
||||
final TextureBindingStack unit = ctx().textures.getTextureUnitBindings(GLSMTextures.getActiveTextureUnit());
|
||||
final int previous2D = previous2D(unit);
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
if (bindTook(GL11.GL_TEXTURE_2D, id)) releaseBoundStorage2D();
|
||||
restoreUnitAfter2DBind(unit, id, previous2D);
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
private static void restoreUnitAfter2DBind(TextureBindingStack unit, int deleted, int previous2D) {
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, previous2D == deleted ? 0 : previous2D);
|
||||
final int target = unit.getTarget();
|
||||
if (is2D(target)) return;
|
||||
RENDER_BACKEND.bindTexture(target, unit.getBinding() == deleted ? 0 : unit.getBinding());
|
||||
}
|
||||
|
||||
static boolean is2D(int target) {
|
||||
return target == 0 || target == GL11.GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
private static int previous2D(TextureBindingStack unit) {
|
||||
return is2D(unit.getTarget()) ? unit.getBinding() : RENDER_BACKEND.getInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
}
|
||||
|
||||
static void recordTarget(int texture, int target) {
|
||||
if (target != GL11.GL_TEXTURE_2D) non2DTextureTargets.put(texture, target);
|
||||
}
|
||||
|
||||
private static void releaseBoundStorage2D() {
|
||||
if (RenderSystem.supportsTextureStorage() && RENDER_BACKEND.getTexParameteri(GL11.GL_TEXTURE_2D, GL42.GL_TEXTURE_IMMUTABLE_FORMAT) != 0) return;
|
||||
final int size = Math.max(RENDER_BACKEND.getTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH), RENDER_BACKEND.getTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT));
|
||||
final int levels = Math.max(1, 32 - Integer.numberOfLeadingZeros(size));
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
for (int level = 0; level < levels; level++) {
|
||||
RENDER_BACKEND.texImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, 0, 0, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
}
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
private static boolean bindTook(int target, int texture) {
|
||||
return RENDER_BACKEND.getInteger(TextureTargets.bindingQuery(target)) == texture;
|
||||
}
|
||||
|
||||
private static void resetRescuedTexture(int target, int texture, long state) {
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TEX_RESCUE_RESET);
|
||||
try {
|
||||
if (!bindTook(target, texture)) return;
|
||||
if (target == GL11.GL_TEXTURE_2D && state >= 0) releaseBoundStorage2D();
|
||||
if (target == GL31.GL_TEXTURE_BUFFER || target == GL32.GL_TEXTURE_2D_MULTISAMPLE || target == GL32.GL_TEXTURE_2D_MULTISAMPLE_ARRAY) return;
|
||||
final boolean rectangle = target == GL31.GL_TEXTURE_RECTANGLE;
|
||||
final int wrap = rectangle ? GL12.GL_CLAMP_TO_EDGE : GL11.GL_REPEAT;
|
||||
RENDER_BACKEND.texParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, rectangle ? GL11.GL_LINEAR : GL11.GL_NEAREST_MIPMAP_LINEAR);
|
||||
RENDER_BACKEND.texParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
RENDER_BACKEND.texParameteri(target, GL11.GL_TEXTURE_WRAP_S, wrap);
|
||||
RENDER_BACKEND.texParameteri(target, GL11.GL_TEXTURE_WRAP_T, wrap);
|
||||
RENDER_BACKEND.texParameteri(target, GL12.GL_TEXTURE_WRAP_R, wrap);
|
||||
RENDER_BACKEND.texParameteri(target, GL12.GL_TEXTURE_BASE_LEVEL, 0);
|
||||
if (!rectangle) RENDER_BACKEND.texParameteri(target, GL12.GL_TEXTURE_MAX_LEVEL, 1000);
|
||||
RENDER_BACKEND.texParameterf(target, GL12.GL_TEXTURE_MIN_LOD, -1000.0f);
|
||||
RENDER_BACKEND.texParameterf(target, GL12.GL_TEXTURE_MAX_LOD, 1000.0f);
|
||||
RENDER_BACKEND.texParameteri(target, GL14.GL_TEXTURE_COMPARE_MODE, GL11.GL_NONE);
|
||||
RENDER_BACKEND.texParameteri(target, GL14.GL_TEXTURE_COMPARE_FUNC, GL11.GL_LEQUAL);
|
||||
RENDER_BACKEND.texParameteri(target, GL33.GL_TEXTURE_SWIZZLE_R, GL11.GL_RED);
|
||||
RENDER_BACKEND.texParameteri(target, GL33.GL_TEXTURE_SWIZZLE_G, GL11.GL_GREEN);
|
||||
RENDER_BACKEND.texParameteri(target, GL33.GL_TEXTURE_SWIZZLE_B, GL11.GL_BLUE);
|
||||
RENDER_BACKEND.texParameteri(target, GL33.GL_TEXTURE_SWIZZLE_A, GL11.GL_ALPHA);
|
||||
if (!RenderSystem.isGLES()) {
|
||||
RENDER_BACKEND.texParameterf(target, GL14.GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||
RENDER_BACKEND.texParameterfv(target, GL11.GL_TEXTURE_BORDER_COLOR, TRANSPARENT_BLACK);
|
||||
}
|
||||
if (RenderSystem.supportsStencilTexturing()) RENDER_BACKEND.texParameteri(target, GL43.GL_DEPTH_STENCIL_TEXTURE_MODE, GL11.GL_DEPTH_COMPONENT);
|
||||
if (RENDER_BACKEND.isAnisotropicSupported()) RENDER_BACKEND.texParameterf(target, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
/** Flush deferred deletes so the driver can recycle names. */
|
||||
static void flushDeferredTextureDeletes() {
|
||||
if (!splashComplete || deferredDeleteTextures.isEmpty()) return;
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_TEX_FLUSH_DELETES);
|
||||
try {
|
||||
final GLContextState primary = primaryContext();
|
||||
final GLContextState[] drawables = drawableContextStates;
|
||||
final var it = Int2LongMaps.fastIterator(deferredDeleteTextures);
|
||||
while (it.hasNext()) {
|
||||
final Int2LongMap.Entry entry = it.next();
|
||||
if (entry.getLongValue() == DEFERRED_ATTACHED) continue;
|
||||
final int id = entry.getIntKey();
|
||||
it.remove();
|
||||
RENDER_BACKEND.deleteTextures(id);
|
||||
non2DTextureTargets.remove(id);
|
||||
GLSMTextures.clearTextureBindings(primary, id);
|
||||
for (int i = 0; i < drawables.length; i++) {
|
||||
GLSMTextures.clearTextureBindings(drawables[i], id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEPTH_SLOT = 16;
|
||||
private static final int STENCIL_SLOT = 17;
|
||||
private static final int FRAMEBUFFER_SLOTS = 18;
|
||||
|
||||
private static int attachmentSlot(int attachment) {
|
||||
return switch (attachment) {
|
||||
case GL30.GL_DEPTH_ATTACHMENT -> DEPTH_SLOT;
|
||||
case GL30.GL_STENCIL_ATTACHMENT -> STENCIL_SLOT;
|
||||
default -> attachment >= GL30.GL_COLOR_ATTACHMENT0 && attachment <= GL30.GL_COLOR_ATTACHMENT15 ? attachment - GL30.GL_COLOR_ATTACHMENT0 : -1;
|
||||
};
|
||||
}
|
||||
|
||||
private static int slotAttachment(int slot) {
|
||||
return switch (slot) {
|
||||
case DEPTH_SLOT -> GL30.GL_DEPTH_ATTACHMENT;
|
||||
case STENCIL_SLOT -> GL30.GL_STENCIL_ATTACHMENT;
|
||||
default -> GL30.GL_COLOR_ATTACHMENT0 + slot;
|
||||
};
|
||||
}
|
||||
|
||||
static boolean beginAttach(int target, int attachment, int texture) {
|
||||
final GLContextState glCtx = ctx();
|
||||
return beginAttach(glCtx, target == GL30.GL_READ_FRAMEBUFFER ? glCtx.readFramebuffer : glCtx.drawFramebuffer, attachment, texture);
|
||||
}
|
||||
|
||||
static boolean beginAttach(GLContextState glCtx, int framebuffer, int attachment, int texture) {
|
||||
if (isDeferredDelete(texture)) return false;
|
||||
TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
trackFramebufferAttachment(glCtx, framebuffer, attachment, texture);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void trackFramebufferAttachment(GLContextState glCtx, int framebuffer, int attachment, int texture) {
|
||||
if (framebuffer == 0) return;
|
||||
if (attachment == GL30.GL_DEPTH_STENCIL_ATTACHMENT) {
|
||||
trackFramebufferAttachment(glCtx, framebuffer, GL30.GL_DEPTH_ATTACHMENT, texture);
|
||||
trackFramebufferAttachment(glCtx, framebuffer, GL30.GL_STENCIL_ATTACHMENT, texture);
|
||||
return;
|
||||
}
|
||||
final int slot = attachmentSlot(attachment);
|
||||
if (slot < 0) return;
|
||||
int[] slots = glCtx.framebufferTextures.get(framebuffer);
|
||||
if (slots == null) {
|
||||
if (texture == 0) return;
|
||||
slots = new int[FRAMEBUFFER_SLOTS];
|
||||
glCtx.framebufferTextures.put(framebuffer, slots);
|
||||
}
|
||||
setFramebufferSlot(slots, slot, texture);
|
||||
}
|
||||
|
||||
static void setFramebufferSlot(int[] slots, int slot, int texture) {
|
||||
final int previous = slots[slot];
|
||||
if (previous == texture) return;
|
||||
slots[slot] = texture;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (texture != 0) textureFramebufferRefs.addTo(texture, 1);
|
||||
if (previous != 0 && textureFramebufferRefs.addTo(previous, -1) == 1) {
|
||||
textureFramebufferRefs.remove(previous);
|
||||
if (deferredDeleteTextures.get(previous) == DEFERRED_ATTACHED) scheduleRelease(previous);
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void detachFromFramebuffer(GLContextState glCtx, int target, int framebuffer, int texture) {
|
||||
final int[] slots = glCtx.framebufferTextures.get(framebuffer);
|
||||
if (slots == null) return;
|
||||
for (int slot = 0; slot < slots.length; slot++) {
|
||||
if (slots[slot] != texture) continue;
|
||||
RENDER_BACKEND.framebufferTexture2D(target, slotAttachment(slot), GL11.GL_TEXTURE_2D, 0, 0);
|
||||
setFramebufferSlot(slots, slot, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,654 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.acquireDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.ctx;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.rejectExitCapture;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.releaseDrawLock;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.requireContext;
|
||||
import static com.gtnewhorizons.angelica.glsm.GLStateManager.warnOnceTrace;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public final class GLSMVertexInput {
|
||||
|
||||
private GLSMVertexInput() {}
|
||||
|
||||
public static int getBoundEBO() {
|
||||
return ctx().vaos.boundEBO;
|
||||
}
|
||||
|
||||
private static int texCoordConventional(GLContextState glCtx) {
|
||||
return VAOManager.CONV_TEX0 + glCtx.clientActiveTextureUnit;
|
||||
}
|
||||
|
||||
static void prepareClientArrays(GLContextState glCtx) {
|
||||
final VAOManager vaos = glCtx.vaos;
|
||||
if (vaos.hasAnyClientSideEnabledAttrib()) {
|
||||
vaos.uploadClientArraysToVBO();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLegalClientArray(int conv, int size, int type, int stride) {
|
||||
return isLegalPointer(VAOManager.isLegalConventional(conv, size, type), "client-array", "client array", conv, size, type, stride);
|
||||
}
|
||||
|
||||
private static boolean isLegalVertexAttrib(int index, int size, int type, int stride, boolean integer) {
|
||||
return isLegalPointer(VAOManager.isLegalGeneric(index, size, type, integer), "vertex-attrib", "vertex attribute", index, size, type, stride);
|
||||
}
|
||||
|
||||
private static boolean isLegalPointer(boolean legal, String key, String what, int slot, int size, int type, int stride) {
|
||||
if (stride < 0) warnOnceTrace(key + "-stride", "Negative {} stride {}", what, stride);
|
||||
if (legal) return true;
|
||||
warnOnceTrace(key + "-" + slot, "Ignoring {} {} with illegal size {} / type 0x{}", what, slot, size, Integer.toHexString(type));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isValidVertexAttribIndex(int index, String function) {
|
||||
if (index >= 0 && index < VAOManager.MAX_ATTRIBS) return true;
|
||||
warnOnceTrace(function, "{}({}) is outside the {} supported vertex attributes; ignoring", function, index, VAOManager.MAX_ATTRIBS);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int conv, int size, int type, boolean normalized, int stride, long offset) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalClientArray(conv, size, type, stride)) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.vaos.setConventional(conv, size, type, normalized, stride, offset, glCtx.boundVBO)) {
|
||||
RENDER_BACKEND.vertexAttribPointer(VAOManager.conventionalLocation(conv), size, type, normalized, stride, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int conv, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalClientArray(conv, size, type, stride)) return;
|
||||
ctx().vaos.setConventional(conv, size, type, normalized, stride, pointer);
|
||||
}
|
||||
|
||||
static final int FIELD_SIZE = 0, FIELD_TYPE = 1 << 8, FIELD_STRIDE = 2 << 8, FIELD_BINDING = 3 << 8, FIELD_POINTER = 4 << 8;
|
||||
|
||||
static int clientArrayField(GLContextState glCtx, int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_VERTEX_ARRAY_SIZE -> VAOManager.CONV_POSITION | FIELD_SIZE;
|
||||
case GL11.GL_VERTEX_ARRAY_TYPE -> VAOManager.CONV_POSITION | FIELD_TYPE;
|
||||
case GL11.GL_VERTEX_ARRAY_STRIDE -> VAOManager.CONV_POSITION | FIELD_STRIDE;
|
||||
case GL15.GL_VERTEX_ARRAY_BUFFER_BINDING -> VAOManager.CONV_POSITION | FIELD_BINDING;
|
||||
case GL11.GL_VERTEX_ARRAY_POINTER -> VAOManager.CONV_POSITION | FIELD_POINTER;
|
||||
case GL11.GL_NORMAL_ARRAY_TYPE -> VAOManager.CONV_NORMAL | FIELD_TYPE;
|
||||
case GL11.GL_NORMAL_ARRAY_STRIDE -> VAOManager.CONV_NORMAL | FIELD_STRIDE;
|
||||
case GL15.GL_NORMAL_ARRAY_BUFFER_BINDING -> VAOManager.CONV_NORMAL | FIELD_BINDING;
|
||||
case GL11.GL_NORMAL_ARRAY_POINTER -> VAOManager.CONV_NORMAL | FIELD_POINTER;
|
||||
case GL11.GL_COLOR_ARRAY_SIZE -> VAOManager.CONV_COLOR | FIELD_SIZE;
|
||||
case GL11.GL_COLOR_ARRAY_TYPE -> VAOManager.CONV_COLOR | FIELD_TYPE;
|
||||
case GL11.GL_COLOR_ARRAY_STRIDE -> VAOManager.CONV_COLOR | FIELD_STRIDE;
|
||||
case GL15.GL_COLOR_ARRAY_BUFFER_BINDING -> VAOManager.CONV_COLOR | FIELD_BINDING;
|
||||
case GL11.GL_COLOR_ARRAY_POINTER -> VAOManager.CONV_COLOR | FIELD_POINTER;
|
||||
case GL11.GL_INDEX_ARRAY_TYPE -> VAOManager.CONV_INDEX | FIELD_TYPE;
|
||||
case GL11.GL_INDEX_ARRAY_STRIDE -> VAOManager.CONV_INDEX | FIELD_STRIDE;
|
||||
case GL15.GL_INDEX_ARRAY_BUFFER_BINDING -> VAOManager.CONV_INDEX | FIELD_BINDING;
|
||||
case GL11.GL_INDEX_ARRAY_POINTER -> VAOManager.CONV_INDEX | FIELD_POINTER;
|
||||
case GL11.GL_EDGE_FLAG_ARRAY_STRIDE -> VAOManager.CONV_EDGE_FLAG | FIELD_STRIDE;
|
||||
case GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING -> VAOManager.CONV_EDGE_FLAG | FIELD_BINDING;
|
||||
case GL11.GL_EDGE_FLAG_ARRAY_POINTER -> VAOManager.CONV_EDGE_FLAG | FIELD_POINTER;
|
||||
case GL14.GL_SECONDARY_COLOR_ARRAY_SIZE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_SIZE;
|
||||
case GL14.GL_SECONDARY_COLOR_ARRAY_TYPE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_TYPE;
|
||||
case GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_STRIDE;
|
||||
case GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING -> VAOManager.CONV_SECONDARY_COLOR | FIELD_BINDING;
|
||||
case GL14.GL_SECONDARY_COLOR_ARRAY_POINTER -> VAOManager.CONV_SECONDARY_COLOR | FIELD_POINTER;
|
||||
case GL14.GL_FOG_COORDINATE_ARRAY_TYPE -> VAOManager.CONV_FOG_COORD | FIELD_TYPE;
|
||||
case GL14.GL_FOG_COORDINATE_ARRAY_STRIDE -> VAOManager.CONV_FOG_COORD | FIELD_STRIDE;
|
||||
case GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING -> VAOManager.CONV_FOG_COORD | FIELD_BINDING;
|
||||
case GL14.GL_FOG_COORDINATE_ARRAY_POINTER -> VAOManager.CONV_FOG_COORD | FIELD_POINTER;
|
||||
case GL11.GL_TEXTURE_COORD_ARRAY_SIZE -> texCoordConventional(glCtx) | FIELD_SIZE;
|
||||
case GL11.GL_TEXTURE_COORD_ARRAY_TYPE -> texCoordConventional(glCtx) | FIELD_TYPE;
|
||||
case GL11.GL_TEXTURE_COORD_ARRAY_STRIDE -> texCoordConventional(glCtx) | FIELD_STRIDE;
|
||||
case GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING -> texCoordConventional(glCtx) | FIELD_BINDING;
|
||||
case GL11.GL_TEXTURE_COORD_ARRAY_POINTER -> texCoordConventional(glCtx) | FIELD_POINTER;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int clientArrayParam(GLContextState glCtx, int key) {
|
||||
final int conv = key & 0xFF;
|
||||
final VAOManager.Attrib a = glCtx.vaos.conventional(conv);
|
||||
final boolean specified = a != null && a.size != 0;
|
||||
return switch (key & ~0xFF) {
|
||||
case FIELD_SIZE -> specified ? a.size : conv == VAOManager.CONV_SECONDARY_COLOR ? 3 : 4;
|
||||
case FIELD_TYPE -> specified ? a.type : GL11.GL_FLOAT;
|
||||
case FIELD_STRIDE -> specified ? a.stride : 0;
|
||||
default -> specified ? a.vboId : 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static ByteBuffer glGetPointer(int pname, long result_size) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final int key = clientArrayField(glCtx, pname);
|
||||
if ((key & ~0xFF) != FIELD_POINTER) return null;
|
||||
final VAOManager.Attrib a = glCtx.vaos.conventional(key & 0xFF);
|
||||
if (a == null) return null;
|
||||
final long address = a.clientPointer != null ? MemoryUtilities.memAddress(a.clientPointer) : a.offset;
|
||||
return address == 0 ? null : MemoryUtilities.memByteBuffer(address, (int) result_size);
|
||||
}
|
||||
|
||||
public static void glEdgeFlagPointer(int stride, ByteBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_EDGE_FLAG, 1, GL11.GL_UNSIGNED_BYTE, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glEdgeFlagPointer(int stride, long pointer_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_EDGE_FLAG, 1, GL11.GL_UNSIGNED_BYTE, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glFogCoordPointer(int stride, FloatBuffer data) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(data));
|
||||
}
|
||||
|
||||
public static void glFogCoordPointer(int stride, DoubleBuffer data) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(data));
|
||||
}
|
||||
|
||||
public static void glFogCoordPointer(int type, int stride, long data_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, type, false, stride, data_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glSecondaryColorPointer(int size, int stride, FloatBuffer data) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, GL11.GL_FLOAT, true, stride, MemoryUtilities.memByteBuffer(data));
|
||||
}
|
||||
|
||||
public static void glSecondaryColorPointer(int size, int stride, DoubleBuffer data) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, GL11.GL_DOUBLE, true, stride, MemoryUtilities.memByteBuffer(data));
|
||||
}
|
||||
|
||||
public static void glSecondaryColorPointer(int size, boolean unsigned, int stride, ByteBuffer data) {
|
||||
final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE;
|
||||
ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, type, true, stride, data);
|
||||
}
|
||||
|
||||
public static void glSecondaryColorPointer(int size, int type, int stride, long data_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, type, true, stride, data_buffer_offset);
|
||||
}
|
||||
|
||||
static void pointAttrib(GLContextState glCtx, int index) {
|
||||
final VAOManager vaos = glCtx.vaos;
|
||||
final VAOManager.Attrib a = vaos.attrib(index);
|
||||
if (a == null) return;
|
||||
RENDER_BACKEND.vertexAttribDivisor(index, a.divisor);
|
||||
if (a.size == 0 || a.vboId == 0 || !vaos.isBufferLive(a)) return;
|
||||
if (glCtx.boundVBO != a.vboId) GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
if (a.integer) {
|
||||
RENDER_BACKEND.vertexAttribIPointer(index, a.size, a.type, a.stride, a.offset);
|
||||
} else {
|
||||
RENDER_BACKEND.vertexAttribPointer(index, a.size, a.type, a.normalized, a.stride, a.offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void repointAttrib(GLContextState glCtx, int index) {
|
||||
final int savedVBO = glCtx.boundVBO;
|
||||
pointAttrib(glCtx, index);
|
||||
if (glCtx.boundVBO != savedVBO) GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, IntBuffer pointer) {
|
||||
glVertexPointer(size, GL11.GL_INT, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, ShortBuffer pointer) {
|
||||
glVertexPointer(size, GL11.GL_SHORT, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, FloatBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_POSITION, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_POSITION, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_POSITION, size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_POSITION, size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, FloatBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_COLOR, size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_COLOR, size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, boolean unsigned, int stride, ByteBuffer pointer) {
|
||||
final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE;
|
||||
ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glColorPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, FloatBuffer pointer) {
|
||||
glNormalPointer(GL11.GL_FLOAT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, ByteBuffer pointer) {
|
||||
glNormalPointer(GL11.GL_BYTE, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, IntBuffer pointer) {
|
||||
glNormalPointer(GL11.GL_INT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, DoubleBuffer pointer) {
|
||||
glNormalPointer(GL11.GL_DOUBLE, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ByteBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, FloatBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ShortBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, IntBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, DoubleBuffer pointer) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, long pointer_buffer_offset) {
|
||||
ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, type, false, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, FloatBuffer pointer) {
|
||||
glTexCoordPointer(size, GL11.GL_FLOAT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, IntBuffer pointer) {
|
||||
glTexCoordPointer(size, GL11.GL_INT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, ShortBuffer pointer) {
|
||||
glTexCoordPointer(size, GL11.GL_SHORT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glTexCoordPointer(size, GL11.GL_DOUBLE, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, FloatBuffer pointer) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, ShortBuffer pointer) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, IntBuffer pointer) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, DoubleBuffer pointer) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
ffpClientArrayPointer(texCoordConventional(ctx()), size, type, false, stride, pointer_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glEnableClientState(int cap) {
|
||||
rejectExitCapture();
|
||||
setClientArrayCap(ctx(), cap, true);
|
||||
}
|
||||
|
||||
public static void glDisableClientState(int cap) {
|
||||
rejectExitCapture();
|
||||
setClientArrayCap(ctx(), cap, false);
|
||||
}
|
||||
|
||||
static boolean setClientArrayCap(GLContextState glCtx, int cap, boolean enabled) {
|
||||
final int conv = clientStateToConventional(glCtx, cap);
|
||||
if (conv < 0) return false;
|
||||
setClientArrayEnabled(glCtx, conv, enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void setClientArrayEnabled(GLContextState glCtx, int conv, boolean enabled) {
|
||||
final VAOManager vaos = glCtx.vaos;
|
||||
final boolean repoint = vaos.setConventionalEnabled(conv, enabled);
|
||||
final int index = VAOManager.conventionalLocation(conv);
|
||||
if (index >= 0) {
|
||||
if (repoint) repointAttrib(glCtx, index);
|
||||
syncAttribEnable(glCtx, index);
|
||||
}
|
||||
final int flag = conventionalVertexFlag(conv);
|
||||
if (flag == 0) return;
|
||||
if (enabled) {
|
||||
vaos.enableClientVertexFlag(flag);
|
||||
} else {
|
||||
vaos.disableClientVertexFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
static void syncAttribEnable(GLContextState glCtx, int index) {
|
||||
if (glCtx.vaos.isAttribEnabled(index)) {
|
||||
RENDER_BACKEND.enableVertexAttribArray(index);
|
||||
} else {
|
||||
RENDER_BACKEND.disableVertexAttribArray(index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalVertexAttrib(index, size, type, stride, false)) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.vaos.setAttribute(index, size, type, normalized, stride, offset, glCtx.boundVBO)) {
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, boolean normalized, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(index, size, GL11.GL_FLOAT, normalized, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) {
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer) {
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer) {
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalVertexAttrib(index, size, type, stride, false)) return;
|
||||
ctx().vaos.setAttribute(index, size, type, normalized, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, long offset) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalVertexAttrib(index, size, type, stride, true)) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.vaos.setIntegerAttribute(index, size, type, stride, offset, glCtx.boundVBO)) {
|
||||
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) {
|
||||
rejectExitCapture();
|
||||
if (!isLegalVertexAttrib(index, size, type, stride, true)) return;
|
||||
ctx().vaos.setIntegerAttribute(index, size, type, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, IntBuffer pointer) {
|
||||
glVertexAttribIPointer(index, size, type, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ShortBuffer pointer) {
|
||||
glVertexAttribIPointer(index, size, type, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexAttribDivisor(int index, int divisor) {
|
||||
rejectExitCapture();
|
||||
if (ctx().vaos.setDivisor(index, divisor)) RENDER_BACKEND.vertexAttribDivisor(index, divisor);
|
||||
}
|
||||
|
||||
public static void glVertexAttribDivisorARB(int index, int divisor) { glVertexAttribDivisor(index, divisor); }
|
||||
public static void glBindVertexBuffer(int bindingindex, int buffer, long offset, int stride) { rejectExitCapture(); RENDER_BACKEND.bindVertexBuffer(bindingindex, buffer, offset, stride); }
|
||||
public static void glVertexAttribFormat(int attribindex, int size, int type, boolean normalized, int relativeoffset) { rejectExitCapture(); RENDER_BACKEND.vertexAttribFormat(attribindex, size, type, normalized, relativeoffset); }
|
||||
public static void glVertexAttribIFormat(int attribindex, int size, int type, int relativeoffset) { rejectExitCapture(); RENDER_BACKEND.vertexAttribIFormat(attribindex, size, type, relativeoffset); }
|
||||
public static void glVertexAttribBinding(int attribindex, int bindingindex) { rejectExitCapture(); RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); }
|
||||
|
||||
public static void glEnableVertexAttribArray(int index) {
|
||||
rejectExitCapture();
|
||||
if (!isValidVertexAttribIndex(index, "glEnableVertexAttribArray")) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.vaos.enableAttribute(index)) repointAttrib(glCtx, index);
|
||||
RENDER_BACKEND.enableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
public static void glDisableVertexAttribArray(int index) {
|
||||
rejectExitCapture();
|
||||
if (!isValidVertexAttribIndex(index, "glDisableVertexAttribArray")) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.vaos.disableAttribute(index)) repointAttrib(glCtx, index);
|
||||
if (!glCtx.vaos.isAttribEnabled(index)) RENDER_BACKEND.disableVertexAttribArray(index);
|
||||
}
|
||||
|
||||
static int clientStateToConventional(GLContextState glCtx, int cap) {
|
||||
return switch (cap) {
|
||||
case GL11.GL_VERTEX_ARRAY -> VAOManager.CONV_POSITION;
|
||||
case GL11.GL_NORMAL_ARRAY -> VAOManager.CONV_NORMAL;
|
||||
case GL11.GL_COLOR_ARRAY -> VAOManager.CONV_COLOR;
|
||||
case GL11.GL_INDEX_ARRAY -> VAOManager.CONV_INDEX;
|
||||
case GL11.GL_EDGE_FLAG_ARRAY -> VAOManager.CONV_EDGE_FLAG;
|
||||
case GL14.GL_SECONDARY_COLOR_ARRAY -> VAOManager.CONV_SECONDARY_COLOR;
|
||||
case GL14.GL_FOG_COORDINATE_ARRAY -> VAOManager.CONV_FOG_COORD;
|
||||
case GL11.GL_TEXTURE_COORD_ARRAY -> texCoordConventional(glCtx);
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
private static int conventionalVertexFlag(int conv) {
|
||||
return switch (conv) {
|
||||
case VAOManager.CONV_COLOR -> VertexFlags.COLOR_BIT;
|
||||
case VAOManager.CONV_NORMAL -> VertexFlags.NORMAL_BIT;
|
||||
case VAOManager.CONV_TEX0 -> VertexFlags.TEXTURE_BIT;
|
||||
case VAOManager.CONV_TEX0 + 1 -> VertexFlags.BRIGHTNESS_BIT;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, long pointer) {
|
||||
interleavedArrays(format, stride, pointer, null);
|
||||
}
|
||||
|
||||
private static void interleavedPointer(int conv, int size, int type, boolean normalized, int stride, long pointer, ByteBuffer base, int offset) {
|
||||
if (base == null) {
|
||||
ffpClientArrayPointer(conv, size, type, normalized, stride, pointer + offset);
|
||||
} else {
|
||||
ffpClientArrayPointer(conv, size, type, normalized, stride, MemoryUtilities.memSlice(base, offset, base.remaining() - offset));
|
||||
}
|
||||
}
|
||||
|
||||
private static void interleavedArrays(int format, int stride, long pointer, ByteBuffer base) {
|
||||
rejectExitCapture();
|
||||
// Mesa _mesa_get_interleaved_layout decomposition
|
||||
final int f = 4; // sizeof(float)
|
||||
final int c = f * ((4 + (f - 1)) / f); // 4 ubytes padded to float alignment = 4
|
||||
|
||||
boolean tflag = false, cflag = false, nflag = false;
|
||||
int tcomps = 0, ccomps = 0, vcomps = 0;
|
||||
int ctype = 0;
|
||||
final int toffset = 0;
|
||||
int coffset = 0;
|
||||
int noffset = 0;
|
||||
int voffset = 0;
|
||||
final int defstride;
|
||||
|
||||
switch (format) {
|
||||
case GL11.GL_V2F:
|
||||
vcomps = 2; defstride = 2 * f; break;
|
||||
case GL11.GL_V3F:
|
||||
vcomps = 3; defstride = 3 * f; break;
|
||||
case GL11.GL_C4UB_V2F:
|
||||
cflag = true; ccomps = 4; vcomps = 2;
|
||||
ctype = GL11.GL_UNSIGNED_BYTE; voffset = c; defstride = c + 2 * f; break;
|
||||
case GL11.GL_C4UB_V3F:
|
||||
cflag = true; ccomps = 4; vcomps = 3;
|
||||
ctype = GL11.GL_UNSIGNED_BYTE; voffset = c; defstride = c + 3 * f; break;
|
||||
case GL11.GL_C3F_V3F:
|
||||
cflag = true; ccomps = 3; vcomps = 3;
|
||||
ctype = GL11.GL_FLOAT; voffset = 3 * f; defstride = 6 * f; break;
|
||||
case GL11.GL_N3F_V3F:
|
||||
nflag = true; vcomps = 3;
|
||||
voffset = 3 * f; defstride = 6 * f; break;
|
||||
case GL11.GL_C4F_N3F_V3F:
|
||||
cflag = true; nflag = true; ccomps = 4; vcomps = 3;
|
||||
ctype = GL11.GL_FLOAT; noffset = 4 * f; voffset = 7 * f; defstride = 10 * f; break;
|
||||
case GL11.GL_T2F_V3F:
|
||||
tflag = true; tcomps = 2; vcomps = 3;
|
||||
voffset = 2 * f; defstride = 5 * f; break;
|
||||
case GL11.GL_T4F_V4F:
|
||||
tflag = true; tcomps = 4; vcomps = 4;
|
||||
voffset = 4 * f; defstride = 8 * f; break;
|
||||
case GL11.GL_T2F_C4UB_V3F:
|
||||
tflag = true; cflag = true; tcomps = 2; ccomps = 4; vcomps = 3;
|
||||
ctype = GL11.GL_UNSIGNED_BYTE; coffset = 2 * f; voffset = c + 2 * f; defstride = c + 5 * f; break;
|
||||
case GL11.GL_T2F_C3F_V3F:
|
||||
tflag = true; cflag = true; tcomps = 2; ccomps = 3; vcomps = 3;
|
||||
ctype = GL11.GL_FLOAT; coffset = 2 * f; voffset = 5 * f; defstride = 8 * f; break;
|
||||
case GL11.GL_T2F_N3F_V3F:
|
||||
tflag = true; nflag = true; tcomps = 2; vcomps = 3;
|
||||
noffset = 2 * f; voffset = 5 * f; defstride = 8 * f; break;
|
||||
case GL11.GL_T2F_C4F_N3F_V3F:
|
||||
tflag = true; cflag = true; nflag = true; tcomps = 2; ccomps = 4; vcomps = 3;
|
||||
ctype = GL11.GL_FLOAT; coffset = 2 * f; noffset = 6 * f; voffset = 9 * f; defstride = 12 * f; break;
|
||||
case GL11.GL_T4F_C4F_N3F_V4F:
|
||||
tflag = true; cflag = true; nflag = true; tcomps = 4; ccomps = 4; vcomps = 4;
|
||||
ctype = GL11.GL_FLOAT; coffset = 4 * f; noffset = 8 * f; voffset = 11 * f; defstride = 15 * f; break;
|
||||
default:
|
||||
return; // Invalid format
|
||||
}
|
||||
|
||||
if (stride == 0) stride = defstride;
|
||||
|
||||
if (tflag) {
|
||||
glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
interleavedPointer(texCoordConventional(ctx()), tcomps, GL11.GL_FLOAT, false, stride, pointer, base, toffset);
|
||||
} else {
|
||||
glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
}
|
||||
|
||||
if (cflag) {
|
||||
glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
interleavedPointer(VAOManager.CONV_COLOR, ccomps, ctype, Usage.COLOR.isNormalized(), stride, pointer, base, coffset);
|
||||
} else {
|
||||
glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
}
|
||||
|
||||
if (nflag) {
|
||||
glEnableClientState(GL11.GL_NORMAL_ARRAY);
|
||||
interleavedPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, pointer, base, noffset);
|
||||
} else {
|
||||
glDisableClientState(GL11.GL_NORMAL_ARRAY);
|
||||
}
|
||||
|
||||
glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
interleavedPointer(VAOManager.CONV_POSITION, vcomps, GL11.GL_FLOAT, false, stride, pointer, base, voffset);
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, ByteBuffer pointer) {
|
||||
interleavedArrays(format, stride, 0L, pointer);
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, FloatBuffer pointer) {
|
||||
interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, DoubleBuffer pointer) {
|
||||
interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, IntBuffer pointer) {
|
||||
interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glInterleavedArrays(int format, int stride, ShortBuffer pointer) {
|
||||
interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glBindVertexArray(int array) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordBindVAO(array);
|
||||
// Since the vao needs to be bound to do stuff like state setup & data upload, it'll still execute the bind call.
|
||||
// This is technically wrong, but I'm not sure if there's a better solution here.
|
||||
}
|
||||
bindVertexArrayInternal(glCtx, array == 0 ? glCtx.defaultVAO : array);
|
||||
}
|
||||
|
||||
static void bindVertexArrayInternal(GLContextState glCtx, int array) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (glCtx.boundVAO != array) {
|
||||
glCtx.boundVAO = array;
|
||||
glCtx.vaos.onBindVertexArrayPre(array);
|
||||
RENDER_BACKEND.bindVertexArray(array);
|
||||
}
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glDeleteVertexArrays(int array) {
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
glCtx.vaos.onDeleteVertexArray(array);
|
||||
if (array == glCtx.boundVAO) {
|
||||
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
|
||||
bindVertexArrayInternal(glCtx, glCtx.defaultVAO);
|
||||
}
|
||||
RENDER_BACKEND.deleteVertexArrays(array);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGenVertexArrays() {
|
||||
requireContext("glGenVertexArrays");
|
||||
return RENDER_BACKEND.genVertexArrays();
|
||||
}
|
||||
|
||||
public static boolean glIsVertexArray(int array) {
|
||||
rejectExitCapture();
|
||||
return RENDER_BACKEND.isVertexArray(array);
|
||||
}
|
||||
|
||||
public static void glBindVertexArrayAPPLE(int array) {
|
||||
glBindVertexArray(array);
|
||||
}
|
||||
|
||||
public static int getBoundVAO() {
|
||||
return ctx().boundVAO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class GLScreenCapture extends ScreenCapture {
|
||||
|
||||
private final int[] textures = new int[RING];
|
||||
private final int[] framebuffers = new int[RING];
|
||||
private final int[] buffers = new int[RING];
|
||||
|
||||
public GLScreenCapture(int width, int height) {
|
||||
super(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean start(int slot, int srcFramebuffer, int srcWidth, int srcHeight) {
|
||||
final int width = width();
|
||||
final int height = height();
|
||||
final int previousRead = GLSMFramebuffer.getReadFramebuffer();
|
||||
final int previousDraw = GLSMFramebuffer.getDrawFramebuffer();
|
||||
final int previousPack = GLStateManager.ctx().boundPixelPackBuffer;
|
||||
final boolean scissor = GLSMRasterizer.getScissorTest().isEnabled();
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
if (framebuffers[slot] == 0) create(slot);
|
||||
if (scissor) GLSMEnable.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, srcFramebuffer);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, framebuffers[slot]);
|
||||
GLSMFramebuffer.glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL11.GL_COLOR_BUFFER_BIT, GL11.GL_LINEAR);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, framebuffers[slot]);
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, buffers[slot]);
|
||||
GLSMTextures.forcePixelPackState(PixelStoreState.DEFAULT);
|
||||
GLSMFramebuffer.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, 0L);
|
||||
return true;
|
||||
} finally {
|
||||
GLSMTextures.restorePixelPackState(PixelStoreState.DEFAULT);
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, previousPack);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, previousRead);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, previousDraw);
|
||||
if (scissor) GLSMEnable.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
private void create(int slot) {
|
||||
final int previousTexture = GLSMTextures.getBoundTextureForServerState();
|
||||
final int texture;
|
||||
try {
|
||||
texture = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
|
||||
GLSMTextures.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, width(), height(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
} finally {
|
||||
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, previousTexture);
|
||||
}
|
||||
textures[slot] = texture;
|
||||
framebuffers[slot] = GLSMFramebuffer.glGenFramebuffers();
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, framebuffers[slot]);
|
||||
GLSMFramebuffer.glFramebufferTexture2D(GL30.GL_DRAW_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, texture, 0);
|
||||
buffers[slot] = GLSMBuffers.glGenBuffers();
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, buffers[slot]);
|
||||
GLSMBuffers.glBufferData(GL21.GL_PIXEL_PACK_BUFFER, (long) width() * height() * 4, GL15.GL_STREAM_READ);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long map(int slot) {
|
||||
final int previousPack = GLStateManager.ctx().boundPixelPackBuffer;
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, buffers[slot]);
|
||||
try {
|
||||
return GLSMBuffers.glMapBufferRangeAddress(GL21.GL_PIXEL_PACK_BUFFER, 0L, (long) width() * height() * 4, GL30.GL_MAP_READ_BIT);
|
||||
} finally {
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, previousPack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unmap(int slot) {
|
||||
final int previousPack = GLStateManager.ctx().boundPixelPackBuffer;
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, buffers[slot]);
|
||||
GLSMBuffers.glUnmapBuffer(GL21.GL_PIXEL_PACK_BUFFER);
|
||||
GLSMBuffers.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, previousPack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteSlot(int slot) {
|
||||
if (buffers[slot] != 0) GLSMBuffers.glDeleteBuffers(buffers[slot]);
|
||||
if (framebuffers[slot] != 0) GLSMFramebuffer.glDeleteFramebuffers(framebuffers[slot]);
|
||||
if (textures[slot] != 0) GLSMTextures.glDeleteTextures(textures[slot]);
|
||||
buffers[slot] = 0;
|
||||
framebuffers[slot] = 0;
|
||||
textures[slot] = 0;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,21 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.BufferedTokenStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.DefaultErrorStrategy;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.WritableToken;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
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.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -49,11 +53,11 @@ public class GlslTransformUtils {
|
||||
private static final Map<Integer, List<ReservedWordRename>> VERSIONED_RESERVED_WORDS = Map.of(
|
||||
// Some reserved words to always rename
|
||||
0, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample"),
|
||||
new ReservedWordRename(Pattern.compile("\\bnew\\b"), RENAMED_PREFIX + "new")
|
||||
new ReservedWordRename(Pattern.compile("sample(?<=\\bsample)\\b"), RENAMED_PREFIX + "sample"),
|
||||
new ReservedWordRename(Pattern.compile("new(?<=\\bnew)\\b"), RENAMED_PREFIX + "new")
|
||||
),
|
||||
400, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsampler\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
|
||||
new ReservedWordRename(Pattern.compile("sampler(?<=\\bsampler)\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
|
||||
)
|
||||
);
|
||||
|
||||
@@ -72,6 +76,10 @@ public class GlslTransformUtils {
|
||||
}
|
||||
|
||||
public static String renameReservedWords(String source, int targetVersion) {
|
||||
if (!containsWholeWord(source, "sample") && !containsWholeWord(source, "new")
|
||||
&& (targetVersion < 400 || !containsWholeWord(source, "sampler"))) {
|
||||
return source;
|
||||
}
|
||||
for (var entry : VERSIONED_RESERVED_WORDS.entrySet()) {
|
||||
// Rename identifiers that become reserved words at or above this GLSL version
|
||||
if (targetVersion >= entry.getKey()) {
|
||||
@@ -83,6 +91,23 @@ public class GlslTransformUtils {
|
||||
return source;
|
||||
}
|
||||
|
||||
static boolean containsWholeWord(String source, String word) {
|
||||
int from = 0;
|
||||
while (true) {
|
||||
final int match = source.indexOf(word, from);
|
||||
if (match < 0) return false;
|
||||
final int end = match + word.length();
|
||||
if ((match == 0 || !isAsciiWordChar(source.charAt(match - 1))) && (end == source.length() || !isAsciiWordChar(source.charAt(end)))) {
|
||||
return true;
|
||||
}
|
||||
from = match + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAsciiWordChar(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
||||
}
|
||||
|
||||
public static String restoreReservedWords(String source) {
|
||||
source = source.replace(RENAMED_PREFIX + "texture", "texture");
|
||||
source = source.replace(RENAMED_PREFIX + "sampler", "sampler");
|
||||
@@ -110,72 +135,35 @@ public class GlslTransformUtils {
|
||||
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();
|
||||
return parseQuiet(new GLSLParser(new CommonTokenStream(quietLexer(source))), GLSLParser::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();
|
||||
final GLSLLexer lexer = quietLexer(source);
|
||||
final GLSLPreParser.Translation_unitContext pre = parseQuiet(new GLSLPreParser(new BufferedTokenStream(lexer)), GLSLPreParser::translation_unit);
|
||||
lexer.reset();
|
||||
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.setBuildParseTree(true);
|
||||
return new QuietParse(parser.translation_unit(), pre);
|
||||
return new QuietParse(parseQuiet(new GLSLParser(new CommonTokenStream(lexer)), GLSLParser::translation_unit), pre);
|
||||
}
|
||||
|
||||
public static Set<String> identifiersInDirectiveText(String source) {
|
||||
private static <P extends Parser, T> T parseQuiet(P parser, Function<P, T> rule) {
|
||||
parser.removeErrorListeners();
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
|
||||
try {
|
||||
return rule.apply(parser);
|
||||
} catch (ParseCancellationException e) {
|
||||
// Rewinds the token stream, which keeps every token SLL already pulled from the lexer.
|
||||
parser.reset();
|
||||
parser.setErrorHandler(new DefaultErrorStrategy());
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
|
||||
return rule.apply(parser);
|
||||
}
|
||||
}
|
||||
|
||||
public static GLSLLexer quietLexer(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;
|
||||
}
|
||||
return lexer;
|
||||
}
|
||||
|
||||
public static String getFormattedShader(ParseTree tree, String header) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -25,10 +25,10 @@ public final class LTWWorkaround {
|
||||
oneTexel = BufferUtils.createByteBuffer(4);
|
||||
}
|
||||
oneTexel.clear();
|
||||
GLStateManager.forcePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLSMTextures.forcePixelUnpackState(PixelStoreState.DEFAULT);
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texSubImage2D(target, level, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, oneTexel);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLStateManager.restorePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
GLSMTextures.restorePixelUnpackState(PixelStoreState.DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
final class LegacyQueries {
|
||||
|
||||
static final int BOOL = 1;
|
||||
static final int INT = 2;
|
||||
static final int FLOAT = 3;
|
||||
static final int FLOATN = 4;
|
||||
|
||||
private static final int COUNT_SHIFT = 3;
|
||||
|
||||
private LegacyQueries() {}
|
||||
|
||||
static int desc(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_ALPHA_TEST, GL11.GL_AUTO_NORMAL, GL11.GL_COLOR_MATERIAL, GL14.GL_COLOR_SUM, GL11.GL_FOG,
|
||||
GL11.GL_INDEX_LOGIC_OP, GL11.GL_LIGHTING, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_LIGHT2, GL11.GL_LIGHT3,
|
||||
GL11.GL_LIGHT4, GL11.GL_LIGHT5, GL11.GL_LIGHT6, GL11.GL_LIGHT7, GL11.GL_LINE_STIPPLE, GL11.GL_MAP1_COLOR_4,
|
||||
GL11.GL_MAP1_INDEX, GL11.GL_MAP1_NORMAL, GL11.GL_MAP1_TEXTURE_COORD_1, GL11.GL_MAP1_TEXTURE_COORD_2,
|
||||
GL11.GL_MAP1_TEXTURE_COORD_3, GL11.GL_MAP1_TEXTURE_COORD_4, GL11.GL_MAP1_VERTEX_3, GL11.GL_MAP1_VERTEX_4,
|
||||
GL11.GL_MAP2_COLOR_4, GL11.GL_MAP2_INDEX, GL11.GL_MAP2_NORMAL, GL11.GL_MAP2_TEXTURE_COORD_1,
|
||||
GL11.GL_MAP2_TEXTURE_COORD_2, GL11.GL_MAP2_TEXTURE_COORD_3, GL11.GL_MAP2_TEXTURE_COORD_4,
|
||||
GL11.GL_MAP2_VERTEX_3, GL11.GL_MAP2_VERTEX_4, GL11.GL_NORMALIZE, GL11.GL_POINT_SMOOTH,
|
||||
GL11.GL_POLYGON_STIPPLE, GL12.GL_RESCALE_NORMAL, GL11.GL_TEXTURE_1D, GL12.GL_TEXTURE_3D,
|
||||
GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_GEN_S, GL11.GL_TEXTURE_GEN_T, GL11.GL_TEXTURE_GEN_R,
|
||||
GL11.GL_TEXTURE_GEN_Q, GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_LIGHT_MODEL_TWO_SIDE,
|
||||
GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY,
|
||||
GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY,
|
||||
GL11.GL_EDGE_FLAG, GL20.GL_POINT_SPRITE, GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> BOOL;
|
||||
case GL11.GL_ALPHA_TEST_FUNC, GL11.GL_LIST_BASE, GL11.GL_MATRIX_MODE, GL11.GL_SHADE_MODEL,
|
||||
GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_COLOR_MATERIAL_PARAMETER, GL12.GL_LIGHT_MODEL_COLOR_CONTROL,
|
||||
GL11.GL_FOG_MODE, GL11.GL_LINE_STIPPLE_PATTERN, GL11.GL_LINE_STIPPLE_REPEAT, GL13.GL_CLIENT_ACTIVE_TEXTURE,
|
||||
GL20.GL_MAX_TEXTURE_COORDS, GL11.GL_RENDER_MODE, GL11.GL_FOG_HINT, GL11.GL_PERSPECTIVE_CORRECTION_HINT,
|
||||
GL11.GL_POINT_SMOOTH_HINT, GL14.GL_GENERATE_MIPMAP_HINT, GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT,
|
||||
GL13.GL_TEXTURE_COMPRESSION_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL20.GL_POINT_SPRITE_COORD_ORIGIN,
|
||||
GL11.GL_VERTEX_ARRAY_SIZE, GL11.GL_VERTEX_ARRAY_TYPE, GL11.GL_VERTEX_ARRAY_STRIDE, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING,
|
||||
GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_NORMAL_ARRAY_STRIDE, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING,
|
||||
GL11.GL_COLOR_ARRAY_SIZE, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_COLOR_ARRAY_STRIDE, GL15.GL_COLOR_ARRAY_BUFFER_BINDING,
|
||||
GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_INDEX_ARRAY_STRIDE, GL15.GL_INDEX_ARRAY_BUFFER_BINDING,
|
||||
GL11.GL_TEXTURE_COORD_ARRAY_SIZE, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE,
|
||||
GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING,
|
||||
GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE,
|
||||
GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE,
|
||||
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING -> INT;
|
||||
case GL11.GL_FOG_DENSITY, GL11.GL_FOG_START, GL11.GL_FOG_END, GL14.GL_POINT_SIZE_MIN, GL14.GL_POINT_SIZE_MAX,
|
||||
GL14.GL_POINT_FADE_THRESHOLD_SIZE -> FLOAT;
|
||||
case GL11.GL_ALPHA_TEST_REF -> FLOATN;
|
||||
case GL14.GL_POINT_DISTANCE_ATTENUATION -> FLOAT | 2 << COUNT_SHIFT;
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> FLOAT | 3 << COUNT_SHIFT;
|
||||
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> FLOATN | 15 << COUNT_SHIFT;
|
||||
case GL11.GL_CURRENT_NORMAL -> FLOATN | 2 << COUNT_SHIFT;
|
||||
case GL11.GL_CURRENT_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR, GL11.GL_FOG_COLOR, GL11.GL_LIGHT_MODEL_AMBIENT -> FLOATN | 3 << COUNT_SHIFT;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
static int type(int desc) {
|
||||
return desc & 7;
|
||||
}
|
||||
|
||||
static int count(int desc) {
|
||||
return (desc >> COUNT_SHIFT) + 1;
|
||||
}
|
||||
|
||||
static int floatToInt(float value) {
|
||||
return (int) (2147483647.0 * value);
|
||||
}
|
||||
|
||||
static int lround(float value) {
|
||||
return value < 0 ? -Math.round(-value) : Math.round(value);
|
||||
}
|
||||
|
||||
static int toInt(int type, float value) {
|
||||
return type == FLOATN ? floatToInt(value) : lround(value);
|
||||
}
|
||||
|
||||
private static FloatBuffer fillScratch(GLContextState glCtx, int pname) {
|
||||
final FloatBuffer scratch = glCtx.queryScratch;
|
||||
scratch.clear();
|
||||
GLSMQueries.glGetFloat(pname, scratch);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
private static IllegalStateException uncached(int pname) {
|
||||
return new IllegalStateException("Legacy pname 0x" + Integer.toHexString(pname) + " has no cached answer for its declared type");
|
||||
}
|
||||
|
||||
static int getInteger(GLContextState glCtx, int pname, int desc) {
|
||||
final int type = type(desc);
|
||||
if (count(desc) > 1) {
|
||||
return toInt(type, fillScratch(glCtx, pname).get(0));
|
||||
}
|
||||
return switch (type) {
|
||||
case BOOL -> GLSMQueries.glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE;
|
||||
case FLOAT -> lround(GLSMQueries.glGetFloat(glCtx, pname));
|
||||
case FLOATN -> floatToInt(GLSMQueries.glGetFloat(glCtx, pname));
|
||||
default -> throw uncached(pname);
|
||||
};
|
||||
}
|
||||
|
||||
static float getFloat(GLContextState glCtx, int pname, int desc) {
|
||||
if (count(desc) > 1) {
|
||||
return fillScratch(glCtx, pname).get(0);
|
||||
}
|
||||
return switch (type(desc)) {
|
||||
case BOOL -> GLSMQueries.glGetBoolean(glCtx, pname) ? 1.0f : 0.0f;
|
||||
case INT -> (float) GLSMQueries.glGetInteger(glCtx, pname);
|
||||
default -> throw uncached(pname);
|
||||
};
|
||||
}
|
||||
|
||||
static boolean getBoolean(GLContextState glCtx, int pname, int desc) {
|
||||
if (count(desc) > 1) {
|
||||
return fillScratch(glCtx, pname).get(0) != 0.0f;
|
||||
}
|
||||
return switch (type(desc)) {
|
||||
case INT -> GLSMQueries.glGetInteger(glCtx, pname) != 0;
|
||||
case FLOAT, FLOATN -> GLSMQueries.glGetFloat(glCtx, pname) != 0.0f;
|
||||
default -> throw uncached(pname);
|
||||
};
|
||||
}
|
||||
|
||||
static boolean getIntegerv(GLContextState glCtx, int pname, IntBuffer params) {
|
||||
final int desc = desc(pname);
|
||||
if (desc == 0) return false;
|
||||
final int pos = params.position();
|
||||
final int type = type(desc);
|
||||
final FloatBuffer scratch = fillScratch(glCtx, pname);
|
||||
for (int i = 0, count = count(desc); i < count; i++) {
|
||||
params.put(pos + i, toInt(type, scratch.get(i)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean getIntegerv(GLContextState glCtx, int pname, int[] params) {
|
||||
final int desc = desc(pname);
|
||||
if (desc == 0) return false;
|
||||
final int type = type(desc);
|
||||
final FloatBuffer scratch = fillScratch(glCtx, pname);
|
||||
for (int i = 0, count = Math.min(count(desc), params.length); i < count; i++) {
|
||||
params[i] = toInt(type, scratch.get(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean getBooleanv(GLContextState glCtx, int pname, ByteBuffer params) {
|
||||
final int desc = desc(pname);
|
||||
if (desc == 0) return false;
|
||||
final int pos = params.position();
|
||||
final FloatBuffer scratch = fillScratch(glCtx, pname);
|
||||
for (int i = 0, count = count(desc); i < count; i++) {
|
||||
params.put(pos + i, (byte) (scratch.get(i) != 0.0f ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightModelState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class LightingSnapshot {
|
||||
final boolean[] lightEnabled = new boolean[VertexKey.FFP_LIGHT_COUNT];
|
||||
final LightState[] lights = new LightState[VertexKey.FFP_LIGHT_COUNT];
|
||||
final LightModelState lightModel = new LightModelState();
|
||||
final MaterialState frontMaterial = new MaterialState(GL11.GL_FRONT);
|
||||
final MaterialState backMaterial = new MaterialState(GL11.GL_BACK);
|
||||
boolean colorMaterial;
|
||||
int colorMaterialFace;
|
||||
int colorMaterialParameter;
|
||||
boolean normalize;
|
||||
boolean rescaleNormal;
|
||||
|
||||
public LightingSnapshot() {
|
||||
for (int i = 0; i < lights.length; i++) {
|
||||
lights[i] = new LightState(GL11.GL_LIGHT0 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public final class QuadConverter {
|
||||
uploadAndDraw(glCtx, dst, quadCount * 6, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
return;
|
||||
}
|
||||
GLStateManager.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
GLSMDraws.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
final int prevEbo = glCtx.vaos.boundEBO;
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
@@ -143,7 +143,7 @@ public final class QuadConverter {
|
||||
}
|
||||
|
||||
private static void uploadAndDraw(GLContextState glCtx, ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
|
||||
GLStateManager.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
GLSMDraws.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = glCtx.vaos.boundEBO;
|
||||
|
||||
|
||||
@@ -46,8 +46,11 @@ public class RenderSystem {
|
||||
private static boolean supportsMultiDrawIndirect;
|
||||
private static boolean supportsBufferStorage;
|
||||
private static boolean supportsClearTexture;
|
||||
private static boolean supportsStencilTexturing;
|
||||
private static boolean supportsTextureStorage;
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static boolean supportsProgramBinary;
|
||||
private static int maxImageUnits;
|
||||
@Getter private static int maxCombinedTextureImageUnits;
|
||||
private static int maxSSBOBindings;
|
||||
@@ -60,7 +63,7 @@ public class RenderSystem {
|
||||
private static volatile boolean isGLES;
|
||||
private static volatile int glesVersion;
|
||||
private static volatile boolean glesDetected;
|
||||
private static volatile boolean hasClipCullDistance;
|
||||
private static boolean clipDistanceEmulated;
|
||||
|
||||
private static volatile boolean isLTW;
|
||||
private static volatile boolean ltwDetected;
|
||||
@@ -127,7 +130,14 @@ public class RenderSystem {
|
||||
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");
|
||||
supportsStencilTexturing = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_stencil_texturing;
|
||||
supportsTextureStorage = isGLES ? glesVersion >= 300 : GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_texture_storage;
|
||||
final boolean esClipExt = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
|
||||
final int maxClipDistances = esClipExt ? RENDER_BACKEND.getInteger(GL30.GL_MAX_CLIP_DISTANCES) : 0;
|
||||
clipDistanceEmulated = SystemProperties.EMULATE_CLIP_DISTANCES || maxClipDistances < GLStateManager.MAX_CLIP_PLANES;
|
||||
if (clipDistanceEmulated) {
|
||||
GLStateManager.LOGGER.info("Clip distances emulated (GL_MAX_CLIP_DISTANCES={}, forced={})", maxClipDistances, SystemProperties.EMULATE_CLIP_DISTANCES);
|
||||
}
|
||||
|
||||
// Cache maximum image units
|
||||
if (supportsImageLoadStore) {
|
||||
@@ -153,12 +163,15 @@ public class RenderSystem {
|
||||
maxSSBOBindings = 0;
|
||||
}
|
||||
|
||||
// Check for sampler objects support (GL 3.3+ or ARB extension)
|
||||
supportsSamplerObjects = GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
supportsSamplerObjects = isGLES ? glesVersion >= 300
|
||||
: GLStateManager.capabilities.OpenGL33 || GLStateManager.capabilities.GL_ARB_sampler_objects;
|
||||
if (supportsSamplerObjects) {
|
||||
maxCombinedTextureImageUnits = RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
}
|
||||
|
||||
supportsProgramBinary = RENDER_BACKEND.supportsProgramBinary();
|
||||
GLStateManager.LOGGER.info("Program Binary: {}", supportsProgramBinary);
|
||||
|
||||
maxGlslVersion = Integer.parseInt(parseGlVersionString(RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION)));
|
||||
supportsGpuShader4 = GLStateManager.capabilities.GL_EXT_gpu_shader4;
|
||||
|
||||
@@ -190,187 +203,231 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.generateMipmaps(texture, mipmapTarget);
|
||||
}
|
||||
|
||||
public static void bindAttributeLocation(int program, int index, CharSequence name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.bindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
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);
|
||||
GLStateManager.rejectExitCapture();
|
||||
final GLSMTextures.GLESTexImageRemap remap = GLSMTextures.remapTexImageForGLES(internalformat, format, type);
|
||||
internalformat = remap.internalFormat();
|
||||
format = remap.format();
|
||||
type = remap.type();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLSMTextures.glBindTexture(target, texture);
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
if (target == GL11.GL_TEXTURE_2D && level == 0) {
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrix);
|
||||
}
|
||||
|
||||
public static void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {
|
||||
GLStateManager.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
|
||||
GLSMTextures.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
|
||||
}
|
||||
|
||||
public static void uniform1f(int location, float v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1f(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform1i(int location, int v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1i(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform2f(int location, float v0, float v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2f(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform2i(int location, int v0, int v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2i(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform3f(int location, float v0, float v1, float v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {RENDER_BACKEND.uniform3i(location, v0, v1, v2);}
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3i(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void uniform4i(int location, int v0, int v1, int v2, int v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static int getAttribLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getAttribLocation(programId, name);
|
||||
}
|
||||
|
||||
public static int getUniformLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getUniformLocation(programId, name);
|
||||
}
|
||||
|
||||
public static void texParameteriv(int texture, int target, int pname, IntBuffer params) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteriv(texture, target, pname, params);
|
||||
}
|
||||
|
||||
public static void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.copyTexSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
if (i == 0) TextureInfoCache.INSTANCE.onContentsChanged(destTexture);
|
||||
}
|
||||
|
||||
public static void texParameteri(int texture, int target, int pname, int param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteri(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void texParameterf(int texture, int target, int pname, float param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameterf(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMTextures.glBindTexture(target, texture);
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMTextures.glBindTexture(target, texture);
|
||||
GLSMBuffers.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLSMBuffers.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgramInfoLog(program, RENDER_BACKEND.getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static String getShaderInfoLog(int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader, RENDER_BACKEND.getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.drawBuffers(framebuffer, buffers);
|
||||
}
|
||||
|
||||
public static void readBuffer(int framebuffer, int buffer) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.readBuffer(framebuffer, buffer);
|
||||
}
|
||||
|
||||
public static String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getActiveUniform(program, index, maxLength, sizeType);
|
||||
}
|
||||
|
||||
public static void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
GLStateManager.suspendPixelPackBuffer();
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLSMBuffers.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelPackBuffer();
|
||||
GLSMBuffers.restorePixelPackBuffer();
|
||||
}
|
||||
|
||||
public static void bufferData(int target, FloatBuffer data, int usage) {
|
||||
GLStateManager.glBufferData(target, data, usage);
|
||||
GLSMBuffers.glBufferData(target, data, usage);
|
||||
}
|
||||
|
||||
public static int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.bufferStorage(target, data, usage);
|
||||
}
|
||||
|
||||
public static void vertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void detachShader(int program, int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.detachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
public static int getTexParameteri(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameteri(texture, target, pname);
|
||||
}
|
||||
|
||||
public static float getTexParameterf(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameterf(texture, target, pname);
|
||||
}
|
||||
|
||||
public static int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
public static void bindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) {
|
||||
GLStateManager.glBindImageTexture(unit, texture, level, layered, layer, access, format);
|
||||
GLSMTextures.glBindImageTexture(unit, texture, level, layered, layer, access, format);
|
||||
}
|
||||
|
||||
public static int getMaxImageUnits() {
|
||||
@@ -382,14 +439,14 @@ public class RenderSystem {
|
||||
return supportsImageLoadStore;
|
||||
}
|
||||
|
||||
public static boolean hasClipCullDistance() {
|
||||
return hasClipCullDistance;
|
||||
public static boolean isClipDistanceEmulated() {
|
||||
return clipDistanceEmulated;
|
||||
}
|
||||
|
||||
private static boolean hasExtension(String name) {
|
||||
final int count = GLStateManager.glGetInteger(GL30.GL_NUM_EXTENSIONS);
|
||||
static boolean hasExtension(String name) {
|
||||
final int count = GLSMQueries.glGetInteger(GL30.GL_NUM_EXTENSIONS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (name.equals(GLStateManager.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
|
||||
if (name.equals(GLSMQueries.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -449,31 +506,43 @@ public class RenderSystem {
|
||||
return supportsClearTexture;
|
||||
}
|
||||
|
||||
public static boolean supportsStencilTexturing() {
|
||||
return supportsStencilTexturing;
|
||||
}
|
||||
|
||||
public static boolean supportsTextureStorage() {
|
||||
return supportsTextureStorage;
|
||||
}
|
||||
|
||||
public static int getMaxSSBOBindings() {
|
||||
return maxSSBOBindings;
|
||||
}
|
||||
|
||||
public static void getProgramiv(int program, int value, IntBuffer storage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.getProgramiv(program, value, storage);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(int workX, int workY, int workZ) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchCompute(workX, workY, workZ);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(Vector3i workGroups) {
|
||||
RENDER_BACKEND.dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
}
|
||||
|
||||
public static void dispatchComputeIndirect(long offset) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchComputeIndirect(offset);
|
||||
}
|
||||
|
||||
public static void bindBuffer(int target, int buffer) {
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
GLSMBuffers.glBindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
public static void memoryBarrier(int barriers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
if (supportsCompute) {
|
||||
RENDER_BACKEND.memoryBarrier(barriers);
|
||||
}
|
||||
@@ -484,57 +553,62 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void disableBufferBlend(int buffer) {
|
||||
GLStateManager.glDisablei(GL11.GL_BLEND, buffer);
|
||||
GLSMEnable.glDisablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void enableBufferBlend(int buffer) {
|
||||
GLStateManager.glEnablei(GL11.GL_BLEND, buffer);
|
||||
GLSMEnable.glEnablei(GL11.GL_BLEND, buffer);
|
||||
}
|
||||
|
||||
public static void blendFuncSeparatei(int buffer, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
GLStateManager.glBlendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
GLSMBlend.glBlendFuncSeparatei(buffer, srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(GL11.GL_TEXTURE_2D, 0);
|
||||
else if (GLSMTextures.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int target, int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(target, 0);
|
||||
else if (GLSMTextures.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(target, unit, texture);
|
||||
}
|
||||
|
||||
public static void setupProjectionMatrix(Matrix4f matrix) {
|
||||
final FloatBuffer projectionMatrixBuffer = GLStateManager.ctx().projectionMatrixBuffer;
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
matrix.get(0, projectionMatrixBuffer);
|
||||
GLStateManager.glLoadMatrix(projectionMatrixBuffer);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glLoadMatrix(projectionMatrixBuffer);
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
public static void restoreProjectionMatrix() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLSMMatrices.glPopMatrix();
|
||||
GLSMMatrices.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) {
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.rejectExitCapture();
|
||||
final boolean scissor = GLSMEnable.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
if (scissor) GLSMEnable.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);
|
||||
if (scissor) GLSMEnable.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
public static int createFramebuffer() {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createFramebuffer();
|
||||
}
|
||||
|
||||
public static int createTexture(int target) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createTexture(target);
|
||||
}
|
||||
|
||||
@@ -548,29 +622,30 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int createBuffers() {
|
||||
return GLStateManager.glGenBuffers();
|
||||
return GLSMBuffers.glGenBuffers();
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
GLStateManager.rejectExitCapture();
|
||||
return GLSMBuffers.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
GLStateManager.glBindBufferBase(target, index, buffer);
|
||||
GLSMBuffers.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
GLStateManager.glBufferStorage(target, size, flags);
|
||||
GLSMBuffers.glBufferStorage(target, size, flags);
|
||||
}
|
||||
|
||||
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
|
||||
final ByteBuffer buf = BufferUtils.createByteBuffer(data.length * 4);
|
||||
buf.asIntBuffer().put(data);
|
||||
GLStateManager.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
GLSMBuffers.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
|
||||
}
|
||||
|
||||
public static void deleteBuffers(int buffer) {
|
||||
GLStateManager.glDeleteBuffers(buffer);
|
||||
GLSMBuffers.glDeleteBuffers(buffer);
|
||||
}
|
||||
|
||||
public static long getVRAM() {
|
||||
@@ -583,21 +658,10 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void clearTexImage(int texture, int target, int level, int format, int type) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.clearTexImage(texture, level, format, type);
|
||||
}
|
||||
|
||||
public static void textureStorage1D(int texture, int target, int levels, int internalFormat, int width) {
|
||||
dsaState.textureStorage1D(texture, target, levels, internalFormat, width);
|
||||
}
|
||||
|
||||
public static void textureStorage2D(int texture, int target, int levels, int internalFormat, int width, int height) {
|
||||
dsaState.textureStorage2D(texture, target, levels, internalFormat, width, height);
|
||||
}
|
||||
|
||||
public static void textureStorage3D(int texture, int target, int levels, int internalFormat, int width, int height, int depth) {
|
||||
dsaState.textureStorage3D(texture, target, levels, internalFormat, width, height, depth);
|
||||
}
|
||||
|
||||
public static int getMaxGlslVersion() {
|
||||
return maxGlslVersion;
|
||||
}
|
||||
@@ -623,39 +687,38 @@ public class RenderSystem {
|
||||
return supportsSamplerObjects;
|
||||
}
|
||||
|
||||
public static boolean supportsProgramBinary() {
|
||||
return supportsProgramBinary;
|
||||
}
|
||||
|
||||
public static int genSampler() {
|
||||
if (!supportsSamplerObjects) return 0;
|
||||
return GLStateManager.glGenSamplers();
|
||||
return GLSMTextures.glGenSamplers();
|
||||
}
|
||||
|
||||
public static void destroySampler(int sampler) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glDeleteSamplers(sampler);
|
||||
GLSMTextures.glDeleteSamplers(sampler);
|
||||
}
|
||||
|
||||
public static void samplerParameteri(int sampler, int pname, int param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glSamplerParameteri(sampler, pname, param);
|
||||
GLSMTextures.glSamplerParameteri(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void samplerParameterf(int sampler, int pname, float param) {
|
||||
if (!supportsSamplerObjects || sampler == 0) return;
|
||||
GLStateManager.glSamplerParameterf(sampler, pname, param);
|
||||
GLSMTextures.glSamplerParameterf(sampler, pname, param);
|
||||
}
|
||||
|
||||
public static void bindSamplerToUnit(int unit, int sampler) {
|
||||
if (!supportsSamplerObjects) return;
|
||||
GLStateManager.glBindSampler(unit, sampler);
|
||||
GLSMTextures.glBindSampler(unit, sampler);
|
||||
}
|
||||
|
||||
public static void unbindAllSamplers() {
|
||||
if (!supportsSamplerObjects) return;
|
||||
final int count = GLStateManager.getSamplerUnitCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (GLStateManager.getSamplerBinding(i) != 0) {
|
||||
GLStateManager.glBindSampler(i, 0);
|
||||
}
|
||||
}
|
||||
GLSMTextures.unbindAllSamplers();
|
||||
}
|
||||
|
||||
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Strips
|
||||
@@ -692,4 +755,12 @@ public class RenderSystem {
|
||||
if (actualVersion < 0) return true;
|
||||
return actualVersion >= requestedMajor * 10 + requestedMinor;
|
||||
}
|
||||
|
||||
public static final int MIN_CORE_VERSION = 33;
|
||||
|
||||
public static int probeCapAfterRejection(int requested, int actual) {
|
||||
if (actual >= MIN_CORE_VERSION && actual < requested) return actual;
|
||||
if (actual >= 0 && actual < MIN_CORE_VERSION && requested > MIN_CORE_VERSION) return MIN_CORE_VERSION;
|
||||
return requested - 1;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1467 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user