Compare commits

..
98 Commits
Author SHA1 Message Date
Jason Mitchell 996fb54cb0 Add a weather API for mods and instance their weather (#2364)
* Mods can supply their own weather columns, drawn alongside vanilla rain and snow
* Mod weather renders instanced with and without shader packs
* NotFine leaf settings apply to Biomes O' Plenty leaves
* Flyby can start in a named biome and fly in survival
2026-11-13 15:03:24 -08:00
Eldrinn Elantey d1e7838cf9 Render fluids stored by LiquidLogged in the chunk mesher (#2356)
* Render fluids stored by LiquidLogged in the chunk mesher

* Update dependencies.gradle.kts
2026-11-13 14:05:32 -08:00
Alexdoru a12d2bb4b0 fix world client leak in MixinRenderBlockConnectedTexturesEthereal (#2360) 2026-11-11 12:33:01 -08:00
Eldrinn Elantey 60d1a3de3c Restore FluidLogged fluid rendering (#2354)
* Restore FluidLogged fluid rendering

* Check region type before reading logged fluid
2026-11-08 18:39:33 -08:00
DarkShadow44 367f9d9161 Fix compatibility with LOTR mod (#2353) 2026-11-08 14:56:07 -08:00
Jason Mitchell d69fd20a8b Tag profiler captures with the Angelica version (#2351)
* RenderDoc, Tracy, async-profiler, spark
2026-11-07 22:49:01 -08:00
Jason Mitchell 9493d630a0 Emulate clip distances on devices without them (#2348) 2026-11-07 13:42:32 -08:00
Jason Mitchell aee989d23b Fix flat item icons on drawers and barrels with shader packs (#2347) 2026-11-07 12:59:19 -08:00
Jason Mitchell 3876218405 Fix GLES crashing at startup and rendering black (#2346) 2026-11-06 21:30:30 -08:00
Armisael5andEclipse-Sol a6d014a9eb Fix falling block texture (#2336)
Co-authored-by: Eclipse-Sol <[email protected]>
2026-11-06 15:25:15 -05:00
Eldrinn Elantey 52444510d0 Move the tilde glyph down in the modern ascii font sheet (#2343) 2026-11-06 09:05:22 -08:00
DarkXFast 8840c2109f Fix replayed "glDrawElements" display lists losing their normals and texcoords (#2328) 2026-11-02 08:40:12 -08:00
Alexdoru e64a4e7214 Fix world client leak in PositionEntries$PlayerPosition (#2327) 2026-11-02 08:35:41 -08:00
Jason Mitchell e46eb46343 Add Tracy frame images (#2326) 2026-11-01 15:09:13 -08:00
Jason Mitchell 5da38c9180 Fix black patches covering the world (#2325) 2026-11-01 12:45:06 -08:00
Rad 85bde5827f Update Polish translation (#2321) 2026-11-01 12:21:47 -08:00
kurrycat ea2d34589e fix thaum mirror render corruption (#2324) 2026-11-01 12:09:09 -08:00
Jason Mitchell 2b46b4f602 Fix StellarSky crash and corrupted player model (#2315) 2026-10-30 20:15:40 -07:00
Jason Mitchell 8e099e9529 Free chunk-meshing memory when leaving a world (#2313) 2026-10-29 19:00:35 -07:00
Jason Mitchell bbbdc12b2a Cut terrain rebuilds and draw-buffer churn (#2312) 2026-10-29 15:07:17 -07:00
Jason Mitchell 08aa37712f Only animate PBR textures that are visible (#2310) 2026-10-28 20:46:51 -07:00
Jason Mitchell ed6dd12db7 Move deferred chunk meshing back off the main thread (#2306) 2026-10-26 22:16:14 -07:00
Jason Mitchell 91e67f8d5d Split up GLStateManager and add public GLState API (#2305) 2026-10-26 21:11:49 -07:00
Jason Mitchell 9bbb0537ee Backport modern F3 debug screen + debug overlays (#2301) 2026-10-25 22:41:56 -07:00
Jason Mitchell a019b7eed8 Update PR template to be in line with the GTNH org 2026-10-25 19:35:59 -07:00
Eclipse-Sol 1772e9eae2 Mipmap stuff again (#2300) 2026-10-25 19:25:54 -07:00
Eclipse-Sol 17a2ed7f37 Initial commit for gui-item-supersampling (#2299) 2026-10-25 13:10:15 -07:00
madoha 34ed1d101b Update Japanese Localizations (#2297) 2026-10-24 23:11:39 -07:00
KolyaandJason Mitchell 7cc9cea337 Cache the tile entity lightmap lookup for one tick (#2296)
Co-authored-by: Jason Mitchell <[email protected]>
2026-10-24 23:10:04 -07:00
Jason Mitchell 6c5e146f96 Fix GL core tests that depended on test order and JIT timing (#2298) 2026-10-24 22:32:13 -07:00
Kolya ea50e0a0b6 Bunch of rendering fixes (#2295) 2026-10-24 19:24:57 -07:00
Eclipse-Sol 713509a87b Entity Shadow Replay (#2294) 2026-10-24 16:21:33 -07:00
Eclipse-Sol 102c6cad86 Initial commit for glsm-exit-state-capture (#2292) 2026-10-24 15:58:17 -07:00
Eclipse-Sol 8af9340be6 Initial commit for shadow-pass-camera (#2291) 2026-10-22 20:38:28 -07:00
Eclipse-Sol fc8d3e8b89 Keep armor and held-item glint materials separate (#2289) 2026-10-22 19:29:22 -07:00
Jason Mitchell e84c8c8d7a Stop stalls on some GPUs when closing the questbook (#2286) 2026-10-22 00:24:31 -07:00
Eclipse-Sol 73d61d385c initial commit for gl-fences (#2285) 2026-10-21 20:12:01 -07:00
Wackypocket 69124e3f2f Stop capping mailbox present mode to the refresh rate (#2279) 2026-10-21 19:47:18 -07:00
VortexSo4andVortexSo4 98703a11be Read only the mcmeta in the interpolated icon check (#2284)
Co-authored-by: VortexSo4 <[email protected]>
2026-10-21 17:32:34 -07:00
brandyn 67ca46a220 why min 16 (#2278) 2026-10-21 17:25:35 -07:00
Jason Mitchell a037a96fa4 Update to Celeritas 2.5.13-GTNH (#2283) 2026-10-21 15:36:58 -07:00
Jason Mitchell ed260a34c4 Fix tests & SDL-GPU framebuffers losing attachments on resize or texture delete (#2282) 2026-10-21 15:12:32 -07:00
Jason Mitchell e98c927a93 Reduce test duplication (#2276) 2026-10-19 00:43:29 -07:00
Jason Mitchell b446968e66 Instance animated TESRs and let mods register shaders for batched TESRs (#2275) 2026-10-18 23:59:55 -07:00
Jason Mitchell 32f8eef15b Fixes a startup CME when BLS (#2274) 2026-10-18 20:40:14 -07:00
Eclipse-Sol 2d9d1a45b7 initial commit for entity-atlases (#2272) 2026-10-18 19:31:16 -07:00
Jason Mitchell fa8842a64b Fix birch and spruce leaves showing the oak biome color with fancy biome blending (#2273) 2026-10-18 17:06:35 -07:00
gorbit99 69019b9a62 Implement metadata checking into the NBT path of block.properties parsing (#2267) 2026-10-17 18:39:12 -07:00
Jason Mitchell 482dece0f7 Expand SDL-GPU's GLSM redirector to GLSM and remove unredirected GL workarounds and move action back to master (#2268) 2026-10-17 17:58:50 -07:00
Eclipse-Sol ab3bf17696 Set entity, block entity and item IDs per vertex (#2265) 2026-10-16 21:47:40 -07:00
Eclipse-Sol cfc6e12e00 Minor Compute Cull Optimizations (#2263) 2026-10-16 21:34:59 -07:00
Eclipse-Sol 8537835d0c Fix Player Capture for EP in SDL-GPU (#2262) 2026-10-16 21:34:16 -07:00
Jason Mitchell 8e1809622c Stop resetting SDL GPU to OpenGL on unrelated crashes (#2266) 2026-10-16 21:33:06 -07:00
HariboApfel f81ee4b03a fixed some osx metal compability with Distant horizon 1.7.10 backport (#2261) 2026-10-15 09:59:28 -07:00
Eclipse-Sol 49db8e111d Allow Iris to use "more" Samplers on SDL-GPU (#2256)
* Initial commit for sdl-over-16-samplers

* Fix WSR not working on SDL
2026-10-14 16:10:54 -07:00
Eclipse-Sol bd9a3c3c5c initial commit for witchery-cauldron-not-drawing-base (#2260) 2026-10-14 16:09:57 -07:00
Ruling-0 01ff612d1d Make animation interpolation respect alpha (#2258) 2026-10-14 15:44:18 -07:00
Jason Mitchell 0cae2be1f6 Fix Chisel energised voidstone missing its border corners (#2254)
Fixes #2246
2026-10-12 18:48:33 -07:00
Jason Mitchell 85d0641a73 Speed up tests and run SDL-GPU device tests on CI (#2253)
* Speed up tests and run SDL-GPU device tests on CI
* Arch rules share one ASM scan instead of ArchUnit
* Tests needing an SDL GPU device fail without one instead of skipping
* Replace checkstyle's wildcard-import check with a test
* Use the runner's preinstalled Temurin JDKs
* Cache build/rfg so the Minecraft decompile chain can be skipped
* Unpack LWJGL natives before tests
* Pin the shared workflow commit
2026-10-12 13:26:28 -07:00
Jason Mitchell 46f8157087 New workflow (#2252) 2026-10-11 16:11:14 -07:00
Jason Mitchell d3dee841e8 Skip zero-size buffer uploads and readbacks on SDL-GPU (#2249) 2026-10-11 09:41:28 -07:00
Jason Mitchell 3d3d0f9e18 Angelica side fix to RC Lantern rendering. Works with or without a patched RC (#2251) 2026-10-11 09:27:53 -07:00
Jason Mitchell 23aa04361d Fix crash replaying display lists compiled during the splash screen (#2250) 2026-10-10 20:38:35 -07:00
DarkShadow44 69428c12f6 Fix MixinRendererLivingEntity_GlintClock expected count (#2248) 2026-10-10 18:06:45 -07:00
Jason Mitchell 3978be161d Answer legacy GL queries from tracked state (#2247) 2026-10-10 17:31:02 -07:00
Jason Mitchell 2ed838acf1 Fix shaders that redefine a GLSL built-in failing on SDL-GPU and GLES (#2245)
* Retry a rejected compile with the shader's own functions renamed
2026-10-10 14:05:33 -07:00
Eclipse-Sol d29a8278cd Make API functions for enderstorage to use (#2244)
* Make API functions for enderstorage to use

* move it into its own package
2026-10-10 16:54:21 -04:00
Eclipse-Sol 2f1f380e8e Bug Cleanup after 2.9RC2 (#2239) 2026-10-10 13:22:49 -07:00
Jason Mitchell 990dadc03f Reject non 3.3 core with a more clear message (#2243) 2026-10-10 12:58:51 -07:00
Ancalagon 1946c2ad65 Fix dark AE2 cables when shaders enabled (#2223) 2026-10-08 23:27:24 -07:00
Jason Mitchell bfb66d84ee Fix interop with Serene Seasons and other similar mods (#2240) 2026-10-08 23:09:34 -07:00
DarkShadow44 cae4fea2bb Fix Little Tiles rendering issues (#2238) 2026-10-08 17:13:36 -07:00
Jason Mitchell 78abc05711 Update Readme 2026-10-07 20:34:33 -07:00
Eclipse-Sol 159f7ff8cc Fix NaNs on EP (#2231)
* Fix NaNs on EP

* Also add hidden writes handling

* optimize this mess

* Another optimization pass
2026-10-07 17:59:21 -04:00
Ranzu 0d959c2b90 Add Darkmode Transformer target (#2221) 2026-10-07 13:55:18 -07:00
Alexdoru ce2462cad3 Fix server thread leak in ThreadedBlockData (#2230) 2026-10-07 12:30:09 -07:00
Death Fuel 5457df5369 Marginal improvements (#2222) 2026-10-07 12:23:32 -07:00
Eclipse-Sol acedba4eac Modern Font Sheets (#2225)
* Initial commit for modern-font-sheets

* Fix dev env error log

* Fix private use characters still using their own unifont character
2026-10-07 11:19:19 -04:00
Alexdoru 21a054a4db fix world client leak in CloudRenderer (#2227) 2026-10-06 06:22:08 -07:00
Jason Mitchell d056bf6fa7 Run image atomics natively on Metal where the runtime allows (#2226) 2026-10-05 22:57:30 -07:00
Eclipse-Sol 5ed1c92590 SDL Vertex Replay + Dragon API (#2224) 2026-10-05 20:23:36 -07:00
Eclipse-Sol 3389b952d0 Fix SDL compile failure when an image atomic's result is used (#2218) 2026-10-04 18:56:12 -07:00
Jason Mitchell b49d37d827 Ignore additional tags when deriving the mod version (#2219) 2026-10-04 18:33:06 -07:00
Jason Mitchell 12857006c6 Harden SDL-GPU buffer mapping lifetime (#2217) 2026-10-04 16:31:48 -07:00
Eclipse-Sol 38af64f10c Initial commit for shader-loading-optimizations (#2213) 2026-10-04 15:36:50 -07:00
Jason Mitchell 10922edca3 Include missed scripts (#2216) 2026-10-04 15:22:09 -07:00
Jason Mitchell 2a8c8756d3 Add renderer selection to video settings - (#2215) 2026-10-04 15:16:51 -07:00
Jason Mitchell 23e36f2b76 Narrow overly broad transformer exclusions for PowerConverters (#2214) 2026-10-04 14:27:43 -07:00
Jason Mitchell fcf8ad064a Fix terrain disappearing at screen edges with compute culling (#2212) 2026-10-04 13:05:14 -07:00
Jason Mitchell 6dfb0312b1 Answer legacy stack and client state queries from GLSM (#2211) 2026-10-04 11:43:56 -07:00
Eclipse-Sol 0a4d48964e Cache Iris Shaders (#2206) 2026-10-03 20:06:39 -07:00
Ranzu 231a2ab216 Add Darkmode Transformer target (#2205) 2026-10-03 15:28:37 -07:00
Jason Mitchell 61bb779dea Fix crash when mods measure text off-thread with a custom font (#2204) 2026-10-03 15:28:15 -07:00
Jason Mitchell 7cd97d1216 Fix HUD caching flicker and match non-cached GL state (#2201) 2026-10-03 13:19:29 -07:00
Eclipse-Sol fe82cd320a Plenty of fixes for GTNH (#2202) 2026-10-03 13:19:15 -07:00
Li Xiangrong c7a0f3a208 Default raw custom textures to bilinear filtering and clamping (#2198) 2026-10-03 11:57:30 -07:00
Matt Van Horn 338d94197c Settle frame pacing after an inconsistent vsync gate (#2197) 2026-10-03 11:51:30 -07:00
Death Fuel 75a0278138 Fix splash issues (#2200) 2026-10-03 11:42:57 -07:00
1290 changed files with 83846 additions and 36780 deletions

No files matched your search

+8 -8
View File
@@ -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
+23
View File
@@ -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"
+9
View File
@@ -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"
+40
View File
@@ -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
+53
View File
@@ -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."
+18 -2
View File
@@ -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."
+1 -1
View File
@@ -3,7 +3,7 @@ name: Release tagged build
on:
push:
tags: [ '*', '!tracy-v*' ]
tags: [ '*', '!tracy-v*', '!moltenvk-v*' ]
permissions:
contents: write
+40 -12
View File
@@ -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)
+71 -2
View File
@@ -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,11 +225,26 @@ 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")
+8
View File
@@ -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}") })
}
+32
View File
@@ -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
}
+8 -2
View File
@@ -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,12 +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)
@@ -48,6 +48,7 @@ repositories {
}
tasks.withType<DowngradeFiles>().configureEach {
outputs.cacheIf { true }
downgradeTo.set(JavaVersion.VERSION_1_8)
multiReleaseOriginal.set(false)
multiReleaseVersions.set(emptySet())
@@ -78,8 +78,8 @@ import java.lang.annotation.Target;
* <pre>
* &#64;Surround(method = ..., at = &#64;At(value = "INVOKE", target = ...))
* private void enter() {
* &#64;Surround.Carry("depth") int depth = GLStateManager.pushState(StateSet.BLEND);
* &#64;Surround.Carry("prev") int prevProgram = GLStateManager.getActiveProgram();
* &#64;Surround.Carry("depth") int depth = GLSMAttribStack.pushState(StateSet.BLEND);
* &#64;Surround.Carry("prev") int prevProgram = GLSMPrograms.getActiveProgram();
* &#64;Surround.Carry Class&lt;?&gt; prevRenderable = TesrAttribution.currentRenderable;
* }
*
+53
View File
@@ -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"
@@ -330,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
@@ -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);
}
}
}
}
@@ -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,13 +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 = Boolean.getBoolean("angelica.flyby.debugHud");
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");
@@ -131,6 +147,12 @@ public final class SystemProperties {
CONFIGURED
}
public enum FlybyCamera {
FIRST_PERSON,
THIRD_PERSON_BACK,
THIRD_PERSON_FRONT
}
public enum FlybyWeather {
CLEAR,
RAIN,
@@ -158,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) {
@@ -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);
}
}
}
@@ -4,6 +4,7 @@ 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;
@@ -456,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,10 +107,13 @@ 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) {
@@ -153,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();
@@ -167,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));
@@ -180,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;
@@ -229,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;
@@ -240,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();
@@ -253,8 +273,9 @@ public class GLContextState {
public int savedFilterMin = -1;
public int savedFilterMag = -1;
public float savedFilterAniso = -1.0f;
public boolean wideLineEmulationActive = false;
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;
@@ -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;
}
}
@@ -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,6 +658,7 @@ 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);
}
@@ -611,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
@@ -680,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;
}
}
@@ -24,6 +24,7 @@ public final class StateSet {
public static final int R_ACTIVE_UNIT = 1 << 13;
public static final int R_PROGRAM = 1 << 14;
public static final int R_SCISSOR = 1 << 15;
public static final int R_HINT = 1 << 16;
public static final int B_FRAG_COLORBUF = 1;
public static final int B_FRAG_TEXTURE = 1 << 1;
@@ -48,7 +49,7 @@ public final class StateSet {
this.restore = restore;
this.bump = bump;
this.blendSnapshot = blendSnapshot;
this.pushCommand = () -> GLStateManager.pushStateLive(this);
this.pushCommand = () -> GLSMAttribStack.pushStateLive(this);
}
public boolean contains(int id) {
@@ -82,18 +83,18 @@ public final class StateSet {
private static StateSet buildBatch() {
return new StateSet(0,
ids(
GLStateManager.getBlendState(), GLStateManager.getAlphaState(), GLStateManager.getDepthState(),
GLStateManager.getColorMask(), GLStateManager.getPolygonState(),
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
GLStateManager.getActiveTextureUnitStack(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
GLStateManager.getLightingState(), GLStateManager.getCullState(), GLStateManager.getPolygonOffsetFillState(),
GLStateManager.getTextures().getTextureUnitStates(0), GLStateManager.getTextures().getTextureUnitStates(1),
GLStateManager.getLightStates()[0], GLStateManager.getLightStates()[1],
GLStateManager.getLightDataStates()[0], GLStateManager.getLightDataStates()[1],
GLStateManager.getColorMaterial(), GLStateManager.getColorMaterialFace(), GLStateManager.getColorMaterialParameter(),
GLStateManager.getLightModel(), GLStateManager.getFrontMaterial(), GLStateManager.getBackMaterial(),
GLStateManager.getNormalizeState(), GLStateManager.getRescaleNormalState()
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
GLSMBlend.getColorMask(), GLSMRasterizer.getPolygonState(),
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
GLSMTextures.getActiveTextureUnitStack(),
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
GLSMLighting.getLightingState(), GLSMRasterizer.getCullState(), GLSMRasterizer.getPolygonOffsetFillState(),
GLSMTextures.getTextures().getTextureUnitStates(0), GLSMTextures.getTextures().getTextureUnitStates(1),
GLSMLighting.getLightStates()[0], GLSMLighting.getLightStates()[1],
GLSMLighting.getLightDataStates()[0], GLSMLighting.getLightDataStates()[1],
GLSMLighting.getColorMaterial(), GLSMLighting.getColorMaterialFace(), GLSMLighting.getColorMaterialParameter(),
GLSMLighting.getLightModel(), GLSMLighting.getFrontMaterial(), GLSMLighting.getBackMaterial(),
GLSMLighting.getNormalizeState(), GLSMLighting.getRescaleNormalState()
),
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
B_FRAG_COLORBUF | B_LIGHTING,
@@ -103,13 +104,15 @@ public final class StateSet {
private static StateSet buildFont() {
return new StateSet(0,
ids(
GLStateManager.getBlendState(), GLStateManager.getDepthState(), GLStateManager.getShadeModelState(),
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
GLStateManager.getActiveTextureUnitStack(), GLStateManager.getProgramStack(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
GLStateManager.getTextures().getTextureUnitStates(0)
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getDepthState(), GLSMRasterizer.getShadeModelState(),
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
GLSMTextures.getActiveTextureUnitStack(), GLSMPrograms.getProgramStack(),
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
GLSMTextures.getTextures().getTextureUnitStates(0),
GLSMBlend.getColorMask(), GLSMRasterizer.getCullState(),
GLSMRasterizer.getPolygonState(), GLSMRasterizer.getPolygonOffsetFillState()
),
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM,
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM | R_POLYGON,
0,
true);
}
@@ -117,13 +120,15 @@ public final class StateSet {
private static StateSet buildFontPipeline() {
return new StateSet(0,
ids(
GLStateManager.getBlendState(), GLStateManager.getDepthState(),
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
GLStateManager.getActiveTextureUnitStack(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
GLStateManager.getTextures().getTextureUnitStates(0)
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getDepthState(),
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
GLSMTextures.getActiveTextureUnitStack(),
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
GLSMTextures.getTextures().getTextureUnitStates(0),
GLSMBlend.getColorMask(), GLSMRasterizer.getCullState(),
GLSMRasterizer.getPolygonState(), GLSMRasterizer.getPolygonOffsetFillState()
),
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT,
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_POLYGON,
0,
true);
}
@@ -131,8 +136,8 @@ public final class StateSet {
private static StateSet buildHand() {
return new StateSet(0,
ids(
GLStateManager.getBlendState(), GLStateManager.getAlphaState(), GLStateManager.getDepthState(),
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getTextures().getTextureUnitStates(1)
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMTextures.getTextures().getTextureUnitStates(1)
),
R_BLEND | R_DEPTH,
B_FRAG_COLORBUF,
@@ -141,7 +146,7 @@ public final class StateSet {
private static StateSet buildBlend() {
return new StateSet(0,
ids(GLStateManager.getBlendState(), GLStateManager.getBlendMode()),
ids(GLSMBlend.getBlendState(), GLSMBlend.getBlendMode()),
R_BLEND,
0,
true);
@@ -149,7 +154,7 @@ public final class StateSet {
private static StateSet buildAlpha() {
return new StateSet(0,
ids(GLStateManager.getAlphaState(), GLStateManager.getAlphaTest()),
ids(GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getAlphaTest()),
0,
B_FRAG_COLORBUF,
false);
@@ -157,7 +162,7 @@ public final class StateSet {
private static StateSet buildCutout() {
return new StateSet(0,
ids(GLStateManager.getAlphaState(), GLStateManager.getAlphaTest(), GLStateManager.getTextures().getTextureUnitStates(1)),
ids(GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMTextures.getTextures().getTextureUnitStates(1)),
0,
B_FRAG_COLORBUF,
false);
@@ -165,7 +170,7 @@ public final class StateSet {
private static StateSet buildMasks() {
return new StateSet(0,
ids(GLStateManager.getDepthState(), GLStateManager.getColorMask()),
ids(GLSMDepthStencilAlpha.getDepthState(), GLSMBlend.getColorMask()),
R_DEPTH | R_COLOR_MASK,
0,
false);
@@ -173,7 +178,7 @@ public final class StateSet {
private static StateSet buildCull() {
return new StateSet(0,
ids(GLStateManager.getPolygonState(), GLStateManager.getCullState()),
ids(GLSMRasterizer.getPolygonState(), GLSMRasterizer.getCullState()),
R_POLYGON,
0,
false);
@@ -220,6 +225,9 @@ public final class StateSet {
if ((mask & GL11.GL_POINT_BIT) != 0) {
restore |= R_POINT;
}
if ((mask & GL11.GL_HINT_BIT) != 0) {
restore |= R_HINT;
}
if ((mask & GL11.GL_POLYGON_BIT) != 0) {
restore |= R_POLYGON;
}
@@ -0,0 +1,42 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizons.angelica.config.SystemProperties;
public final class BackendOptions {
private static boolean configSdlGpu;
private static String configDriver = "";
private static int configGlVersion;
private BackendOptions() {}
public static void latch(boolean sdlGpu, String driver, int glVersion) {
configSdlGpu = sdlGpu;
configDriver = driver;
configGlVersion = glVersion;
}
public static boolean sdlGpuRequested() {
return SystemProperties.SDL_GPU_OVERRIDE != null ? SystemProperties.SDL_GPU_OVERRIDE : configSdlGpu;
}
public static String sdlGpuDriver() {
return sdlGpuDriver(configDriver);
}
public static String sdlGpuDriver(String driver) {
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : driver;
}
public static boolean configSdlGpu() {
return configSdlGpu;
}
public static String configDriver() {
return configDriver;
}
public static int configGlVersion() {
return configGlVersion;
}
}
@@ -0,0 +1,96 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.google.common.primitives.Ints;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public final class BackendStartGuard {
private static final Logger LOGGER = LogManager.getLogger("Angelica/BackendStartGuard");
static final int STRIKE_LIMIT = 3;
private static File marker;
private static File verified;
private static String key;
private static int strikes;
private static boolean pending;
private static boolean tripped;
private BackendStartGuard() {}
public static boolean begin(File dir, boolean sdlGpuConfigured, boolean flagForced, int requirementsRevision, String requestedDriver) {
marker = new File(dir, "sdlgpu-starting");
verified = new File(dir, "sdlgpu-verified");
key = requirementsRevision + "/" + requestedDriver;
pending = tripped = false;
if (flagForced) return false;
if (!sdlGpuConfigured) {
delete(marker);
return false;
}
if (key.equals(read(verified))) return false;
final String count = read(marker);
final Integer parsed = count == null ? null : Ints.tryParse(count.trim());
strikes = parsed == null ? 0 : parsed;
if (strikes >= STRIKE_LIMIT) {
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; resetting the render backend to OpenGL", strikes);
delete(marker);
tripped = true;
return true;
}
if (strikes == STRIKE_LIMIT - 1) {
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; one more and the render backend switches to OpenGL", strikes);
} else if (strikes > 0) {
LOGGER.info("The last start ended while bringing up the SDL GPU device");
}
pending = true;
return false;
}
public static void arm() {
if (pending) write(marker, Integer.toString(strikes + 1));
}
public static void disarm() {
if (pending) delete(marker);
}
public static void latch() {
if (!pending) return;
pending = false;
write(verified, key);
LOGGER.info("SDL GPU booted; the startup guard stays off until the requirements revision or driver changes");
}
public static boolean tripped() {
return tripped;
}
private static String read(File file) {
try {
return file.isFile() ? new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8) : null;
} catch (IOException e) {
LOGGER.warn("Could not read {}: {}", file, e.toString());
return null;
}
}
private static void write(File file, String content) {
try {
Files.createDirectories(file.toPath().getParent());
Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
LOGGER.warn("Could not write {}: {}", file, e.toString());
}
}
private static void delete(File file) {
if (file.isFile() && !file.delete()) LOGGER.warn("Could not delete {}", file);
}
}
@@ -1,6 +1,8 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizons.angelica.glsm.GLScreenCapture;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.AMDDebugOutput;
import org.lwjgl.opengl.AMDDebugOutputCallback;
@@ -23,6 +25,7 @@ import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL41;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL42;
@@ -40,6 +43,9 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetAddress;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.Pointer.POINTER_SIZE;
import static org.taumc.celeritas.lwjgl.LWJGLServiceProvider.LWJGL;
/**
@@ -378,7 +384,9 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
@Override
public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
throw new UnsupportedOperationException("GLRenderBackend.multiDrawElementsBaseVertex should not be called — LWJGL service handles this");
for (int i = 0; i < drawcount; i++) {
GL32.glDrawElementsBaseVertex(mode, memGetInt(pCount + 4L * i), type, memGetAddress(pIndices + (long) i * POINTER_SIZE), memGetInt(pBaseVertex + 4L * i));
}
}
@Override
@@ -607,6 +615,21 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL30.glFramebufferTexture2D(target, attachment, textarget, texture, level);
}
@Override
public void framebufferTexture1D(int target, int attachment, int textarget, int texture, int level) {
GL30.glFramebufferTexture1D(target, attachment, textarget, texture, level);
}
@Override
public void framebufferTexture3D(int target, int attachment, int textarget, int texture, int level, int zoffset) {
GL30.glFramebufferTexture3D(target, attachment, textarget, texture, level, zoffset);
}
@Override
public void framebufferTextureLayer(int target, int attachment, int texture, int level, int layer) {
GL30.glFramebufferTextureLayer(target, attachment, texture, level, layer);
}
@Override
public void framebufferTexture(int target, int attachment, int texture, int level) {
GL32.glFramebufferTexture(target, attachment, texture, level);
@@ -637,6 +660,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glReadBuffer(mode);
}
@Override
public ScreenCapture createScreenCapture(int width, int height) {
return new GLScreenCapture(width, height);
}
@Override
public void readPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) {
GL11.glReadPixels(x, y, width, height, format, type, pixels);
@@ -807,6 +835,28 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return GL20.glIsProgram(obj);
}
@Override
public boolean supportsProgramBinary() {
final ContextCapabilities caps = GLStateManager.capabilities;
return !RenderSystem.isGLES() && (caps.OpenGL41 || caps.GL_ARB_get_program_binary)
&& GL11.glGetInteger(GL41.GL_NUM_PROGRAM_BINARY_FORMATS) > 0;
}
@Override
public void programParameteri(int program, int pname, int value) {
GL41.glProgramParameteri(program, pname, value);
}
@Override
public void getProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {
GL41.glGetProgramBinary(program, length, binaryFormat, binary);
}
@Override
public void programBinary(int program, int binaryFormat, ByteBuffer binary) {
GL41.glProgramBinary(program, binaryFormat, binary);
}
@Override
public void validateProgram(int program) {
GL20.glValidateProgram(program);
@@ -0,0 +1,125 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizons.angelica.config.SystemProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
public final class MoltenVK {
public enum Source {
ENV,
JAR,
SYSTEM,
NONE
}
public static final String ENV_VULKAN_LIBRARY = "SDL_VULKAN_LIBRARY";
public static final String JAR_NAME = "angelica-moltenvk";
private static final Logger LOGGER = LogManager.getLogger("Angelica/MoltenVK");
private static final String LIB_NAME = "libMoltenVK.dylib";
private static final String[] SYSTEM_PATHS = { "/opt/homebrew/lib/" + LIB_NAME, "/usr/local/lib/" + LIB_NAME };
private static final String ARCH = archName(System.getProperty("os.arch", ""));
private static Source source;
private static String path;
private static boolean located;
private MoltenVK() {}
public static synchronized Source source() {
if (source == null) {
final boolean macos = System.getProperty("os.name", "").toLowerCase().contains("mac");
source = detect(macos, System.getenv(ENV_VULKAN_LIBRARY), MoltenVK.class.getResource(bundledResource(ARCH)) != null, SYSTEM_PATHS);
}
return source;
}
public static synchronized String locate() {
if (!located) {
path = resolve(source(), System.getenv(ENV_VULKAN_LIBRARY), bundledResource(ARCH), new File(SystemProperties.MOLTENVK_DIR), ARCH, SYSTEM_PATHS);
located = true;
}
return path;
}
static String archName(String osArch) {
final String arch = osArch.toLowerCase();
return arch.contains("aarch64") || arch.contains("arm64") ? "arm64" : "x64";
}
static String bundledResource(String arch) {
return "/macos/" + arch + "/org/lwjgl/vulkan/" + LIB_NAME;
}
static Source detect(boolean macos, String env, boolean bundled, String[] systemPaths) {
if (!macos) return Source.NONE;
if (env != null && !env.isEmpty()) return Source.ENV;
if (bundled) return Source.JAR;
return systemPath(systemPaths) != null ? Source.SYSTEM : Source.NONE;
}
static String resolve(Source source, String env, String resource, File dir, String arch, String[] systemPaths) {
return switch (source) {
case ENV -> env;
case JAR -> {
final String extracted = extract(resource, dir, arch);
yield extracted != null ? extracted : systemPath(systemPaths);
}
case SYSTEM -> systemPath(systemPaths);
case NONE -> null;
};
}
private static String systemPath(String[] systemPaths) {
for (final String candidate : systemPaths) {
if (new File(candidate).isFile()) return candidate;
}
return null;
}
private static String extract(String resource, File dir, String arch) {
final byte[] lib;
try (InputStream in = MoltenVK.class.getResourceAsStream(resource)) {
if (in == null) return null;
lib = readAll(in);
} catch (IOException e) {
LOGGER.warn("MoltenVK: failed reading {}: {}", resource, e.toString());
return null;
}
final File target = new File(dir, "libMoltenVK-" + arch + ".dylib");
File tmp = null;
try {
if (!target.isFile() || target.length() != lib.length || !Arrays.equals(Files.readAllBytes(target.toPath()), lib)) {
if (!dir.isDirectory() && !dir.mkdirs()) throw new IOException("cannot create " + dir);
tmp = File.createTempFile("libMoltenVK", ".dylib", dir);
Files.write(tmp.toPath(), lib);
Files.move(tmp.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
LOGGER.warn("MoltenVK: failed extracting to {}: {}", target, e.toString());
if (tmp != null) tmp.delete();
return null;
}
return target.getAbsolutePath();
}
private static byte[] readAll(InputStream in) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream(1 << 20);
final byte[] buf = new byte[16384];
int n;
while ((n = in.read(buf)) != -1) {
out.write(buf, 0, n);
}
return out.toByteArray();
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLESFormatRemap;
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.texture.TextureStaging;
@@ -26,6 +27,8 @@ import java.util.function.Predicate;
*/
public abstract class RenderBackend {
public static final int MAX_FRAMES_IN_FLIGHT = 3;
public void onPostWindowCreate(long window) {}
public abstract void init();
@@ -36,6 +39,9 @@ public abstract class RenderBackend {
/** Returns true if the current thread has a valid render context. */
public abstract boolean hasContext();
/** Returns true if the calling thread may issue render commands. */
public boolean hasContextOnThread() { return hasContext(); }
/** Returns true if the calling thread owns the "GL Context" */
public abstract boolean isCurrent();
@@ -56,6 +62,7 @@ public abstract class RenderBackend {
public boolean framebufferCompletenessIsMeaningful() { return true; }
public void onPersistentBufferWrite(int glId, long offset, long size) {}
public boolean isBufferImmutable(int glId) { return false; }
public boolean isAnisotropicSupported() { return true; }
@@ -95,7 +102,7 @@ public abstract class RenderBackend {
static VSyncMode resolveTearFreeMode(VSyncMode preferred, Predicate<VSyncMode> supported) {
if (preferred != null && preferred.tearFree() && supported.test(preferred)) return preferred;
return supported.test(VSyncMode.MAILBOX) ? VSyncMode.MAILBOX : VSyncMode.ON;
return VSyncMode.ON;
}
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
@@ -243,7 +250,7 @@ public abstract class RenderBackend {
public void onFrameBegin() {
if (GLStateManager.takeStateSeedPending()) {
GLStateManager.replayStateToBackend();
GLSMAttribStack.replayStateToBackend();
}
}
@@ -272,6 +279,12 @@ public abstract class RenderBackend {
public void voxelizeRegion(long pass, boolean rebindVertexBuffer, float x, float y, float z, int rangeBase, int rangeCount, int vertexTotal) {}
public void endVoxelizationBatch(long pass) {}
public void setVertexWriteReplay(int graphicsProgram, VertexWriteReplaySetup setup) {}
public boolean supportsComputeImageAtomics() { return true; }
public boolean supportsComputeImageAtomics(int width, int height, int depth) { return supportsComputeImageAtomics(); }
public abstract void enable(int cap);
public abstract void enablei(int cap, int index);
public abstract void disable(int cap);
@@ -447,6 +460,9 @@ public abstract class RenderBackend {
public abstract void deleteFramebuffers(int framebuffer);
public abstract void bindFramebuffer(int target, int framebuffer);
public abstract void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
public abstract void framebufferTexture1D(int target, int attachment, int textarget, int texture, int level);
public abstract void framebufferTexture3D(int target, int attachment, int textarget, int texture, int level, int zoffset);
public abstract void framebufferTextureLayer(int target, int attachment, int texture, int level, int layer);
public abstract void framebufferTexture(int target, int attachment, int texture, int level);
public abstract int genRenderbuffers();
public abstract void deleteRenderbuffers(int renderbuffer);
@@ -463,6 +479,7 @@ public abstract class RenderBackend {
public abstract void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels);
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
public abstract void readPixels(int x, int y, int width, int height, int format, int type, long pixelBufferOffset);
public abstract ScreenCapture createScreenCapture(int width, int height);
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
@@ -504,6 +521,10 @@ public abstract class RenderBackend {
public abstract int getUniformLocation(int program, ByteBuffer name);
public boolean isShader(int obj) { return false; }
public boolean isProgram(int obj) { return false; }
public boolean supportsProgramBinary() { return false; }
public void programParameteri(int program, int pname, int value) {}
public void getProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {}
public void programBinary(int program, int binaryFormat, ByteBuffer binary) {}
public void validateProgram(int program) {}
public void getAttachedShaders(int program, IntBuffer count, IntBuffer shaders) {
if (count != null) count.put(0, 0);
@@ -0,0 +1,102 @@
package com.gtnewhorizons.angelica.glsm.backend;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import org.lwjgl.opengl.GL32;
/** Async downscaled RGBA8 copy of a framebuffer's color. */
public abstract class ScreenCapture {
public static final int RING = RenderBackend.MAX_FRAMES_IN_FLIGHT + 2;
private final int width;
private final int height;
private final long[] tags = new long[RING];
private final long[] syncs = new long[RING];
private int head;
private int tail;
private long mapped;
private boolean deleted;
protected ScreenCapture(int width, int height) {
if (width <= 0 || height <= 0) throw new IllegalArgumentException("capture size " + width + "x" + height);
this.width = width;
this.height = height;
}
public final int width() { return width; }
public final int height() { return height; }
public final boolean idle() { return head == tail; }
public final boolean begin(int srcFramebuffer, int srcWidth, int srcHeight, long tag) {
if (deleted || head - tail == RING || srcWidth <= 0 || srcHeight <= 0) return false;
final int slot = head % RING;
if (!start(slot, srcFramebuffer, srcWidth, srcHeight)) return false;
final long sync = GLSMBuffers.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (sync == 0L) return false;
syncs[slot] = sync;
tags[slot] = tag;
head++;
return true;
}
public final boolean poll() {
if (mapped != 0L) throw new IllegalStateException("poll() before release() of the previous capture");
while (tail != head) {
final int slot = tail % RING;
final int status = GLSMBuffers.glClientWaitSync(syncs[slot], 0, 0L);
if (status == GL32.GL_TIMEOUT_EXPIRED) return false;
dropSync(slot);
if (status != GL32.GL_WAIT_FAILED) {
mapped = map(slot);
if (mapped != 0L) return true;
}
tail++;
}
return false;
}
public final long address() {
if (mapped == 0L) throw new IllegalStateException("no polled capture");
return mapped;
}
public final long tag() {
if (mapped == 0L) throw new IllegalStateException("no polled capture");
return tags[tail % RING];
}
public final void release() {
if (mapped == 0L) throw new IllegalStateException("no polled capture");
mapped = 0L;
unmap(tail % RING);
tail++;
}
public final void delete() {
if (deleted) return;
if (mapped != 0L) release();
deleted = true;
for (int slot = 0; slot < RING; slot++) {
dropSync(slot);
deleteSlot(slot);
}
head = tail = 0;
}
private void dropSync(int slot) {
if (syncs[slot] == 0L) return;
GLSMBuffers.glDeleteSync(syncs[slot]);
syncs[slot] = 0L;
}
/** Returns false when nothing was recorded; the slot then stays free. */
protected abstract boolean start(int slot, int srcFramebuffer, int srcWidth, int srcHeight);
protected abstract long map(int slot);
protected abstract void unmap(int slot);
/** Called for every slot, including ones never started. */
protected abstract void deleteSlot(int slot);
}
@@ -0,0 +1,24 @@
package com.gtnewhorizons.angelica.glsm.backend;
import java.util.function.IntSupplier;
/**
* How to replay a graphics program's vertex-stage image writes.
*
* @param inputLocations per input, the declared location or -1 for whatever the graphics program linked
* @param images per image the replay uses, in binding order: the written ones, then the read-only ones
* @param writtenImageCount how many of {@code images} the replay writes
* @param samplerNames samplers the graphics program may not have, because only the stripped writes read them
* @param samplerUnits per sampler name, the texture unit it reads at each draw, or -1 to use samplerTextures
* @param samplerTextures per sampler name, its texture when samplerUnits is -1
* @param samplerObjects per sampler name, supplies its current GL sampler object when samplerUnits is -1;
* null for unassigned or external bindings, and returns 0 to use the texture's own filtering
* @param vertexBufferBinding storage buffer binding of the first vertex buffer the replay program declares
* @param vertexBufferCount how many vertex buffers it declares, at consecutive bindings
* @param indexBufferBinding storage buffer binding of its index buffer
*/
public record VertexWriteReplaySetup(int replayProgram, String[] inputNames, int[] inputLocations,
IntSupplier[] images, int writtenImageCount,
String[] samplerNames, int[] samplerUnits, IntSupplier[] samplerTextures, IntSupplier[] samplerObjects,
int vertexBufferBinding, int vertexBufferCount, int indexBufferBinding) {
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
import org.lwjgl.util.glu.Cylinder;
import static org.lwjgl.opengl.GL11.GL_LINES;
@@ -28,11 +29,11 @@ public class AngelicaCylinder extends Cylinder {
y /= mag;
z /= mag;
}
GLStateManager.glNormal3f(x, y, z);
GLSMCurrentAttribs.glNormal3f(x, y, z);
}
private void emitTexCoord(float x, float y) {
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
if (super.textureFlag) GLSMCurrentAttribs.glTexCoord2f(x, y);
}
@Override
@@ -48,7 +49,7 @@ public class AngelicaCylinder extends Cylinder {
nz = (baseRadius - topRadius) / height;
if (super.drawStyle == GLU_POINT) {
GLStateManager.glBegin(GL_POINTS);
GLSMDraws.glBegin(GL_POINTS);
for (int i = 0; i < slices; i++) {
x = cos(i * da);
y = sin(i * da);
@@ -57,60 +58,60 @@ public class AngelicaCylinder extends Cylinder {
z = 0.0f;
r = baseRadius;
for (int j = 0; j <= stacks; j++) {
GLStateManager.glVertex3f(x * r, y * r, z);
GLSMDraws.glVertex3f(x * r, y * r, z);
z += dz;
r += dr;
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
} else if (super.drawStyle == GLU_LINE || super.drawStyle == GLU_SILHOUETTE) {
// Draw rings
if (super.drawStyle == GLU_LINE) {
z = 0.0f;
r = baseRadius;
for (int j = 0; j <= stacks; j++) {
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (int i = 0; i < slices; i++) {
x = cos(i * da);
y = sin(i * da);
emitNormal(x * nsign, y * nsign, nz * nsign);
GLStateManager.glVertex3f(x * r, y * r, z);
GLSMDraws.glVertex3f(x * r, y * r, z);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
z += dz;
r += dr;
}
} else {
// draw one ring at each end
if (baseRadius != 0.0) {
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (int i = 0; i < slices; i++) {
x = cos(i * da);
y = sin(i * da);
emitNormal(x * nsign, y * nsign, nz * nsign);
GLStateManager.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
GLSMDraws.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
}
GLStateManager.glEnd();
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glEnd();
GLSMDraws.glBegin(GL_LINE_LOOP);
for (int i = 0; i < slices; i++) {
x = cos(i * da);
y = sin(i * da);
emitNormal(x * nsign, y * nsign, nz * nsign);
GLStateManager.glVertex3f(x * topRadius, y * topRadius, height);
GLSMDraws.glVertex3f(x * topRadius, y * topRadius, height);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
// draw length lines
GLStateManager.glBegin(GL_LINES);
GLSMDraws.glBegin(GL_LINES);
for (int i = 0; i < slices; i++) {
x = cos(i * da);
y = sin(i * da);
emitNormal(x * nsign, y * nsign, nz * nsign);
GLStateManager.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
GLStateManager.glVertex3f(x * topRadius, y * topRadius, height);
GLSMDraws.glVertex3f(x * baseRadius, y * baseRadius, 0.0f);
GLSMDraws.glVertex3f(x * topRadius, y * topRadius, height);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
} else if (super.drawStyle == GLU_FILL) {
final float ds = 1.0f / slices;
final float dt = 1.0f / stacks;
@@ -119,20 +120,20 @@ public class AngelicaCylinder extends Cylinder {
r = baseRadius;
for (int j = 0; j < stacks; j++) {
float s = 0.0f;
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
GLSMDraws.glBegin(GL_TRIANGLE_STRIP);
for (int i = 0; i <= slices; i++) {
final float angle = (i == slices) ? 0.0f : i * da;
x = sin(angle);
y = cos(angle);
emitNormal(x * nsign, y * nsign, nz * nsign);
emitTexCoord(s, t);
GLStateManager.glVertex3f(x * r, y * r, z);
GLSMDraws.glVertex3f(x * r, y * r, z);
emitNormal(x * nsign, y * nsign, nz * nsign);
emitTexCoord(s, t + dt);
GLStateManager.glVertex3f(x * (r + dr), y * (r + dr), z + dz);
GLSMDraws.glVertex3f(x * (r + dr), y * (r + dr), z + dz);
s += ds;
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
r += dr;
t += dt;
z += dz;
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
import org.lwjgl.util.glu.Disk;
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
@@ -23,7 +24,7 @@ public class AngelicaDisk extends Disk {
private static final float PI = (float) Math.PI;
private void emitTexCoord(float x, float y) {
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
if (super.textureFlag) GLSMCurrentAttribs.glTexCoord2f(x, y);
}
@Override
@@ -31,7 +32,7 @@ public class AngelicaDisk extends Disk {
final float da, dr;
if (super.normals != GLU_NONE) {
GLStateManager.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
}
da = 2.0f * PI / slices;
@@ -45,29 +46,29 @@ public class AngelicaDisk extends Disk {
for (int l = 0; l < loops; l++) {
final float r2 = r1 + dr;
if (super.orientation == GLU_OUTSIDE) {
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
GLSMDraws.glBegin(GL_TRIANGLE_STRIP);
for (int s = 0; s <= slices; s++) {
final float a = (s == slices) ? 0.0f : s * da;
sa = sin(a);
ca = cos(a);
emitTexCoord(0.5f + sa * r2 / dtc, 0.5f + ca * r2 / dtc);
GLStateManager.glVertex2f(r2 * sa, r2 * ca);
GLSMDraws.glVertex2f(r2 * sa, r2 * ca);
emitTexCoord(0.5f + sa * r1 / dtc, 0.5f + ca * r1 / dtc);
GLStateManager.glVertex2f(r1 * sa, r1 * ca);
GLSMDraws.glVertex2f(r1 * sa, r1 * ca);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
} else {
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
GLSMDraws.glBegin(GL_TRIANGLE_STRIP);
for (int s = slices; s >= 0; s--) {
final float a = (s == slices) ? 0.0f : s * da;
sa = sin(a);
ca = cos(a);
emitTexCoord(0.5f - sa * r2 / dtc, 0.5f + ca * r2 / dtc);
GLStateManager.glVertex2f(r2 * sa, r2 * ca);
GLSMDraws.glVertex2f(r2 * sa, r2 * ca);
emitTexCoord(0.5f - sa * r1 / dtc, 0.5f + ca * r1 / dtc);
GLStateManager.glVertex2f(r1 * sa, r1 * ca);
GLSMDraws.glVertex2f(r1 * sa, r1 * ca);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
r1 = r2;
}
@@ -76,56 +77,56 @@ public class AngelicaDisk extends Disk {
/* draw loops */
for (int l = 0; l <= loops; l++) {
final float r = innerRadius + l * dr;
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (int s = 0; s < slices; s++) {
final float a = s * da;
GLStateManager.glVertex2f(r * sin(a), r * cos(a));
GLSMDraws.glVertex2f(r * sin(a), r * cos(a));
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
/* draw spokes */
for (int s = 0; s < slices; s++) {
final float a = s * da;
final float x = sin(a);
final float y = cos(a);
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int l = 0; l <= loops; l++) {
final float r = innerRadius + l * dr;
GLStateManager.glVertex2f(r * x, r * y);
GLSMDraws.glVertex2f(r * x, r * y);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
case GLU_POINT -> {
GLStateManager.glBegin(GL_POINTS);
GLSMDraws.glBegin(GL_POINTS);
for (int s = 0; s < slices; s++) {
final float a = s * da;
final float x = sin(a);
final float y = cos(a);
for (int l = 0; l <= loops; l++) {
final float r = innerRadius * l * dr;
GLStateManager.glVertex2f(r * x, r * y);
GLSMDraws.glVertex2f(r * x, r * y);
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
case GLU_SILHOUETTE -> {
if (innerRadius != 0.0) {
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (float a = 0.0f; a < 2.0 * PI; a += da) {
final float x = innerRadius * sin(a);
final float y = innerRadius * cos(a);
GLStateManager.glVertex2f(x, y);
GLSMDraws.glVertex2f(x, y);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (float a = 0; a < 2.0f * PI; a += da) {
final float x = outerRadius * sin(a);
final float y = outerRadius * cos(a);
GLStateManager.glVertex2f(x, y);
GLSMDraws.glVertex2f(x, y);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
default -> {
}
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.lwjgl.util.glu.PartialDisk;
@@ -63,7 +65,7 @@ public class AngelicaPartialDisk extends PartialDisk {
}
switch (super.normals) {
case GLU_FLAT, GLU_SMOOTH -> GLStateManager.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
case GLU_FLAT, GLU_SMOOTH -> GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, (super.orientation == GLU_OUTSIDE) ? 1.0f : -1.0f);
default -> {
}
}
@@ -73,11 +75,11 @@ public class AngelicaPartialDisk extends PartialDisk {
if (innerRadius == .0f) {
finish = loops - 1;
/* Triangle strip for inner polygons */
GLStateManager.glBegin(GL_TRIANGLE_FAN);
GLSMDraws.glBegin(GL_TRIANGLE_FAN);
if (super.textureFlag) {
GLStateManager.glTexCoord2f(0.5f, 0.5f);
GLSMCurrentAttribs.glTexCoord2f(0.5f, 0.5f);
}
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
GLSMDraws.glVertex3f(0.0f, 0.0f, 0.0f);
radiusLow = outerRadius - deltaRadius * ((float) (loops - 1) / loops);
if (super.textureFlag) {
texLow = radiusLow / outerRadius / 2;
@@ -86,19 +88,19 @@ public class AngelicaPartialDisk extends PartialDisk {
if (super.orientation == GLU_OUTSIDE) {
for (int i = slices; i >= 0; i--) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
}
} else {
for (int i = 0; i <= slices; i++) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
} else {
finish = loops;
}
@@ -110,35 +112,35 @@ public class AngelicaPartialDisk extends PartialDisk {
texHigh = radiusHigh / outerRadius / 2;
}
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
GLSMDraws.glBegin(GL_TRIANGLE_STRIP);
for (int i = 0; i <= slices; i++) {
if (super.orientation == GLU_OUTSIDE) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
} else {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texHigh * sinCache[i] + 0.5f, texHigh * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusHigh * sinCache[i], radiusHigh * cosCache[i], 0.0f);
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
case GLU_POINT -> {
GLStateManager.glBegin(GL_POINTS);
GLSMDraws.glBegin(GL_POINTS);
for (int i = 0; i < slices2; i++) {
sintemp = sinCache[i];
costemp = cosCache[i];
@@ -148,24 +150,24 @@ public class AngelicaPartialDisk extends PartialDisk {
if (super.textureFlag) {
texLow = radiusLow / outerRadius / 2;
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
GLSMDraws.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
case GLU_LINE -> {
if (innerRadius == outerRadius) {
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int i = 0; i <= slices; i++) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(sinCache[i] / 2 + 0.5f, cosCache[i] / 2 + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(sinCache[i] / 2 + 0.5f, cosCache[i] / 2 + 0.5f);
}
GLStateManager.glVertex3f(innerRadius * sinCache[i], innerRadius * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(innerRadius * sinCache[i], innerRadius * cosCache[i], 0.0f);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
break;
}
for (int j = 0; j <= loops; j++) {
@@ -174,19 +176,19 @@ public class AngelicaPartialDisk extends PartialDisk {
texLow = radiusLow / outerRadius / 2;
}
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int i = 0; i <= slices; i++) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
for (int i = 0; i < slices2; i++) {
sintemp = sinCache[i];
costemp = cosCache[i];
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int j = 0; j <= loops; j++) {
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
if (super.textureFlag) {
@@ -194,11 +196,11 @@ public class AngelicaPartialDisk extends PartialDisk {
}
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
GLSMDraws.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
case GLU_SILHOUETTE -> {
@@ -206,17 +208,17 @@ public class AngelicaPartialDisk extends PartialDisk {
for (int i = 0; i <= slices; i += slices) {
sintemp = sinCache[i];
costemp = cosCache[i];
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int j = 0; j <= loops; j++) {
radiusLow = outerRadius - deltaRadius * ((float) j / loops);
if (super.textureFlag) {
texLow = radiusLow / outerRadius / 2;
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
GLSMDraws.glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
for (int j = 0; j <= loops; j += loops) {
@@ -225,14 +227,14 @@ public class AngelicaPartialDisk extends PartialDisk {
texLow = radiusLow / outerRadius / 2;
}
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int i = 0; i <= slices; i++) {
if (super.textureFlag) {
GLStateManager.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
GLSMCurrentAttribs.glTexCoord2f(texLow * sinCache[i] + 0.5f, texLow * cosCache[i] + 0.5f);
}
GLStateManager.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
GLSMDraws.glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
if (innerRadius == outerRadius) break;
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.compat.lwjgl;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
import org.lwjgl.util.glu.Sphere;
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
@@ -30,11 +31,11 @@ public class AngelicaSphere extends Sphere {
y /= mag;
z /= mag;
}
GLStateManager.glNormal3f(x, y, z);
GLSMCurrentAttribs.glNormal3f(x, y, z);
}
private void emitTexCoord(float x, float y) {
if (super.textureFlag) GLStateManager.glTexCoord2f(x, y);
if (super.textureFlag) GLSMCurrentAttribs.glTexCoord2f(x, y);
}
@Override
@@ -54,9 +55,9 @@ public class AngelicaSphere extends Sphere {
if (super.drawStyle == GLU_FILL) {
if (!super.textureFlag) {
// draw +Z end as a triangle fan
GLStateManager.glBegin(GL_TRIANGLE_FAN);
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
GLStateManager.glVertex3f(0.0f, 0.0f, nsign * radius);
GLSMDraws.glBegin(GL_TRIANGLE_FAN);
GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, 1.0f);
GLSMDraws.glVertex3f(0.0f, 0.0f, nsign * radius);
for (int j = 0; j <= slices; j++) {
theta = (j == slices) ? 0.0f : j * dtheta;
x = -sin(theta) * sin(drho);
@@ -65,9 +66,9 @@ public class AngelicaSphere extends Sphere {
if (normals) {
emitNormal(x * nsign, y * nsign, z * nsign);
}
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
ds = 1.0f / slices;
@@ -84,7 +85,7 @@ public class AngelicaSphere extends Sphere {
// draw intermediate stacks as triangle strips (GL_QUAD_STRIP removed in core profile)
for (int i = imin; i < imax; i++) {
rho = i * drho;
GLStateManager.glBegin(GL_TRIANGLE_STRIP);
GLSMDraws.glBegin(GL_TRIANGLE_STRIP);
s = 0.0f;
for (int j = 0; j <= slices; j++) {
theta = (j == slices) ? 0.0f : j * dtheta;
@@ -95,7 +96,7 @@ public class AngelicaSphere extends Sphere {
emitNormal(x * nsign, y * nsign, z * nsign);
}
emitTexCoord(s, t);
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
x = -sin(theta) * sin(rho + drho);
y = cos(theta) * sin(rho + drho);
z = nsign * cos(rho + drho);
@@ -104,17 +105,17 @@ public class AngelicaSphere extends Sphere {
}
emitTexCoord(s, t - dt);
s += ds;
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
t -= dt;
}
if (!super.textureFlag) {
// draw -Z end as a triangle fan
GLStateManager.glBegin(GL_TRIANGLE_FAN);
GLStateManager.glNormal3f(0.0f, 0.0f, -1.0f);
GLStateManager.glVertex3f(0.0f, 0.0f, -radius * nsign);
GLSMDraws.glBegin(GL_TRIANGLE_FAN);
GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, -1.0f);
GLSMDraws.glVertex3f(0.0f, 0.0f, -radius * nsign);
rho = PI - drho;
s = 1.0f;
for (int j = slices; j >= 0; j--) {
@@ -124,46 +125,46 @@ public class AngelicaSphere extends Sphere {
z = nsign * cos(rho);
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
s -= ds;
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
} else if (super.drawStyle == GLU_LINE || super.drawStyle == GLU_SILHOUETTE) {
// draw stack lines
for (int i = 1; i < stacks; i++) {
rho = i * drho;
GLStateManager.glBegin(GL_LINE_LOOP);
GLSMDraws.glBegin(GL_LINE_LOOP);
for (int j = 0; j < slices; j++) {
theta = j * dtheta;
x = cos(theta) * sin(rho);
y = sin(theta) * sin(rho);
z = cos(rho);
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
// draw slice lines
for (int j = 0; j < slices; j++) {
theta = j * dtheta;
GLStateManager.glBegin(GL_LINE_STRIP);
GLSMDraws.glBegin(GL_LINE_STRIP);
for (int i = 0; i <= stacks; i++) {
rho = i * drho;
x = cos(theta) * sin(rho);
y = sin(theta) * sin(rho);
z = cos(rho);
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
} else if (super.drawStyle == GLU_POINT) {
// top and bottom-most points
GLStateManager.glBegin(GL_POINTS);
if (normals) GLStateManager.glNormal3f(0.0f, 0.0f, nsign);
GLStateManager.glVertex3f(0.0f, 0.0f, radius);
if (normals) GLStateManager.glNormal3f(0.0f, 0.0f, -nsign);
GLStateManager.glVertex3f(0.0f, 0.0f, -radius);
GLSMDraws.glBegin(GL_POINTS);
if (normals) GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, nsign);
GLSMDraws.glVertex3f(0.0f, 0.0f, radius);
if (normals) GLSMCurrentAttribs.glNormal3f(0.0f, 0.0f, -nsign);
GLSMDraws.glVertex3f(0.0f, 0.0f, -radius);
// loop over stacks
for (int i = 1; i < stacks - 1; i++) {
@@ -174,10 +175,10 @@ public class AngelicaSphere extends Sphere {
y = sin(theta) * sin(rho);
z = cos(rho);
if (normals) emitNormal(x * nsign, y * nsign, z * nsign);
GLStateManager.glVertex3f(x * radius, y * radius, z * radius);
GLSMDraws.glVertex3f(x * radius, y * radius, z * radius);
}
}
GLStateManager.glEnd();
GLSMDraws.glEnd();
}
}
}
@@ -1,5 +1,8 @@
package com.gtnewhorizons.angelica.glsm.dsa;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import org.lwjgl.opengl.GL11;
@@ -42,23 +45,23 @@ public class DSAARB extends DSAUnsupported {
@Override
public void texParameteri(int texture, int target, int pname, int param) {
param = GLStateManager.remapTexClamp(pname, param);
if(!GLStateManager.updateTexParameteriCache(target, texture, pname, param)) return;
param = GLSMTextures.remapTexClamp(pname, param);
if(!GLSMTextures.updateTexParameteriCache(target, texture, pname, param)) return;
RENDER_BACKEND.textureParameteri(texture, target, pname, param);
}
@Override
public void texParameterf(int texture, int target, int pname, float param) {
param = GLStateManager.remapTexClamp(pname, param);
if(!GLStateManager.updateTexParameterfCache(target, texture, pname, param)) return;
param = GLSMTextures.remapTexClamp(pname, param);
if(!GLSMTextures.updateTexParameterfCache(target, texture, pname, param)) return;
RENDER_BACKEND.textureParameterf(texture, target, pname, param);
}
@Override
public void texParameteriv(int texture, int target, int pname, IntBuffer params) {
GLStateManager.remapTexClampBuffer(pname, params);
GLSMTextures.remapTexClampBuffer(pname, params);
RENDER_BACKEND.textureParameteriv(texture, target, pname, params);
}
@@ -74,8 +77,8 @@ public class DSAARB extends DSAUnsupported {
@Override
public int getTexParameteri(int texture, int target, int pname) {
final long v = GLStateManager.lookupTexParameteri(TextureInfoCache.INSTANCE.getInfo(texture), pname);
return v != GLStateManager.TEX_PARAM_MISS ? (int) v : RENDER_BACKEND.getTextureParameteri(texture, target, pname);
final long v = GLSMTextures.lookupTexParameteri(TextureInfoCache.INSTANCE.getInfo(texture), pname);
return v != GLSMTextures.TEX_PARAM_MISS ? (int) v : RENDER_BACKEND.getTextureParameteri(texture, target, pname);
}
@Override
@@ -100,23 +103,23 @@ public class DSAARB extends DSAUnsupported {
@Override
public void bindTextureToUnit(int target, int unit, int texture) {
if(GLStateManager.getBoundTextureForServerState(unit) == texture) return;
if(GLSMTextures.getBoundTextureForServerState(unit) == texture) return;
if (texture == 0) {
super.bindTextureToUnit(target != 0 ? target : GL11.GL_TEXTURE_2D, unit, texture);
} else {
RENDER_BACKEND.bindTextureUnit(unit, texture);
final var binding = GLStateManager.getTextures().getTextureUnitBindings(unit);
final var binding = GLSMTextures.getTextures().getTextureUnitBindings(unit);
binding.beforeModify();
binding.setBinding(texture, target);
GLStateManager.trackMaxBoundTextureUnit(unit);
GLSMTextures.trackMaxBoundTextureUnit(unit);
}
}
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = GLStateManager.glCreateBuffers();
GLStateManager.glNamedBufferData(buffer, data, usage);
final int buffer = GLSMBuffers.glCreateBuffers();
GLSMBuffers.glNamedBufferData(buffer, data, usage);
return buffer;
}
@@ -128,16 +131,17 @@ public class DSAARB extends DSAUnsupported {
@Override
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
RENDER_BACKEND.namedFramebufferTexture(fb, attachment, texture, levels);
GLSMFramebuffer.glNamedFramebufferTexture(fb, attachment, texture, levels);
}
@Override
public int createFramebuffer() {
GLStateManager.requireContext("glCreateFramebuffers");
return RENDER_BACKEND.createFramebuffers();
}
@Override
public int createTexture(int target) {
return RENDER_BACKEND.createTextures(target);
return GLSMTextures.glCreateTextures(target);
}
}
@@ -1,14 +1,16 @@
package com.gtnewhorizons.angelica.glsm.dsa;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
import com.gtnewhorizons.angelica.glsm.texture.TextureTargets;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
@@ -18,7 +20,7 @@ public class DSAUnsupported implements DSAAccess {
@Override
public void generateMipmaps(int texture, int target) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
RENDER_BACKEND.generateMipmap(target);
}
@@ -57,40 +59,40 @@ public class DSAUnsupported implements DSAAccess {
@Override
public void texParameteri(int texture, int target, int pname, int param) {
final int previous = bindForEdit(target, texture);
GLStateManager.glTexParameteri(target, pname, param);
GLSMTextures.glTexParameteri(target, pname, param);
restoreBinding(target, previous);
}
@Override
public void texParameterf(int texture, int target, int pname, float param) {
final int previous = bindForEdit(target, texture);
GLStateManager.glTexParameterf(target, pname, param);
GLSMTextures.glTexParameterf(target, pname, param);
restoreBinding(target, previous);
}
@Override
public void texParameteriv(int texture, int target, int pname, IntBuffer params) {
final int previous = bindForEdit(target, texture);
GLStateManager.glTexParameter(target, pname, params);
GLSMTextures.glTexParameter(target, pname, params);
restoreBinding(target, previous);
}
@Override
public void readBuffer(int framebuffer, int buffer) {
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
GLSMFramebuffer.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
RENDER_BACKEND.readBuffer(buffer);
}
@Override
public void drawBuffers(int framebuffer, IntBuffer buffers) {
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
GLSMFramebuffer.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
RENDER_BACKEND.drawBuffers(buffers);
}
@Override
public int getTexParameteri(int texture, int target, int pname) {
final int previous = bindForEdit(target, texture);
final int result = GLStateManager.glGetTexParameteri(target, pname);
final int result = GLSMTextures.glGetTexParameteri(target, pname);
restoreBinding(target, previous);
return result;
}
@@ -98,7 +100,7 @@ public class DSAUnsupported implements DSAAccess {
@Override
public float getTexParameterf(int texture, int target, int pname) {
final int previous = bindForEdit(target, texture);
final float result = GLStateManager.glGetTexParameterf(target, pname);
final float result = GLSMTextures.glGetTexParameterf(target, pname);
restoreBinding(target, previous);
return result;
}
@@ -115,29 +117,29 @@ public class DSAUnsupported implements DSAAccess {
@Override
public void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
final int previous = previous2D();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, destTexture);
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, destTexture);
RENDER_BACKEND.copyTexSubImage2D(target, i, i1, i2, i3, i4, width, height);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, previous);
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, previous);
}
@Override
public void bindTextureToUnit(int unit, int texture) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, texture);
}
@Override
public void bindTextureToUnit(int target, int unit, int texture) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLStateManager.glBindTexture(target, texture);
GLSMTextures.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLSMTextures.glBindTexture(target, texture);
}
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(target, buffer);
GLStateManager.glBufferData(target, data, usage);
GLStateManager.glBindBuffer(target, 0);
final int buffer = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(target, buffer);
GLSMBuffers.glBufferData(target, data, usage);
GLSMBuffers.glBindBuffer(target, 0);
return buffer;
}
@@ -145,49 +147,38 @@ public class DSAUnsupported implements DSAAccess {
@Override
public 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) {
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, source);
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, dest);
GLSMFramebuffer.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, source);
GLSMFramebuffer.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, dest);
RENDER_BACKEND.blitFramebuffer(offsetX, offsetY, width, height, offsetX2, offsetY2, width2, height2, bufferChoice, filter);
}
@Override
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
GLStateManager.glBindFramebuffer(fbtarget, fb);
RENDER_BACKEND.framebufferTexture2D(fbtarget, attachment, target, texture, levels);
GLSMFramebuffer.glBindFramebuffer(fbtarget, fb);
GLSMFramebuffer.glFramebufferTexture2D(fbtarget, attachment, target, texture, levels);
}
@Override
public int createFramebuffer() {
final int framebuffer = GLStateManager.glGenFramebuffers();
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
final int framebuffer = GLSMFramebuffer.glGenFramebuffers();
GLSMFramebuffer.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
return framebuffer;
}
@Override
public int createTexture(int target) {
final int texture = RENDER_BACKEND.genTextures();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
final int texture = GLSMTextures.glGenTextures();
GLSMTextures.glBindTexture(target, texture);
return texture;
}
private static int bindingQuery(int target) {
return switch (target) {
case GL11.GL_TEXTURE_1D -> GL11.GL_TEXTURE_BINDING_1D;
case GL12.GL_TEXTURE_3D -> GL12.GL_TEXTURE_BINDING_3D;
case GL13.GL_TEXTURE_CUBE_MAP -> GL13.GL_TEXTURE_BINDING_CUBE_MAP;
case GL30.GL_TEXTURE_2D_ARRAY -> GL30.GL_TEXTURE_BINDING_2D_ARRAY;
case GL31.GL_TEXTURE_RECTANGLE -> GL31.GL_TEXTURE_BINDING_RECTANGLE;
default -> GL11.GL_TEXTURE_BINDING_2D;
};
}
private static int previousBinding(int target) {
return target == GL11.GL_TEXTURE_2D ? previous2D() : RENDER_BACKEND.getInteger(bindingQuery(target));
return target == GL11.GL_TEXTURE_2D ? previous2D() : RENDER_BACKEND.getInteger(TextureTargets.bindingQuery(target));
}
private static int previous2D() {
if (GLStateManager.isCachingEnabled()) {
final TextureBindingStack slot = GLStateManager.getTextures().getTextureUnitBindings(GLStateManager.getActiveTextureUnit());
final TextureBindingStack slot = GLSMTextures.getTextures().getTextureUnitBindings(GLSMTextures.getActiveTextureUnit());
final int t = slot.getTarget();
if (t == 0 || t == GL11.GL_TEXTURE_2D) return slot.getBinding();
}
@@ -196,13 +187,13 @@ public class DSAUnsupported implements DSAAccess {
private static int bindForEdit(int target, int texture) {
final int previous = previousBinding(target);
if (target == GL11.GL_TEXTURE_2D) GLStateManager.glBindTexture(target, texture);
if (target == GL11.GL_TEXTURE_2D) GLSMTextures.glBindTexture(target, texture);
else RENDER_BACKEND.bindTexture(target, texture);
return previous;
}
private static void restoreBinding(int target, int previous) {
if (target == GL11.GL_TEXTURE_2D) GLStateManager.glBindTexture(target, previous);
if (target == GL11.GL_TEXTURE_2D) GLSMTextures.glBindTexture(target, previous);
else RENDER_BACKEND.bindTexture(target, previous);
}
}
@@ -0,0 +1,17 @@
package com.gtnewhorizons.angelica.glsm.ffp;
public final class ClipDistanceGlsl {
public static final String VS_OUT_DIST0 = "out vec4 angelica_ClipDist0;";
public static final String VS_OUT_DIST1 = "out vec4 angelica_ClipDist1;";
public static final String FS_IN_DIST0 = "in vec4 angelica_ClipDist0;";
public static final String FS_IN_DIST1 = "in vec4 angelica_ClipDist1;";
public static final String FS_DISCARD = "if (any(lessThan(angelica_ClipDist0, vec4(0.0))) || any(lessThan(angelica_ClipDist1, vec4(0.0)))) discard;\n";
private ClipDistanceGlsl() {}
public static void appendVsAssign(StringBuilder sb, String[] distances) {
sb.append(" angelica_ClipDist0 = vec4(").append(distances[0]).append(", ").append(distances[1]).append(", ").append(distances[2]).append(", ").append(distances[3]).append(");\n");
sb.append(" angelica_ClipDist1 = vec4(").append(distances[4]).append(", ").append(distances[5]).append(", ").append(distances[6]).append(", ").append(distances[7]).append(");\n");
}
}
@@ -1,6 +1,12 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11;
@@ -24,14 +30,14 @@ public final class CombinedGlint {
}
public static boolean begin(Matrix4fc secondTextureMatrix) {
if (active || GLStateManager.getActiveProgram() != 0 || GLStateManager.getLightingState().isEnabled() || GLStateManager.getColorSumState().isEnabled() || !GLStateManager.glIsEnabled(GL11.GL_BLEND)) return false;
final var blend = GLStateManager.getBlendState();
if (active || GLSMPrograms.getActiveProgram() != 0 || GLSMLighting.getLightingState().isEnabled() || GLSMCurrentAttribs.getColorSumState().isEnabled() || !GLSMEnable.glIsEnabled(GL11.GL_BLEND)) return false;
final var blend = GLSMBlend.getBlendState();
final boolean lastAlpha = blend.getSrcAlpha() == GL11.GL_ONE && blend.getDstAlpha() == GL11.GL_ZERO && blend.getEquationAlpha() == GL14.GL_FUNC_ADD;
final boolean preserveAlpha = blend.getSrcAlpha() == GL11.GL_ZERO && blend.getDstAlpha() == GL11.GL_ONE && (blend.getEquationAlpha() == GL14.GL_FUNC_ADD || blend.getEquationAlpha() == GL14.GL_FUNC_REVERSE_SUBTRACT);
if (blend.getSrcRgb() != GL11.GL_SRC_COLOR || blend.getDstRgb() != GL11.GL_ONE
|| (!lastAlpha && !preserveAlpha && (blend.getSrcAlpha() != GL11.GL_SRC_COLOR || blend.getDstAlpha() != GL11.GL_ONE || blend.getEquationAlpha() != blend.getEquationRgb()))
|| (blend.getEquationRgb() != GL14.GL_FUNC_ADD && blend.getEquationRgb() != GL14.GL_FUNC_REVERSE_SUBTRACT)) return false;
final var textures = GLStateManager.getTextures();
final var textures = GLSMTextures.getTextures();
if (!textures.getTextureUnitStates(0).isEnabled() || textures.getTextureUnitBindings(0).getBinding() == 0 || textures.getTexEnvState(0).mode != GL11.GL_MODULATE) return false;
if (textures.getTextureUnitStates(1).isEnabled() && textures.getTextureUnitBindings(1).getBinding() != 0 && textures.getTexEnvState(1).mode != GL11.GL_MODULATE) return false;
for (int unit = 2; unit < 4; unit++) {
@@ -41,7 +47,7 @@ public final class CombinedGlint {
relative.set(secondTextureMatrix).mul(inverse);
if (!relative.isFinite()) return false;
savedMatrix.set(textures.getTextureUnitMatrix(3));
GLStateManager.setTextureMatrix(3, relative);
GLSMMatrices.setTextureMatrix(3, relative);
replaceAlpha = lastAlpha;
active = true;
return true;
@@ -50,6 +56,6 @@ public final class CombinedGlint {
public static void end() {
if (!active) return;
active = false;
GLStateManager.setTextureMatrix(3, savedMatrix);
GLSMMatrices.setTextureMatrix(3, savedMatrix);
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
public final class CubeInstancedAttribs {
@@ -14,8 +14,8 @@ public final class CubeInstancedAttribs {
public static void pointInstanceAttribs(long base) {
InstancedAttribs.pointHead(base, STRIDE);
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP_SCALE, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP_SCALE);
GLStateManager.glVertexAttribPointer(LOC_CUBE_TEX, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CUBE_TEX);
GLSMVertexInput.glVertexAttribPointer(LOC_LIGHTMAP_SCALE, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP_SCALE);
GLSMVertexInput.glVertexAttribPointer(LOC_CUBE_TEX, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CUBE_TEX);
}
static void enableInstanceArrays() {
@@ -5,6 +5,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
public record CubeParams(float minX, float minY, float minZ, float spanX, float spanY, float spanZ, float u0, float v0, float uScaleX, float vScaleY, float uScaleZ, float vScaleZ) {
private static final float MIN_SPAN = 1.0e-4f;
private static final float UV_EPSILON = 1.0e-5f;
public CubeParams {
if (!(spanX > MIN_SPAN && spanY > MIN_SPAN && spanZ > MIN_SPAN)) throw new IllegalArgumentException("degenerate cube span");
@@ -43,12 +44,25 @@ public record CubeParams(float minX, float minY, float minZ, float spanX, float
}
}
public void writeTexture(long ptr) {
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, u0);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, v0);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY);
public boolean uvInsideTexture() {
final float u1 = u0 + 2.0f * (Math.abs(uScaleX) + uScaleZ);
final float v1 = v0 + vScaleY + vScaleZ;
return u0 >= 0.0f && v0 >= 0.0f && u1 <= 1.0f + UV_EPSILON && v1 <= 1.0f + UV_EPSILON;
}
public static boolean allInsideTexture(CubeParams[] cubes) {
for (CubeParams cube : cubes) {
if (!cube.uvInsideTexture()) return false;
}
return true;
}
public void writeTexture(long ptr, float regionU, float regionV, float regionW, float regionH) {
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ * regionW);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ * regionH);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, regionU + u0 * regionW);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, regionV + v0 * regionH);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX * regionW);
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY * regionH);
}
}
@@ -104,14 +104,17 @@ public final class FFPUniformBlock {
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
public static final int SECONDARY_COLOR = vec3("u_SecondaryColor");
public static final int ATLAS_GUTTER = flt("u_AtlasGutter");
public static final int FOG_PARAMS = vec4("u_FogParams");
public static final int FOG_COLOR = vec4("u_FogColor");
// Rain and snow, for Instancing.WEATHER. 0 = (translate.xyz, 1/radius),
// 1 = (cameraFrac.xy, partialTicks, age), 2 = (rainScroll, snowScroll, rainStrength, unused).
// Weather columns, for Instancing.WEATHER. 0 = (translate.xyz, 1/radius),
// 1 = (cameraFrac.xy, partialTicks, age), 2 = (vanillaTick, providerTick, rainStrength, providerAge),
// 3 = (provider 1/radius, unused, unused, unused).
public static final int WEATHER_PARAMS_0 = vec4("u_WeatherParams0");
public static final int WEATHER_PARAMS_1 = vec4("u_WeatherParams1");
public static final int WEATHER_PARAMS_2 = vec4("u_WeatherParams2");
public static final int WEATHER_PARAMS_3 = vec4("u_WeatherParams3");
public static final int SIZE;
public static final String GLSL_DECL;
@@ -1,6 +1,8 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
import com.gtnewhorizons.angelica.glsm.states.LightState;
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
import org.joml.Matrix3f;
@@ -14,68 +16,145 @@ public final class FFPVertexLighting {
private static final Vector4f scratchEyePos = new Vector4f();
private static final Matrix3f scratchNormalMat = new Matrix3f();
private static final Vector3f scratchVec = new Vector3f();
private static final Vector3f scratchFactor = new Vector3f();
private static final Vector3f base = new Vector3f();
private static final Vector3f[] lightDirs = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
private static final Vector3f[] lightDiffuse = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
private static int litCount;
private static boolean normalize;
private static float rescale;
static {
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
lightDirs[i] = new Vector3f();
lightDiffuse[i] = new Vector3f();
}
}
private FFPVertexLighting() {}
public static boolean modulatesVertexColor(Vector3f destFactor) {
destFactor.set(1.0f);
if (!prepare(false)) return false;
if (!GLStateManager.getLightingState().isEnabled()) return false;
if (!GLStateManager.getColorMaterial().isEnabled()) return false;
if (VertexKey.encodeColorMaterialMode(GLStateManager.getColorMaterialParameter().getValue()) != VertexKey.CM_AMBIENT_AND_DIFFUSE) return false;
final MaterialState material = GLStateManager.getFrontMaterial();
if (material.emission.x != 0.0f || material.emission.y != 0.0f || material.emission.z != 0.0f)
return false;
final boolean materialHasSpecular = material.shininess > 0.0f && (material.specular.x != 0.0f || material.specular.y != 0.0f || material.specular.z != 0.0f);
final Vector4f lightModelAmbient = GLStateManager.getLightModel().ambient;
float r = lightModelAmbient.x;
float g = lightModelAmbient.y;
float b = lightModelAmbient.z;
final var lightEnables = GLStateManager.getLightStates();
final var lightData = GLStateManager.getLightDataStates();
boolean normalReady = false;
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
if (!lightEnables[i].isEnabled()) continue;
final LightState light = lightData[i];
if (materialHasSpecular && (light.specular.x != 0.0f || light.specular.y != 0.0f || light.specular.z != 0.0f))
return false;
if (!normalReady) {
transformCurrentNormal();
normalReady = true;
}
final float nDotVP = Math.max(scratchNormal.dot(lightDirection(light)), 0.0f);
r += light.ambient.x + nDotVP * light.diffuse.x;
g += light.ambient.y + nDotVP * light.diffuse.y;
b += light.ambient.z + nDotVP * light.diffuse.z;
}
factor(ShaderManager.getCurrentNormal(), scratchFactor);
final float r = scratchFactor.x;
final float g = scratchFactor.y;
final float b = scratchFactor.z;
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
if (!Float.isFinite(r + g + b)) return false;
destFactor.set(r, g, b);
return true;
}
private static void transformCurrentNormal() {
GLStateManager.getModelViewMatrix().normal(scratchNormalMat);
scratchNormal.set(ShaderManager.getCurrentNormal());
scratchNormalMat.transform(scratchNormal);
if (GLStateManager.getNormalizeState().isEnabled()) {
scratchNormal.normalize();
} else if (GLStateManager.getRescaleNormalState().isEnabled()) {
scratchNormal.mul(Uniforms.rescaleFactor(scratchNormalMat, scratchVec));
public static boolean bakeColors(int[] rawBuffer, int vertexCount, boolean hasNormals, boolean hasColor) {
if (vertexCount <= 0 || !prepare(true)) return false;
if (!hasNormals) factor(ShaderManager.getCurrentNormal(), scratchFactor);
float cr = 0.0f, cg = 0.0f, cb = 0.0f;
int alphaBits = 0;
if (!hasColor) {
final var color = GLSMCurrentAttribs.getColor();
cr = clamp01(color.getRed());
cg = clamp01(color.getGreen());
cb = clamp01(color.getBlue());
alphaBits = toByte(clamp01(color.getAlpha())) << 24;
}
boolean haveNormal = false;
int lastNormal = 0;
for (int v = 0, i = 0; v < vertexCount; v++, i += 8) {
if (hasNormals) {
final int packed = rawBuffer[i + 6];
if (!haveNormal || packed != lastNormal) {
factor((byte) packed / 127.0f, (byte) (packed >> 8) / 127.0f, (byte) (packed >> 16) / 127.0f, scratchFactor);
lastNormal = packed;
haveNormal = true;
}
}
if (hasColor) {
final int packed = rawBuffer[i + 5];
cr = (packed & 0xFF) / 255.0f;
cg = ((packed >>> 8) & 0xFF) / 255.0f;
cb = ((packed >>> 16) & 0xFF) / 255.0f;
alphaBits = packed & 0xFF000000;
}
rawBuffer[i + 5] = alphaBits | (toByte(clamp01(cb * scratchFactor.z)) << 16) | (toByte(clamp01(cg * scratchFactor.y)) << 8) | toByte(clamp01(cr * scratchFactor.x));
}
return true;
}
private static boolean prepare(boolean directionalOnly) {
if (!GLSMLighting.getLightingState().isEnabled()) return false;
if (!GLSMLighting.getColorMaterial().isEnabled()) return false;
if (VertexKey.encodeColorMaterialMode(GLSMLighting.getColorMaterialParameter().getValue()) != VertexKey.CM_AMBIENT_AND_DIFFUSE) return false;
final MaterialState material = GLSMLighting.getFrontMaterial();
if (material.emission.x != 0.0f || material.emission.y != 0.0f || material.emission.z != 0.0f)
return false;
final boolean materialHasSpecular = material.shininess > 0.0f && (material.specular.x != 0.0f || material.specular.y != 0.0f || material.specular.z != 0.0f);
final Vector4f lightModelAmbient = GLSMLighting.getLightModel().ambient;
base.set(lightModelAmbient.x, lightModelAmbient.y, lightModelAmbient.z);
final var lightEnables = GLSMLighting.getLightStates();
final var lightData = GLSMLighting.getLightDataStates();
litCount = 0;
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
if (!lightEnables[i].isEnabled()) continue;
final LightState light = lightData[i];
if (materialHasSpecular && (light.specular.x != 0.0f || light.specular.y != 0.0f || light.specular.z != 0.0f))
return false;
if (directionalOnly && light.position.w != 0.0f) return false;
base.add(light.ambient.x, light.ambient.y, light.ambient.z);
lightDirs[litCount].set(lightDirection(light));
lightDiffuse[litCount].set(light.diffuse.x, light.diffuse.y, light.diffuse.z);
litCount++;
}
if (litCount > 0) {
GLSMMatrices.getModelViewMatrix().normal(scratchNormalMat);
normalize = GLSMLighting.getNormalizeState().isEnabled();
rescale = !normalize && GLSMLighting.getRescaleNormalState().isEnabled() ? Uniforms.rescaleFactor(scratchNormalMat, scratchVec) : 1.0f;
}
return true;
}
private static void factor(Vector3f normal, Vector3f dest) {
factor(normal.x, normal.y, normal.z, dest);
}
private static void factor(float nx, float ny, float nz, Vector3f dest) {
dest.set(base);
if (litCount == 0) return;
scratchNormal.set(nx, ny, nz);
scratchNormalMat.transform(scratchNormal);
if (normalize) {
scratchNormal.normalize();
} else {
scratchNormal.mul(rescale);
}
for (int i = 0; i < litCount; i++) {
final float nDotVP = Math.max(scratchNormal.dot(lightDirs[i]), 0.0f);
dest.fma(nDotVP, lightDiffuse[i]);
}
}
private static float clamp01(float value) {
return Math.min(Math.max(value, 0.0f), 1.0f);
}
private static int toByte(float unit) {
return (int) (unit * 255.0f + 0.5f);
}
private static Vector3f lightDirection(LightState light) {
if (light.position.w == 0.0f) {
scratchLightVec.set(light.position.x, light.position.y, light.position.z);
} else {
scratchEyePos.set(0.0f, 0.0f, 0.0f, 1.0f).mul(GLStateManager.getModelViewMatrix());
scratchEyePos.set(0.0f, 0.0f, 0.0f, 1.0f).mul(GLSMMatrices.getModelViewMatrix());
scratchLightVec.set(light.position.x - scratchEyePos.x, light.position.y - scratchEyePos.y, light.position.z - scratchEyePos.z);
}
return scratchLightVec.normalize();
@@ -3,6 +3,9 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
@@ -213,24 +216,24 @@ public final class FfpExtendedAttribs {
final int extVbo = getOrBuild(h, pos, uv, normal, first, count, extPrim, mode);
if (extVbo == 0) return false;
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
return true;
}
public static void unbind() {
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
GLSMVertexInput.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
GLSMVertexInput.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
setNeutralCurrentValues();
}
public static void setNeutralCurrentValues() {
BatchStateGuard.suspend();
try {
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
GLSMCurrentAttribs.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
GLSMCurrentAttribs.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
} finally {
BatchStateGuard.resume();
}
@@ -372,22 +375,22 @@ public final class FfpExtendedAttribs {
final int newCap = Math.min(MAX_EXT_VERTS, Math.max(neededVerts, buf.capacityVerts * 2));
beginInternalGl();
try {
final int vbo = GLStateManager.glGenBuffers();
final int vbo = GLSMBuffers.glGenBuffers();
if (vbo == 0) return false;
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
if (buf.vbo != 0) {
final int savedRead = GLStateManager.getBoundCopyReadBuffer();
final int savedWrite = GLStateManager.getBoundCopyWriteBuffer();
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
final int savedRead = GLSMBuffers.getBoundCopyReadBuffer();
final int savedWrite = GLSMBuffers.getBoundCopyWriteBuffer();
GLSMBuffers.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
GLSMBuffers.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
GLSMBuffers.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
(long) buf.capacityVerts * extStride);
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
GLStateManager.glDeleteBuffers(buf.vbo);
GLSMBuffers.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
GLSMBuffers.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
GLSMBuffers.glDeleteBuffers(buf.vbo);
}
buf.vbo = vbo;
buf.capacityVerts = newCap;
@@ -433,11 +436,11 @@ public final class FfpExtendedAttribs {
beginInternalGl();
try {
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
ext.position(0).limit(count * extStride);
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMBuffers.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
} finally {
endInternalGl();
}
@@ -454,10 +457,10 @@ public final class FfpExtendedAttribs {
final int bytes = (count - 1) * stride + elementBytes;
final ByteBuffer dst = memAlloc(bytes).order(ByteOrder.nativeOrder());
dst.position(0).limit(bytes);
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, a.offset + (long) first * stride, dst);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
dst.position(0).limit(bytes);
return dst;
}
@@ -484,7 +487,7 @@ public final class FfpExtendedAttribs {
beginInternalGl();
try {
for (int i = 0; i < vbos.size(); i++) {
GLStateManager.glDeleteBuffers(vbos.getInt(i));
GLSMBuffers.glDeleteBuffers(vbos.getInt(i));
}
} finally {
endInternalGl();
@@ -0,0 +1,32 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.states.FogState;
public final class FfpGlsl {
private FfpGlsl() {}
// Mesa ffvertex_prog.c build_fog(): FDM_EYE_RADIAL/EYE_PLANE/EYE_PLANE_ABS
public static String fogDistance(int mode, String eye) {
return switch (mode) {
case FogState.DISTANCE_EYE_RADIAL -> "length(" + eye + ".xyz)";
case FogState.DISTANCE_EYE_PLANE -> eye + ".z";
default -> "abs(" + eye + ".z)";
};
}
public static void appendFogFactor(StringBuilder sb, String indent, int fogMode, String coord, String params) {
switch (fogMode) {
case FragmentKey.FOG_LINEAR -> sb.append(indent).append("f = ").append(coord).append(" * ").append(params).append(".x + ").append(params).append(".y;\n");
case FragmentKey.FOG_EXP -> sb.append(indent).append("f = exp2(-(").append(coord).append(" * ").append(params).append(".z));\n");
case FragmentKey.FOG_EXP2 -> {
sb.append(indent).append("float fogTmp = ").append(coord).append(" * ").append(params).append(".w;\n");
sb.append(indent).append("f = exp2(-(fogTmp * fogTmp));\n");
}
}
}
public static String quantizeAlpha(String alpha) {
return "round(" + alpha + " * 255.0)";
}
}
@@ -3,9 +3,11 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import java.util.Arrays;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
@@ -65,14 +67,25 @@ public final class FragmentKey {
public static final int FOG_EXP = 2;
public static final int FOG_EXP2 = 3;
public static int activeFogMode(GLContextState glCtx) {
if (!glCtx.fogMode.isEnabled()) return FOG_NONE;
return switch (glCtx.fogState.getFogMode()) {
case GL11.GL_LINEAR -> FOG_LINEAR;
case GL11.GL_EXP -> FOG_EXP;
case GL11.GL_EXP2 -> FOG_EXP2;
default -> FOG_NONE;
};
}
private static final int GLOBAL_BITS = 14;
private static final int BIT_COMBINED_GLINT = 61;
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
private static final int BIT_CLIP_DISCARD = 63;
private static final int BIT_FOG_MODE = 0; // 2 bits
private static final int BIT_ALPHA_TEST = 2; // 1 bit
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
private static final int BIT_TEXTURE_SUPERSAMPLING = 7; // 3 bits, log2(samples)
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
private static final int BIT_COLOR_SUM = 12; // 1 bit
@@ -115,17 +128,10 @@ public final class FragmentKey {
long global = 0;
if (CombinedGlint.isActive()) global |= 1L << BIT_COMBINED_GLINT;
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
if (GLSMRasterizer.clipEmulationActive(glCtx)) global |= 1L << BIT_CLIP_DISCARD;
// Fog
if (glCtx.fogMode.isEnabled()) {
final int fogMode = switch (glCtx.fogState.getFogMode()) {
case GL11.GL_LINEAR -> FOG_LINEAR;
case GL11.GL_EXP -> FOG_EXP;
case GL11.GL_EXP2 -> FOG_EXP2;
default -> FOG_NONE;
};
global |= (long) fogMode;
}
global |= activeFogMode(glCtx);
// Alpha test
if (glCtx.alphaTest.isEnabled()) {
@@ -170,7 +176,10 @@ public final class FragmentKey {
}
final int nrEnabled = highestEnabled + 1;
global |= ((long) (nrEnabled & 0x7)) << BIT_NR_ENABLED_UNITS;
if (glCtx.textureSupersamplingSamples > 1 && (unit0Bits & 1L) != 0
&& TextureInfoCache.INSTANCE.getInfo(textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter() > 0) {
global |= (long) Integer.numberOfTrailingZeros(glCtx.textureSupersamplingSamples) << BIT_TEXTURE_SUPERSAMPLING;
}
global |= (unit0Bits << GLOBAL_BITS);
out[0] = global;
@@ -253,13 +262,15 @@ public final class FragmentKey {
public int fogMode() { return (int) (packed[0] & 0x3); }
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
public boolean clipDiscard() { return (packed[0] & (1L << BIT_CLIP_DISCARD)) != 0; }
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
public int textureSupersamplingSamples() { return 1 << (int) ((packed[0] >> BIT_TEXTURE_SUPERSAMPLING) & 0x7); }
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; }
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
public int nrEnabledUnits() { return packed.length == 1 && !unitEnabled(0) ? 0 : packed.length; }
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
public boolean overlayInstanced() { return ((packed[0] >> BIT_OVERLAY_INSTANCED) & 1) != 0; }
@@ -398,6 +409,7 @@ public final class FragmentKey {
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(),
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(),
overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
if (textureSupersamplingSamples() > 1) sb.append(" samples=").append(textureSupersamplingSamples());
for (int i = 0; i < nrEnabledUnits(); i++) {
if (!unitEnabled(i)) {
sb.append(String.format(" u%d=OFF", i));
@@ -28,8 +28,15 @@ public final class FragmentShaderGenerator {
return sb.toString();
}
sb.append("out vec4 fragColor;\n\n");
if (key.textureSupersamplingSamples() > 1) {
sb.append("#define TEXTURE_SUPERSAMPLING_SAMPLES ").append(key.textureSupersamplingSamples()).append('\n');
sb.append(ShaderLoader.getShaderSource("angelica:texture_supersampling.glsl"));
}
sb.append("void main() {\n");
if (key.clipDiscard()) {
sb.append(ClipDistanceGlsl.FS_DISCARD);
}
emitLineStipple(sb, key);
emitTextureSampling(sb, key);
emitTexEnvChain(sb, key);
@@ -45,6 +52,9 @@ public final class FragmentShaderGenerator {
private static void emitInputs(StringBuilder sb, FragmentKey key) {
sb.append("// Inputs from vertex shader\n");
sb.append("in vec4 v_Color;\n");
if (key.clipDiscard()) {
sb.append(ClipDistanceGlsl.FS_IN_DIST0).append('\n').append(ClipDistanceGlsl.FS_IN_DIST1).append('\n');
}
if (key.overlayInstanced()) {
sb.append("in vec4 v_Overlay;\n");
}
@@ -86,7 +96,8 @@ public final class FragmentShaderGenerator {
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
for (int i = 0; i < key.nrEnabledUnits(); i++) {
if (!key.unitEnabled(i)) continue;
sb.append(" vec4 tex").append(i).append("Color = textureProj(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
final String sample = i == 0 && key.textureSupersamplingSamples() > 1 ? "sampleSupersampledTexture" : "textureProj";
sb.append(" vec4 tex").append(i).append("Color = ").append(sample).append("(u_Sampler").append(i).append(", v_TexCoord").append(i).append(");\n");
}
}
@@ -305,7 +316,7 @@ public final class FragmentShaderGenerator {
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
sb.append(" float qa = round(color.a * 255.0);\n");
sb.append(" float qa = ").append(FfpGlsl.quantizeAlpha("color.a")).append(";\n");
switch (func) {
case GL11.GL_NEVER ->
sb.append(" discard;\n");
@@ -335,18 +346,7 @@ public final class FragmentShaderGenerator {
sb.append(" // Fog\n");
sb.append(" float f;\n");
switch (fogMode) {
case FragmentKey.FOG_LINEAR ->
sb.append(" f = v_FogCoord * u_FogParams.x + u_FogParams.y;\n");
case FragmentKey.FOG_EXP ->
sb.append(" f = exp2(-(v_FogCoord * u_FogParams.z));\n");
case FragmentKey.FOG_EXP2 -> {
sb.append(" float fogTmp = v_FogCoord * u_FogParams.w;\n");
sb.append(" f = exp2(-(fogTmp * fogTmp));\n");
}
}
FfpGlsl.appendFogFactor(sb, " ", fogMode, "v_FogCoord", "u_FogParams");
sb.append(" f = clamp(f, 0.0, 1.0);\n");
sb.append(" color.rgb = mix(u_FogColor.rgb, color.rgb, f);\n");
}
@@ -1,107 +0,0 @@
package com.gtnewhorizons.angelica.glsm.ffp;
/**
* Generates GLSL 330 core geometry shaders for wide line emulation.
*/
public final class GeometryShaderGenerator {
private GeometryShaderGenerator() {}
public static String generate(VertexKey key) {
final StringBuilder sb = new StringBuilder(1024);
sb.append("#version 330 core\n\n");
sb.append("layout(lines) in;\n");
sb.append("layout(triangle_strip, max_vertices = 4) out;\n\n");
sb.append(FFPUniformBlock.GLSL_DECL);
sb.append('\n');
sb.append("// Pass-through varyings\n");
sb.append("in vec4 v_Color_gs[];\n");
if (key.separateSpecular()) {
sb.append("in vec3 v_SpecularColor_gs[];\n");
}
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("in vec4 v_TexCoord0_gs[];\n");
if (key.lightmapEnabled()) sb.append("in vec4 v_TexCoord1_gs[];\n");
if (key.unitTexCoordEnabled(2)) sb.append("in vec4 v_TexCoord2_gs[];\n");
if (key.unitTexCoordEnabled(3)) sb.append("in vec4 v_TexCoord3_gs[];\n");
if (key.fogEnabled()) {
sb.append("in float v_FogCoord_gs[];\n");
}
if (key.lineStipple()) {
sb.append("flat in vec2 v_LineStart_gs[];\n");
}
sb.append('\n');
sb.append("out vec4 v_Color;\n");
if (key.separateSpecular()) {
sb.append("out vec3 v_SpecularColor;\n");
}
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("out vec4 v_TexCoord0;\n");
if (key.lightmapEnabled()) sb.append("out vec4 v_TexCoord1;\n");
if (key.unitTexCoordEnabled(2)) sb.append("out vec4 v_TexCoord2;\n");
if (key.unitTexCoordEnabled(3)) sb.append("out vec4 v_TexCoord3;\n");
if (key.fogEnabled()) {
sb.append("out float v_FogCoord;\n");
}
if (key.lineStipple()) {
sb.append("flat out vec2 v_LineStart;\n");
}
sb.append('\n');
sb.append("void main() {\n");
sb.append(" vec4 c0 = gl_in[0].gl_Position;\n");
sb.append(" vec4 c1 = gl_in[1].gl_Position;\n\n");
sb.append(" // NDC positions\n");
sb.append(" vec2 n0 = c0.xy / c0.w;\n");
sb.append(" vec2 n1 = c1.xy / c1.w;\n\n");
sb.append(" // Screen-space direction and perpendicular\n");
sb.append(" vec2 dir = normalize((n1 - n0) * u_ViewportSize);\n");
sb.append(" vec2 offset = vec2(-dir.y, dir.x) * u_LineWidth / u_ViewportSize;\n\n");
emitVertex(sb, key, 0, "+offset");
emitVertex(sb, key, 0, "-offset");
emitVertex(sb, key, 1, "+offset");
emitVertex(sb, key, 1, "-offset");
sb.append(" EndPrimitive();\n");
sb.append("}\n");
return sb.toString();
}
private static void emitVertex(StringBuilder sb, VertexKey key, int endpointIdx, String offsetExpr) {
final String ci = "c" + endpointIdx;
final String ni = "n" + endpointIdx;
final String idx = "[" + endpointIdx + "]";
sb.append(" // Endpoint ").append(endpointIdx).append(' ').append(offsetExpr).append('\n');
sb.append(" v_Color = v_Color_gs").append(idx).append(";\n");
if (key.separateSpecular()) {
sb.append(" v_SpecularColor = v_SpecularColor_gs").append(idx).append(";\n");
}
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = v_TexCoord2_gs").append(idx).append(";\n");
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = v_TexCoord3_gs").append(idx).append(";\n");
if (key.fogEnabled()) {
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
}
if (key.lineStipple()) {
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
}
if (key.clipPlanesEnabled()) {
for (int i = 0; i < 8; i++) {
sb.append(" gl_ClipDistance[").append(i).append("] = gl_in").append(idx).append(".gl_ClipDistance[").append(i).append("];\n");
}
}
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(ci).append(".w, ").append(ci).append(".z, ").append(ci).append(".w);\n");
sb.append(" EmitVertex();\n\n");
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
@@ -25,6 +25,8 @@ public final class InstancedAttribs {
public static final int OFFSET_LIGHTMAP = HEAD_SIZE;
public static final int STRIDE = 72;
public static final long DEFAULT_ENTITY_IDS = packEntityInfo(-1, 0, -1);
public static void writeHead(long ptr, float[] colMajor, int off, int colorABGR, int overlayABGR, long entityInfo) {
writeRows(ptr, colMajor, off);
writeTail(ptr, colorABGR, overlayABGR, entityInfo);
@@ -60,18 +62,26 @@ public final class InstancedAttribs {
return id == 0xFFFF ? -1 : id;
}
public static float normalizedId(long info, int shift) {
return ((info >>> shift) & 0xFFFF) / 65535.0f;
}
public static void pointHead(long base, int stride) {
for (int r = 0; r < 3; r++) {
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
GLSMVertexInput.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
}
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_UNSIGNED_SHORT, stride, base + OFFSET_ENTITY);
GLSMVertexInput.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
GLSMVertexInput.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
pointEntityIds(LOC_ENTITY, stride, base + OFFSET_ENTITY);
}
public static void pointEntityIds(int location, int stride, long offset) {
GLSMVertexInput.glVertexAttribPointer(location, 4, GL11.GL_UNSIGNED_SHORT, true, stride, offset);
}
public static void pointTemplate(long base) {
pointHead(base, STRIDE);
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
GLSMVertexInput.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
}
public static void enableHeadArrays() {
@@ -81,8 +91,8 @@ public final class InstancedAttribs {
}
static void enableInstanced(int loc) {
GLStateManager.glEnableVertexAttribArray(loc);
GLStateManager.glVertexAttribDivisor(loc, 1);
GLSMVertexInput.glEnableVertexAttribArray(loc);
GLSMVertexInput.glVertexAttribDivisor(loc, 1);
}
private InstancedAttribs() {}
@@ -39,17 +39,16 @@ public final class InstancedGlslHelpers {
vec2 {p}wColumn = {span}.xy;
float {p}wBottom = {span}.z;
float {p}wTop = {span}.w;
vec2 {p}wLightmap = {params}.xy;
float {p}wHash = {params}.z;
bool {p}wSnow = {params}.w > 0.5;
float {p}wLightV = floor({params}.x / 65536.0);
vec2 {p}wLightmap = vec2({params}.x - {p}wLightV * 65536.0, {p}wLightV);
bool {p}wProvider = {params}.w > 0.5;
vec3 {p}wTranslate = {p}WeatherParams0.xyz;
float {p}wInvRadius = {p}WeatherParams0.w;
float {p}wInvRadius = {p}wProvider ? {p}WeatherParams3.x : {p}WeatherParams0.w;
vec2 {p}wCameraFrac = {p}WeatherParams1.xy;
float {p}wPartialTicks = {p}WeatherParams1.z;
float {p}wAge = {p}WeatherParams1.w;
float {p}wRainScroll = {p}WeatherParams2.x;
float {p}wSnowScroll = {p}WeatherParams2.y;
float {p}wStrength = {p}WeatherParams2.z;
float {p}wAge = {p}wProvider ? {p}WeatherParams2.w : {p}WeatherParams1.w;
float {p}wTick = {p}wProvider ? {p}WeatherParams2.y : {p}WeatherParams2.x;
float {p}wStrength = {p}wProvider ? 1.0 : {p}WeatherParams2.z;
vec2 {p}wHalfWidth = vec2(-{p}wColumn.y, {p}wColumn.x) * (0.5 / sqrt(dot({p}wColumn, {p}wColumn)));
vec2 {p}wCornerXZ = {p}wColumn + vec2(0.5) + ({corner}.x * 2.0 - 1.0) * {p}wHalfWidth;
@@ -58,22 +57,13 @@ public final class InstancedGlslHelpers {
float {p}wDistance = length({p}wColumn + vec2(0.5) - {p}wCameraFrac) * {p}wInvRadius;
float {p}wFade = 1.0 - {p}wDistance * {p}wDistance;
float {p}wScroll;
vec2 {p}wDrift;
float {p}wAlpha;
if ({p}wSnow) {
{p}wScroll = ({p}wSnowScroll + {p}wPartialTicks) / 512.0;
{p}wDrift = vec2({jitter}.x + {p}wAge * 0.01 * {jitter}.y, {jitter}.z + {p}wAge * {jitter}.w * 0.001);
{p}wAlpha = {p}wFade * 0.3 + 0.5;
} else {
{p}wScroll = (mod({p}wRainScroll + {p}wHash, 32.0) + {p}wPartialTicks) / 32.0 * (3.0 + {jitter}.x);
{p}wDrift = vec2(0.0);
{p}wAlpha = {p}wFade * 0.5 + 0.5;
}
float {p}wScroll = (mod({p}wTick + {params}.y, {layer}.x) + {p}wPartialTicks) / {layer}.x * {params}.z;
vec2 {p}wDrift = vec2({jitter}.x + {p}wAge * {layer}.y * {jitter}.y, {jitter}.z + {p}wAge * {layer}.z * {jitter}.w);
float {p}wAlpha = {p}wFade * {layer}.w + 0.5;
{tex0} = vec4({corner}.x + {p}wDrift.x, {p}wCornerY * 0.25 + {p}wScroll + {p}wDrift.y, 0.0, 1.0);
{tex1} = vec4({p}wLightmap, 0.0, 1.0);
{color} = vec4(1.0, 1.0, 1.0, {p}wAlpha * {p}wStrength);""";
{color} = floor(clamp(vec4({colorAttr}.rgb, {p}wAlpha * {colorAttr}.a * {p}wStrength), 0.0, 1.0) * 255.0) / 255.0;""";
public static String weatherPrologue(String uniformPrefix, String attribPrefix, String corner,
String vertex, String color, String tex0, String tex1, String sep) {
@@ -82,6 +72,8 @@ public final class InstancedGlslHelpers {
.replace("{span}", attribPrefix + "InstColumnSpan")
.replace("{jitter}", attribPrefix + "InstJitter")
.replace("{params}", attribPrefix + "InstWeatherParams")
.replace("{layer}", attribPrefix + "InstWeatherLayer")
.replace("{colorAttr}", attribPrefix + "InstWeatherColor")
.replace("{corner}", corner)
.replace("{vertex}", vertex)
.replace("{color}", color)
@@ -125,6 +117,8 @@ public final class InstancedGlslHelpers {
decls.add(decl(WeatherInstancedAttribs.LOC_COLUMN_SPAN, "vec4", prefix + "InstColumnSpan"));
decls.add(decl(WeatherInstancedAttribs.LOC_JITTER, "vec4", prefix + "InstJitter"));
decls.add(decl(WeatherInstancedAttribs.LOC_PARAMS, "vec4", prefix + "InstWeatherParams"));
decls.add(decl(WeatherInstancedAttribs.LOC_COLOR, "vec4", prefix + "InstWeatherColor"));
decls.add(decl(WeatherInstancedAttribs.LOC_LAYER, "vec4", prefix + "InstWeatherLayer"));
}
}
return decls.toArray(String[]::new);
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
@@ -20,10 +20,10 @@ public final class ParticleInstancedAttribs {
public static final int STRIDE = 48;
public static void pointInstanceAttribs(long base) {
GLStateManager.glVertexAttribPointer(LOC_CENTER_HALF, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CENTER_HALF);
GLStateManager.glVertexAttribPointer(LOC_UV, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_UV);
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, base + OFFSET_COLOR);
GLStateManager.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
GLSMVertexInput.glVertexAttribPointer(LOC_CENTER_HALF, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_CENTER_HALF);
GLSMVertexInput.glVertexAttribPointer(LOC_UV, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_UV);
GLSMVertexInput.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, base + OFFSET_COLOR);
GLSMVertexInput.glVertexAttribPointer(LOC_LIGHTMAP, 2, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LIGHTMAP);
}
static void enableInstanceArrays() {
@@ -3,7 +3,8 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -76,11 +77,11 @@ public final class ParticleQuadMesh {
written = true;
write(memAddress0(staging), rotX, rotXZ, rotZ, rotYZ, rotXY);
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
staging.position(0).limit(VERTEX_COUNT * VERTEX_STRIDE);
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, staging);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMBuffers.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, staging);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
private static void write(long dst, float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) {
@@ -116,13 +117,13 @@ public final class ParticleQuadMesh {
}
private static void build() {
final int savedVAO = GLStateManager.getBoundVAO();
final int savedVBO = GLStateManager.getBoundVBO();
vao = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
vbo = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) VERTEX_COUNT * VERTEX_STRIDE, GL15.GL_DYNAMIC_DRAW);
final int savedVAO = GLSMVertexInput.getBoundVAO();
final int savedVBO = GLSMBuffers.getBoundVBO();
vao = GLSMVertexInput.glGenVertexArrays();
GLSMVertexInput.glBindVertexArray(vao);
vbo = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, (long) VERTEX_COUNT * VERTEX_STRIDE, GL15.GL_DYNAMIC_DRAW);
attrib(VertexFormatElement.Usage.POSITION.getAttributeLocation(), 3, GL11.GL_FLOAT, false, OFFSET_OFFSET);
attrib(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), 2, GL11.GL_FLOAT, false, OFFSET_CORNER);
@@ -130,25 +131,25 @@ public final class ParticleQuadMesh {
ParticleInstancedAttribs.enableInstanceArrays();
GLStateManager.glBindVertexArray(savedVAO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMVertexInput.glBindVertexArray(savedVAO);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
if (staging == null) staging = memAlloc(VERTEX_COUNT * VERTEX_STRIDE);
written = false;
}
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
GLStateManager.glEnableVertexAttribArray(loc);
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
GLSMVertexInput.glEnableVertexAttribArray(loc);
GLSMVertexInput.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
}
public static void delete() {
if (vbo != 0) {
GLStateManager.glDeleteBuffers(vbo);
GLSMBuffers.glDeleteBuffers(vbo);
vbo = 0;
}
if (vao != 0) {
GLStateManager.glDeleteVertexArrays(vao);
GLSMVertexInput.glDeleteVertexArrays(vao);
vao = 0;
}
if (staging != null) {
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.CompatShaderTransformer;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
@@ -23,7 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* A compiled and linked FFP emulation program (vertex + fragment + optional geometry shaders). Owns the GL program handle and caches uniform locations.
* A compiled and linked FFP emulation program (vertex + fragment shaders). Owns the GL program handle and caches uniform locations.
*/
public class Program {
@@ -45,27 +46,23 @@ public class Program {
}
public void destroy() {
GLStateManager.glDeleteProgram(programId);
GLSMPrograms.glDeleteProgram(programId);
}
/**
* Compile vertex + fragment + optional geometry shaders, link into a program, and return the FFPProgram.
* Compile vertex + fragment shaders, link into a program, and return the FFPProgram.
*/
static Program create(VertexKey vk, FragmentKey fk, String vertSrc, String fragSrc, String geomSrc) {
static Program create(VertexKey vk, FragmentKey fk, String vertSrc, String fragSrc) {
final RenderBackend backend = RENDER_BACKEND;
final int id = PROGRAM_COUNTER.getAndIncrement();
final String vkHex = Long.toHexString(vk.pack());
final boolean hasGeom = geomSrc != null;
int shaderCount = 0;
final int[] shaders = new int[5]; // vertex, fragment, geometry, tess control, tess evaluation
final int[] shaders = new int[2];
int program = 0;
try {
shaders[shaderCount++] = compileShader(GL20.GL_VERTEX_SHADER, vertSrc, "ffp_v_" + vkHex);
shaders[shaderCount++] = compileShader(GL20.GL_FRAGMENT_SHADER, fragSrc, "ffp_f_" + id);
if (geomSrc != null) {
shaders[shaderCount++] = compileShader(GL32.GL_GEOMETRY_SHADER, geomSrc, "ffp_g_" + id);
}
program = backend.createProgram();
for (int i = 0; i < shaderCount; i++) backend.attachShader(program, shaders[i]);
@@ -80,7 +77,7 @@ public class Program {
throw new RuntimeException("FFP shader link failed (vk=0x" + vkHex + ", fk=" + fk + "): " + log);
}
final String debugName = "FFP(v=0x" + vkHex + ",f=" + id + (hasGeom ? ",g" : "") + ")";
final String debugName = "FFP(v=0x" + vkHex + ",f=" + id + ")";
GLDebug.nameObject(KHRDebug.GL_PROGRAM, program, debugName);
// Detach and delete individual shaders - they're linked into the program
@@ -97,7 +94,7 @@ public class Program {
backend.uniformBlockBinding(program, blockIndex, FFPUniformBlock.BINDING_POINT);
}
final int previousProgram = GLStateManager.getActiveProgram();
final int previousProgram = GLSMPrograms.getActiveProgram();
backend.useProgram(program);
for (int i = 0; i < 4; i++) {
if (ffpProgram.locSampler[i] != -1) backend.uniform1i(ffpProgram.locSampler[i], i);
@@ -32,7 +32,6 @@ public class ShaderCache {
private static final long EMPTY_MARKER = 0L; // slot [1] == 0 means empty (fkLen >= 1 guarantees nonzero)
private final Long2ObjectOpenHashMap<String> vertexSourceCache = new Long2ObjectOpenHashMap<>();
private final Long2ObjectOpenHashMap<String> geometrySourceCache = new Long2ObjectOpenHashMap<>();
// Open-addressed table — parallel arrays
private long[] keys;
@@ -103,7 +102,6 @@ public class ShaderCache {
final FragmentKey fk = FragmentKey.fromPacked(fkPacked, fkLen);
final String vertSrc;
String fragSrc;
final String geomSrc;
final Program program;
if (Tracy.ENABLED) Tracy.beginZone(Z_FFP_SHADER_GEN);
try {
@@ -113,8 +111,7 @@ public class ShaderCache {
fragSrc = FragmentShaderGenerator.generate(fk);
uniqueFragCount++;
}
geomSrc = vk.wideLineEmulation() ? getOrGenerateGeometry(vk) : null;
program = Program.create(vk, fk, vertSrc, fragSrc, geomSrc);
program = Program.create(vk, fk, vertSrc, fragSrc);
} finally {
if (Tracy.ENABLED) Tracy.endZone();
}
@@ -135,9 +132,6 @@ public class ShaderCache {
if (dumpDir != null) {
dumpShader(Long.toHexString(vkPacked), ".vert.glsl", vertSrc);
dumpShader("frag_" + fragmentDumpCounter++, ".frag.glsl", fragSrc);
if (geomSrc != null) {
dumpShader(Long.toHexString(vkPacked), ".geom.glsl", geomSrc);
}
}
return program;
@@ -228,10 +222,6 @@ public class ShaderCache {
return vertexSourceCache.computeIfAbsent(vk.pack(), k -> VertexShaderGenerator.generate(vk));
}
private String getOrGenerateGeometry(VertexKey vk) {
return geometrySourceCache.computeIfAbsent(vk.pack(), k -> GeometryShaderGenerator.generate(vk));
}
private void dumpShader(String name, String suffix, String source) {
if (dumpDir == null) return;
try {
@@ -260,11 +250,8 @@ public class ShaderCache {
dumpShader("frag_" + idx, ".frag.glsl", fragSources[i]);
}
}
for (var entry : geometrySourceCache.entrySet()) {
dumpShader(Long.toHexString(entry.getKey()), ".geom.glsl", entry.getValue());
}
GLStateManager.LOGGER.info("Dumped {} vertex, {} geometry, and {} fragment FFP shader sources to {}",
vertexSourceCache.size(), geometrySourceCache.size(), seen.size(), outputDir);
GLStateManager.LOGGER.info("Dumped {} vertex and {} fragment FFP shader sources to {}",
vertexSourceCache.size(), seen.size(), outputDir);
} catch (IOException e) {
GLStateManager.LOGGER.warn("Failed to dump FFP shaders: {}", e.getMessage());
}
@@ -5,6 +5,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
@@ -100,7 +101,6 @@ public final class ShaderManager {
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.CUBE)));
VertexShaderGenerator.generate(VertexKey.fromPacked(VertexKey.withInstancing(vkPacked, Instancing.PARTICLE)));
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
GeometryShaderGenerator.generate(vk);
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
for (Class<?> c : touched) {
c.getName();
@@ -133,13 +133,14 @@ public final class ShaderManager {
lastBoundProgramId = -1;
currentVertexKeyPacked = Long.MIN_VALUE;
currentFKLen = 0;
GLStateManager.forceAttribDefaultsDirty();
GLSMCurrentAttribs.forceAttribDefaultsDirty();
}
public void preDraw(GLContextState glCtx) {
GLSMHooks.resolvePendingProgram();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null) bh.flushDeferredBlend();
GLSMCurrentAttribs.flushEntityIds(glCtx);
// Handle FFP & Iris uniforms
final int currentProgramId = glCtx.activeProgram;
@@ -154,7 +155,7 @@ public final class ShaderManager {
final boolean hasNormal = (vertexFlags & VertexFlags.NORMAL_BIT) != 0;
final boolean hasTexCoord = (vertexFlags & VertexFlags.TEXTURE_BIT) != 0;
final boolean hasLightmap = (vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0;
GLStateManager.flushDeferredVertexAttribs(glCtx, hasColor, hasNormal, hasTexCoord, hasLightmap);
GLSMCurrentAttribs.flushDeferredVertexAttribs(glCtx, hasColor, hasNormal, hasTexCoord, hasLightmap);
if (!active) return;
@@ -190,6 +191,10 @@ public final class ShaderManager {
}
}
boolean isTextureSupersampling() {
return currentProgram != null && currentProgram.getFragmentKey().textureSupersamplingSamples() > 1;
}
public static void endFrame() {
final ShaderManager sm = GLStateManager.ctx().ffp;
sm.lastFramePreDrawCalls = sm.preDrawCalls;
@@ -199,18 +204,21 @@ public final class ShaderManager {
}
public void setNormal(float x, float y, float z) {
GLStateManager.recordExitNormal();
normalStack.beforeModify();
currentNormal.set(x, y, z);
normalGeneration++;
}
public void setTexCoord(float s, float t, float r, float q) {
GLStateManager.recordExitTexCoord(0);
texCoordStack.beforeModify();
currentTexCoords[0].set(s, t, r, q);
texCoordGeneration++;
}
public void setTexCoord(int unit, float s, float t, float r, float q) {
GLStateManager.recordExitTexCoord(unit);
if (unit == 0) texCoordStack.beforeModify();
currentTexCoords[unit].set(s, t, r, q);
texCoordGeneration++;
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.FogState;
import com.gtnewhorizons.angelica.glsm.states.LightState;
@@ -11,6 +11,7 @@ import com.gtnewhorizons.angelica.glsm.states.TexGenState;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import org.joml.Math;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
@@ -49,6 +50,7 @@ public class Uniforms {
private int viewportX = Integer.MIN_VALUE, viewportY = Integer.MIN_VALUE, viewportWidth = -1, viewportHeight = -1;
private int lineStipple = -1;
private int weatherGen = -1;
private int atlasGutter = -1;
int blockWrites;
int blockSkips;
@@ -80,9 +82,6 @@ public class Uniforms {
private final Vector3f tempVec3 = new Vector3f();
private final FloatBuffer clipPlaneBuf = memAllocFloat(32); // 8 planes * vec4
private static final double LN2 = java.lang.Math.log(2.0);
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
public void upload(GLContextState glCtx) {
boolean dirty = false;
contentChanged = false;
@@ -173,6 +172,15 @@ public class Uniforms {
stagedFragment++;
dirty = true;
}
if (glCtx.textureSupersamplingSamples > 1 && ffp.isTextureSupersampling()) {
final int gutter = TextureInfoCache.INSTANCE.getInfo(glCtx.textures.getTextureUnitBindings(0).getBinding()).getAtlasGutter();
if (gutter != atlasGutter) {
putFloat(FFPUniformBlock.ATLAS_GUTTER, gutter);
atlasGutter = gutter;
stagedMisc++;
dirty = true;
}
}
final LineState line = glCtx.lineState;
final float lw = line.getWidth();
if (lw != lineWidth) {
@@ -217,7 +225,7 @@ public class Uniforms {
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
}
final int offset = ring.writeBlock(staging);
GLStateManager.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
GLSMBuffers.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
ring.getBufferId(), offset, FFPUniformBlock.SIZE);
bound = true;
blockWrites++;
@@ -233,8 +241,11 @@ public class Uniforms {
if (projChanged) {
putMat4(FFPUniformBlock.PROJECTION_MATRIX, proj);
}
proj.mul(mv, mvpMatrix);
putMat4(FFPUniformBlock.MVP_MATRIX, mvpMatrix);
putMat4(FFPUniformBlock.MVP_MATRIX, modelViewProjection(proj, mv, mvpMatrix));
}
public static Matrix4f modelViewProjection(Matrix4f proj, Matrix4f mv, Matrix4f dest) {
return proj.mul(mv, dest);
}
private void stageNormalMatrix(GLContextState glCtx) {
@@ -318,9 +329,7 @@ public class Uniforms {
private void stageClipPlanes(GLContextState glCtx) {
final ClipPlaneState cps = glCtx.clipPlaneState;
clipPlaneBuf.clear();
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
cps.putEyePlane(i, clipPlaneBuf);
}
cps.putEffectivePlanes(glCtx.clipPlaneEnabledMask, clipPlaneBuf);
final long src = memAddress0(clipPlaneBuf);
for (int i = 0; i < 32 * 4; i += 4) {
putInt(FFPUniformBlock.CLIP_PLANES + i, memGetInt(src + i));
@@ -343,17 +352,8 @@ public class Uniforms {
final var secondary = glCtx.secondaryColor;
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
// Mesa STATE_FOG_PARAMS_OPTIMIZED
final FogState fog = glCtx.fogState;
final float start = fog.getStart();
final float end = fog.getEnd();
final float density = fog.getDensity();
final float range = end - start;
putVec4(FFPUniformBlock.FOG_PARAMS,
range != 0.0f ? -1.0f / range : 0.0f,
range != 0.0f ? end / range : 1.0f,
(float) (density / LN2),
(float) (density / SQRT_LN2));
putVec4(FFPUniformBlock.FOG_PARAMS, fog.linearScale(), fog.linearBias(), fog.expScale(), fog.exp2Scale());
putVec4(FFPUniformBlock.FOG_COLOR,
(float) fog.getFogColor().x, (float) fog.getFogColor().y, (float) fog.getFogColor().z,
fog.getFogAlpha());
@@ -391,8 +391,9 @@ public class Uniforms {
WeatherParams.translateZ, WeatherParams.invRadius);
putVec4(FFPUniformBlock.WEATHER_PARAMS_1, WeatherParams.cameraFracX, WeatherParams.cameraFracZ,
WeatherParams.partialTicks, WeatherParams.age);
putVec4(FFPUniformBlock.WEATHER_PARAMS_2, WeatherParams.rainScroll, WeatherParams.snowScroll,
WeatherParams.rainStrength, 0.0f);
putVec4(FFPUniformBlock.WEATHER_PARAMS_2, WeatherParams.vanillaTick, WeatherParams.providerTick,
WeatherParams.rainStrength, WeatherParams.providerAge);
putVec4(FFPUniformBlock.WEATHER_PARAMS_3, WeatherParams.providerInvRadius, 0.0f, 0.0f, 0.0f);
}
private void putVec4(int offset, Vector4f v) {
@@ -420,6 +421,7 @@ public class Uniforms {
if (ring != null) {
ring.endFrame();
}
invalidateBinding();
lastFrameBlockWrites = blockWrites;
lastFrameBlockSkips = blockSkips;
lastFrameStagedMatrices = stagedMatrices;
@@ -3,7 +3,8 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -113,19 +114,19 @@ public final class UnitCubeMesh {
}
private static void build() {
final int savedVAO = GLStateManager.getBoundVAO();
final int savedVBO = GLStateManager.getBoundVBO();
vao = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
vbo = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
final int savedVAO = GLSMVertexInput.getBoundVAO();
final int savedVBO = GLSMBuffers.getBoundVBO();
vao = GLSMVertexInput.glGenVertexArrays();
GLSMVertexInput.glBindVertexArray(vao);
vbo = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
final int bytes = VERTEX_COUNT * VERTEX_STRIDE;
final ByteBuffer data = memAlloc(bytes);
try {
write(memAddress0(data));
data.position(0).limit(bytes);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
} finally {
memFree(data);
}
@@ -139,22 +140,22 @@ public final class UnitCubeMesh {
CubeInstancedAttribs.enableInstanceArrays();
GLStateManager.glBindVertexArray(savedVAO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMVertexInput.glBindVertexArray(savedVAO);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
private static void attrib(int loc, int size, int type, boolean normalized, int offset) {
GLStateManager.glEnableVertexAttribArray(loc);
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
GLSMVertexInput.glEnableVertexAttribArray(loc);
GLSMVertexInput.glVertexAttribPointer(loc, size, type, normalized, VERTEX_STRIDE, offset);
}
public static void delete() {
if (vbo != 0) {
GLStateManager.glDeleteBuffers(vbo);
GLSMBuffers.glDeleteBuffers(vbo);
vbo = 0;
}
if (vao != 0) {
GLStateManager.glDeleteVertexArrays(vao);
GLSMVertexInput.glDeleteVertexArrays(vao);
vao = 0;
}
}
@@ -1,13 +1,21 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL41;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -19,12 +27,164 @@ public final class VAOManager {
final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
private VAOData current = null;
public static final int CONV_POSITION = 0;
public static final int CONV_NORMAL = 1;
public static final int CONV_COLOR = 2;
public static final int CONV_SECONDARY_COLOR = 3;
public static final int CONV_FOG_COORD = 4;
public static final int CONV_INDEX = 5;
public static final int CONV_EDGE_FLAG = 6;
public static final int CONV_TEX0 = 7;
public static final int TEX_COORD_UNITS = 8;
public static final int CONV_COUNT = CONV_TEX0 + TEX_COORD_UNITS;
private static final int[] CONV_LOCATION = new int[CONV_COUNT];
private static final int[] LOCATION_CONV = new int[MAX_ATTRIBS];
static {
Arrays.fill(CONV_LOCATION, -1);
Arrays.fill(LOCATION_CONV, -1);
mapConventional(CONV_POSITION, Usage.POSITION);
mapConventional(CONV_NORMAL, Usage.NORMAL);
mapConventional(CONV_COLOR, Usage.COLOR);
mapConventional(CONV_TEX0, Usage.PRIMARY_UV);
mapConventional(CONV_TEX0 + 1, Usage.SECONDARY_UV);
}
private static void mapConventional(int conv, Usage usage) {
final int location = usage.getAttributeLocation();
CONV_LOCATION[conv] = location;
LOCATION_CONV[location] = conv;
}
public static int conventionalLocation(int conv) {
return CONV_LOCATION[conv];
}
private static final int T_BYTE = 1, T_UBYTE = 1 << 1, T_SHORT = 1 << 2, T_USHORT = 1 << 3, T_INT = 1 << 4, T_UINT = 1 << 5, T_FLOAT = 1 << 6;
private static final int T_DOUBLE = 1 << 10, T_HALF = 1 << 11, T_FIXED = 1 << 12, T_PACKED = 1 << 13, T_PACKED_FLOAT = 1 << 14;
private static final int T_INTEGER = T_BYTE | T_UBYTE | T_SHORT | T_USHORT | T_INT | T_UINT;
private static final int T_COLOR = T_INTEGER | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED;
private static final int T_GENERIC = T_COLOR | T_FIXED | T_PACKED_FLOAT;
private static final int[] LEGAL_TYPES = new int[CONV_COUNT];
private static final int[] MIN_SIZE = new int[CONV_COUNT];
private static final int[] MAX_SIZE = new int[CONV_COUNT];
static {
legal(CONV_POSITION, T_SHORT | T_INT | T_FLOAT | T_DOUBLE | T_HALF | T_PACKED, 2, 4);
legal(CONV_NORMAL, T_BYTE | T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 3, 3);
legal(CONV_COLOR, T_COLOR, 3, 4);
legal(CONV_SECONDARY_COLOR, T_COLOR, 3, 4);
legal(CONV_FOG_COORD, T_HALF | T_FLOAT | T_DOUBLE, 1, 1);
legal(CONV_INDEX, T_UBYTE | T_SHORT | T_INT | T_FLOAT | T_DOUBLE, 1, 1);
legal(CONV_EDGE_FLAG, T_UBYTE, 1, 1);
for (int unit = 0; unit < TEX_COORD_UNITS; unit++) {
legal(CONV_TEX0 + unit, T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 1, 4);
}
}
private static void legal(int conv, int types, int minSize, int maxSize) {
LEGAL_TYPES[conv] = types;
MIN_SIZE[conv] = minSize;
MAX_SIZE[conv] = maxSize;
}
private static int typeBit(int type) {
if (type >= GL11.GL_BYTE && type <= GL30.GL_HALF_FLOAT) return 1 << type - GL11.GL_BYTE;
return switch (type) {
case GL41.GL_FIXED -> T_FIXED;
case GL12.GL_UNSIGNED_INT_2_10_10_10_REV, GL33.GL_INT_2_10_10_10_REV -> T_PACKED;
case GL30.GL_UNSIGNED_INT_10F_11F_11F_REV -> T_PACKED_FLOAT;
default -> 0;
};
}
private static boolean isLegal(int types, int minSize, int maxSize, boolean bgra, int size, int type) {
final int bit = typeBit(type);
if ((types & bit) == 0) return false;
if (size == GL12.GL_BGRA) return bgra && (bit & (T_UBYTE | T_PACKED)) != 0;
return size >= minSize && size <= maxSize;
}
public static boolean isLegalConventional(int conv, int size, int type) {
return isLegal(LEGAL_TYPES[conv], MIN_SIZE[conv], MAX_SIZE[conv], conv == CONV_COLOR || conv == CONV_SECONDARY_COLOR, size, type);
}
public static boolean isLegalGeneric(int index, int size, int type, boolean integer) {
return index >= 0 && index < MAX_ATTRIBS && isLegal(integer ? T_INTEGER : T_GENERIC, 1, 4, !integer, size, type);
}
private int currentVertexFlags = 0;
private int genericEnabled = 0;
private int conventionalEnabled = 0;
private Attrib[] currentAttribs = null;
private Attrib[] currentGeneric = null;
private Attrib[] currentConventional = null;
public int boundEBO = 0;
private int clientSideEnabledCount = 0;
private int arrayVersion = 0;
private int versionClock = 0;
private static final int DELETED_NAME_PRUNE_THRESHOLD = 4096;
private static final int DEAD_EPOCH = -1;
private final int pruneThreshold;
private int bufferEpoch = 0;
private final Int2IntOpenHashMap bufferDeletedAt = new Int2IntOpenHashMap();
public VAOManager() {
this(DELETED_NAME_PRUNE_THRESHOLD);
}
VAOManager(int pruneThreshold) {
this.pruneThreshold = pruneThreshold;
}
private void touch() {
arrayVersion = ++versionClock;
}
private boolean isBufferLive(int buffer, int sinceEpoch) {
return buffer == 0 || bufferDeletedAt.get(buffer) <= sinceEpoch;
}
private int retiredEpoch(int buffer, int epoch, boolean rebase) {
if (rebase) return epoch == DEAD_EPOCH ? DEAD_EPOCH : 0;
return isBufferLive(buffer, epoch) ? epoch : DEAD_EPOCH;
}
private void retire(@Nullable Attrib[] slots, boolean rebase) {
if (slots == null) return;
for (final Attrib a : slots) {
if (a != null) a.epoch = retiredEpoch(a.vboId, a.epoch, rebase);
}
}
private void retireAll(ClientArrays[] savedStack, boolean rebase) {
for (final VAOData data : vaoMap.values()) {
retire(data.attribs, rebase);
retire(data.generic, rebase);
retire(data.conventional, rebase);
}
retire(currentAttribs, rebase);
retire(currentGeneric, rebase);
retire(currentConventional, rebase);
for (final ClientArrays saved : savedStack) {
if (saved == null) continue;
saved.vboEpoch = retiredEpoch(saved.boundVBO, saved.vboEpoch, rebase);
saved.eboEpoch = retiredEpoch(saved.boundEBO, saved.eboEpoch, rebase);
retire(saved.generic, rebase);
retire(saved.conventional, rebase);
}
}
private void pruneDeletedNames(ClientArrays[] savedStack) {
retireAll(savedStack, false);
bufferDeletedAt.clear();
retireAll(savedStack, true);
bufferEpoch = 0;
}
public boolean isBufferLive(Attrib a) {
return isBufferLive(a.vboId, a.epoch);
}
public void init(int defaultVAO) {
final VAOData data = new VAOData();
@@ -45,6 +205,10 @@ public final class VAOManager {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.genericEnabled = genericEnabled;
current.conventionalEnabled = conventionalEnabled;
current.generic = currentGeneric;
current.conventional = currentConventional;
if (current.ebo != boundEBO) {
removeEboOwner(current.ebo, current);
current.ebo = boundEBO;
@@ -52,6 +216,7 @@ public final class VAOManager {
}
current.attribs = currentAttribs;
current.clientSideEnabledCount = clientSideEnabledCount;
current.arrayVersion = arrayVersion;
}
VAOData data = vaoMap.get(vaoId);
@@ -61,9 +226,14 @@ public final class VAOManager {
}
current = data;
currentVertexFlags = data.vertexFlags;
genericEnabled = data.genericEnabled;
conventionalEnabled = data.conventionalEnabled;
currentAttribs = data.attribs;
currentGeneric = data.generic;
currentConventional = data.conventional;
boundEBO = data.ebo;
clientSideEnabledCount = data.clientSideEnabledCount;
arrayVersion = data.arrayVersion;
}
public void onDeleteVertexArray(int vaoId) {
@@ -73,8 +243,26 @@ public final class VAOManager {
if (data == current) current = null;
}
public void onDeleteBuffer(int buffer) {
private static boolean detach(Attrib[] slots, int buffer) {
boolean detached = false;
for (final Attrib a : slots) {
if (a != null && a.vboId == buffer) {
a.vboId = 0;
detached = true;
}
}
return detached;
}
public void onDeleteBuffer(int buffer, ClientArrays[] savedStack) {
if (buffer == 0) return;
bufferDeletedAt.put(buffer, ++bufferEpoch);
if (bufferEpoch >= pruneThreshold) pruneDeletedNames(savedStack);
if (currentAttribs != null) {
boolean detached = detach(currentGeneric != null ? currentGeneric : currentAttribs, buffer);
if (currentConventional != null) detached |= detach(currentConventional, buffer);
if (detached) touch();
}
VAOData owner = eboOwners.remove(buffer);
while (owner != null) {
final VAOData next = owner.nextEboOwner;
@@ -112,14 +300,19 @@ public final class VAOManager {
public void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
public void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
public int getVertexFlags() { return currentVertexFlags; }
public void setVertexFlags(int flags) { currentVertexFlags = flags; }
public boolean isConventionalEnabled(int conv) { return (conventionalEnabled >> conv & 1) != 0; }
public static void setCurrentVertexFlags(int flags) { GLStateManager.ctx().vaos.currentVertexFlags = flags; }
public static final class VAOData {
public Attrib[] attribs;
public int vertexFlags;
public int genericEnabled;
public int conventionalEnabled;
public Attrib[] generic;
public Attrib[] conventional;
public int ebo;
public int clientSideEnabledCount;
int arrayVersion;
private VAOData nextEboOwner;
public VAOData() {
@@ -127,92 +320,313 @@ public final class VAOManager {
}
}
public void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
return setGeneric(index, size, type, normalized, false, stride, offset, vboId, null);
}
public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
return setGeneric(index, size, type, normalized, false, stride, 0, 0, pointer);
}
public boolean setIntegerAttribute(int index, int size, int type, int stride, long offset, int vboId) {
return setGeneric(index, size, type, false, true, stride, offset, vboId, null);
}
public boolean setIntegerAttribute(int index, int size, int type, int stride, ByteBuffer pointer) {
return setGeneric(index, size, type, false, true, stride, 0, 0, pointer);
}
public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId) {
return setConventional(conv, size, type, normalized, stride, offset, vboId, null);
}
public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
return setConventional(conv, size, type, normalized, stride, 0, 0, pointer);
}
private boolean setGeneric(int index, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
final Attrib g = genericSlot(index);
store(g, size, type, normalized, integer, stride, offset, vboId, pointer);
if (currentAttribs[index] == g) return true;
if (!isGenericEnabled(index) && isLocationConventionalEnabled(index)) return false;
apply(index, g);
return true;
}
private boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId, ByteBuffer pointer) {
if (conv < 0 || conv >= CONV_COUNT) return false;
final Attrib c = conventionalSlot(conv);
store(c, size, type, normalized, false, stride, offset, vboId, pointer);
final int index = CONV_LOCATION[conv];
if (index < 0) return false;
if (currentAttribs[index] == c) return true;
if (isGenericEnabled(index)) return false;
apply(index, c);
return true;
}
private void store(Attrib a, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) {
final boolean wasClient = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.integer = integer;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
a.genericPointer = true;
if (wasClient && a.enabled) {
clientSideEnabledCount--;
}
}
public void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = 0;
a.epoch = bufferEpoch;
a.clientPointer = pointer;
a.genericPointer = true;
if (!wasClient && a.enabled) {
clientSideEnabledCount++;
if (wasClient != a.isClientSide()) clientSideEnabledCount += wasClient ? -1 : 1;
touch();
}
private void apply(int index, Attrib source) {
final Attrib old = currentAttribs[index];
if (old != null) {
if (old.isClientSide()) clientSideEnabledCount--;
source.enabled = old.enabled;
old.enabled = false;
if (source.isClientSide()) clientSideEnabledCount++;
}
currentAttribs[index] = source;
}
private boolean isGenericEnabled(int index) {
return (genericEnabled >> index & 1) != 0;
}
private boolean isLocationConventionalEnabled(int index) {
final int conv = LOCATION_CONV[index];
return conv >= 0 && isConventionalEnabled(conv);
}
private Attrib generic(int index) {
return (currentGeneric != null ? currentGeneric : currentAttribs)[index];
}
private Attrib genericSlot(int index) {
final Attrib[] slots = currentGeneric != null ? currentGeneric : currentAttribs;
Attrib g = slots[index];
if (g == null) {
g = new Attrib();
g.genericPointer = true;
slots[index] = g;
if (currentAttribs[index] == null) currentAttribs[index] = g;
}
return g;
}
private Attrib conventionalSlot(int conv) {
if (currentConventional == null) {
currentConventional = new Attrib[CONV_COUNT];
currentGeneric = currentAttribs.clone();
}
Attrib c = currentConventional[conv];
if (c == null) {
c = new Attrib();
currentConventional[conv] = c;
}
return c;
}
private boolean refresh(int index) {
touch();
boolean repoint = false;
final boolean conventional = isLocationConventionalEnabled(index);
final Attrib winner = isGenericEnabled(index) ? genericSlot(index) : conventional ? currentConventional[LOCATION_CONV[index]] : null;
if (winner != null && winner != currentAttribs[index]) {
apply(index, winner);
repoint = true;
}
final boolean enabled = winner != null;
Attrib e = currentAttribs[index];
if (e == null) {
if (!enabled) return repoint;
e = genericSlot(index);
}
if (e.enabled != enabled) {
if (e.clientPointer != null) clientSideEnabledCount += enabled ? 1 : -1;
e.enabled = enabled;
}
return repoint;
}
public boolean enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
genericEnabled |= 1 << index;
return refresh(index);
}
public boolean disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
genericEnabled &= ~(1 << index);
return refresh(index);
}
public boolean setConventionalEnabled(int conv, boolean enabled) {
if (conv < 0 || conv >= CONV_COUNT) return false;
conventionalSlot(conv);
if (enabled) {
conventionalEnabled |= 1 << conv;
} else {
conventionalEnabled &= ~(1 << conv);
}
final int index = CONV_LOCATION[conv];
if (index >= 0) return refresh(index);
touch();
return false;
}
public boolean setDivisor(int index, int divisor) {
if (index < 0 || index >= MAX_ATTRIBS) return true;
final Attrib g = genericSlot(index);
g.divisor = divisor;
touch();
return currentAttribs[index] == g;
}
public @Nullable Attrib conventional(int conv) {
return conv >= 0 && currentConventional != null ? currentConventional[conv] : null;
}
public static final class ClientArrays {
public int boundVAO;
public int boundVBO;
public int boundEBO;
public int clientActiveTextureUnit;
int arrayVersion;
int vboEpoch;
int eboEpoch;
int vertexFlags;
int genericEnabled;
int conventionalEnabled;
int genericPresent;
int conventionalPresent;
int effectiveGeneric;
final Attrib[] generic = new Attrib[MAX_ATTRIBS];
final Attrib[] conventional = new Attrib[CONV_COUNT];
}
private static void saveSlot(Attrib[] saved, int i, Attrib source) {
if (saved[i] == null) saved[i] = new Attrib();
saved[i].copyPointer(source);
}
public boolean isSavedVboLive(ClientArrays saved) {
return isBufferLive(saved.boundVBO, saved.vboEpoch);
}
public boolean isSavedEboLive(ClientArrays saved) {
return isBufferLive(saved.boundEBO, saved.eboEpoch);
}
public void saveClientArrays(ClientArrays out) {
out.arrayVersion = arrayVersion;
out.vboEpoch = bufferEpoch;
out.eboEpoch = bufferEpoch;
out.vertexFlags = currentVertexFlags;
out.genericEnabled = genericEnabled;
out.conventionalEnabled = conventionalEnabled;
out.genericPresent = 0;
out.conventionalPresent = 0;
out.effectiveGeneric = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final Attrib g = generic(i);
if (g != null) {
out.genericPresent |= 1 << i;
saveSlot(out.generic, i, g);
}
final Attrib e = currentAttribs[i];
if (e != null && e.genericPointer) out.effectiveGeneric |= 1 << i;
}
if (currentConventional == null) return;
for (int i = 0; i < CONV_COUNT; i++) {
final Attrib c = currentConventional[i];
if (c != null) {
out.conventionalPresent |= 1 << i;
saveSlot(out.conventional, i, c);
}
}
}
public boolean isGenericPointerEnabled(int index) {
private final Attrib[] restoreBefore = new Attrib[MAX_ATTRIBS];
public int restoreClientArrays(ClientArrays in) {
currentVertexFlags = in.vertexFlags;
if (arrayVersion == in.arrayVersion) return 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (restoreBefore[i] == null) restoreBefore[i] = new Attrib();
final Attrib e = currentAttribs[i];
if (e == null) {
restoreBefore[i].clearPointer();
restoreBefore[i].enabled = false;
} else {
restoreBefore[i].copyPointer(e);
restoreBefore[i].enabled = e.enabled;
}
}
genericEnabled = in.genericEnabled;
conventionalEnabled = in.conventionalEnabled;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if ((in.genericPresent >> i & 1) != 0) {
genericSlot(i).copyPointer(in.generic[i]);
} else {
final Attrib g = generic(i);
if (g != null) g.clearPointer();
}
}
if (currentConventional != null || in.conventionalPresent != 0) {
for (int i = 0; i < CONV_COUNT; i++) {
if ((in.conventionalPresent >> i & 1) != 0) {
conventionalSlot(i).copyPointer(in.conventional[i]);
} else if (currentConventional != null && currentConventional[i] != null) {
currentConventional[i].clearPointer();
}
}
}
clientSideEnabledCount = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final int conv = LOCATION_CONV[i];
final Attrib g = generic(i);
final Attrib c = conventional(conv);
final boolean conventional = isLocationConventionalEnabled(i);
Attrib e;
if (isGenericEnabled(i)) {
e = genericSlot(i);
} else if (conventional) {
e = conventionalSlot(conv);
} else {
e = c == null || (in.effectiveGeneric >> i & 1) != 0 ? g : c;
}
if (g != null) g.enabled = false;
if (c != null) c.enabled = false;
currentAttribs[i] = e;
if (e != null) {
e.enabled = isGenericEnabled(i) || conventional;
if (e.isClientSide()) clientSideEnabledCount++;
}
}
int changed = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final Attrib e = currentAttribs[i];
final Attrib before = restoreBefore[i];
if (e == null) {
if (before.enabled) changed |= 0x10000 << i;
} else {
if (e.enabled != before.enabled) changed |= 0x10000 << i;
if (!e.samePointer(before)) changed |= 1 << i;
}
before.clientPointer = null;
}
arrayVersion = in.arrayVersion;
return changed;
}
public boolean isAttribEnabled(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
final Attrib a = currentAttribs[index];
return a != null && a.genericPointer && a.enabled;
}
public void markConventional(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = currentAttribs[index];
if (a != null) a.genericPointer = false;
}
public void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public void enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && !a.enabled) {
clientSideEnabledCount++;
}
a.enabled = true;
}
public void disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled) {
clientSideEnabledCount--;
}
a.enabled = false;
}
// Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16.
private Attrib getAttrib(int index) {
Attrib a = currentAttribs[index];
if (a == null) {
a = new Attrib();
currentAttribs[index] = a;
}
return a;
}
public boolean isClientSideAttrib(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null && attrib.isClientSide();
return a != null && a.enabled;
}
public Attrib attrib(int index) {
@@ -272,8 +686,8 @@ public final class VAOManager {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
final int savedVBO = GLSMBuffers.getBoundVBO();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
@@ -286,10 +700,14 @@ public final class VAOManager {
if (clientArraysVBOOffsets[i] < 0) continue;
final VAOManager.Attrib a = currentAttribs[i];
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
if (a.integer) {
RENDER_BACKEND.vertexAttribIPointer(i, a.size, a.type, a.stride, (long) clientArraysVBOOffsets[i]);
} else {
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
@@ -298,9 +716,12 @@ public final class VAOManager {
public int size;
public int type;
public boolean normalized;
public boolean integer;
public int stride;
public long offset;
public int vboId;
public int divisor;
int epoch;
public ByteBuffer clientPointer;
public boolean genericPointer;
@@ -308,16 +729,35 @@ public final class VAOManager {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
void copyPointer(Attrib source) {
size = source.size;
type = source.type;
normalized = source.normalized;
integer = source.integer;
stride = source.stride;
offset = source.offset;
vboId = source.vboId;
divisor = source.divisor;
epoch = source.epoch;
clientPointer = source.clientPointer;
}
boolean samePointer(Attrib other) {
return size == other.size && type == other.type && normalized == other.normalized && integer == other.integer
&& stride == other.stride && offset == other.offset && vboId == other.vboId && divisor == other.divisor
&& clientPointer == other.clientPointer;
}
void clearPointer() {
size = 0;
type = 0;
normalized = false;
integer = false;
stride = 0;
offset = 0;
vboId = 0;
divisor = 0;
clientPointer = null;
genericPointer = false;
}
public int effectiveStride() {
@@ -3,7 +3,10 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
import org.joml.Matrix4f;
@@ -46,11 +49,12 @@ public final class VertexKey {
private static final int BIT_TEXGEN_R = BIT_TEXGEN_T + 3;
private static final int BIT_TEXGEN_Q = BIT_TEXGEN_R + 3;
private static final int BIT_CLIP_PLANES = BIT_TEXGEN_Q + 3;
private static final int BIT_WIDE_LINE = BIT_CLIP_PLANES + 1;
private static final int BIT_UNIT_TEXMAT_BASE = BIT_WIDE_LINE + 1;
private static final int BIT_LINE_QUADS = BIT_CLIP_PLANES + 1;
private static final int BIT_UNIT_TEXMAT_BASE = BIT_LINE_QUADS + 1;
private static final int BIT_UNIT23_UV_FROM_UNIT0 = BIT_UNIT_TEXMAT_BASE + MAX_UNITS;
private static final int BIT_LINE_STIPPLE = BIT_UNIT23_UV_FROM_UNIT0 + 1;
private static final int BIT_INSTANCING = BIT_LINE_STIPPLE + 1;
private static final int BIT_CLIP_EMULATED = BIT_INSTANCING + 3;
private static final long INSTANCING_MASK = 0x7L;
static {
@@ -103,8 +107,9 @@ public final class VertexKey {
public int texGenModeQ() { return (int)((packed >> BIT_TEXGEN_Q) & 0x7); }
public boolean texGenEnabled() { return texGenModeS() != TG_NONE || texGenModeT() != TG_NONE || texGenModeR() != TG_NONE || texGenModeQ() != TG_NONE; }
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
public boolean clipEmulated() { return bit(BIT_CLIP_EMULATED); }
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
public boolean lineQuads() { return bit(BIT_LINE_QUADS); }
public Instancing instancing() { return Instancing.VALUES[(int) ((packed >> BIT_INSTANCING) & INSTANCING_MASK)]; }
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
@@ -226,26 +231,27 @@ public final class VertexKey {
// Vertex format flags
if (hasColor) bits |= (1L << BIT_HAS_VERTEX_COLOR);
if (hasNormal) bits |= (1L << BIT_HAS_VERTEX_NORMAL);
if (hasNormal && !glCtx.lineQuadsActive) bits |= (1L << BIT_HAS_VERTEX_NORMAL);
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
if (GLStateManager.consumeUnit23TexCoordSetDuringDraw(glCtx) && hasTexCoord) {
if (GLSMCurrentAttribs.consumeUnit23TexCoordSetDuringDraw(glCtx) && hasTexCoord) {
bits |= (1L << BIT_UNIT23_UV_FROM_UNIT0);
}
if (GLStateManager.anyClipPlaneEnabled(glCtx)) {
if (GLSMRasterizer.anyClipPlaneEnabled(glCtx)) {
bits |= (1L << BIT_CLIP_PLANES);
}
if (glCtx.wideLineEmulationActive) {
bits |= (1L << BIT_WIDE_LINE);
if (RenderSystem.isClipDistanceEmulated()) bits |= 1L << BIT_CLIP_EMULATED;
}
if (glCtx.lineStippleActive) {
bits |= (1L << BIT_LINE_STIPPLE);
}
if (glCtx.lineQuadsActive) {
bits |= (1L << BIT_LINE_QUADS);
}
bits |= (long) glCtx.ffpInstancing.ordinal() << BIT_INSTANCING;
return bits;
@@ -277,6 +283,6 @@ public final class VertexKey {
@Override
public String toString() {
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b inst=%s]", packed,lightingEnabled(), lightEnabled(0), lightEnabled(1), colorMaterialEnabled(), fogEnabled(), unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0, unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0, hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(), texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation(), instancing());
return String.format("FFPVertexKey[0x%012X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b clipEmu=%b lquads=%b inst=%s]", packed,lightingEnabled(), lightEnabled(0), lightEnabled(1), colorMaterialEnabled(), fogEnabled(), unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0, unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0, hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(), texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), clipEmulated(), lineQuads(), instancing());
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
/**
* Generates GLSL 330 core vertex shaders for FFP emulation.
@@ -19,6 +20,9 @@ public final class VertexShaderGenerator {
emitAttributes(sb, key);
emitUniforms(sb, key);
emitOutputs(sb, key);
if (key.lineQuads()) {
sb.append(ShaderLoader.getShaderSource("angelica:include/line_quads.glsl")).append('\n');
}
sb.append("void main() {\n");
if (key.instancing() == Instancing.WEATHER) {
@@ -34,28 +38,16 @@ public final class VertexShaderGenerator {
}
emitTexCoordPassthrough(sb, key);
if (key.lineStipple()) {
emitLineStart(sb);
emitLineStart(sb, key.lineQuads() ? "lineCenter" : "gl_Position");
}
if (key.fogEnabled()) {
emitFogDistance(sb, key);
}
if (key.clipPlanesEnabled()) {
emitClipDistances(sb);
emitClipDistances(sb, key);
}
sb.append("}\n");
String source = sb.toString();
if (key.wideLineEmulation()) {
source = source.replace("v_SpecularColor", "v_SpecularColor_gs")
.replace("v_TexCoord0", "v_TexCoord0_gs")
.replace("v_TexCoord1", "v_TexCoord1_gs")
.replace("v_TexCoord2", "v_TexCoord2_gs")
.replace("v_TexCoord3", "v_TexCoord3_gs")
.replace("v_FogCoord", "v_FogCoord_gs")
.replace("v_LineStart", "v_LineStart_gs")
.replace("v_Color", "v_Color_gs");
}
return source;
return sb.toString();
}
private static void emitAttributes(StringBuilder sb, VertexKey key) {
@@ -78,6 +70,8 @@ public final class VertexShaderGenerator {
}
if (key.hasVertexNormal() || kind == Instancing.CUBE) {
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_Normal;\n");
} else if (key.lineQuads()) {
sb.append("layout(location = ").append(VertexFormatElement.Usage.NORMAL.getAttributeLocation()).append(") in vec3 a_LineOtherEnd;\n");
}
}
for (String d : InstancedGlslHelpers.attributeDecls("a_", kind)) {
@@ -97,8 +91,8 @@ public final class VertexShaderGenerator {
};
}
private static void emitLineStart(StringBuilder sb) {
sb.append(" vec2 ndc = gl_Position.xy / gl_Position.w;\n");
private static void emitLineStart(StringBuilder sb, String clipPosition) {
sb.append(" vec2 ndc = ").append(clipPosition).append(".xy / ").append(clipPosition).append(".w;\n");
sb.append(" v_LineStart = u_Viewport.xy + (ndc * 0.5 + 0.5) * u_Viewport.zw;\n\n");
}
@@ -110,13 +104,16 @@ public final class VertexShaderGenerator {
private static void emitOutputs(StringBuilder sb, VertexKey key) {
sb.append("// Outputs\n");
sb.append("out vec4 v_Color;\n");
sb.append("invariant gl_Position;\n");
if (key.instancing().hasInstanceHead()) {
sb.append("out vec4 v_Overlay;\n");
sb.append("invariant gl_Position;\n");
}
if (key.lineStipple()) {
sb.append("flat out vec2 v_LineStart;\n");
}
if (key.clipEmulated()) {
sb.append(ClipDistanceGlsl.VS_OUT_DIST0).append('\n').append(ClipDistanceGlsl.VS_OUT_DIST1).append('\n');
}
if (key.separateSpecular()) {
sb.append("out vec3 v_SpecularColor;\n");
}
@@ -151,15 +148,32 @@ public final class VertexShaderGenerator {
sb.append(" v_Overlay = a_InstOverlay;\n");
} else {
sb.append(" gl_Position = u_MVPMatrix * pos4;\n");
if (key.lineQuads()) {
emitLineQuadWidening(sb, key);
}
// Eye position needed for lighting, fog, EYE_LINEAR texgen, and clip planes
if (key.lightingEnabled() || key.fogEnabled() || texGenNeedsEyePos(key) || key.clipPlanesEnabled()) {
sb.append(" vec4 eyePos = u_ModelViewMatrix * pos4;\n");
}
if (key.lineQuads() && (key.fogEnabled() || key.clipPlanesEnabled())) {
sb.append(" vec4 lineEyeOther = u_ModelViewMatrix * vec4(a_LineOtherEnd, 1.0);\n");
}
}
sb.append('\n');
}
private static void emitLineQuadWidening(StringBuilder sb, VertexKey key) {
sb.append(" int lineCorner = gl_VertexID % 6;\n");
sb.append(" bool lineStart = angelica_lineStart(lineCorner);\n");
sb.append(" vec4 lineOther = u_MVPMatrix * vec4(a_LineOtherEnd, 1.0);\n");
if (key.lineStipple()) {
sb.append(" vec4 lineCenter = lineStart ? gl_Position : lineOther;\n");
}
sb.append(" float lineT;\n");
sb.append(" gl_Position = angelica_lineQuad(gl_Position, lineOther, lineCorner, u_ViewportSize, u_LineWidth, lineT);\n");
}
private static void emitNormalTransform(StringBuilder sb, VertexKey key) {
sb.append(" // Normal transform\n");
final String srcNormal = key.hasVertexNormal() && key.instancing() != Instancing.PARTICLE ? "a_Normal" : "u_CurrentNormal";
@@ -380,24 +394,30 @@ public final class VertexShaderGenerator {
private static void emitFogDistance(StringBuilder sb, VertexKey key) {
sb.append(" // Fog distance\n");
// Mesa ffvertex_prog.c build_fog(): FDM_EYE_RADIAL/EYE_PLANE/EYE_PLANE_ABS
final int fogDistMode = key.fogDistanceMode();
switch (fogDistMode) {
case 0 -> // FDM_EYE_RADIAL: Euclidean distance
sb.append(" v_FogCoord = length(eyePos.xyz);\n");
case 1 -> // FDM_EYE_PLANE: raw Z (can be negative, clamped by fog factor)
sb.append(" v_FogCoord = eyePos.z;\n");
case 2 -> // FDM_EYE_PLANE_ABS: absolute Z
sb.append(" v_FogCoord = abs(eyePos.z);\n");
default ->
sb.append(" v_FogCoord = abs(eyePos.z);\n");
final String own = FfpGlsl.fogDistance(key.fogDistanceMode(), "eyePos");
final String fog = key.lineQuads() ? acrossLine(own, FfpGlsl.fogDistance(key.fogDistanceMode(), "lineEyeOther")) : own;
sb.append(" v_FogCoord = ").append(fog).append(";\n");
}
private static void emitClipDistances(StringBuilder sb, VertexKey key) {
sb.append(" // Clip distances\n");
final boolean emulated = key.clipEmulated();
final String[] distances = emulated ? new String[8] : null;
for (int i = 0; i < 8; i++) {
final String own = "dot(u_ClipPlane[" + i + "], eyePos)";
final String distance = key.lineQuads() ? acrossLine(own, "dot(u_ClipPlane[" + i + "], lineEyeOther)") : own;
if (emulated) {
distances[i] = distance;
} else {
sb.append(" gl_ClipDistance[").append(i).append("] = ").append(distance).append(";\n");
}
}
if (emulated) {
ClipDistanceGlsl.appendVsAssign(sb, distances);
}
}
private static void emitClipDistances(StringBuilder sb) {
sb.append(" // Clip distances\n");
for (int i = 0; i < 8; i++) {
sb.append(" gl_ClipDistance[").append(i).append("] = dot(u_ClipPlane[").append(i).append("], eyePos);\n");
}
private static String acrossLine(String own, String other) {
return "mix(lineStart ? " + own + " : " + other + ", lineStart ? " + other + " : " + own + ", lineT)";
}
}
@@ -1,33 +1,78 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
/**
* One rain or snow column per instance.
* One weather column per instance.
*/
public final class WeatherInstancedAttribs {
public static final int LOC_COLUMN_SPAN = ParticleInstancedAttribs.LOC_CENTER_HALF;
public static final int LOC_JITTER = ParticleInstancedAttribs.LOC_UV;
public static final int LOC_PARAMS = InstancedAttribs.LOC_COLOR;
public static final int LOC_COLOR = InstancedAttribs.LOC_ROW2;
public static final int LOC_LAYER = InstancedAttribs.LOC_OVERLAY;
public static final int OFFSET_COLUMN_SPAN = 0;
public static final int OFFSET_JITTER = 16;
public static final int OFFSET_PARAMS = 32;
public static final int STRIDE = 48;
public static final int OFFSET_COLOR = 48;
public static final int OFFSET_LAYER = 64;
public static final int STRIDE = 80;
public static final int RAIN_PERIOD = 32;
public static final float RAIN_SCROLL_BASE = 3.0F;
public static final float RAIN_FADE = 0.5F;
public static final int SNOW_PERIOD = 512;
public static final float SNOW_U_DRIFT = 0.01F;
public static final float SNOW_V_DRIFT = 0.001F;
public static final float SNOW_FADE = 0.3F;
public static final float SOURCE_VANILLA = 0.0F;
public static final float SOURCE_PROVIDER = 1.0F;
public static void putColumn(ByteBuffer data, int index, float x, float z, float bottom, float top, float uBase, float uDrift,
float vBase, float vDrift, float brightness, int scrollOffset, float scrollScale, float source, float r, float g, float b,
float a, int period, float uDriftScale, float vDriftScale, float fadeScale) {
final int base = index * STRIDE;
data.putFloat(base + OFFSET_COLUMN_SPAN, x);
data.putFloat(base + OFFSET_COLUMN_SPAN + 4, z);
data.putFloat(base + OFFSET_COLUMN_SPAN + 8, bottom);
data.putFloat(base + OFFSET_COLUMN_SPAN + 12, top);
data.putFloat(base + OFFSET_JITTER, uBase);
data.putFloat(base + OFFSET_JITTER + 4, uDrift);
data.putFloat(base + OFFSET_JITTER + 8, vBase);
data.putFloat(base + OFFSET_JITTER + 12, vDrift);
data.putFloat(base + OFFSET_PARAMS, brightness);
data.putFloat(base + OFFSET_PARAMS + 4, scrollOffset & (period - 1));
data.putFloat(base + OFFSET_PARAMS + 8, scrollScale);
data.putFloat(base + OFFSET_PARAMS + 12, source);
data.putFloat(base + OFFSET_COLOR, r);
data.putFloat(base + OFFSET_COLOR + 4, g);
data.putFloat(base + OFFSET_COLOR + 8, b);
data.putFloat(base + OFFSET_COLOR + 12, a);
data.putFloat(base + OFFSET_LAYER, period);
data.putFloat(base + OFFSET_LAYER + 4, uDriftScale);
data.putFloat(base + OFFSET_LAYER + 8, vDriftScale);
data.putFloat(base + OFFSET_LAYER + 12, fadeScale);
}
public static void pointInstanceAttribs(long base) {
GLStateManager.glVertexAttribPointer(LOC_COLUMN_SPAN, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_COLUMN_SPAN);
GLStateManager.glVertexAttribPointer(LOC_JITTER, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_JITTER);
GLStateManager.glVertexAttribPointer(LOC_PARAMS, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_PARAMS);
GLSMVertexInput.glVertexAttribPointer(LOC_COLUMN_SPAN, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_COLUMN_SPAN);
GLSMVertexInput.glVertexAttribPointer(LOC_JITTER, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_JITTER);
GLSMVertexInput.glVertexAttribPointer(LOC_PARAMS, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_PARAMS);
GLSMVertexInput.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_COLOR);
GLSMVertexInput.glVertexAttribPointer(LOC_LAYER, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LAYER);
}
static void enableInstanceArrays() {
InstancedAttribs.enableInstanced(LOC_COLUMN_SPAN);
InstancedAttribs.enableInstanced(LOC_JITTER);
InstancedAttribs.enableInstanced(LOC_PARAMS);
InstancedAttribs.enableInstanced(LOC_COLOR);
InstancedAttribs.enableInstanced(LOC_LAYER);
}
private WeatherInstancedAttribs() {}
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm.ffp;
/**
* The per-frame half of the rain and snow instancing, set once per weather draw.
* The per-frame half of the weather column instancing, set once per frame.
*/
public final class WeatherParams {
@@ -15,9 +15,14 @@ public final class WeatherParams {
public static float partialTicks;
public static float age;
public static float rainScroll;
public static float snowScroll;
public static float vanillaTick;
public static float providerTick;
public static float rainStrength;
public static float providerAge;
public static float providerInvRadius;
public static final int TICK_WRAP = 1024;
public static int generation;
@@ -25,7 +30,7 @@ public final class WeatherParams {
public static void set(float translateX, float translateY, float translateZ, float invRadius,
float cameraFracX, float cameraFracZ, float partialTicks, float age,
float rainScroll, float snowScroll, float rainStrength) {
int vanillaTick, float rainStrength, int providerTick, float providerAge, float providerInvRadius) {
WeatherParams.translateX = translateX;
WeatherParams.translateY = translateY;
WeatherParams.translateZ = translateZ;
@@ -34,9 +39,11 @@ public final class WeatherParams {
WeatherParams.cameraFracZ = cameraFracZ;
WeatherParams.partialTicks = partialTicks;
WeatherParams.age = age;
WeatherParams.rainScroll = rainScroll;
WeatherParams.snowScroll = snowScroll;
WeatherParams.vanillaTick = vanillaTick & (TICK_WRAP - 1);
WeatherParams.providerTick = providerTick & (TICK_WRAP - 1);
WeatherParams.rainStrength = rainStrength;
WeatherParams.providerAge = providerAge;
WeatherParams.providerInvRadius = providerInvRadius;
generation++;
}
}
@@ -2,7 +2,8 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -10,7 +11,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* The four corners every rain and snow column is expanded from. x picks the end of the billboard, y picks
* The four corners every weather column is expanded from. x picks the end of the billboard, y picks
* the bottom or top edge.
*/
public final class WeatherQuadMesh {
@@ -32,37 +33,29 @@ public final class WeatherQuadMesh {
for (float corner : CORNERS) data.putFloat(corner);
data.flip();
vao = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
vbo = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
vao = GLSMVertexInput.glGenVertexArrays();
GLSMVertexInput.glBindVertexArray(vao);
vbo = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
final int corner = VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation();
GLStateManager.glEnableVertexAttribArray(corner);
GLStateManager.glVertexAttribPointer(corner, 2, GL11.GL_FLOAT, false, 2 * Float.BYTES, 0L);
GLSMVertexInput.glEnableVertexAttribArray(corner);
GLSMVertexInput.glVertexAttribPointer(corner, 2, GL11.GL_FLOAT, false, 2 * Float.BYTES, 0L);
WeatherInstancedAttribs.enableInstanceArrays();
GLStateManager.glBindVertexArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLSMVertexInput.glBindVertexArray(0);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
return vao;
}
public static void bindInstanceBuffer(int bufferId) {
GLStateManager.glBindVertexArray(vao());
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
WeatherInstancedAttribs.pointInstanceAttribs(0L);
GLStateManager.glBindVertexArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
public static void delete() {
if (vbo != 0) {
GLStateManager.glDeleteBuffers(vbo);
GLSMBuffers.glDeleteBuffers(vbo);
vbo = 0;
}
if (vao != 0) {
GLStateManager.glDeleteVertexArrays(vao);
GLSMVertexInput.glDeleteVertexArrays(vao);
vao = 0;
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
public interface DeferredAlphaHandler {
boolean isAlphaTestLocked();
boolean isOverrideHeld();
boolean deferAlphaTestToggle(boolean enabled);
boolean deferAlphaFunc(int function, float reference);
}
@@ -3,8 +3,10 @@ package com.gtnewhorizons.angelica.glsm.hooks;
import com.gtnewhorizons.angelica.glsm.CaptureGate;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLSMTextureLifetime;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -15,17 +17,25 @@ public final class FrameHooks {
private static boolean gameLoopStarted;
private static long frameCounter;
private static boolean frameGroupActive;
private static String framePrefix = "frame:";
private static String buildLabel = "";
public static void bootstrapFirstFrame() {
DisplayListManager.abortIfLeaked();
if (!gameLoopStarted) {
gameLoopStarted = true;
final GLSMInitConfig config = GLStateManager.getInitConfig();
if (config != null && !config.getBuildLabel().isEmpty()) {
buildLabel = config.getBuildLabel();
framePrefix = buildLabel + " frame:";
}
RENDER_BACKEND.onFrameBegin();
pushFrameGroup();
}
}
public static void frameEnd() {
GLSMTextureLifetime.onFrameEnd();
popFrameGroup();
RENDER_BACKEND.onFrameEnd();
}
@@ -46,8 +56,10 @@ public final class FrameHooks {
}
private static void pushFrameGroup() {
final boolean wasConnected = CaptureGate.tracyConnected;
CaptureGate.refresh();
GLDebug.pushGroup("frame:", frameCounter);
if (CaptureGate.tracyConnected && !wasConnected && !buildLabel.isEmpty()) Tracy.message(buildLabel);
GLDebug.pushGroup(framePrefix, frameCounter);
frameGroupActive = true;
}
@@ -11,6 +11,7 @@ public final class GLSMHooks {
public static DeferredDepthColorHandler depthColorHandler;
public static GlintColorHandler glintColorHandler;
public static ImmediateExtendedAttribHandler immediateExtendedHandler;
public static LineDrawHandler lineDrawHandler;
public static ShaderTransformPostProcessor postTransformProcessor;
public static PerFrameUniformBlock perFrameUniformBlock;
public static PerFrameUniformBlock perPassUniformBlock;
@@ -30,6 +31,7 @@ public final class GLSMHooks {
public static final EventBus<ProgramDeleteEvent> PROGRAM_DELETE = EventBus.create(ProgramDeleteEvent.class);
public static final EventBus<BlendFuncChangeEvent> BLEND_FUNC_CHANGE = EventBus.create(BlendFuncChangeEvent.class);
public static final EventBus<FogStateChangeEvent> FOG_STATE_CHANGE = EventBus.create(FogStateChangeEvent.class);
public static final EventBus<ClipPlanesInUseEvent> CLIP_PLANES_IN_USE = EventBus.create(ClipPlanesInUseEvent.class);
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
@@ -45,6 +47,7 @@ public final class GLSMHooks {
public static final ProgramDeleteEvent programDeleteEvent = new ProgramDeleteEvent();
public static final BlendFuncChangeEvent blendFuncChangeEvent = new BlendFuncChangeEvent();
public static final FogStateChangeEvent fogStateChangeEvent = new FogStateChangeEvent();
public static final ClipPlanesInUseEvent clipPlanesInUseEvent = new ClipPlanesInUseEvent();
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
Loaded 100 of 1290 files, more files were not shown because too many files have changed in this diff. Show more