mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69428c12f6 | ||
|
|
3978be161d | ||
|
|
2ed838acf1 | ||
|
|
d29a8278cd | ||
|
|
2f1f380e8e | ||
|
|
990dadc03f | ||
|
|
1946c2ad65 | ||
|
|
bfb66d84ee | ||
|
|
cae4fea2bb | ||
|
|
78abc05711 | ||
|
|
159f7ff8cc | ||
|
|
0d959c2b90 | ||
|
|
ce2462cad3 | ||
|
|
5457df5369 | ||
|
|
acedba4eac | ||
|
|
21a054a4db | ||
|
|
d056bf6fa7 | ||
|
|
5ed1c92590 |
No files matched your search
@@ -10,4 +10,6 @@ on:
|
||||
jobs:
|
||||
build-and-test:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||
with:
|
||||
disable-spotless-pr: true
|
||||
secrets: inherit
|
||||
@@ -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)
|
||||
|
||||
@@ -148,6 +148,17 @@ 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
|
||||
@@ -62,6 +62,7 @@ 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 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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -96,6 +96,7 @@ public class Feature {
|
||||
, GLStateManager.getSecondaryColor() // Current secondary color
|
||||
, ShaderManager.getNormalStack() // Current normal vector
|
||||
, ShaderManager.getTexCoordStack() // Current texture coordinates
|
||||
, GLStateManager.getEdgeFlagState()
|
||||
// Current color index
|
||||
// Current raster position
|
||||
// GL_CURRENT_RASTER_POSITION_VALID flag
|
||||
@@ -158,6 +159,8 @@ public class Feature {
|
||||
, GLStateManager.getPolygonOffsetPointState()
|
||||
, GLStateManager.getPolygonOffsetLineState()
|
||||
, GLStateManager.getPolygonOffsetFillState()
|
||||
, GLStateManager.getPointSpriteState()
|
||||
, GLStateManager.getVertexProgramTwoSideState()
|
||||
, GLStateManager.getPolygonSmoothState()
|
||||
, GLStateManager.getPolygonStippleState()
|
||||
, GLStateManager.getRescaleNormalState()
|
||||
@@ -178,6 +181,7 @@ public class Feature {
|
||||
enableBits.add(GLStateManager.getTextures().getTexture1DStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTexture3DStates(i));
|
||||
enableBits.add(GLStateManager.getTextures().getTextureCubeMapStates(i));
|
||||
}
|
||||
|
||||
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
@@ -216,13 +220,7 @@ 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
|
||||
GLStateManager.getHintState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of(
|
||||
GLStateManager.getColorMaterial()
|
||||
@@ -286,6 +284,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
|
||||
GLStateManager.getPointSmoothState()
|
||||
, GLStateManager.getPointSpriteState()
|
||||
, GLStateManager.getPointState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
|
||||
@@ -328,6 +327,10 @@ public class Feature {
|
||||
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
|
||||
textureAttribs.add(GLStateManager.getTextures().getTextureUnitBindings(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTexture1DStates(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTextureUnitStates(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTexture3DStates(i));
|
||||
textureAttribs.add(GLStateManager.getTextures().getTextureCubeMapStates(i));
|
||||
}
|
||||
|
||||
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -261,6 +265,14 @@ 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;
|
||||
|
||||
|
||||
@@ -47,6 +47,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;
|
||||
@@ -169,6 +170,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));
|
||||
@@ -182,6 +186,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;
|
||||
@@ -238,10 +243,9 @@ public class GLContextState {
|
||||
public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedUnpackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final boolean[] clientAttribSavedVertexArray = new boolean[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public float maxPointSize = Float.NaN;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
@@ -262,7 +266,7 @@ 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 Instancing ffpInstancing = Instancing.NONE;
|
||||
public float lastBrightnessX;
|
||||
|
||||
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;
|
||||
|
||||
@@ -131,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) {
|
||||
|
||||
@@ -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();
|
||||
GLStateManager.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 -> GLStateManager.glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE;
|
||||
case FLOAT -> lround(GLStateManager.glGetFloat(glCtx, pname));
|
||||
case FLOATN -> floatToInt(GLStateManager.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 -> GLStateManager.glGetBoolean(glCtx, pname) ? 1.0f : 0.0f;
|
||||
case INT -> (float) GLStateManager.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 -> GLStateManager.glGetInteger(glCtx, pname) != 0;
|
||||
case FLOAT, FLOATN -> GLStateManager.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;
|
||||
}
|
||||
}
|
||||
@@ -688,4 +688,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;
|
||||
@@ -224,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;
|
||||
}
|
||||
|
||||
@@ -276,6 +276,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);
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
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, the GL sampler object to sample it with when samplerUnits is -1, or 0
|
||||
* @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, int[] samplerObjects,
|
||||
int vertexBufferBinding, int vertexBufferCount, int indexBufferBinding) {
|
||||
}
|
||||
@@ -1,119 +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(" float d0 = c0.z + c0.w;\n");
|
||||
sb.append(" float d1 = c1.z + c1.w;\n");
|
||||
sb.append(" if (d0 < 0.0 && d1 < 0.0) return;\n");
|
||||
sb.append(" float t0 = d0 < 0.0 ? d0 / (d0 - d1) : 0.0;\n");
|
||||
sb.append(" float t1 = d1 < 0.0 ? d0 / (d0 - d1) : 1.0;\n");
|
||||
sb.append(" vec4 p0 = mix(c0, c1, t0);\n");
|
||||
sb.append(" vec4 p1 = mix(c0, c1, t1);\n\n");
|
||||
|
||||
sb.append(" // NDC positions\n");
|
||||
sb.append(" vec2 n0 = p0.xy / p0.w;\n");
|
||||
sb.append(" vec2 n1 = p1.xy / p1.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 pi = "p" + endpointIdx;
|
||||
final String ni = "n" + endpointIdx;
|
||||
final String ti = "t" + endpointIdx;
|
||||
|
||||
sb.append(" // Endpoint ").append(endpointIdx).append(' ').append(offsetExpr).append('\n');
|
||||
|
||||
sb.append(" v_Color = ").append(lerp("v_Color_gs", ti)).append(";\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append(" v_SpecularColor = ").append(lerp("v_SpecularColor_gs", ti)).append(";\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = ").append(lerp("v_TexCoord0_gs", ti)).append(";\n");
|
||||
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = ").append(lerp("v_TexCoord1_gs", ti)).append(";\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = ").append(lerp("v_TexCoord2_gs", ti)).append(";\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = ").append(lerp("v_TexCoord3_gs", ti)).append(";\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append(" v_FogCoord = ").append(lerp("v_FogCoord_gs", ti)).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("] = mix(gl_in[0].gl_ClipDistance[").append(i).append("], gl_in[1].gl_ClipDistance[").append(i).append("], ").append(ti).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(pi).append(".w, ").append(pi).append(".z, ").append(pi).append(".w);\n");
|
||||
sb.append(" EmitVertex();\n\n");
|
||||
}
|
||||
|
||||
private static String lerp(String input, String t) {
|
||||
return "mix(" + input + "[0], " + input + "[1], " + t + ")";
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,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 {
|
||||
|
||||
@@ -49,23 +49,19 @@ public class Program {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +76,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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -100,7 +100,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();
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
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 +26,116 @@ 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 int bufferEpoch = 0;
|
||||
private final Int2IntOpenHashMap bufferDeletedAt = new Int2IntOpenHashMap();
|
||||
|
||||
private void touch() {
|
||||
arrayVersion = ++versionClock;
|
||||
}
|
||||
|
||||
private boolean isBufferLive(int buffer, int sinceEpoch) {
|
||||
return bufferDeletedAt.get(buffer) <= sinceEpoch;
|
||||
}
|
||||
|
||||
public boolean isBufferLive(Attrib a) {
|
||||
return isBufferLive(a.vboId, a.epoch);
|
||||
}
|
||||
|
||||
public void init(int defaultVAO) {
|
||||
final VAOData data = new VAOData();
|
||||
@@ -45,6 +156,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 +167,7 @@ public final class VAOManager {
|
||||
}
|
||||
current.attribs = currentAttribs;
|
||||
current.clientSideEnabledCount = clientSideEnabledCount;
|
||||
current.arrayVersion = arrayVersion;
|
||||
}
|
||||
|
||||
VAOData data = vaoMap.get(vaoId);
|
||||
@@ -61,9 +177,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 +194,25 @@ public final class VAOManager {
|
||||
if (data == current) current = null;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (buffer == 0) return;
|
||||
bufferDeletedAt.put(buffer, ++bufferEpoch);
|
||||
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 +250,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 +270,307 @@ 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 bufferEpoch;
|
||||
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 isBufferLive(int buffer, ClientArrays saved) {
|
||||
return isBufferLive(buffer, saved.bufferEpoch);
|
||||
}
|
||||
|
||||
public void saveClientArrays(ClientArrays out) {
|
||||
out.arrayVersion = arrayVersion;
|
||||
out.bufferEpoch = 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) {
|
||||
@@ -286,7 +644,11 @@ 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);
|
||||
@@ -298,9 +660,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 +673,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() {
|
||||
|
||||
@@ -46,8 +46,8 @@ 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;
|
||||
@@ -103,8 +103,8 @@ 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 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,7 +226,7 @@ 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);
|
||||
|
||||
@@ -238,14 +238,14 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
if (glCtx.wideLineEmulationActive) {
|
||||
bits |= (1L << BIT_WIDE_LINE);
|
||||
}
|
||||
|
||||
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 +277,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 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(), lineQuads(), instancing());
|
||||
}
|
||||
}
|
||||
@@ -34,28 +34,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 +66,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 +87,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");
|
||||
}
|
||||
|
||||
@@ -151,15 +141,59 @@ 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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
sb.append(" int lineCorner = gl_VertexID % 6;\n");
|
||||
sb.append(" bool lineStart = lineCorner == 0 || lineCorner == 1 || lineCorner == 3;\n");
|
||||
sb.append(" vec4 lineOther = u_MVPMatrix * vec4(a_LineOtherEnd, 1.0);\n");
|
||||
sb.append(" vec4 lineC0 = lineStart ? gl_Position : lineOther;\n");
|
||||
sb.append(" vec4 lineC1 = lineStart ? lineOther : gl_Position;\n");
|
||||
sb.append(" vec4 lineCenter = lineC0;\n");
|
||||
sb.append(" float lineT = 0.0;\n");
|
||||
sb.append(" float lineD0 = lineC0.z + lineC0.w;\n");
|
||||
sb.append(" float lineD1 = lineC1.z + lineC1.w;\n");
|
||||
sb.append(" if (lineD0 < 0.0 && lineD1 < 0.0) {\n");
|
||||
sb.append(" gl_Position = vec4(0.0, 0.0, 2.0, 1.0);\n");
|
||||
sb.append(" } else {\n");
|
||||
sb.append(" float lineT0 = lineD0 < 0.0 ? lineD0 / (lineD0 - lineD1) : 0.0;\n");
|
||||
sb.append(" float lineT1 = lineD1 < 0.0 ? lineD0 / (lineD0 - lineD1) : 1.0;\n");
|
||||
sb.append(" lineT = lineStart ? lineT0 : lineT1;\n");
|
||||
sb.append(" vec4 lineP0 = mix(lineC0, lineC1, lineT0);\n");
|
||||
sb.append(" vec4 lineP1 = mix(lineC0, lineC1, lineT1);\n");
|
||||
sb.append(" vec2 lineN0 = lineP0.xy / lineP0.w;\n");
|
||||
sb.append(" vec2 lineN1 = lineP1.xy / lineP1.w;\n");
|
||||
sb.append(" vec2 linePixel = 2.0 / u_ViewportSize;\n");
|
||||
sb.append(" float lineHalfWidth = 0.5 * u_LineWidth;\n");
|
||||
sb.append(" vec2 lineOffset;\n");
|
||||
sb.append(" vec2 lineShift;\n");
|
||||
sb.append(" vec2 lineSpan = abs(lineN1 - lineN0) * u_ViewportSize;\n");
|
||||
sb.append(" if (lineSpan.x > lineSpan.y) {\n");
|
||||
sb.append(" lineOffset = vec2(0.0, lineHalfWidth * linePixel.y);\n");
|
||||
sb.append(" lineShift = vec2(lineN0.x < lineN1.x ? -0.5 : 0.5, -0.125) * linePixel;\n");
|
||||
sb.append(" } else {\n");
|
||||
sb.append(" lineOffset = vec2(lineHalfWidth * linePixel.x, 0.0);\n");
|
||||
sb.append(" lineShift = vec2(0.125, lineN0.y < lineN1.y ? -0.5 : 0.5) * linePixel;\n");
|
||||
sb.append(" }\n");
|
||||
sb.append(" vec4 lineP = lineStart ? lineP0 : lineP1;\n");
|
||||
sb.append(" vec2 lineN = lineStart ? lineN0 : lineN1;\n");
|
||||
sb.append(" gl_Position = vec4((lineN + (lineCorner % 2 == 0 ? lineOffset : -lineOffset) + lineShift) * lineP.w, lineP.z, lineP.w);\n");
|
||||
sb.append(" }\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 +414,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 = fogDistance(key, "eyePos");
|
||||
final String fog = key.lineQuads() ? acrossLine(own, fogDistance(key, "lineEyeOther")) : own;
|
||||
sb.append(" v_FogCoord = ").append(fog).append(";\n");
|
||||
}
|
||||
|
||||
// Mesa ffvertex_prog.c build_fog(): FDM_EYE_RADIAL/EYE_PLANE/EYE_PLANE_ABS
|
||||
private static String fogDistance(VertexKey key, String eye) {
|
||||
return switch (key.fogDistanceMode()) {
|
||||
case 0 -> "length(" + eye + ".xyz)"; // FDM_EYE_RADIAL: Euclidean distance
|
||||
case 1 -> eye + ".z"; // FDM_EYE_PLANE: raw Z (can be negative, clamped by fog factor)
|
||||
default -> "abs(" + eye + ".z)"; // FDM_EYE_PLANE_ABS: absolute Z
|
||||
};
|
||||
}
|
||||
|
||||
private static void emitClipDistances(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Clip distances\n");
|
||||
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;
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = ").append(distance).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
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)";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface LineDrawHandler {
|
||||
|
||||
boolean bindLineProgram();
|
||||
|
||||
void restoreProgram();
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.LineQuads;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -55,9 +58,20 @@ public final class DisplayListVBO {
|
||||
private final int count;
|
||||
private final int vertexFlags;
|
||||
private int pendingExtVbo = 0;
|
||||
private IVertexArrayObject lineQuads;
|
||||
private int lineQuadStart;
|
||||
private int lineQuadCount;
|
||||
private int lineQuadFlags;
|
||||
|
||||
void setPendingExtVbo(int extVbo) { this.pendingExtVbo = extVbo; }
|
||||
|
||||
void setLineQuads(IVertexArrayObject quads, int start, int count, int flags) {
|
||||
this.lineQuads = quads;
|
||||
this.lineQuadStart = start;
|
||||
this.lineQuadCount = count;
|
||||
this.lineQuadFlags = flags;
|
||||
}
|
||||
|
||||
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count, int vertexFlags) {
|
||||
this.vao = vao;
|
||||
this.drawMode = drawMode;
|
||||
@@ -80,10 +94,15 @@ public final class DisplayListVBO {
|
||||
|
||||
public void delete() {
|
||||
vao.delete();
|
||||
if (lineQuads != null) lineQuads.delete();
|
||||
}
|
||||
|
||||
public void render() {
|
||||
if (vao == null) return;
|
||||
if (lineQuads != null && GLStateManager.ffpWidensLineQuads()) {
|
||||
renderLineQuads();
|
||||
return;
|
||||
}
|
||||
vao.bind();
|
||||
if (pendingExtVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, pendingExtVbo);
|
||||
@@ -102,6 +121,22 @@ public final class DisplayListVBO {
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
private void renderLineQuads() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
lineQuads.bind();
|
||||
VAOManager.setCurrentVertexFlags(lineQuadFlags);
|
||||
final boolean culled = LineQuads.disableCulling();
|
||||
glCtx.lineQuadsActive = true;
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
lineQuads.draw(GL11.GL_TRIANGLES, lineQuadStart, lineQuadCount);
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
glCtx.lineQuadsActive = false;
|
||||
LineQuads.restoreCulling(culled);
|
||||
}
|
||||
lineQuads.unbind();
|
||||
}
|
||||
|
||||
public IVertexArrayObject getVAO() {
|
||||
return vao;
|
||||
|
||||
+40
@@ -13,6 +13,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.LineQuads;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -78,6 +79,8 @@ public final class DisplayListVBOBuilder {
|
||||
final IntArrayList extStarts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extCounts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extPrims = wantExt ? new IntArrayList() : null;
|
||||
// drawIndex, start, vertexCount, drawMode for each line draw
|
||||
final IntArrayList lineDraws = GLStateManager.wideLineEmulationEnabled ? new IntArrayList() : null;
|
||||
|
||||
for (FormatData data : formatData) {
|
||||
int vertexCount;
|
||||
@@ -123,6 +126,12 @@ public final class DisplayListVBOBuilder {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount, flags);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount, flags);
|
||||
if (lineDraws != null && LineQuads.segmentCount(data.drawMode, vertexCount) > 0) {
|
||||
lineDraws.add(data.drawIndex);
|
||||
lineDraws.add(start);
|
||||
lineDraws.add(vertexCount);
|
||||
lineDraws.add(data.drawMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +139,9 @@ public final class DisplayListVBOBuilder {
|
||||
}
|
||||
ByteBuffer bigBuffer = mergeAndDelete(allBuffers);
|
||||
vbo.allocate(bigBuffer, start);
|
||||
if (lineDraws != null && !lineDraws.isEmpty()) {
|
||||
attachLineQuads(vbos, format, bigBuffer, lineDraws);
|
||||
}
|
||||
|
||||
if (wantExt && !extDrawIndexes.isEmpty()) {
|
||||
final int extVbo = buildExt(extHandler, format, bigBuffer, extStarts, extCounts, extPrims);
|
||||
@@ -148,6 +160,34 @@ public final class DisplayListVBOBuilder {
|
||||
return new DisplayListVBO(vbos, extVbos.toIntArray());
|
||||
}
|
||||
|
||||
// The line width is only known at playback, so lines keep their own draw and also get quads to widen
|
||||
private static void attachLineQuads(DisplayListVBO.SubVBO[] vbos, VertexFormat format, ByteBuffer vertices, IntArrayList lineDraws) {
|
||||
final VertexFormat quadFormat = LineQuads.quadFormat(format);
|
||||
final int quadSize = quadFormat.getVertexSize();
|
||||
int quadVertices = 0;
|
||||
for (int i = 0; i < lineDraws.size(); i += 4) {
|
||||
quadVertices += LineQuads.segmentCount(lineDraws.getInt(i + 3), lineDraws.getInt(i + 2)) * LineQuads.VERTICES_PER_SEGMENT;
|
||||
}
|
||||
|
||||
final ByteBuffer quads = memAlloc(quadVertices * quadSize);
|
||||
final long src = memAddress0(vertices);
|
||||
final long dst = memAddress0(quads);
|
||||
final int stride = format.getVertexSize();
|
||||
final IVertexArrayObject quadVao = VAOManager.createStorageVAO(quadFormat, -1, 0);
|
||||
int quadStart = 0;
|
||||
for (int i = 0; i < lineDraws.size(); i += 4) {
|
||||
final int start = lineDraws.getInt(i + 1);
|
||||
final int count = lineDraws.getInt(i + 2);
|
||||
final int mode = lineDraws.getInt(i + 3);
|
||||
final int corners = LineQuads.segmentCount(mode, count) * LineQuads.VERTICES_PER_SEGMENT;
|
||||
LineQuads.expandWithOtherEnd(src + (long) start * stride, format, mode, count, dst + (long) quadStart * quadSize);
|
||||
vbos[lineDraws.getInt(i)].setLineQuads(quadVao, quadStart, corners, quadFormat.getVertexFlags());
|
||||
quadStart += corners;
|
||||
}
|
||||
quadVao.getVBO().allocate(quads, quadVertices);
|
||||
memFree(quads);
|
||||
}
|
||||
|
||||
private static int buildExt(ImmediateExtendedAttribHandler handler, VertexFormat format, ByteBuffer bigBuffer,
|
||||
IntArrayList extStarts, IntArrayList extCounts, IntArrayList extPrims) {
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
|
||||
+34
@@ -5,6 +5,8 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -13,6 +15,10 @@ import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
|
||||
|
||||
/**
|
||||
* Records immediate mode GL calls (glBegin/glEnd/glVertex) via {@link DirectTessellator}.
|
||||
* Used both during display list compilation and for live immediate mode emulation in core profile.
|
||||
@@ -213,6 +219,34 @@ public final class ImmediateModeRecorder {
|
||||
}
|
||||
}
|
||||
|
||||
public static DirectTessellator processDrawElementsFromAttribs(int mode, int count, int indexType, long indices) {
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(indices, indexType, count);
|
||||
if (minMax == -1L) return null;
|
||||
final int minVertex = (int) (minMax & 0xFFFFFFFFL);
|
||||
final int maxVertex = (int) (minMax >>> 32);
|
||||
final AttribSnapshot snap = AttribSnapshot.snapshot(minVertex, maxVertex - minVertex + 1);
|
||||
if (snap == null) return null;
|
||||
try {
|
||||
prepareReaders(snap);
|
||||
if (positionReaderIndex < 0) return null;
|
||||
begin(mode);
|
||||
final DirectTessellator t = tessellator();
|
||||
final int indexSize = GLTypes.sizeBytes(indexType);
|
||||
for (int i = 0; i < count; i++) {
|
||||
final long at = indices + (long) i * indexSize;
|
||||
final int vertex = switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> memGetByte(at) & 0xFF;
|
||||
case GL11.GL_UNSIGNED_SHORT -> memGetShort(at) & 0xFFFF;
|
||||
default -> memGetInt(at);
|
||||
};
|
||||
readVertexFromBuffer(t, vertex);
|
||||
}
|
||||
return end();
|
||||
} finally {
|
||||
snap.free();
|
||||
}
|
||||
}
|
||||
|
||||
private static void prepareReaders(AttribSnapshot snap) {
|
||||
nonPositionCount = 0;
|
||||
positionReaderIndex = -1;
|
||||
|
||||
@@ -143,6 +143,7 @@ public class GLSMRedirector {
|
||||
private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper";
|
||||
private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate";
|
||||
private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor";
|
||||
private static final String EXTFogCoord = "org/lwjgl/opengl/EXTFogCoord";
|
||||
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
|
||||
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
|
||||
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
|
||||
@@ -378,6 +379,8 @@ public class GLSMRedirector {
|
||||
.add("glEnableClientState")
|
||||
.add("glDisableClientState")
|
||||
.add("glInterleavedArrays")
|
||||
.add("glEdgeFlagPointer")
|
||||
.add("glGetPointer")
|
||||
.add("glPushClientAttrib")
|
||||
.add("glPopClientAttrib")
|
||||
.add("glViewport")
|
||||
@@ -408,11 +411,16 @@ public class GLSMRedirector {
|
||||
.add("glMultiDrawArrays")
|
||||
.add("glPointParameterf")
|
||||
.add("glPointParameteri")
|
||||
.add("glPointParameter")
|
||||
.add("glPointParameterfv", "glPointParameter")
|
||||
.add("glPointParameteriv", "glPointParameter")
|
||||
// Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM
|
||||
.add("glSecondaryColor3f")
|
||||
.add("glSecondaryColor3d")
|
||||
.add("glSecondaryColor3b")
|
||||
.add("glSecondaryColor3ub");
|
||||
.add("glSecondaryColor3ub")
|
||||
.add("glSecondaryColorPointer")
|
||||
.add("glFogCoordPointer");
|
||||
final var gl15 = RedirectMap.newMap()
|
||||
.add("glGenBuffers")
|
||||
.add("glBindBuffer")
|
||||
@@ -604,7 +612,9 @@ public class GLSMRedirector {
|
||||
.add("glSecondaryColor3bEXT", "glSecondaryColor3b")
|
||||
.add("glSecondaryColor3fEXT", "glSecondaryColor3f")
|
||||
.add("glSecondaryColor3dEXT", "glSecondaryColor3d")
|
||||
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub"));
|
||||
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub")
|
||||
.add("glSecondaryColorPointerEXT", "glSecondaryColorPointer"));
|
||||
methodRedirects.put(EXTFogCoord, RedirectMap.newMap().add("glFogCoordPointerEXT", "glFogCoordPointer"));
|
||||
|
||||
// ARB
|
||||
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
|
||||
|
||||
+126
-135
@@ -1,20 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.grammar.GLSLPreParser;
|
||||
import org.taumc.glsl.grammar.GLSLParserBaseListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -99,7 +96,7 @@ public final class GlslVulkanPreprocess {
|
||||
}
|
||||
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
final Metadata meta = collectEdits(source, root, glShaderType, debugName, separateReadOnlyImages, edits);
|
||||
final Metadata meta = collectEdits(new ShaderIndex(root, source), glShaderType, debugName, separateReadOnlyImages, edits);
|
||||
|
||||
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs());
|
||||
synchronized (CACHE) {
|
||||
@@ -111,129 +108,100 @@ public final class GlslVulkanPreprocess {
|
||||
|
||||
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless) {}
|
||||
|
||||
public static Metadata collectEdits(String source, GLSLParser.Translation_unitContext root, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
||||
public static Metadata collectEdits(ShaderIndex index, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
||||
final boolean isVertex = glShaderType == GL20.GL_VERTEX_SHADER;
|
||||
final Set<String> bools = new HashSet<>();
|
||||
final Set<String> explicitInputs = new HashSet<>();
|
||||
final Set<String> readOnlyImages = new HashSet<>();
|
||||
final boolean[] needsSamplerless = { false };
|
||||
boolean needsSamplerless = false;
|
||||
|
||||
final int[] maxExplicitLoc = { -1 };
|
||||
int maxExplicitLoc = -1;
|
||||
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
|
||||
|
||||
ParseTreeWalker.DEFAULT.walk(new GLSLParserBaseListener() {
|
||||
@Override
|
||||
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
|
||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||
if (single == null || single.fully_specified_type() == null) return;
|
||||
for (GLSLParser.DeclarationContext declaration : index.globalDeclarations()) {
|
||||
final GLSLParser.Init_declarator_listContext list = declaration.init_declarator_list();
|
||||
if (list == null) continue;
|
||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||
if (single == null || single.fully_specified_type() == null) continue;
|
||||
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
final GLSLParser.Type_qualifierContext tq = fst.type_qualifier();
|
||||
if (tq == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
final GLSLParser.Type_qualifierContext tq = fst.type_qualifier();
|
||||
if (tq == null) continue;
|
||||
|
||||
boolean hasUniform = false, hasIn = false, hasLocation = false, hasReadonly = false;
|
||||
int locValue = -1;
|
||||
int bindingValue = -1;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : tq.single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null) {
|
||||
final String s = stq.storage_qualifier().getText();
|
||||
if ("uniform".equals(s)) hasUniform = true;
|
||||
else if ("in".equals(s)) hasIn = true;
|
||||
else if ("readonly".equals(s)) hasReadonly = true;
|
||||
} else if (stq.layout_qualifier() != null) {
|
||||
for (GLSLParser.Layout_qualifier_idContext id : stq.layout_qualifier().layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() == null) continue;
|
||||
final String qualifier = id.IDENTIFIER().getText();
|
||||
if ("location".equals(qualifier)) {
|
||||
hasLocation = true;
|
||||
if (id.constant_expression() != null) {
|
||||
try { locValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
} else if ("binding".equals(qualifier) && id.constant_expression() != null) {
|
||||
try { bindingValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
boolean hasUniform = false, hasIn = false, hasLocation = false, hasReadonly = false;
|
||||
int locValue = -1;
|
||||
int bindingValue = -1;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : tq.single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null) {
|
||||
final String s = stq.storage_qualifier().getText();
|
||||
if ("uniform".equals(s)) hasUniform = true;
|
||||
else if ("in".equals(s)) hasIn = true;
|
||||
else if ("readonly".equals(s)) hasReadonly = true;
|
||||
} else if (stq.layout_qualifier() != null) {
|
||||
for (GLSLParser.Layout_qualifier_idContext id : stq.layout_qualifier().layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() == null) continue;
|
||||
final String qualifier = id.IDENTIFIER().getText();
|
||||
if ("location".equals(qualifier)) {
|
||||
hasLocation = true;
|
||||
if (id.constant_expression() != null) {
|
||||
try { locValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
} else if ("binding".equals(qualifier) && id.constant_expression() != null) {
|
||||
try { bindingValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean boolDecl = hasUniform && isBool(fst);
|
||||
final boolean explicitInputDecl = isVertex && hasIn && hasLocation;
|
||||
final boolean boolDecl = hasUniform && isBool(fst);
|
||||
final boolean explicitInputDecl = isVertex && hasIn && hasLocation;
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = ShaderIndex.declarators(list);
|
||||
for (GLSLParser.Typeless_declarationContext td : declarators) {
|
||||
handleDeclarator(td, hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
|
||||
if (single.typeless_declaration() != null) {
|
||||
handleDeclarator(single.typeless_declaration(), hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
|
||||
handleDeclarator(td, hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
|
||||
if (separateReadOnlyImages && hasUniform && fst.type_specifier() != null) {
|
||||
final String typeText = fst.type_specifier().getText();
|
||||
if (separateTextureType(typeText) != null) {
|
||||
needsSamplerless[0] = true;
|
||||
} else if (hasReadonly) {
|
||||
final String asTexture = separateTextureTypeForImage(typeText);
|
||||
if (asTexture != null) {
|
||||
needsSamplerless[0] = true;
|
||||
collectDeclaredNames(single, ctx, readOnlyImages);
|
||||
final String binding = bindingValue >= 0 ? "layout(binding = " + bindingValue + ") " : "";
|
||||
edits.add(new Edit(startIdx(fst), stopIdx(fst), binding + "uniform " + asTexture));
|
||||
if (separateReadOnlyImages && hasUniform && fst.type_specifier() != null) {
|
||||
final String typeText = fst.type_specifier().getText();
|
||||
if (separateTextureType(typeText) != null) {
|
||||
needsSamplerless = true;
|
||||
} else if (hasReadonly) {
|
||||
final String asTexture = separateTextureTypeForImage(typeText);
|
||||
if (asTexture != null) {
|
||||
needsSamplerless = true;
|
||||
for (GLSLParser.Typeless_declarationContext td : declarators) {
|
||||
if (td.IDENTIFIER() != null) readOnlyImages.add(td.IDENTIFIER().getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isVertex && hasIn) {
|
||||
if (hasLocation && locValue >= 0) {
|
||||
if (locValue > maxExplicitLoc[0]) maxExplicitLoc[0] = locValue;
|
||||
} else if (!hasLocation) {
|
||||
unlocatedVsInputStarts.add(ctx.getStart().getStartIndex());
|
||||
final String binding = bindingValue >= 0 ? "layout(binding = " + bindingValue + ") " : "";
|
||||
edits.add(new Edit(startIdx(fst), stopIdx(fst), binding + "uniform " + asTexture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPostfix_expression(GLSLParser.Postfix_expressionContext ctx) {
|
||||
if (readOnlyImages.isEmpty() || ctx.LEFT_PAREN() == null || ctx.RIGHT_PAREN() == null) return;
|
||||
final GLSLParser.Function_call_parametersContext params = ctx.function_call_parameters();
|
||||
if (params == null || params.assignment_expression() == null || params.assignment_expression().isEmpty()) return;
|
||||
if (!readOnlyImages.contains(params.assignment_expression(0).getText())) return;
|
||||
|
||||
final ParseTree callee = ctx.getChild(0);
|
||||
if (!(callee instanceof ParserRuleContext calleeCtx)) return;
|
||||
final String replacement = switch (calleeCtx.getText()) {
|
||||
case "imageLoad" -> "texelFetch";
|
||||
case "imageSize" -> "textureSize";
|
||||
default -> null;
|
||||
};
|
||||
if (replacement == null) return;
|
||||
|
||||
edits.add(new Edit(startIdx(calleeCtx), stopIdx(calleeCtx), replacement));
|
||||
final int rparen = ctx.RIGHT_PAREN().getSymbol().getStartIndex();
|
||||
edits.add(new Edit(rparen, rparen - 1, ", 0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTerminal(TerminalNode node) {
|
||||
final Token tok = node.getSymbol();
|
||||
if (tok.getType() != GLSLLexer.IDENTIFIER) return;
|
||||
final String text = tok.getText();
|
||||
switch (text) {
|
||||
case "sampler" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), SAMPLER_RENAMED));
|
||||
case "gl_VertexID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_VertexIndex"));
|
||||
case "gl_InstanceID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_InstanceIndex"));
|
||||
default -> {
|
||||
if (TARGET_RESERVED.contains(text)) edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), RESERVED_RENAMED_PREFIX + text));
|
||||
}
|
||||
if (isVertex && hasIn) {
|
||||
if (hasLocation && locValue >= 0) {
|
||||
if (locValue > maxExplicitLoc) maxExplicitLoc = locValue;
|
||||
} else if (!hasLocation) {
|
||||
unlocatedVsInputStarts.add(list.getStart().getStartIndex());
|
||||
}
|
||||
}
|
||||
}, root);
|
||||
|
||||
if (needsSamplerless[0] && source != null) {
|
||||
final int at = versionDirectiveEnd(source);
|
||||
if (at >= 0) edits.add(new Edit(at, at - 1, "\n" + SAMPLERLESS_EXTENSION));
|
||||
}
|
||||
|
||||
if (maxExplicitLoc[0] >= 0 && !unlocatedVsInputStarts.isEmpty()) {
|
||||
int next = maxExplicitLoc[0] + 1;
|
||||
if (!readOnlyImages.isEmpty()) {
|
||||
rewriteReadOnlyImageCalls(index.callsTo("imageLoad"), "texelFetch", readOnlyImages, edits);
|
||||
rewriteReadOnlyImageCalls(index.callsTo("imageSize"), "textureSize", readOnlyImages, edits);
|
||||
}
|
||||
if (needsSamplerless) {
|
||||
final int versionEnd = index.directives().versionEnd();
|
||||
if (versionEnd >= 0) edits.add(new Edit(versionEnd, versionEnd - 1, "\n" + SAMPLERLESS_EXTENSION));
|
||||
}
|
||||
|
||||
rename(index, "sampler", SAMPLER_RENAMED, edits);
|
||||
rename(index, "gl_VertexID", "gl_VertexIndex", edits);
|
||||
rename(index, "gl_InstanceID", "gl_InstanceIndex", edits);
|
||||
for (String reserved : TARGET_RESERVED) rename(index, reserved, RESERVED_RENAMED_PREFIX + reserved, edits);
|
||||
|
||||
if (maxExplicitLoc >= 0 && !unlocatedVsInputStarts.isEmpty()) {
|
||||
int next = maxExplicitLoc + 1;
|
||||
int dropped = 0;
|
||||
for (int start : unlocatedVsInputStarts) {
|
||||
if (next >= MAX_VS_INPUT_LOCATIONS) { dropped++; continue; }
|
||||
@@ -245,22 +213,65 @@ public final class GlslVulkanPreprocess {
|
||||
}
|
||||
}
|
||||
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless[0]);
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless);
|
||||
}
|
||||
|
||||
private static void rename(ShaderIndex index, String name, String replacement, List<Edit> edits) {
|
||||
for (Token tok : index.occurrences(name)) edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), replacement));
|
||||
}
|
||||
|
||||
static @Nullable String renameUserFunctions(String source) {
|
||||
final GLSLParser.Translation_unitContext root;
|
||||
try {
|
||||
root = GlslTransformUtils.parseFullQuiet(source);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object2IntOpenHashMap<String> prototypes = null;
|
||||
for (GLSLParser.External_declarationContext external : root.external_declaration()) {
|
||||
final GLSLParser.Function_definitionContext function = external.function_definition();
|
||||
final GLSLParser.DeclarationContext declaration = function == null ? external.declaration() : null;
|
||||
final GLSLParser.Function_prototypeContext prototype = function != null ? function.function_prototype() : declaration != null ? declaration.function_prototype() : null;
|
||||
final TerminalNode identifier = prototype == null ? null : prototype.IDENTIFIER();
|
||||
if (identifier == null) continue;
|
||||
final String name = identifier.getText();
|
||||
if ("main".equals(name)) continue;
|
||||
if (prototypes == null) prototypes = new Object2IntOpenHashMap<>();
|
||||
prototypes.addTo(name, 1);
|
||||
}
|
||||
if (prototypes == null) return null;
|
||||
|
||||
final ShaderIndex index = new ShaderIndex(root, source);
|
||||
final Set<String> inDirectives = index.directives().identifiers();
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
for (Object2IntMap.Entry<String> prototype : prototypes.object2IntEntrySet()) {
|
||||
final String name = prototype.getKey();
|
||||
if (inDirectives.contains(name)) continue;
|
||||
if (index.occurrences(name).size() != prototype.getIntValue() + index.callsTo(name).size()) continue;
|
||||
rename(index, name, RESERVED_RENAMED_PREFIX + name, edits);
|
||||
}
|
||||
return edits.isEmpty() ? null : applyEdits(source, edits);
|
||||
}
|
||||
|
||||
private static void rewriteReadOnlyImageCalls(List<GLSLParser.Postfix_expressionContext> calls, String replacement, Set<String> readOnlyImages, List<Edit> edits) {
|
||||
for (GLSLParser.Postfix_expressionContext call : calls) {
|
||||
if (call.RIGHT_PAREN() == null) continue;
|
||||
final GLSLParser.Function_call_parametersContext params = call.function_call_parameters();
|
||||
if (params == null || params.assignment_expression().isEmpty()) continue;
|
||||
if (!readOnlyImages.contains(params.assignment_expression(0).getText())) continue;
|
||||
|
||||
final ParserRuleContext callee = (ParserRuleContext) call.getChild(0);
|
||||
edits.add(new Edit(startIdx(callee), stopIdx(callee), replacement));
|
||||
final int rparen = call.RIGHT_PAREN().getSymbol().getStartIndex();
|
||||
edits.add(new Edit(rparen, rparen - 1, ", 0"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
synchronized (CACHE) { CACHE.clear(); }
|
||||
}
|
||||
|
||||
private static void collectDeclaredNames(GLSLParser.Single_declarationContext single, GLSLParser.Init_declarator_listContext ctx, Set<String> out) {
|
||||
if (single.typeless_declaration() != null && single.typeless_declaration().IDENTIFIER() != null) {
|
||||
out.add(single.typeless_declaration().IDENTIFIER().getText());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
|
||||
if (td.IDENTIFIER() != null) out.add(td.IDENTIFIER().getText());
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String separateTextureTypeForImage(String typeText) {
|
||||
final String dim = switch (typeText) {
|
||||
case "image1D", "iimage1D", "uimage1D" -> "1D";
|
||||
@@ -281,26 +292,6 @@ public final class GlslVulkanPreprocess {
|
||||
};
|
||||
}
|
||||
|
||||
private static int versionDirectiveEnd(String source) {
|
||||
final GLSLPreParser.Translation_unitContext pre;
|
||||
try {
|
||||
pre = GlslTransformUtils.parsePreQuiet(source);
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
final GLSLPreParser.Version_directiveContext ctx = firstVersionDirective(pre);
|
||||
return ctx == null || ctx.getStop() == null ? -1 : ctx.getStop().getStopIndex() + 1;
|
||||
}
|
||||
|
||||
private static GLSLPreParser.@Nullable Version_directiveContext firstVersionDirective(ParseTree node) {
|
||||
if (node instanceof GLSLPreParser.Version_directiveContext v) return v;
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
final GLSLPreParser.Version_directiveContext found = firstVersionDirective(node.getChild(i));
|
||||
if (found != null) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void handleDeclarator(GLSLParser.Typeless_declarationContext td, boolean hasUniform, boolean asBool, boolean asInput, Set<String> bools, Set<String> inputs, List<Edit> edits) {
|
||||
if (td.IDENTIFIER() == null) return;
|
||||
final String name = td.IDENTIFIER().getText();
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* What the source passes look up in a parsed shader.
|
||||
*/
|
||||
public final class ShaderIndex {
|
||||
|
||||
public record Directives(Set<String> identifiers, boolean hasConditionalOrMacro, int versionEnd) {
|
||||
private static final Directives NONE = new Directives(Set.of(), false, -1);
|
||||
}
|
||||
|
||||
private final GLSLParser.Translation_unitContext root;
|
||||
private final String source;
|
||||
private final Map<String, List<Token>> identifiers = new HashMap<>();
|
||||
private final Map<String, List<GLSLParser.Postfix_expressionContext>> calls = new HashMap<>();
|
||||
private final List<GLSLParser.DeclarationContext> globalDeclarations = new ArrayList<>();
|
||||
private @Nullable Directives directives;
|
||||
|
||||
public ShaderIndex(GLSLParser.Translation_unitContext root, String source) {
|
||||
this.root = root;
|
||||
this.source = source;
|
||||
collect(root);
|
||||
for (GLSLParser.External_declarationContext external : root.external_declaration()) {
|
||||
if (external.declaration() != null) globalDeclarations.add(external.declaration());
|
||||
}
|
||||
}
|
||||
|
||||
private void collect(ParseTree node) {
|
||||
if (node instanceof TerminalNode terminal) {
|
||||
final Token token = terminal.getSymbol();
|
||||
if (token.getType() == GLSLLexer.IDENTIFIER) identifiers.computeIfAbsent(token.getText(), k -> new ArrayList<>(2)).add(token);
|
||||
return;
|
||||
}
|
||||
if (node instanceof GLSLParser.Postfix_expressionContext p && p.LEFT_PAREN() != null) {
|
||||
calls.computeIfAbsent(calleeName(p), k -> new ArrayList<>(2)).add(p);
|
||||
}
|
||||
for (int i = 0, n = node.getChildCount(); i < n; i++) collect(node.getChild(i));
|
||||
}
|
||||
|
||||
public GLSLParser.Translation_unitContext root() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public String source() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public List<Token> occurrences(String name) {
|
||||
return identifiers.getOrDefault(name, List.of());
|
||||
}
|
||||
|
||||
public List<GLSLParser.Postfix_expressionContext> callsTo(String name) {
|
||||
return calls.getOrDefault(name, List.of());
|
||||
}
|
||||
|
||||
public List<GLSLParser.DeclarationContext> globalDeclarations() {
|
||||
return globalDeclarations;
|
||||
}
|
||||
|
||||
/** Lexes the source on first use. */
|
||||
public Directives directives() {
|
||||
if (directives == null) directives = scanDirectives(source);
|
||||
return directives;
|
||||
}
|
||||
|
||||
private static Directives scanDirectives(String source) {
|
||||
if (source.indexOf('#') < 0) return Directives.NONE;
|
||||
final GLSLLexer lexer = GlslTransformUtils.quietLexer(source);
|
||||
final Set<String> names = new HashSet<>();
|
||||
boolean conditionalOrMacro = false;
|
||||
int versionEnd = -1;
|
||||
boolean inVersion = false;
|
||||
for (Token token = lexer.nextToken(); token.getType() != Token.EOF; token = lexer.nextToken()) {
|
||||
final int type = token.getType();
|
||||
if (type == GLSLLexer.VERSION_DIRECTIVE && versionEnd < 0) {
|
||||
inVersion = true;
|
||||
versionEnd = token.getStopIndex() + 1;
|
||||
continue;
|
||||
}
|
||||
if (inVersion && (type == GLSLLexer.NUMBER || type == GLSLLexer.PROFILE)) {
|
||||
versionEnd = token.getStopIndex() + 1;
|
||||
continue;
|
||||
}
|
||||
inVersion = false;
|
||||
switch (type) {
|
||||
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
|
||||
case GLSLLexer.IF_DIRECTIVE, GLSLLexer.IFDEF_DIRECTIVE, GLSLLexer.IFNDEF_DIRECTIVE, GLSLLexer.DEFINE_DIRECTIVE -> conditionalOrMacro = true;
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
return new Directives(names, conditionalOrMacro, versionEnd);
|
||||
}
|
||||
|
||||
private static void collectIdentifiers(String text, Set<String> out) {
|
||||
if (text == null) return;
|
||||
final int len = text.length();
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
final char c = text.charAt(i);
|
||||
if (c != '_' && !Character.isLetter(c)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
int end = i + 1;
|
||||
while (end < len) {
|
||||
final char n = text.charAt(end);
|
||||
if (n != '_' && !Character.isLetterOrDigit(n)) break;
|
||||
end++;
|
||||
}
|
||||
out.add(text.substring(i, end));
|
||||
i = end;
|
||||
}
|
||||
}
|
||||
|
||||
public static String calleeName(GLSLParser.Postfix_expressionContext call) {
|
||||
final ParseTree callee = call.getChild(0);
|
||||
return callee instanceof ParserRuleContext ? callee.getText() : "";
|
||||
}
|
||||
|
||||
public static List<GLSLParser.Typeless_declarationContext> declarators(GLSLParser.Init_declarator_listContext list) {
|
||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||
final List<GLSLParser.Typeless_declarationContext> rest = list.typeless_declaration();
|
||||
if (single == null || single.typeless_declaration() == null) return rest;
|
||||
final List<GLSLParser.Typeless_declarationContext> all = new ArrayList<>(rest.size() + 1);
|
||||
all.add(single.typeless_declaration());
|
||||
all.addAll(rest);
|
||||
return all;
|
||||
}
|
||||
|
||||
public static boolean hasStorageQualifier(@Nullable GLSLParser.Type_qualifierContext qualifier, String keyword) {
|
||||
if (qualifier == null) return false;
|
||||
for (GLSLParser.Single_type_qualifierContext q : qualifier.single_type_qualifier()) {
|
||||
if (q.storage_qualifier() != null && keyword.equals(q.storage_qualifier().getText())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,16 @@ public final class SpirvCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
final Result result = compileUncached(cleanSrc, shaderKind, debugName, opts);
|
||||
Result result = compileUncached(cleanSrc, shaderKind, debugName, opts);
|
||||
if (result.spirv() == null) {
|
||||
final String renamed = GlslVulkanPreprocess.renameUserFunctions(cleanSrc);
|
||||
final Result retry = renamed == null ? null : compileUncached(renamed, shaderKind, debugName, opts);
|
||||
if (retry == null || retry.spirv() == null) {
|
||||
return new Result(null, result.error(), writeFailureDump(debugName, shaderKind, result.error(), cleanSrc));
|
||||
}
|
||||
LOGGER.info("'{}' compiled after renaming its functions; shaderc rejected the original: {}", debugName, result.error());
|
||||
result = retry;
|
||||
}
|
||||
final ByteBuffer spirv = result.spirv();
|
||||
if (spirv != null) {
|
||||
final byte[] heap = ShaderCacheIO.toHeap(spirv);
|
||||
@@ -121,8 +130,7 @@ public final class SpirvCompiler {
|
||||
final int status = Shaderc.shaderc_result_get_compilation_status(result);
|
||||
if (status != Shaderc.shaderc_compilation_status_success) {
|
||||
final String msg = Shaderc.shaderc_result_get_error_message(result);
|
||||
final Path dumpPath = writeFailureDump(debugName, shaderKind, msg, cleanSrc);
|
||||
return new Result(null, msg == null ? "(no error message)" : msg, dumpPath);
|
||||
return new Result(null, msg == null ? "(no error message)" : msg, null);
|
||||
}
|
||||
final ByteBuffer spirv = Shaderc.shaderc_result_get_bytes(result);
|
||||
if (spirv == null || spirv.remaining() == 0) {
|
||||
|
||||
+3
-21
@@ -47,14 +47,6 @@ public final class SpirvShaderTranslator {
|
||||
private SpirvShaderTranslator() {}
|
||||
|
||||
public static @Nullable String glslToGlslEs(String source, int glShaderType, String debugName) {
|
||||
return glslToGlslEsImpl(source, glShaderType, debugName, false);
|
||||
}
|
||||
|
||||
static @Nullable String glslToGlslEsRawForTest(String source, int glShaderType, String debugName) {
|
||||
return glslToGlslEsImpl(source, glShaderType, debugName, true);
|
||||
}
|
||||
|
||||
private static @Nullable String glslToGlslEsImpl(String source, int glShaderType, String debugName, boolean rawOutput) {
|
||||
final int shaderKind = shaderKindForGlType(glShaderType);
|
||||
if (shaderKind < 0) {
|
||||
LOGGER.warn("Unknown GL shader type 0x{} for '{}' - skipping SPIR-V translation", Integer.toHexString(glShaderType), debugName);
|
||||
@@ -67,14 +59,13 @@ public final class SpirvShaderTranslator {
|
||||
final SpirvCompiler.Result cr = SpirvCompiler.compile(pre.rewrittenSource(), shaderKind, debugName, SpirvCompiler.Options.vulkanRelaxed());
|
||||
if (cr.spirv() == null) {
|
||||
final String firstErr = cr.error() == null ? "(no error)" : cr.error().split("\\R", 2)[0];
|
||||
LOGGER.error("shaderc failed for '{}' (kind=0x{}): {} (dumped to {})",
|
||||
debugName, Integer.toHexString(shaderKind), firstErr, cr.dumpPath() == null ? "<dump failed>" : cr.dumpPath());
|
||||
LOGGER.error("shaderc failed for '{}' (kind=0x{}): {} (dumped to {})", debugName, Integer.toHexString(shaderKind), firstErr, cr.dumpPath() == null ? "<dump failed>" : cr.dumpPath());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
|
||||
if (emitted == null) return null;
|
||||
return rawOutput ? emitted : postProcessEsOutput(emitted, pre.boolUniforms());
|
||||
return postProcessEsOutput(emitted, pre.boolUniforms());
|
||||
} finally {
|
||||
memFree(cr.spirv());
|
||||
}
|
||||
@@ -163,16 +154,7 @@ public final class SpirvShaderTranslator {
|
||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||
if (single == null || single.typeless_declaration() == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
if (fst == null || fst.type_qualifier() == null) return;
|
||||
|
||||
boolean isUniform = false;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : fst.type_qualifier().single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null && "uniform".equals(stq.storage_qualifier().getText())) {
|
||||
isUniform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isUniform) return;
|
||||
if (fst == null || !ShaderIndex.hasStorageQualifier(fst.type_qualifier(), "uniform")) return;
|
||||
|
||||
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
|
||||
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
/**
|
||||
* Tracks GL point state: size.
|
||||
@@ -10,10 +11,24 @@ import lombok.Setter;
|
||||
@Getter @Setter
|
||||
public class PointState implements ISettableState<PointState> {
|
||||
private float size = 1.0f;
|
||||
private float minSize = 0.0f;
|
||||
private float maxSize = Float.NaN;
|
||||
private float fadeThreshold = 1.0f;
|
||||
private float attenuationConstant = 1.0f;
|
||||
private float attenuationLinear = 0.0f;
|
||||
private float attenuationQuadratic = 0.0f;
|
||||
private int spriteCoordOrigin = GL20.GL_UPPER_LEFT;
|
||||
|
||||
@Override
|
||||
public PointState set(PointState state) {
|
||||
this.size = state.size;
|
||||
this.minSize = state.minSize;
|
||||
this.maxSize = state.maxSize;
|
||||
this.fadeThreshold = state.fadeThreshold;
|
||||
this.attenuationConstant = state.attenuationConstant;
|
||||
this.attenuationLinear = state.attenuationLinear;
|
||||
this.attenuationQuadratic = state.attenuationQuadratic;
|
||||
this.spriteCoordOrigin = state.spriteCoordOrigin;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -21,7 +36,14 @@ public class PointState implements ISettableState<PointState> {
|
||||
public boolean sameAs(Object state) {
|
||||
if (this == state) return true;
|
||||
if (!(state instanceof PointState pointState)) return false;
|
||||
return Float.compare(size, pointState.size) == 0;
|
||||
return Float.compare(size, pointState.size) == 0
|
||||
&& Float.compare(minSize, pointState.minSize) == 0
|
||||
&& Float.compare(maxSize, pointState.maxSize) == 0
|
||||
&& Float.compare(fadeThreshold, pointState.fadeThreshold) == 0
|
||||
&& Float.compare(attenuationConstant, pointState.attenuationConstant) == 0
|
||||
&& Float.compare(attenuationLinear, pointState.attenuationLinear) == 0
|
||||
&& Float.compare(attenuationQuadratic, pointState.attenuationQuadratic) == 0
|
||||
&& spriteCoordOrigin == pointState.spriteCoordOrigin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,6 +9,7 @@ import lombok.Getter;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -19,6 +20,7 @@ public class TextureUnitArray {
|
||||
private final TextureUnitBooleanStateStack[] states;
|
||||
private final TextureUnitBooleanStateStack[] texture1DStates;
|
||||
private final TextureUnitBooleanStateStack[] texture3DStates;
|
||||
private final TextureUnitBooleanStateStack[] textureCubeMapStates;
|
||||
private final TextureUnitBooleanStateStack[] texGenSStates;
|
||||
private final TextureUnitBooleanStateStack[] texGenTStates;
|
||||
private final TextureUnitBooleanStateStack[] texGenRStates;
|
||||
@@ -36,6 +38,7 @@ public class TextureUnitArray {
|
||||
states = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
texture1DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
texture3DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
textureCubeMapStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
texGenSStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
texGenTStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
texGenRStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
@@ -49,6 +52,7 @@ public class TextureUnitArray {
|
||||
states[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_2D, i));
|
||||
texture1DStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_1D, i));
|
||||
texture3DStates[i] = track(new TextureUnitBooleanStateStack(GL12.GL_TEXTURE_3D, i));
|
||||
textureCubeMapStates[i] = track(new TextureUnitBooleanStateStack(GL13.GL_TEXTURE_CUBE_MAP, i));
|
||||
texGenSStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_S, i));
|
||||
texGenTStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_T, i));
|
||||
texGenRStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_R, i));
|
||||
@@ -90,6 +94,10 @@ public class TextureUnitArray {
|
||||
return texture3DStates[index];
|
||||
}
|
||||
|
||||
public TextureUnitBooleanStateStack getTextureCubeMapStates(int index) {
|
||||
return textureCubeMapStates[index];
|
||||
}
|
||||
|
||||
public TextureUnitBooleanStateStack getTexGenSStates(int index) {
|
||||
return texGenSStates[index];
|
||||
}
|
||||
@@ -127,6 +135,7 @@ public class TextureUnitArray {
|
||||
if (!states[i].sameAs(textureUnitArray.states[i])) return false;
|
||||
if (!texture1DStates[i].sameAs(textureUnitArray.texture1DStates[i])) return false;
|
||||
if (!texture3DStates[i].sameAs(textureUnitArray.texture3DStates[i])) return false;
|
||||
if (!textureCubeMapStates[i].sameAs(textureUnitArray.textureCubeMapStates[i])) return false;
|
||||
if (!texGenSStates[i].sameAs(textureUnitArray.texGenSStates[i])) return false;
|
||||
if (!texGenTStates[i].sameAs(textureUnitArray.texGenTStates[i])) return false;
|
||||
if (!texGenRStates[i].sameAs(textureUnitArray.texGenRStates[i])) return false;
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
|
||||
/**
|
||||
* Rewrites line primitives as quads for shaders that widen lines in the vertex stage.
|
||||
*/
|
||||
public final class LineQuads {
|
||||
|
||||
public static final int VERTICES_PER_SEGMENT = 6;
|
||||
|
||||
private static final VertexFormatElement NORMAL_SLOT = new VertexFormatElement(0, VertexFormatElement.Type.FLOAT,
|
||||
VertexFormatElement.Usage.NORMAL, 3, VertexFlags.NORMAL_BIT, DefaultVertexFormat.NORMAL_ELEMENT.getWriter());
|
||||
|
||||
private static final VertexFormat[] QUAD_FORMATS = new VertexFormat[VertexFlags.BITSET_SIZE];
|
||||
private static final int[][] SOURCE_OFFSETS = new int[VertexFlags.BITSET_SIZE][];
|
||||
|
||||
static {
|
||||
for (int flags = 0; flags < QUAD_FORMATS.length; flags++) {
|
||||
if ((flags & VertexFlags.NORMAL_BIT) == 0) continue;
|
||||
final VertexFormatElement[] elements = DefaultVertexFormat.ALL_FORMATS[flags].elementsArray.clone();
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
if (elements[i] == DefaultVertexFormat.NORMAL_ELEMENT) elements[i] = NORMAL_SLOT;
|
||||
}
|
||||
QUAD_FORMATS[flags] = new VertexFormat(elements);
|
||||
}
|
||||
for (int flags = 0; flags < SOURCE_OFFSETS.length; flags++) {
|
||||
final VertexFormatElement[] quadElements = QUAD_FORMATS[flags | VertexFlags.NORMAL_BIT].elementsArray;
|
||||
final int[] offsets = new int[quadElements.length];
|
||||
for (int i = 0; i < quadElements.length; i++) {
|
||||
offsets[i] = offsetOf(DefaultVertexFormat.ALL_FORMATS[flags], quadElements[i]);
|
||||
}
|
||||
SOURCE_OFFSETS[flags] = offsets;
|
||||
}
|
||||
}
|
||||
|
||||
private LineQuads() {}
|
||||
|
||||
public static boolean isLineMode(int drawMode) {
|
||||
return drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP;
|
||||
}
|
||||
|
||||
public static int segmentCount(int drawMode, int vertexCount) {
|
||||
return switch (drawMode) {
|
||||
case GL11.GL_LINES -> vertexCount / 2;
|
||||
case GL11.GL_LINE_STRIP -> Math.max(0, vertexCount - 1);
|
||||
case GL11.GL_LINE_LOOP -> vertexCount < 2 ? 0 : vertexCount;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean disableCulling() {
|
||||
final boolean wasEnabled = GLStateManager.getCullState().isEnabled();
|
||||
if (wasEnabled) {
|
||||
GLStateManager.disableCull();
|
||||
}
|
||||
return wasEnabled;
|
||||
}
|
||||
|
||||
public static void restoreCulling(boolean wasEnabled) {
|
||||
if (wasEnabled) {
|
||||
GLStateManager.enableCull();
|
||||
}
|
||||
}
|
||||
|
||||
public static VertexFormat quadFormat(VertexFormat lineFormat) {
|
||||
return QUAD_FORMATS[lineFormat.getVertexFlags() | VertexFlags.NORMAL_BIT];
|
||||
}
|
||||
|
||||
public static boolean isQuadFormat(VertexFormat format) {
|
||||
return QUAD_FORMATS[format.getVertexFlags()] == format;
|
||||
}
|
||||
|
||||
public static void expandWithDirection(long src, VertexFormat srcFormat, int drawMode, int vertexCount, long dst) {
|
||||
expand(src, srcFormat, drawMode, vertexCount, dst, false);
|
||||
}
|
||||
|
||||
public static void expandWithOtherEnd(long src, VertexFormat srcFormat, int drawMode, int vertexCount, long dst) {
|
||||
expand(src, srcFormat, drawMode, vertexCount, dst, true);
|
||||
}
|
||||
|
||||
private static void expand(long src, VertexFormat srcFormat, int drawMode, int vertexCount, long dst, boolean otherEnd) {
|
||||
final VertexFormatElement[] dstElements = quadFormat(srcFormat).elementsArray;
|
||||
final int[] srcOffsets = SOURCE_OFFSETS[srcFormat.getVertexFlags()];
|
||||
final int srcStride = srcFormat.getVertexSize();
|
||||
final int segments = segmentCount(drawMode, vertexCount);
|
||||
long out = dst;
|
||||
for (int s = 0; s < segments; s++) {
|
||||
final int a = drawMode == GL11.GL_LINES ? s * 2 : s;
|
||||
final int b = drawMode == GL11.GL_LINE_LOOP && s == vertexCount - 1 ? 0 : a + 1;
|
||||
final long srcA = src + (long) a * srcStride;
|
||||
final long srcB = src + (long) b * srcStride;
|
||||
// What A's and B's corners carry in the normal slot
|
||||
final float ax, ay, az, bx, by, bz;
|
||||
if (otherEnd) {
|
||||
ax = memGetFloat(srcB); ay = memGetFloat(srcB + 4); az = memGetFloat(srcB + 8);
|
||||
bx = memGetFloat(srcA); by = memGetFloat(srcA + 4); bz = memGetFloat(srcA + 8);
|
||||
} else {
|
||||
final float x = memGetFloat(srcB) - memGetFloat(srcA);
|
||||
final float y = memGetFloat(srcB + 4) - memGetFloat(srcA + 4);
|
||||
final float z = memGetFloat(srcB + 8) - memGetFloat(srcA + 8);
|
||||
final float lengthSquared = x * x + y * y + z * z;
|
||||
final float scale = lengthSquared == 0.0f ? 0.0f : 1.0f / (float) Math.sqrt(lengthSquared);
|
||||
ax = bx = x * scale;
|
||||
ay = by = y * scale;
|
||||
az = bz = z * scale;
|
||||
}
|
||||
|
||||
out = writeVertex(srcA, out, dstElements, srcOffsets, ax, ay, az);
|
||||
out = writeVertex(srcA, out, dstElements, srcOffsets, ax, ay, az);
|
||||
out = writeVertex(srcB, out, dstElements, srcOffsets, bx, by, bz);
|
||||
out = writeVertex(srcA, out, dstElements, srcOffsets, ax, ay, az);
|
||||
out = writeVertex(srcB, out, dstElements, srcOffsets, bx, by, bz);
|
||||
out = writeVertex(srcB, out, dstElements, srcOffsets, bx, by, bz);
|
||||
}
|
||||
}
|
||||
|
||||
private static long writeVertex(long src, long dst, VertexFormatElement[] dstElements, int[] srcOffsets, float x, float y, float z) {
|
||||
long out = dst;
|
||||
for (int i = 0; i < dstElements.length; i++) {
|
||||
final VertexFormatElement element = dstElements[i];
|
||||
final int size = element.getByteSize();
|
||||
if (element == NORMAL_SLOT) {
|
||||
memPutFloat(out, x);
|
||||
memPutFloat(out + 4, y);
|
||||
memPutFloat(out + 8, z);
|
||||
} else {
|
||||
memCopy(src + srcOffsets[i], out, size);
|
||||
}
|
||||
out += size;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int offsetOf(VertexFormat format, VertexFormatElement element) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement candidate : format.elementsArray) {
|
||||
if (candidate == element) {
|
||||
return offset;
|
||||
}
|
||||
offset += candidate.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,7 @@ public final class StreamingVaos {
|
||||
|
||||
final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
final int[] lineQuadPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] lineQuadOrphanVAOs = new int[FORMAT_COUNT];
|
||||
}
|
||||
+84
-8
@@ -15,6 +15,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.LineDrawHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -56,6 +57,10 @@ public class TessellatorStreamingDrawer {
|
||||
private static long repackAddress;
|
||||
private static int repackCapacity;
|
||||
|
||||
private static ByteBuffer lineQuadBuffer;
|
||||
private static long lineQuadAddress;
|
||||
private static int lineQuadCapacity;
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
// Tracy profiling counters
|
||||
@@ -315,12 +320,18 @@ public class TessellatorStreamingDrawer {
|
||||
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
|
||||
*/
|
||||
private static void uploadAndDraw(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
|
||||
if (LineQuads.isLineMode(drawMode) && drawLinesAsQuads(packed, format, drawMode, vertexCount)) {
|
||||
return;
|
||||
}
|
||||
Tracy.beginZone(Z_SDL_STREAM_DRAW);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos, flags, format);
|
||||
final boolean lineQuads = LineQuads.isQuadFormat(format);
|
||||
final int[] persistentVAOs = lineQuads ? vaos.lineQuadPersistentVAOs : vaos.persistentVAOs;
|
||||
final int[] orphanVAOs = lineQuads ? vaos.lineQuadOrphanVAOs : vaos.orphanVAOs;
|
||||
ensureVAO(persistentVAOs, orphanVAOs, flags, format);
|
||||
|
||||
if (Tracy.ENABLED) {
|
||||
streamedBytes += packed.remaining();
|
||||
@@ -330,15 +341,16 @@ public class TessellatorStreamingDrawer {
|
||||
int firstVertex = -1;
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize, drawMode == GL11.GL_QUADS ? 4 : 1);
|
||||
final int alignment = drawMode == GL11.GL_QUADS ? 4 : lineQuads ? LineQuads.VERTICES_PER_SEGMENT : 1;
|
||||
firstVertex = persistentBuffer.upload(packed, vertexSize, alignment);
|
||||
}
|
||||
|
||||
final boolean fromRing = firstVertex >= 0;
|
||||
if (fromRing) {
|
||||
GLStateManager.glBindVertexArray(vaos.persistentVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
|
||||
} else {
|
||||
if (Tracy.ENABLED && persistentBuffer != null) orphanFallbacks++;
|
||||
GLStateManager.glBindVertexArray(vaos.orphanVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(packed);
|
||||
firstVertex = 0;
|
||||
}
|
||||
@@ -357,6 +369,72 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean drawLinesAsQuads(ByteBuffer packed, VertexFormat format, int drawMode, int vertexCount) {
|
||||
final int segments = LineQuads.segmentCount(drawMode, vertexCount);
|
||||
if (segments == 0) {
|
||||
return false;
|
||||
}
|
||||
final LineDrawHandler handler = GLSMHooks.lineDrawHandler;
|
||||
if (handler != null && handler.bindLineProgram()) {
|
||||
try {
|
||||
drawLineQuads(packed, format, drawMode, vertexCount, segments, false);
|
||||
} finally {
|
||||
handler.restoreProgram();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (GLStateManager.ffpWidensLineQuads()) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
glCtx.lineQuadsActive = true;
|
||||
try {
|
||||
drawLineQuads(packed, format, drawMode, vertexCount, segments, true);
|
||||
} finally {
|
||||
glCtx.lineQuadsActive = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void drawLineQuads(ByteBuffer packed, VertexFormat format, int drawMode, int vertexCount, int segments, boolean otherEnd) {
|
||||
final VertexFormat quadFormat = LineQuads.quadFormat(format);
|
||||
final int quadVertices = segments * LineQuads.VERTICES_PER_SEGMENT;
|
||||
final int quadBytes = quadVertices * quadFormat.getVertexSize();
|
||||
ensureLineQuadCapacity(quadBytes);
|
||||
final long src = memAddress0(packed) + packed.position();
|
||||
if (otherEnd) {
|
||||
LineQuads.expandWithOtherEnd(src, format, drawMode, vertexCount, lineQuadAddress);
|
||||
} else {
|
||||
LineQuads.expandWithDirection(src, format, drawMode, vertexCount, lineQuadAddress);
|
||||
}
|
||||
lineQuadBuffer.position(0);
|
||||
lineQuadBuffer.limit(quadBytes);
|
||||
final boolean culled = LineQuads.disableCulling();
|
||||
try {
|
||||
uploadAndDraw(lineQuadBuffer, quadFormat.getVertexFlags(), quadFormat, quadFormat.getVertexSize(), GL11.GL_TRIANGLES, quadVertices);
|
||||
} finally {
|
||||
LineQuads.restoreCulling(culled);
|
||||
}
|
||||
if (lineQuadCapacity > 0x20000 && quadBytes < (lineQuadCapacity >> 3)) {
|
||||
memFree(lineQuadBuffer);
|
||||
lineQuadBuffer = null;
|
||||
lineQuadCapacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureLineQuadCapacity(int requiredBytes) {
|
||||
if (lineQuadBuffer != null && requiredBytes <= lineQuadCapacity) return;
|
||||
|
||||
int newCapacity = Math.max(0x4000, lineQuadCapacity);
|
||||
while (newCapacity < requiredBytes) {
|
||||
newCapacity *= 2;
|
||||
}
|
||||
if (lineQuadBuffer != null) memFree(lineQuadBuffer);
|
||||
lineQuadBuffer = memAlloc(newCapacity);
|
||||
lineQuadAddress = memAddress0(lineQuadBuffer);
|
||||
lineQuadCapacity = newCapacity;
|
||||
}
|
||||
|
||||
public static long ringWraps() {
|
||||
return persistentBuffer == null ? 0L : persistentBuffer.getWraps();
|
||||
}
|
||||
@@ -377,7 +455,7 @@ public class TessellatorStreamingDrawer {
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos, flags, format);
|
||||
ensureVAO(vaos.persistentVAOs, vaos.orphanVAOs, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format);
|
||||
|
||||
int firstVertex = -1;
|
||||
@@ -480,11 +558,9 @@ public class TessellatorStreamingDrawer {
|
||||
return repackBuffer;
|
||||
}
|
||||
|
||||
private static void ensureVAO(StreamingVaos vaos, int flags, VertexFormat format) {
|
||||
private static void ensureVAO(int[] persistentVAOs, int[] orphanVAOs, int flags, VertexFormat format) {
|
||||
init();
|
||||
if (orphanBuffers[flags] == null) orphanBuffers[flags] = new OrphanStreamingBuffer();
|
||||
final int[] orphanVAOs = vaos.orphanVAOs;
|
||||
final int[] persistentVAOs = vaos.persistentVAOs;
|
||||
|
||||
if (orphanVAOs[flags] == 0) {
|
||||
orphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -14,29 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@GLCoreTest
|
||||
class ArrayUniformUploadGLTest {
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static int link(String vs, String fs) {
|
||||
final int v = compile(GL20.GL_VERTEX_SHADER, vs);
|
||||
final int f = compile(GL20.GL_FRAGMENT_SHADER, fs);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, v);
|
||||
GL20.glAttachShader(p, f);
|
||||
GL20.glLinkProgram(p);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096));
|
||||
GL20.glDeleteShader(v);
|
||||
GL20.glDeleteShader(f);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static final String VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
|
||||
|
||||
@Test
|
||||
void glUniform2FloatBufferUploadsWholeArray() {
|
||||
// Runtime index keeps the whole array active so element locations stay queryable.
|
||||
@@ -45,7 +21,7 @@ class ArrayUniformUploadGLTest {
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(cosmicuvs[idx], cosmicuvs[(idx+1)%10]); }\n";
|
||||
final int p = link(VS, fs);
|
||||
final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
@@ -66,7 +42,7 @@ class ArrayUniformUploadGLTest {
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
GLStateManager.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +53,7 @@ class ArrayUniformUploadGLTest {
|
||||
+ "uniform int idx;\n"
|
||||
+ "out vec4 o;\n"
|
||||
+ "void main(){ o = vec4(float(vals[idx])); }\n";
|
||||
final int p = link(VS, fs);
|
||||
final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs);
|
||||
try {
|
||||
GL20.glUseProgram(p);
|
||||
|
||||
@@ -96,7 +72,7 @@ class ArrayUniformUploadGLTest {
|
||||
}
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
GL20.glDeleteProgram(p);
|
||||
GLStateManager.glDeleteProgram(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,30 +5,24 @@ import org.junit.jupiter.api.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CaptureGateTest {
|
||||
|
||||
@Test
|
||||
void gateOffWhenNothingAttached() {
|
||||
assertFalse(CaptureGate.gateValue(false, false, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gateOnWhenForced() {
|
||||
assertTrue(CaptureGate.gateValue(true, false, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gateOnWhenToolAttached() {
|
||||
assertTrue(CaptureGate.gateValue(false, true, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gateOnWhenTracyConnected() {
|
||||
assertTrue(CaptureGate.gateValue(false, false, true));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"nothing attached, false, false, false, false",
|
||||
"forced, true, false, false, true",
|
||||
"tool attached, false, true, false, true",
|
||||
"tracy connected, false, false, true, true"
|
||||
})
|
||||
void gateIsOnWhenAnySourceIs(String name, boolean forced, boolean toolAttached, boolean tracyConnected, boolean expected) {
|
||||
assertEquals(expected, CaptureGate.gateValue(forced, toolAttached, tracyConnected));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+200
-318
@@ -5,9 +5,12 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -17,66 +20,104 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class CompatShaderTransformerTest {
|
||||
|
||||
@Test
|
||||
void testModelViewMatrixReplacement() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("angelica_ModelViewMatrix"));
|
||||
assertTrue(result.contains("angelica_ProjectionMatrix"));
|
||||
assertFalse(result.contains("gl_ModelViewMatrix"));
|
||||
assertFalse(result.contains("gl_ProjectionMatrix"));
|
||||
assertTrue(result.contains("#version 330 core"));
|
||||
private static String[] strings(String... values) {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNormalMatrixReplacement() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("uniform mat3 angelica_NormalMatrix"));
|
||||
assertFalse(result.contains("gl_NormalMatrix"));
|
||||
private static void assertRewrite(boolean fragment, String src, String[] present, String[] absent) {
|
||||
final String result = CompatShaderTransformer.transform(src, fragment);
|
||||
for (final String s : present) {
|
||||
assertTrue(result.contains(s), () -> "expected '" + s + "' in\n\n" + result);
|
||||
}
|
||||
for (final String s : absent) {
|
||||
assertFalse(result.contains(s), () -> "'" + s + "' must not remain in\n\n" + result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModelViewProjectionMatrixExpression() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_ModelViewProjectionMatrix"));
|
||||
assertTrue(result.contains("angelica_ProjectionMatrix"));
|
||||
static Stream<Arguments> matrixBuiltins() {
|
||||
return Stream.of(
|
||||
Arguments.of("modelview and projection matrices", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
}
|
||||
""", strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix")),
|
||||
Arguments.of("normal matrix", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
}
|
||||
""", strings("uniform mat3 angelica_NormalMatrix"), strings("gl_NormalMatrix")),
|
||||
Arguments.of("modelview-projection matrix", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("angelica_ProjectionMatrix"), strings("gl_ModelViewProjectionMatrix")),
|
||||
Arguments.of("inverse matrices", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0);
|
||||
}
|
||||
""", strings(), strings("gl_ModelViewMatrixInverse", "gl_ProjectionMatrixInverse")),
|
||||
Arguments.of("texture matrix", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1;
|
||||
}
|
||||
""", strings(), strings("gl_TextureMatrix")),
|
||||
Arguments.of("several builtins in one fragment shader", true, """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
vec4 color = texture2D(tex, vec2(0.0));
|
||||
gl_FragColor = color;
|
||||
}
|
||||
""", strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "angelica_NormalMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix", "gl_NormalMatrix", "texture2D", "gl_FragColor")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTextureFunctionRenames() {
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
vec4 color = texture2D(tex, vec2(0.0));
|
||||
}
|
||||
""";
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("matrixBuiltins")
|
||||
void matrixBuiltinsAreRewritten(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
// texture2D -> texture
|
||||
assertFalse(result.contains("texture2D"));
|
||||
assertTrue(result.contains("texture"));
|
||||
static Stream<Arguments> textureFunctions() {
|
||||
return Stream.of(
|
||||
Arguments.of("texture2D", false, """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
vec4 color = texture2D(tex, vec2(0.0));
|
||||
}
|
||||
""", strings("texture"), strings("texture2D")),
|
||||
Arguments.of("shadow2D", true, """
|
||||
#version 120
|
||||
uniform sampler2DShadow shadowMap;
|
||||
void main() {
|
||||
float s = shadow2D(shadowMap, vec3(0.0)).r;
|
||||
gl_FragColor = vec4(s);
|
||||
}
|
||||
""", strings("texture"), strings("shadow2D(")),
|
||||
Arguments.of("texelFetch3D", true, """
|
||||
#version 120
|
||||
uniform sampler3D vol;
|
||||
void main() {
|
||||
vec4 v = texelFetch3D(vol, ivec3(0), 0);
|
||||
gl_FragColor = v;
|
||||
}
|
||||
""", strings("texelFetch"), strings("texelFetch3D")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("textureFunctions")
|
||||
void legacyTextureFunctionsAreRenamed(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,32 +136,26 @@ class CompatShaderTransformerTest {
|
||||
assertTrue(result.contains("layout"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionUpgradeTo330() {
|
||||
String src = """
|
||||
#version 110
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("#version 330 core"));
|
||||
assertFalse(result.contains("#version 110"));
|
||||
static Stream<Arguments> versionDirectives() {
|
||||
return Stream.of(
|
||||
Arguments.of("110 becomes 330 core", true, """
|
||||
#version 110
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0);
|
||||
}
|
||||
""", strings("#version 330 core"), strings("#version 110")),
|
||||
Arguments.of("compatibility profile is stripped", false, """
|
||||
#version 330 compatibility
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("#version 330 core"), strings("compatibility")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompatibilityProfileStripped() {
|
||||
String src = """
|
||||
#version 330 compatibility
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("#version 330 core"));
|
||||
assertFalse(result.contains("compatibility"));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("versionDirectives")
|
||||
void versionIsUpgradedToCore(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,60 +174,6 @@ class CompatShaderTransformerTest {
|
||||
assertEquals(src, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCombinedBuiltinsInSingleShader() {
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * pos;
|
||||
vec4 color = texture2D(tex, vec2(0.0));
|
||||
gl_FragColor = color;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("angelica_ModelViewMatrix"));
|
||||
assertTrue(result.contains("angelica_ProjectionMatrix"));
|
||||
assertTrue(result.contains("angelica_NormalMatrix"));
|
||||
assertFalse(result.contains("gl_ModelViewMatrix"));
|
||||
assertFalse(result.contains("gl_ProjectionMatrix"));
|
||||
assertFalse(result.contains("gl_NormalMatrix"));
|
||||
assertFalse(result.contains("texture2D"));
|
||||
assertFalse(result.contains("gl_FragColor"));
|
||||
assertTrue(result.contains("#version 330 core"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInverseMatrixReplacement() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_ModelViewMatrixInverse"));
|
||||
assertFalse(result.contains("gl_ProjectionMatrixInverse"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTextureMatrixReplacement() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_TextureMatrix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFogStructAccess() {
|
||||
String src = """
|
||||
@@ -211,34 +192,27 @@ class CompatShaderTransformerTest {
|
||||
assertTrue(result.contains("uniform vec4 angelica_FogColor"), "Fog color uniform should be injected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFogFragCoordVertex() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("out float angelica_FogFragCoord"), "Vertex shader should get 'out' declaration");
|
||||
assertTrue(result.contains("angelica_FogFragCoord"), "gl_FogFragCoord should be renamed");
|
||||
assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain");
|
||||
static Stream<Arguments> fogFragCoord() {
|
||||
return Stream.of(
|
||||
Arguments.of("vertex shader declares an out", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex);
|
||||
}
|
||||
""", strings("out float angelica_FogFragCoord"), strings("gl_FogFragCoord")),
|
||||
Arguments.of("fragment shader declares an in", true, """
|
||||
#version 120
|
||||
void main() {
|
||||
float f = gl_FogFragCoord;
|
||||
gl_FragColor = vec4(f);
|
||||
}
|
||||
""", strings("in float angelica_FogFragCoord"), strings("gl_FogFragCoord")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFogFragCoordFragment() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
float f = gl_FogFragCoord;
|
||||
gl_FragColor = vec4(f);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("in float angelica_FogFragCoord"), "Fragment shader should get 'in' declaration");
|
||||
assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("fogFragCoord")
|
||||
void fogFragCoordBecomesAVarying(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -255,23 +229,6 @@ class CompatShaderTransformerTest {
|
||||
assertFalse(result.contains("gl_FrontColor"), "gl_FrontColor should not remain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShadow2DRename() {
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler2DShadow shadowMap;
|
||||
void main() {
|
||||
float s = shadow2D(shadowMap, vec3(0.0)).r;
|
||||
gl_FragColor = vec4(s);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertFalse(result.contains("shadow2D("), "shadow2D should be renamed");
|
||||
// shadow2D -> texture with vec4 wrapping
|
||||
assertTrue(result.contains("texture"), "shadow2D should be renamed to texture");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReservedWordTextureAsVariable() {
|
||||
// Shader declares 'texture' as a sampler variable - this is valid in GLSL 120
|
||||
@@ -321,155 +278,81 @@ class CompatShaderTransformerTest {
|
||||
"Alpha test discard should be appended to main()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTexelFetch3DTriggersTransformation() {
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler3D vol;
|
||||
void main() {
|
||||
vec4 v = texelFetch3D(vol, ivec3(0), 0);
|
||||
gl_FragColor = v;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
// texelFetch3D should be renamed to texelFetch
|
||||
assertFalse(result.contains("texelFetch3D"), "texelFetch3D should be renamed");
|
||||
assertTrue(result.contains("texelFetch"), "texelFetch3D should become texelFetch");
|
||||
static Stream<Arguments> vertexAttributes() {
|
||||
return Stream.of(
|
||||
Arguments.of("gl_Vertex at location 0", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in vec4 angelica_Vertex", "location = 0"), strings("gl_Vertex")),
|
||||
Arguments.of("gl_Color at location 1", false, """
|
||||
#version 120
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
v_Color = gl_Color;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in vec4 angelica_Color", "location = 1"), strings("gl_Color")),
|
||||
Arguments.of("gl_MultiTexCoord0 at location 2", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in vec4 angelica_MultiTexCoord0", "location = 2"), strings("gl_MultiTexCoord0")),
|
||||
Arguments.of("gl_MultiTexCoord1 at location 3", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("angelica_MultiTexCoord1", "location = 3"), strings("gl_MultiTexCoord1")),
|
||||
Arguments.of("gl_Normal at location 4", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in vec3 angelica_Normal", "location = 4"), strings("gl_Normal")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVertexAttributeGlVertex() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_Vertex"), "gl_Vertex should be replaced");
|
||||
assertTrue(result.contains("angelica_Vertex"), "should become angelica_Vertex");
|
||||
assertTrue(result.contains("in vec4 angelica_Vertex"), "should have in declaration");
|
||||
assertTrue(result.contains("location = 0"), "should use attribute location 0");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("vertexAttributes")
|
||||
void vertexAttributeBuiltinsGetFixedLocations(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVertexAttributeGlColorInVertexShader() {
|
||||
// gl_Color in a vertex shader is the per-vertex color attribute, not the fragment varying
|
||||
String src = """
|
||||
#version 120
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
v_Color = gl_Color;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("angelica_Color"), "gl_Color -> angelica_Color (vertex attribute)");
|
||||
assertTrue(result.contains("in vec4 angelica_Color"), "should have in declaration");
|
||||
assertTrue(result.contains("location = 1"), "should use attribute location 1");
|
||||
assertFalse(result.contains("gl_Color"), "gl_Color should not remain");
|
||||
static Stream<Arguments> texCoordVaryings() {
|
||||
return Stream.of(
|
||||
Arguments.of("vertex shader declares an out", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("out vec4 angelica_TexCoord0"), strings("gl_TexCoord")),
|
||||
Arguments.of("fragment shader declares an in", true, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec2 uv = gl_TexCoord[0].xy;
|
||||
gl_FragColor = vec4(uv, 0.0, 1.0);
|
||||
}
|
||||
""", strings("in vec4 angelica_TexCoord0"), strings("gl_TexCoord")),
|
||||
Arguments.of("one varying per index", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_TexCoord[1] = gl_MultiTexCoord1;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("out vec4 angelica_TexCoord0", "out vec4 angelica_TexCoord1"), strings("gl_TexCoord")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVertexAttributeMultiTexCoord0() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_MultiTexCoord0"), "gl_MultiTexCoord0 should be replaced");
|
||||
assertTrue(result.contains("angelica_MultiTexCoord0"), "should become angelica_MultiTexCoord0");
|
||||
assertTrue(result.contains("in vec4 angelica_MultiTexCoord0"), "should have in declaration");
|
||||
assertTrue(result.contains("location = 2"), "should use attribute location 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVertexAttributeMultiTexCoord1() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_MultiTexCoord1"), "gl_MultiTexCoord1 should be replaced");
|
||||
assertTrue(result.contains("angelica_MultiTexCoord1"), "should become angelica_MultiTexCoord1");
|
||||
assertTrue(result.contains("location = 3"), "should use attribute location 3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVertexAttributeGlNormal() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_Normal"), "gl_Normal should be replaced");
|
||||
assertTrue(result.contains("angelica_Normal"), "should become angelica_Normal");
|
||||
assertTrue(result.contains("in vec3 angelica_Normal"), "should have in vec3 declaration");
|
||||
assertTrue(result.contains("location = 4"), "should use attribute location 4");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTexCoordVaryingVertexShader() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced");
|
||||
assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0");
|
||||
assertTrue(result.contains("out vec4 angelica_TexCoord0"), "vertex shader should have 'out' declaration");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTexCoordVaryingFragmentShader() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec2 uv = gl_TexCoord[0].xy;
|
||||
gl_FragColor = vec4(uv, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced");
|
||||
assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0");
|
||||
assertTrue(result.contains("in vec4 angelica_TexCoord0"), "fragment shader should have 'in' declaration");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTexCoordMultipleIndices() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
gl_TexCoord[1] = gl_MultiTexCoord1;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("out vec4 angelica_TexCoord0"), "should have TexCoord0 out");
|
||||
assertTrue(result.contains("out vec4 angelica_TexCoord1"), "should have TexCoord1 out");
|
||||
assertFalse(result.contains("gl_TexCoord"), "no gl_TexCoord should remain");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("texCoordVaryings")
|
||||
void texCoordBecomesPerIndexVaryings(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -533,7 +416,6 @@ class CompatShaderTransformerTest {
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_FrontLightModelProduct"), "gl_FrontLightModelProduct should be replaced");
|
||||
assertTrue(result.contains("uniform vec4 angelica_SceneColor"), "angelica_SceneColor uniform should be injected");
|
||||
assertTrue(result.contains("angelica_SceneColor"), "should reference angelica_SceneColor");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-6
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.assertMatrixEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -64,12 +65,6 @@ class DisplayListCompilationParityTest {
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static void assertMatrixEquals(FloatBuffer expected, FloatBuffer actual, String message) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expected.get(i), actual.get(i), 0.001f, message + " (element " + i + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
|
||||
@@ -3,11 +3,9 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.matrix;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -48,12 +46,6 @@ class DisplayListCompilationTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
|
||||
private static FloatBuffer matrix(int pname) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(pname, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_compileAndExecute_matrixModeWithTransforms() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
@@ -102,69 +94,4 @@ class DisplayListCompilationTest {
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rawGL_compile_immediateModeWithTransforms() {
|
||||
GL11.glLoadIdentity();
|
||||
GL11.glTranslatef(100.0f, 0.0f, 0.0f);
|
||||
|
||||
testList = GL11.glGenLists(1);
|
||||
GL11.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(50.0f, 25.0f, 0.0f);
|
||||
GL11.glScalef(0.5f, 0.5f, 1.0f);
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
GL11.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GL11.glVertex3f(16.0f, 0.0f, 0.0f);
|
||||
GL11.glVertex3f(16.0f, 16.0f, 0.0f);
|
||||
GL11.glVertex3f(0.0f, 16.0f, 0.0f);
|
||||
GL11.glEnd();
|
||||
|
||||
GL11.glPopMatrix();
|
||||
GL11.glEndList();
|
||||
|
||||
final FloatBuffer afterCompile = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterCompile);
|
||||
assertEquals(100.0f, afterCompile.get(12), 0.001f, "After GL_COMPILE: matrix unchanged");
|
||||
|
||||
GL11.glCallList(testList);
|
||||
|
||||
final FloatBuffer afterPlayback = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterPlayback);
|
||||
assertEquals(100.0f, afterPlayback.get(12), 0.001f, "After playback: base position preserved");
|
||||
assertEquals(1.0f, afterPlayback.get(0), 0.001f, "After playback: no scale (popped)");
|
||||
|
||||
GL11.glLoadIdentity();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rawGL_compileAndExecute_immediateModeExecutesImmediately() {
|
||||
GL11.glLoadIdentity();
|
||||
|
||||
testList = GL11.glGenLists(1);
|
||||
GL11.glNewList(testList, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
|
||||
GL11.glScalef(0.5f, 0.5f, 0.5f);
|
||||
|
||||
final FloatBuffer during = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, during);
|
||||
assertEquals(0.5f, during.get(0), 0.001f, "During COMPILE_AND_EXECUTE: scale applied");
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
GL11.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GL11.glVertex3f(1.0f, 0.0f, 0.0f);
|
||||
GL11.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GL11.glVertex3f(0.0f, 1.0f, 0.0f);
|
||||
GL11.glEnd();
|
||||
|
||||
GL11.glEndList();
|
||||
|
||||
final FloatBuffer after = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, after);
|
||||
assertEquals(0.5f, after.get(0), 0.001f, "After COMPILE_AND_EXECUTE: scale remains");
|
||||
|
||||
GL11.glLoadIdentity();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -39,25 +38,14 @@ class DisplayListIdAllocatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void zeroNeverReturned() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator(1);
|
||||
// Allocate enough IDs to fill the first word (32 IDs). None should be 0.
|
||||
for (int i = 0; i < 31; i++) {
|
||||
int id = alloc.allocRange(1);
|
||||
assertNotEquals(0, id, "ID 0 must never be returned");
|
||||
assertTrue(id > 0, "All returned IDs must be positive");
|
||||
void singleAllocsAreSequentialFromOne() {
|
||||
// Sequential from 1 across several words: 0 is never handed out and no id repeats
|
||||
final DisplayListIDAllocator alloc = new DisplayListIDAllocator(1);
|
||||
for (int expected = 1; expected <= 500; expected++) {
|
||||
assertEquals(expected, alloc.allocRange(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleAllocReturnsSequential() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
// First alloc should be 1 (0 is reserved)
|
||||
assertEquals(1, alloc.allocRange(1));
|
||||
assertEquals(2, alloc.allocRange(1));
|
||||
assertEquals(3, alloc.allocRange(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllocatedTracksState() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
@@ -159,9 +147,9 @@ class DisplayListIdAllocatorTest {
|
||||
@Test
|
||||
void freeZeroIsNoOp() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
// Should not throw
|
||||
alloc.free(0);
|
||||
alloc.free(-1);
|
||||
assertEquals(1, alloc.allocRange(1), "freeing the reserved id 0 must not make it allocatable");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -205,17 +193,6 @@ class DisplayListIdAllocatorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDoubleAllocation() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
// Allocate many IDs and verify uniqueness
|
||||
Set<Integer> seen = new HashSet<>();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
int id = alloc.allocRange(1);
|
||||
assertTrue(seen.add(id), "Duplicate ID returned: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rangeAfterSingleAllocs() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
|
||||
+1
-3
@@ -54,9 +54,7 @@ class DisplayListMatrixRestorationTest extends DisplayListTestFixture {
|
||||
}
|
||||
|
||||
private static float translation(int target) {
|
||||
FloatBuffer b = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(target, b);
|
||||
return b.get(12);
|
||||
return GlTestSupport.matrix(target).get(12);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
@@ -62,4 +69,58 @@ public final class DisplayListTestHelper {
|
||||
assertNotNull(compiled, "Display list should be compiled");
|
||||
return compiled;
|
||||
}
|
||||
|
||||
/** Calls {@code list} on an identity modelview and returns the resulting matrix. The caller's matrix is restored. */
|
||||
public static FloatBuffer modelviewAfterCall(int list) {
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(list);
|
||||
return GlTestSupport.modelview();
|
||||
} finally {
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertMatrixEquals(Matrix4f expected, FloatBuffer actual, String message) {
|
||||
final FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actual.get(i), 0.0001f, message + " (element " + i + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertModelviewAfterCall(int list, Matrix4f expected, String message) {
|
||||
assertMatrixEquals(expected, modelviewAfterCall(list), message);
|
||||
}
|
||||
|
||||
/** Immediate-mode quad from the origin to (size, size) at z = 0. */
|
||||
public static void immediateQuad(float size) {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(size, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(size, size, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, size, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
|
||||
/** Textured quad written straight into the vanilla Tessellator's raw buffer and handed to the draw interceptor. */
|
||||
public static void rawQuad(float size) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } };
|
||||
tess.rawBuffer = new int[4 * 8];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final int base = i * 8;
|
||||
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
|
||||
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
|
||||
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
|
||||
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
|
||||
}
|
||||
tess.isDrawing = true;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasTexture = true;
|
||||
tess.rawBufferIndex = 4 * 8;
|
||||
tess.vertexCount = 4;
|
||||
TessellatorManager.interceptDraw(tess);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -97,68 +101,23 @@ class FeedbackManagerTest {
|
||||
assertEquals(42.0f, buf.get(1), "Second token should be the pass-through value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTriangleFeedback_GL2D() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
// Identity MV/proj, viewport (0,0,800,600)
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
// Upload 3 vertices forming a triangle
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
// NDC vertices: (0,0,0), (1,0,0), (0,1,0)
|
||||
// With identity MVP and viewport 800x600:
|
||||
// NDC(0,0) → window(400, 300)
|
||||
// NDC(1,0) → window(800, 300)
|
||||
// NDC(0,1) → window(400, 600)
|
||||
final float[] verts = { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f };
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(9);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
// GL_2D: POLYGON_TOKEN, 3, x0, y0, x1, y1, x2, y2 = 8 floats
|
||||
assertEquals(8, count, "Triangle in GL_2D should produce 8 floats");
|
||||
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(3.0f, buf.get(1), 0.001f);
|
||||
|
||||
// Vertex 0: NDC(0,0) → window(400, 300)
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
|
||||
// Vertex 1: NDC(1,0) → window(800, 300)
|
||||
assertEquals(800.0f, buf.get(4), 0.01f);
|
||||
assertEquals(300.0f, buf.get(5), 0.01f);
|
||||
|
||||
// Vertex 2: NDC(0,1) → window(400, 600)
|
||||
assertEquals(400.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
// Identity MVP on an 800x600 viewport: NDC (0,0) -> window (400,300), (1,0) -> (800,300), (0,1) -> (400,600)
|
||||
static Stream<Arguments> primitives() {
|
||||
return Stream.of(
|
||||
Arguments.of("triangle", GL11.GL_TRIANGLES,
|
||||
new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f },
|
||||
new float[] { GL11.GL_POLYGON_TOKEN, 3f, 400f, 300f, 800f, 300f, 400f, 600f }),
|
||||
Arguments.of("polygon", GL11.GL_POLYGON,
|
||||
new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 0f },
|
||||
new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 800f, 300f, 800f, 600f, 400f, 600f }),
|
||||
Arguments.of("quad strip", GL11.GL_QUAD_STRIP,
|
||||
new float[] { 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f, 0f },
|
||||
new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 400f, 600f, 800f, 600f, 800f, 300f }));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPolygonFeedback_GL2D() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("primitives")
|
||||
void testPrimitiveFeedback_GL2D(String name, int mode, float[] verts, float[] expected) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
@@ -171,12 +130,6 @@ class FeedbackManagerTest {
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = {
|
||||
0f, 0f, 0f,
|
||||
1f, 0f, 0f,
|
||||
1f, 1f, 0f,
|
||||
0f, 1f, 0f
|
||||
};
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
@@ -186,72 +139,15 @@ class FeedbackManagerTest {
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_POLYGON, 0, 4);
|
||||
FeedbackManager.processDrawArrays(mode, 0, verts.length / 3);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
assertEquals(10, count, "Polygon in GL_2D should produce 10 floats");
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(4.0f, buf.get(1), 0.001f);
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
assertEquals(800.0f, buf.get(4), 0.01f);
|
||||
assertEquals(300.0f, buf.get(5), 0.01f);
|
||||
assertEquals(800.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
assertEquals(400.0f, buf.get(8), 0.01f);
|
||||
assertEquals(600.0f, buf.get(9), 0.01f);
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuadStripFeedback_GL2D() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = {
|
||||
0f, 0f, 0f,
|
||||
0f, 1f, 0f,
|
||||
1f, 0f, 0f,
|
||||
1f, 1f, 0f
|
||||
};
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_QUAD_STRIP, 0, 4);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
assertEquals(10, count, "Quad strip in GL_2D should produce 10 floats");
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(4.0f, buf.get(1), 0.001f);
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
assertEquals(400.0f, buf.get(4), 0.01f);
|
||||
assertEquals(600.0f, buf.get(5), 0.01f);
|
||||
assertEquals(800.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
assertEquals(800.0f, buf.get(8), 0.01f);
|
||||
assertEquals(300.0f, buf.get(9), 0.01f);
|
||||
// GL_2D: POLYGON_TOKEN, vertex count, then x, y per vertex
|
||||
assertEquals(expected.length, count, name + " float count");
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], buf.get(i), 0.01f, name + " feedback value " + i);
|
||||
}
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
-7
@@ -22,7 +22,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
|
||||
@BeforeEach
|
||||
void requireBufferBlending() {
|
||||
assumeTrue(RenderSystem.supportsBufferBlending());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -30,11 +29,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
|
||||
if (!RenderSystem.supportsBufferBlending()) return;
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void seedVanillaSeparate() {
|
||||
@@ -59,7 +53,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
|
||||
assertBufferBlend(buffer, VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
|
||||
}
|
||||
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+22
-38
@@ -3,11 +3,13 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
@@ -51,46 +53,28 @@ class GLSM_BlendUnknownState_UnitTest {
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "GL blend should be re-enabled after setUnknownState");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisableBufferBlendInvalidatesCache() {
|
||||
GLStateManager.enableBlend();
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
@ParameterizedTest(name = "global blend enabled={0}")
|
||||
@ValueSource(booleans = {true, false})
|
||||
void perBufferBlendToggleInvalidatesCache(boolean enabled) {
|
||||
setBlend(enabled);
|
||||
assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
|
||||
// Use RenderSystem which should call setUnknownState internally
|
||||
RenderSystem.disableBufferBlend(0);
|
||||
// Flips buffer 0 behind the cache and marks it unknown, so the repeated global call must reach the driver
|
||||
if (enabled) {
|
||||
RenderSystem.disableBufferBlend(0);
|
||||
} else {
|
||||
RenderSystem.enableBufferBlend(0);
|
||||
}
|
||||
|
||||
// Now re-enable via GLSM — should issue the GL call
|
||||
GLStateManager.enableBlend();
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be re-enabled after disableBufferBlend");
|
||||
setBlend(enabled);
|
||||
assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND), "global blend state must be re-issued after a per-buffer toggle");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEnableBufferBlendInvalidatesCache() {
|
||||
// Start with blend disabled
|
||||
GLStateManager.disableBlend();
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
|
||||
// Per-buffer enable bypasses cache
|
||||
RenderSystem.enableBufferBlend(0);
|
||||
|
||||
// Disable via GLSM — should issue the GL call despite cache saying disabled
|
||||
GLStateManager.disableBlend();
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be disabled after enableBufferBlend + disableBlend");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownStateClearedAfterSetEnabled() {
|
||||
GLStateManager.enableBlend();
|
||||
|
||||
// Mark unknown
|
||||
GLStateManager.getBlendMode().setUnknownState();
|
||||
|
||||
// First setEnabled clears the unknown flag
|
||||
GLStateManager.enableBlend();
|
||||
|
||||
// Second setEnabled should use normal caching (no redundant GL call)
|
||||
// Verify state is consistent
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertTrue(GLStateManager.getBlendMode().isEnabled());
|
||||
private static void setBlend(boolean enabled) {
|
||||
if (enabled) {
|
||||
GLStateManager.enableBlend();
|
||||
} else {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,672 @@
|
||||
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.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.CLIENT_ARRAY_CAPS;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
/** Conventional client arrays and the client attrib stack, emulated on a core-profile vertex array object. */
|
||||
@GLCoreTest
|
||||
public class GLSM_ClientArrays_GLTest {
|
||||
|
||||
private int savedVao;
|
||||
private int vao;
|
||||
private int vbo;
|
||||
|
||||
@BeforeEach
|
||||
void bindFreshVertexArray() {
|
||||
savedVao = GLStateManager.getBoundVAO();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void deleteVertexArray() {
|
||||
GLStateManager.glBindVertexArray(savedVao);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
}
|
||||
|
||||
private static boolean attribHasSize3(Usage usage) {
|
||||
final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(usage.getAttributeLocation());
|
||||
return attrib != null && attrib.size == 3;
|
||||
}
|
||||
|
||||
private static boolean positionArrayEnabled() {
|
||||
final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation());
|
||||
return position != null && position.enabled;
|
||||
}
|
||||
|
||||
private static boolean colorArrayEnabled() {
|
||||
return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0;
|
||||
}
|
||||
|
||||
private static int driverAttrib(int index, int pname) {
|
||||
final IntBuffer out = BufferUtils.createIntBuffer(16);
|
||||
GL20.glGetVertexAttrib(index, pname, out);
|
||||
return out.get(0);
|
||||
}
|
||||
|
||||
private static void assertClientArray(String name, int sizePname, int size, int typePname, int type, int stridePname, int stride, int bindingPname, int binding) {
|
||||
if (sizePname != 0) assertEquals(size, GLStateManager.glGetInteger(sizePname), name + " size");
|
||||
if (typePname != 0) assertEquals(type, GLStateManager.glGetInteger(typePname), name + " type");
|
||||
assertEquals(stride, GLStateManager.glGetInteger(stridePname), name + " stride");
|
||||
assertEquals(binding, GLStateManager.glGetInteger(bindingPname), name + " buffer binding");
|
||||
}
|
||||
|
||||
private static void assertVertexArray(String name, int size, int type, int stride, int binding) {
|
||||
assertClientArray(name, GL11.GL_VERTEX_ARRAY_SIZE, size, GL11.GL_VERTEX_ARRAY_TYPE, type, GL11.GL_VERTEX_ARRAY_STRIDE, stride, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING, binding);
|
||||
}
|
||||
|
||||
private static void assertTexCoordArray(String name, int size, int type, int stride, int binding) {
|
||||
assertClientArray(name, GL11.GL_TEXTURE_COORD_ARRAY_SIZE, size, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, type, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE, stride,
|
||||
GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, binding);
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexArrayStateRestoredOnlyWithVertexArrayBit() {
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags();
|
||||
assertFalse(positionArrayEnabled());
|
||||
assertFalse(colorArrayEnabled());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertTrue(positionArrayEnabled());
|
||||
assertTrue(colorArrayEnabled());
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(positionArrayEnabled(), "pop disables an array enabled inside the push");
|
||||
assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
assertFalse(colorArrayEnabled());
|
||||
assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags());
|
||||
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled(), "pop re-enables an array disabled inside the push");
|
||||
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled(), "a pixel-store push must not restore array enables");
|
||||
assertTrue(colorArrayEnabled());
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientActiveTextureRoundTrips() {
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
assertEquals(GLStateManager.MAX_TEXTURE_COORDS, GLStateManager.glGetInteger(GL20.GL_MAX_TEXTURE_COORDS));
|
||||
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
GLStateManager.glClientActiveTexture(0);
|
||||
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_UNITS);
|
||||
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS);
|
||||
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE), "units past GL_MAX_TEXTURE_COORDS are rejected");
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0L);
|
||||
final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(Usage.SECONDARY_UV.getAttributeLocation());
|
||||
assertNotNull(attrib);
|
||||
assertEquals(2, attrib.size);
|
||||
|
||||
assumeTrue(GLStateManager.MAX_TEXTURE_COORDS > 5);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE5);
|
||||
assertEquals(GL13.GL_TEXTURE5, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
GLStateManager.glTexCoordPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_TEXTURE_COORD_ARRAY_SIZE));
|
||||
assertFalse(attribHasSize3(Usage.PRIMARY_UV));
|
||||
assertFalse(attribHasSize3(Usage.SECONDARY_UV));
|
||||
} finally {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void conventionalPointerSurvivesGenericOverride() {
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 4L);
|
||||
assertEquals(3, VAOManager.get(position).size, "disabled generic pointer must not displace an enabled conventional array");
|
||||
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
|
||||
GLStateManager.glEnableVertexAttribArray(position);
|
||||
assertEquals(2, VAOManager.get(position).size, "enabled generic attribute wins");
|
||||
assertTrue(VAOManager.get(position).genericPointer);
|
||||
assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
assertEquals(8, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
assertEquals(3, VAOManager.get(position).size, "conventional pointer must come back when the generic attribute is disabled");
|
||||
assertEquals(12, VAOManager.get(position).stride);
|
||||
assertFalse(VAOManager.get(position).genericPointer);
|
||||
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
|
||||
assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED), "conventional array stays enabled at draw level");
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), "repoint must restore the array buffer binding");
|
||||
} finally {
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void conventionalPointerSetUnderEnabledGenericAppliesOnceGenericIsDisabled() {
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(position);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
assertEquals(2, VAOManager.get(position).size);
|
||||
assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
assertEquals(3, VAOManager.get(position).size);
|
||||
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
assertFalse(positionArrayEnabled());
|
||||
} finally {
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawVertexFlagsDoNotAnswerClientArrayEnables() {
|
||||
try {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
VAOManager.setCurrentVertexFlags(VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT | VertexFlags.TEXTURE_BIT);
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY));
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
|
||||
} finally {
|
||||
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientArrayPointerQueriesAreAnsweredFromCache() {
|
||||
final int vaoB = GLStateManager.glGenVertexArrays();
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
assertVertexArray("default vertex", 4, GL11.GL_FLOAT, 0, 0);
|
||||
assertClientArray("default normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_NORMAL_ARRAY_STRIDE, 0, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, 0);
|
||||
assertClientArray("default color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_COLOR_ARRAY_STRIDE, 0, GL15.GL_COLOR_ARRAY_BUFFER_BINDING, 0);
|
||||
assertClientArray("default secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT,
|
||||
GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 0, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, 0);
|
||||
assertClientArray("default fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 0,
|
||||
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, 0);
|
||||
assertClientArray("default index", 0, 0, GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_INDEX_ARRAY_STRIDE, 0, GL15.GL_INDEX_ARRAY_BUFFER_BINDING, 0);
|
||||
assertClientArray("default edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 0, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, 0);
|
||||
assertTexCoordArray("default texcoord", 4, GL11.GL_FLOAT, 0, 0);
|
||||
assertNull(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4));
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_SHORT, 20, 8L);
|
||||
GLStateManager.glNormalPointer(GL11.GL_BYTE, 16, 4L);
|
||||
GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L);
|
||||
GLStateManager.glSecondaryColorPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
|
||||
GLStateManager.glEdgeFlagPointer(7, 0L);
|
||||
GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 8, 0L);
|
||||
|
||||
assertVertexArray("vertex", 3, GL11.GL_SHORT, 20, vbo);
|
||||
assertClientArray("normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_BYTE, GL11.GL_NORMAL_ARRAY_STRIDE, 16, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, vbo);
|
||||
assertClientArray("color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_UNSIGNED_BYTE, GL11.GL_COLOR_ARRAY_STRIDE, 16,
|
||||
GL15.GL_COLOR_ARRAY_BUFFER_BINDING, vbo);
|
||||
assertClientArray("secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT,
|
||||
GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 12, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, vbo);
|
||||
assertClientArray("fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 4,
|
||||
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, vbo);
|
||||
assertClientArray("edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 7, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, vbo);
|
||||
assertTexCoordArray("texcoord unit 0", 2, GL11.GL_FLOAT, 8, vbo);
|
||||
|
||||
assertEquals(3.0f, GLStateManager.glGetFloat(GL11.GL_VERTEX_ARRAY_SIZE));
|
||||
GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE, ints);
|
||||
assertEquals(20, ints.get(0));
|
||||
assertEquals(8L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
|
||||
assertEquals(12L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4)));
|
||||
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS - 1);
|
||||
assertTexCoordArray("unset texcoord unit", 4, GL11.GL_FLOAT, 0, 0);
|
||||
GLStateManager.glTexCoordPointer(3, GL11.GL_SHORT, 6, 0L);
|
||||
assertTexCoordArray("last texcoord unit", 3, GL11.GL_SHORT, 6, vbo);
|
||||
assertFalse(attribHasSize3(Usage.PRIMARY_UV));
|
||||
assertFalse(attribHasSize3(Usage.SECONDARY_UV));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertTexCoordArray("texcoord unit 0 again", 2, GL11.GL_FLOAT, 8, vbo);
|
||||
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
assertVertexArray("other vao", 4, GL11.GL_FLOAT, 0, 0);
|
||||
final FloatBuffer client = BufferUtils.createFloatBuffer(12);
|
||||
GLStateManager.glVertexPointer(2, 0, client);
|
||||
assertVertexArray("client pointer", 2, GL11.GL_FLOAT, 0, 0);
|
||||
assertEquals(MemoryUtilities.memAddress(client), MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
assertVertexArray("first vao again", 3, GL11.GL_SHORT, 20, vbo);
|
||||
} finally {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteVertexArrays(vaoB);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void interleavedArraysSetClientStateWithoutAnActiveFfpProgram() {
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
assumeFalse(GLStateManager.ctx().ffp.isActive());
|
||||
|
||||
GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, 0L);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
|
||||
assertVertexArray("interleaved vertex", 3, GL11.GL_FLOAT, 16, vbo);
|
||||
assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE));
|
||||
} finally {
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
GLStateManager.glDisableClientState(cap);
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientVertexArrayBitRestoresPointersEnablesAndBindings() {
|
||||
final int vaoB = GLStateManager.glGenVertexArrays();
|
||||
final int vbo2 = GLStateManager.glGenBuffers();
|
||||
final int ebo = GLStateManager.glGenBuffers();
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
final int color = Usage.COLOR.getAttributeLocation();
|
||||
final int normal = Usage.NORMAL.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L);
|
||||
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2);
|
||||
GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 4L);
|
||||
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 8, 0L);
|
||||
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glEnableClientState(GL11.GL_NORMAL_ARRAY);
|
||||
GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(5);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertEquals(vao, GLStateManager.getBoundVAO());
|
||||
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(ebo, GLStateManager.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(ebo, GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
assertVertexArray("restored vertex", 3, GL11.GL_FLOAT, 12, vbo);
|
||||
assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE));
|
||||
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
|
||||
assertTrue(colorArrayEnabled());
|
||||
|
||||
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
|
||||
assertEquals(GL11.GL_FLOAT, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_TYPE));
|
||||
assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
|
||||
assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
assertEquals(GL11.GL_TRUE, driverAttrib(color, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
assertEquals(GL11.GL_FALSE, driverAttrib(normal, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glDeleteVertexArrays(vaoB);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(vao, GLStateManager.getBoundVAO(), "a deleted VAO must not be restored");
|
||||
assertVertexArray("after dead-vao pop", 3, GL11.GL_FLOAT, 12, vbo);
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
GLStateManager.glDisableClientState(cap);
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteVertexArrays(vaoB);
|
||||
GLStateManager.glDeleteBuffers(vbo2);
|
||||
GLStateManager.glDeleteBuffers(ebo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enabledGenericAttributeWinsWithoutAPointer() {
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
assertFalse(VAOManager.get(position).genericPointer);
|
||||
|
||||
GLStateManager.glEnableVertexAttribArray(position);
|
||||
assertTrue(VAOManager.get(position).genericPointer, "generic 0 supersedes position on its enable bit alone");
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
assertFalse(VAOManager.get(position).genericPointer);
|
||||
assertEquals(3, VAOManager.get(position).size);
|
||||
} finally {
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedBufferIsDetachedFromTheBoundVertexArray() {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 4L);
|
||||
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
|
||||
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
|
||||
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE), "offset and stride survive the detach");
|
||||
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shadowedPointerIsNotRepointedAtAReusedBufferName() {
|
||||
final int other = GLStateManager.glGenVertexArrays();
|
||||
final int conventional = GLStateManager.glGenBuffers();
|
||||
final int generic = GLStateManager.glGenBuffers();
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
int reused = 0;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, conventional);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, generic);
|
||||
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(position);
|
||||
|
||||
GLStateManager.glBindVertexArray(other);
|
||||
GLStateManager.glDeleteBuffers(conventional);
|
||||
reused = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, reused);
|
||||
assumeTrue(reused == conventional, "driver did not reuse the buffer name");
|
||||
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
assertEquals(generic, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING), "a pointer set before the delete must not attach to the new buffer");
|
||||
} finally {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glDisableVertexAttribArray(position);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteVertexArrays(other);
|
||||
GLStateManager.glDeleteBuffers(generic);
|
||||
if (reused != 0) GLStateManager.glDeleteBuffers(reused);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popKeepsCurrentArraysWhenTheSavedArrayBufferWasDeleted() {
|
||||
final int vbo2 = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2);
|
||||
GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 0L);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "array state is not restored once its array buffer is gone");
|
||||
assertEquals(vbo2, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(vbo2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientAttribStackIsNotCompiledIntoDisplayLists() {
|
||||
final int other = GLStateManager.glGenVertexArrays();
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLStateManager.glBindVertexArray(other);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(other, GLStateManager.getBoundVAO(), "replaying the list must not rebind the vertex array that was bound while compiling");
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glDeleteVertexArrays(other);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popRestoresDivisorAndStateWithoutAnAttributeLocation() {
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glVertexAttribDivisor(5, 3);
|
||||
assertEquals(3, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(GLStateManager.glIsEnabled(GL14.GL_FOG_COORDINATE_ARRAY));
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableVertexAttribArray(5);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glDisableVertexAttribArray(5);
|
||||
GLStateManager.glDisableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void interleavedArraysFromAClientBufferRegisterClientPointers() {
|
||||
final FloatBuffer data = BufferUtils.createFloatBuffer(64);
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
data.position(2);
|
||||
|
||||
GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, data);
|
||||
final long base = MemoryUtilities.memAddress(data);
|
||||
assertNotNull(VAOManager.get(position).clientPointer);
|
||||
assertEquals(base + 4, MemoryUtilities.memAddress(VAOManager.get(position).clientPointer));
|
||||
assertEquals(base + 4, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
|
||||
assertEquals(base, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4)));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(16, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE));
|
||||
assertTrue(GLStateManager.ctx().vaos.hasAnyClientSideEnabledAttrib());
|
||||
} finally {
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
GLStateManager.glDisableClientState(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableAndDisableRouteClientArrayCapsToClientState() {
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
GLStateManager.glEnable(cap);
|
||||
assertTrue(GLStateManager.glIsEnabled(cap), hex(cap));
|
||||
GLStateManager.glDisable(cap);
|
||||
assertFalse(GLStateManager.glIsEnabled(cap), hex(cap));
|
||||
}
|
||||
GLStateManager.glEnable(GL11.GL_VERTEX_ARRAY);
|
||||
assertTrue(positionArrayEnabled());
|
||||
GLStateManager.glDisable(GL11.GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
void illegalPointerArgumentsAreIgnored() {
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
|
||||
GLStateManager.glNormalPointer(GL11.GL_FLOAT, 12, 0L);
|
||||
|
||||
GLStateManager.glVertexPointer(1, GL11.GL_FLOAT, 4, 0L);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_UNSIGNED_BYTE, 3, 0L);
|
||||
GLStateManager.glNormalPointer(GL11.GL_UNSIGNED_BYTE, 3, 0L);
|
||||
assertVertexArray("vertex after illegal calls", 3, GL11.GL_FLOAT, 12, vbo);
|
||||
assertEquals(GL11.GL_FLOAT, GLStateManager.glGetInteger(GL11.GL_NORMAL_ARRAY_TYPE));
|
||||
|
||||
GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 4, 0L);
|
||||
assertEquals(GL12.GL_BGRA, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_SIZE));
|
||||
GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_FLOAT, 16, 0L);
|
||||
assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE));
|
||||
|
||||
GLStateManager.glVertexAttribPointer(VAOManager.MAX_ATTRIBS, 3, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(VAOManager.MAX_ATTRIBS);
|
||||
GLStateManager.glDisableVertexAttribArray(VAOManager.MAX_ATTRIBS);
|
||||
GLStateManager.glVertexAttribPointer(5, 5, GL11.GL_FLOAT, false, 0, 0L);
|
||||
assertNull(VAOManager.get(5));
|
||||
} finally {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientArrayEnablesAreAnsweredFromCache() {
|
||||
final int vaoB = GLStateManager.glGenVertexArrays();
|
||||
try {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
assertFalse(GLStateManager.glIsEnabled(cap), "default isEnabled " + hex(cap));
|
||||
assertEnableRoundTrip(cap, true);
|
||||
}
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY), "texcoord array enable is per client texture unit");
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY));
|
||||
GLStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_INDEX_ARRAY);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY), "array enables are per vertex array object");
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY));
|
||||
GLStateManager.glDisableClientState(GL11.GL_INDEX_ARRAY);
|
||||
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY), "an enabled generic attribute 0 is not GL_VERTEX_ARRAY");
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
} finally {
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
for (final int cap : CLIENT_ARRAY_CAPS) {
|
||||
GLStateManager.glDisableClientState(cap);
|
||||
}
|
||||
GLStateManager.glDeleteVertexArrays(vaoB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
assertFalse(GLStateManager.glIsEnabled(cap));
|
||||
} finally {
|
||||
GLStateManager.glDisable(cap);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +90,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
assertFalse(GLStateManager.glIsEnabled(cap));
|
||||
} finally {
|
||||
GLStateManager.glDisable(cap);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +103,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
assertSecondaryColor(0.25F, 0.5F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +118,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
assertSecondaryColor(0.125F, 0.25F, 0.375F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +131,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM));
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL14.GL_COLOR_SUM);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +151,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +170,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_DeferredTextureDelete_GLTest {
|
||||
|
||||
private static int immutableTexture() {
|
||||
assumeTrue(GLStateManager.capabilities.OpenGL42, "no glTexStorage2D on this context");
|
||||
final int tex = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R8, 4, 4);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return tex;
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingAnImmutableTextureRaisesNoError() {
|
||||
final int tex = immutableTexture();
|
||||
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingARescuedImmutableTextureRaisesNoError() {
|
||||
final int tex = immutableTexture();
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingA2DTextureWhileA3DTextureIsBoundLeavesNothingBoundTo2D() {
|
||||
final int t2d = GLStateManager.glGenTextures();
|
||||
final int t3d = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d);
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d);
|
||||
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "the deleted texture must not stay bound");
|
||||
assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-45
@@ -6,18 +6,12 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_DirtyTracking_UnitTest {
|
||||
|
||||
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
||||
@BeforeEach
|
||||
void pushTrackedState() {
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT | GL11.GL_FOG_BIT | GL11.GL_CURRENT_BIT);
|
||||
@@ -30,38 +24,31 @@ public class GLSM_DirtyTracking_UnitTest {
|
||||
|
||||
@Test
|
||||
void materialBumpsOnlyOnChange() {
|
||||
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F));
|
||||
final int gen = GLStateManager.getLightingGeneration();
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F));
|
||||
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical material must not bump lightingGeneration");
|
||||
|
||||
newf4b(0.9F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.9F, 0.2F, 0.3F, 0.4F));
|
||||
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "changed material must bump lightingGeneration once");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightModelBumpsOnlyOnChange() {
|
||||
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F));
|
||||
final int gen = GLStateManager.getLightingGeneration();
|
||||
|
||||
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F));
|
||||
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light model ambient must not bump");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightColorBumpsOnlyOnChange() {
|
||||
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F));
|
||||
final int gen = GLStateManager.getLightingGeneration();
|
||||
|
||||
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F));
|
||||
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light diffuse must not bump");
|
||||
}
|
||||
|
||||
@@ -72,17 +59,14 @@ public class GLSM_DirtyTracking_UnitTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
|
||||
final int gen = GLStateManager.getLightingGeneration();
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
|
||||
assertEquals(gen, GLStateManager.getLightingGeneration(), "same position under same modelview must not bump");
|
||||
|
||||
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
|
||||
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw position under changed modelview must bump");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
@@ -128,18 +112,15 @@ public class GLSM_DirtyTracking_UnitTest {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
|
||||
final int gen = GLStateManager.getLightingGeneration();
|
||||
|
||||
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
|
||||
assertEquals(gen, GLStateManager.getLightingGeneration(), "spot direction under translation-only must not bump");
|
||||
|
||||
GLStateManager.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
|
||||
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw spot direction under changed 3x3 must bump");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
@@ -158,16 +139,4 @@ public class GLSM_DirtyTracking_UnitTest {
|
||||
assertEquals(0.0F, diffuse.y, "green channel must be 0");
|
||||
assertEquals(1.0F, diffuse.z, "blue channel must be 1");
|
||||
}
|
||||
|
||||
static void newf4b(float x, float y, float z, float w) {
|
||||
f4b.clear();
|
||||
f4b.put(x).put(y).put(z).put(w);
|
||||
f4b.flip();
|
||||
}
|
||||
|
||||
static void newf3b(float x, float y, float z) {
|
||||
f3b.clear();
|
||||
f3b.put(x).put(y).put(z);
|
||||
f3b.flip();
|
||||
}
|
||||
}
|
||||
+14
-92
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
@@ -28,106 +30,26 @@ class GLSM_DisplayList_FormatOptimization_Test {
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPositionOnlyFormat() {
|
||||
// Create a display list with only position data (no color, texture, brightness, or normals)
|
||||
// Use GLStateManager for all calls to ensure they go through GLSM (test env doesn't redirect GL11)
|
||||
private static final float[][] CORNERS = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
|
||||
private static final float[][] COLORS = {{1, 1, 1, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}};
|
||||
|
||||
@ParameterizedTest(name = "normals={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void colorTextureFormatLeavesLastColorCurrent(boolean normals) {
|
||||
displayList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (normals) GLStateManager.glNormal3f(0, 0, 1);
|
||||
GLStateManager.glColor4f(COLORS[i][0], COLORS[i][1], COLORS[i][2], COLORS[i][3]);
|
||||
GLStateManager.glTexCoord2f(CORNERS[i][0], CORNERS[i][1]);
|
||||
GLStateManager.glVertex3f(CORNERS[i][0], CORNERS[i][1], 0);
|
||||
}
|
||||
GLStateManager.glEnd();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the list to ensure it doesn't crash (verifies compilation succeeded)
|
||||
GLStateManager.glCallList(displayList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPositionTextureFormat() {
|
||||
// Create a display list with position and texture coordinates
|
||||
displayList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glTexCoord2f(0, 0);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glTexCoord2f(1, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glTexCoord2f(1, 1);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glTexCoord2f(0, 1);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the list to ensure it doesn't crash (verifies compilation succeeded)
|
||||
GLStateManager.glCallList(displayList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPositionColorTextureFormat() {
|
||||
// Create a display list with position, color, and texture
|
||||
displayList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glTexCoord2f(0, 0);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glColor4f(1, 0, 0, 1);
|
||||
GLStateManager.glTexCoord2f(1, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glColor4f(0, 1, 0, 1);
|
||||
GLStateManager.glTexCoord2f(1, 1);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glColor4f(0, 0, 1, 1);
|
||||
GLStateManager.glTexCoord2f(0, 1);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the list and verify color is restored to last value (blue)
|
||||
GLStateManager.glCallList(displayList);
|
||||
verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f},
|
||||
"After playback, color should be blue (last color in immediate mode block)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFullFormat() {
|
||||
// Create a display list with all attributes: position, color, texture, and normal
|
||||
displayList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glNormal3f(0, 0, 1);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glTexCoord2f(0, 0);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glNormal3f(0, 0, 1);
|
||||
GLStateManager.glColor4f(1, 0, 0, 1);
|
||||
GLStateManager.glTexCoord2f(1, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glNormal3f(0, 0, 1);
|
||||
GLStateManager.glColor4f(0, 1, 0, 1);
|
||||
GLStateManager.glTexCoord2f(1, 1);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glNormal3f(0, 0, 1);
|
||||
GLStateManager.glColor4f(0, 0, 1, 1);
|
||||
GLStateManager.glTexCoord2f(0, 1);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the list and verify color is restored to last value (blue)
|
||||
GLStateManager.glCallList(displayList);
|
||||
verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f},
|
||||
"After playback, color should be blue (last color in immediate mode block)");
|
||||
|
||||
+27
-50
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -15,6 +17,23 @@ public class GLSM_DisplayList_PixelStore_Test {
|
||||
private static final int SRC = 8;
|
||||
private static final int SUB = 4;
|
||||
|
||||
enum Upload {
|
||||
TEX_SUB_IMAGE_2D {
|
||||
@Override
|
||||
void run(ByteBuffer src) {
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
}
|
||||
},
|
||||
TEX_IMAGE_2D {
|
||||
@Override
|
||||
void run(ByteBuffer src) {
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
}
|
||||
};
|
||||
|
||||
abstract void run(ByteBuffer src);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void resetUnpackState() {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
@@ -96,8 +115,9 @@ public class GLSM_DisplayList_PixelStore_Test {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stridedTexSubImage2DCompile() {
|
||||
@ParameterizedTest
|
||||
@EnumSource(Upload.class)
|
||||
void stridedUploadCompile(Upload upload) {
|
||||
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
|
||||
final ByteBuffer src = makeSource(SRC);
|
||||
|
||||
@@ -105,7 +125,7 @@ public class GLSM_DisplayList_PixelStore_Test {
|
||||
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
upload.run(src);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertEquals(0, red(readback(SUB), SUB, 0, 0), "GL_COMPILE must not execute the upload");
|
||||
@@ -146,58 +166,15 @@ public class GLSM_DisplayList_PixelStore_Test {
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stridedTexImage2DCompile() {
|
||||
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
|
||||
final ByteBuffer src = makeSource(SRC);
|
||||
|
||||
setStridedUnpack();
|
||||
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
setUnpack(0, 0, 0);
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
assertStridedSubImage(readback(SUB));
|
||||
assertUnpack(0, 0, 0);
|
||||
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultRecordedTexSubImage2DReplaysWithDefaults() {
|
||||
@ParameterizedTest
|
||||
@EnumSource(Upload.class)
|
||||
void defaultRecordedUploadReplaysWithDefaults(Upload upload) {
|
||||
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
|
||||
final ByteBuffer src = makeSource(SUB);
|
||||
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
setStridedUnpack();
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
assertTightSubImage(readback(SUB));
|
||||
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
|
||||
assertUnpack(SRC, 1, 2);
|
||||
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultRecordedTexImage2DReplaysWithDefaults() {
|
||||
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
|
||||
final ByteBuffer src = makeSource(SUB);
|
||||
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
|
||||
upload.run(src);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
+2
-30
@@ -1,14 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -27,34 +27,6 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
|
||||
private static void rawQuad(float size) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } };
|
||||
tess.rawBuffer = new int[4 * 8];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final int base = i * 8;
|
||||
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
|
||||
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
|
||||
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
|
||||
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
|
||||
}
|
||||
tess.isDrawing = true;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasTexture = true;
|
||||
tess.rawBufferIndex = 4 * 8;
|
||||
tess.vertexCount = 4;
|
||||
TessellatorManager.interceptDraw(tess);
|
||||
}
|
||||
|
||||
private static void immediateQuad(float size) {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(size, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(size, size, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, size, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pendingTransformIsEmittedBeforeARawDraw() {
|
||||
testList = GLStateManager.glGenLists(1);
|
||||
|
||||
+46
-357
@@ -1,19 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.assertMatrixEquals;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.modelviewAfterCall;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -70,12 +76,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
"No individual transform commands should remain in optimized list");
|
||||
|
||||
// Verify behavioral correctness: matrix state should match expected
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
Matrix4f expected = new Matrix4f()
|
||||
.translate(1.0f, 2.0f, 3.0f)
|
||||
@@ -84,15 +85,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
.translate(-0.5f, -0.5f, -0.5f)
|
||||
.rotate((float) Math.toRadians(30.0f), 1.0f, 0.0f, 0.0f);
|
||||
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch after collapsed transforms", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertMatrixEquals(expected, actualBuf, "mismatch after collapsed transforms");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,12 +114,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertEquals(1, popCount, "Should have exactly 1 PopMatrix");
|
||||
|
||||
// Verify behavioral correctness
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
// Expected: translate(10,0,0) then translate(0,3,0) = translate(10,3,0)
|
||||
// The inner push/pop isolates the (5,0,0) translate and rotation
|
||||
@@ -134,15 +122,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
.translate(10.0f, 0.0f, 0.0f)
|
||||
.translate(0.0f, 3.0f, 0.0f);
|
||||
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch - push/pop should isolate inner transforms", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertMatrixEquals(expected, actualBuf, "mismatch - push/pop should isolate inner transforms");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,12 +144,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify behavioral correctness
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
// Final state: translate(1,0,0) + translate(5,0,0) = translate(6,0,0)
|
||||
// Level 1 and 2 transforms are isolated by push/pop
|
||||
@@ -182,8 +157,6 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
|
||||
assertEquals(expectedBuf.get(12), actualBuf.get(12), 0.0001f,
|
||||
"X translation should be 6.0 (1+5, inner transforms isolated)");
|
||||
|
||||
GLStateManager.glPopMatrix(); // Balance the push from line 185
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -219,12 +192,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
"MultMatrix should be emitted BEFORE CallList (transform flush at barrier)");
|
||||
|
||||
// Verify behavioral correctness
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
// Build expected: translate(10,0,0) * rotate(90,Y) * translate(100,0,0) * translate(1,0,0)
|
||||
// After 90-degree Y rotation, X axis points into -Z, so nested translate goes along -Z
|
||||
@@ -234,15 +202,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
.translate(100.0f, 0.0f, 0.0f)
|
||||
.translate(1.0f, 0.0f, 0.0f);
|
||||
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch - nested list should see parent's transformed state", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertMatrixEquals(expected, actualBuf, "mismatch - nested list should see parent's transformed state");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -271,63 +231,33 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
"Identity-equivalent transforms should not emit MultMatrix");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransformBetweenDrawsCreatesSeparateBatches() {
|
||||
// This test verifies that draws with different transforms are not batched together
|
||||
// Note: Without actual Tessellator integration, we can only verify command structure
|
||||
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
// Simulate: Transform A, Draw, Transform B, Draw
|
||||
// The draws should NOT be in the same batch
|
||||
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f);
|
||||
// (Would have a draw here in real usage)
|
||||
GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f);
|
||||
// (Would have another draw here)
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify the transforms are accumulated correctly
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// Total translation should be 3.0 (1+2)
|
||||
assertEquals(3.0f, actualBuf.get(12), 0.0001f,
|
||||
"Consecutive translates should accumulate");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
static Stream<Arguments> discardedByLoadIdentity() {
|
||||
return Stream.of(
|
||||
Arguments.of("accumulated translate", (Runnable) () -> GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f)),
|
||||
Arguments.of("loaded matrix", (Runnable) () -> {
|
||||
final FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16);
|
||||
new Matrix4f().translate(50.0f, 60.0f, 70.0f).rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f).get(loadBuf);
|
||||
GLStateManager.glLoadMatrix(loadBuf);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadIdentityResetsAccumulation() {
|
||||
// LoadIdentity should reset accumulated transforms
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("discardedByLoadIdentity")
|
||||
void testLoadIdentityDiscardsEarlierTransforms(String name, Runnable earlier) {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f); // Big translate
|
||||
GLStateManager.glLoadIdentity(); // Reset
|
||||
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f); // New translate
|
||||
earlier.run();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify: final state should only have the second translate
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0 (LoadIdentity reset)");
|
||||
assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0 (LoadIdentity reset)");
|
||||
assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0 (LoadIdentity reset)");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -359,121 +289,14 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
"Translate + MultMatrix + Translate should collapse to 1 MultMatrix");
|
||||
|
||||
// Verify behavioral correctness
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
Matrix4f expected = new Matrix4f()
|
||||
.translate(1.0f, 0.0f, 0.0f)
|
||||
.rotate((float) Math.toRadians(90.0f), 0.0f, 1.0f, 0.0f)
|
||||
.translate(2.0f, 0.0f, 0.0f);
|
||||
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch for MultMatrix accumulation", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
// ==================== Batcher Flush Ordering Tests ====================
|
||||
|
||||
@Test
|
||||
void testDrawsBeforePushMatrixAreEmittedBeforePush() {
|
||||
// This test verifies that draws occurring before a PushMatrix are flushed
|
||||
// before the PushMatrix command, not delayed until later.
|
||||
// This was a bug where the batcher wasn't flushed at PushMatrix.
|
||||
|
||||
// Create a list with: Transform, (implicit draw would go here), PushMatrix, Transform, PopMatrix
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
// Outer transform
|
||||
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f);
|
||||
// Note: If there was a draw here, it should be emitted BEFORE PushMatrix
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(5.0f, 0.0f, 0.0f); // Inner transform
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
// Final transform after pop
|
||||
GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify the command structure
|
||||
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled, "Display list should be compiled");
|
||||
|
||||
IntList opcodes = compiled.getCommandOpcodes();
|
||||
|
||||
// Find indices of key commands
|
||||
int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX);
|
||||
int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX);
|
||||
|
||||
// Count MultMatrix commands before first PushMatrix
|
||||
int multMatrixBeforePushCount = 0;
|
||||
for (int i = 0; i < opcodes.size() && i < pushIndex; i++) {
|
||||
if (opcodes.get(i) == GLCommand.TRANSLATE) {
|
||||
multMatrixBeforePushCount++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(pushIndex >= 0, "Should have PushMatrix command");
|
||||
assertTrue(popIndex >= 0, "Should have PopMatrix command");
|
||||
assertTrue(pushIndex < popIndex, "PushMatrix should come before PopMatrix");
|
||||
|
||||
// The outer translate(1,0,0) should be emitted as MultMatrix before PushMatrix
|
||||
assertTrue(multMatrixBeforePushCount >= 1,
|
||||
"Transform before PushMatrix should be emitted before the Push");
|
||||
|
||||
// Verify behavioral correctness: final state should be translate(1,0,0) + translate(2,0,0) = translate(3,0,0)
|
||||
// The inner translate(5,0,0) is isolated by push/pop
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
assertEquals(3.0f, actualBuf.get(12), 0.0001f,
|
||||
"X translation should be 3.0 (1+2, inner transform isolated by push/pop)");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDrawsBeforePopMatrixAreEmittedBeforePop() {
|
||||
// Verify draws inside a push/pop block are emitted before the pop
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f);
|
||||
// Draws here should be emitted before PopMatrix
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
IntList opcodes = compiled.getCommandOpcodes();
|
||||
|
||||
int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX);
|
||||
int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX);
|
||||
|
||||
assertTrue(pushIndex >= 0, "Should have PushMatrix");
|
||||
assertTrue(popIndex >= 0, "Should have PopMatrix");
|
||||
assertTrue(pushIndex < popIndex, "Push should come before Pop");
|
||||
|
||||
// The inner translate should be emitted between push and pop (or collapsed to residual)
|
||||
// At minimum, the structure should be valid
|
||||
assertTrue(popIndex > pushIndex, "PopMatrix should follow PushMatrix");
|
||||
assertMatrixEquals(expected, actualBuf, "mismatch for MultMatrix accumulation");
|
||||
}
|
||||
|
||||
// ==================== LoadMatrix Tests ====================
|
||||
@@ -509,22 +332,9 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
"Should have exactly 1 LoadMatrix command (can't be absorbed)");
|
||||
|
||||
// Verify behavioral correctness: matrix should equal the loaded matrix
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
loadedMatrix.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] should match loaded matrix (not multiplied)", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertMatrixEquals(loadedMatrix, actualBuf, "should match loaded matrix (not multiplied)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -547,62 +357,11 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify behavioral correctness
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
// Expected: loadedMatrix * translate(10,0,0)
|
||||
Matrix4f expected = new Matrix4f(loadedMatrix).translate(10.0f, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] - transform after LoadMatrix should be relative to loaded matrix", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoadMatrixThenLoadIdentity() {
|
||||
// LoadMatrix followed by LoadIdentity should result in identity
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
// Load a complex matrix
|
||||
Matrix4f loadedMatrix = new Matrix4f()
|
||||
.translate(50.0f, 60.0f, 70.0f)
|
||||
.rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f);
|
||||
FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16);
|
||||
loadedMatrix.get(loadBuf);
|
||||
loadBuf.rewind();
|
||||
GLStateManager.glLoadMatrix(loadBuf);
|
||||
|
||||
// Then reset to identity
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
// Then apply a simple translate
|
||||
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify: final state should just be translate(1,2,3)
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0");
|
||||
assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0");
|
||||
assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertMatrixEquals(expected, actualBuf, "transform after LoadMatrix should be relative to loaded matrix");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -628,17 +387,10 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify: final state should be matrix2
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
final FloatBuffer actualBuf = modelviewAfterCall(testList);
|
||||
|
||||
assertEquals(0.0f, actualBuf.get(12), 0.0001f, "X should be 0.0 (from second LoadMatrix)");
|
||||
assertEquals(20.0f, actualBuf.get(13), 0.0001f, "Y should be 20.0 (from second LoadMatrix)");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
// ==================== Immediate Mode with Transforms Tests ====================
|
||||
@@ -652,12 +404,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glScalef(0.25f, 0.25f, 0.25f);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(2.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(2.0f, 2.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 2.0f, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
immediateQuad(2.0f);
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glEndList();
|
||||
@@ -683,12 +430,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glTranslatef(100.0f, 50.0f, 0.0f);
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 1.0f);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(16.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(16.0f, 16.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 16.0f, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
immediateQuad(16.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
@@ -700,20 +442,13 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertEquals(0, counts.getOrDefault(GLCommand.SCALE, 0), "No individual Scale");
|
||||
|
||||
// Verify matrix values after playback
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
|
||||
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
|
||||
|
||||
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X");
|
||||
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y");
|
||||
assertEquals(1.0f, matrixBuf.get(10), 0.0001f, "Scale Z");
|
||||
assertEquals(100.0f, matrixBuf.get(12), 0.0001f, "Translate X");
|
||||
assertEquals(50.0f, matrixBuf.get(13), 0.0001f, "Translate Y");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
// ==================== Cross-Mode Transform Collapsing Tests ====================
|
||||
@@ -806,12 +541,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
immediateQuad(1.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
@@ -826,16 +556,9 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertTrue(opcodes.indexOf(GLCommand.MULT_MATRIX) > opcodes.indexOf(GLCommand.DRAW_RANGE),
|
||||
"MultMatrix after DrawRange for mode " + mode);
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
|
||||
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
|
||||
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X for mode " + mode);
|
||||
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y for mode " + mode);
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,38 +569,16 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
|
||||
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final float[][] corners = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };
|
||||
tess.rawBuffer = new int[4 * 8];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final int base = i * 8;
|
||||
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
|
||||
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
|
||||
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
|
||||
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
|
||||
}
|
||||
tess.isDrawing = true;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasTexture = true;
|
||||
tess.rawBufferIndex = 4 * 8;
|
||||
tess.vertexCount = 4;
|
||||
TessellatorManager.interceptDraw(tess);
|
||||
rawQuad(1.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled);
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
|
||||
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
|
||||
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X after playback");
|
||||
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y after playback");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
// ==================== Optimizer Push/Pop Reset Tests ====================
|
||||
@@ -898,12 +599,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
|
||||
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
immediateQuad(1.0f);
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glEndList();
|
||||
@@ -930,18 +626,11 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertTrue(drawIndex < popIndex, "DRAW before POP");
|
||||
|
||||
// Verify final matrix after pop = T1
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(testList);
|
||||
|
||||
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
|
||||
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
|
||||
|
||||
assertEquals(2.0f, matrixBuf.get(0), 0.0001f, "Scale X after pop");
|
||||
assertEquals(2.0f, matrixBuf.get(5), 0.0001f, "Scale Y after pop");
|
||||
assertEquals(5.0f, matrixBuf.get(12), 0.0001f, "Translate X after pop");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+65
-265
@@ -1,7 +1,16 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -9,128 +18,77 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@GLCompatTest
|
||||
class GLSM_DisplayList_UnitTest {
|
||||
|
||||
@Test
|
||||
void testDisplayListCompileCall() {
|
||||
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State");
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Post Display List Compile");
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Call");
|
||||
|
||||
// Reset state that we changed during test
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
/** One recordable state command: {@code apply} moves the driver value read by {@code driver} to {@code applied}. */
|
||||
private static Arguments state(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
|
||||
return Arguments.of(name, apply, reset, driver, applied);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisplayListCompileExecute() {
|
||||
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State");
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Compile and Execute");
|
||||
|
||||
// Reset state that we changed during test
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
private static double lighting() {
|
||||
final boolean enabled = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
|
||||
verifyState(GL11.GL_LIGHTING, enabled, "driver must agree with the GLSM cache");
|
||||
return enabled ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
// ==================== CullFace Tests ====================
|
||||
|
||||
@Test
|
||||
void testCullFaceInDisplayList() {
|
||||
// Get initial cull face mode
|
||||
int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile, mode should be unchanged
|
||||
assertEquals(initialMode, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be unchanged after GL_COMPILE");
|
||||
|
||||
// Call the display list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Now mode should be GL_FRONT
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT after calling display list");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glCullFace(GL11.GL_BACK); // Reset to default
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
private static double blendColorRed() {
|
||||
final FloatBuffer color = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL14.GL_BLEND_COLOR, color);
|
||||
return color.get(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCullFaceCompileAndExecute() {
|
||||
// Get initial cull face mode
|
||||
int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT_AND_BACK);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile and execute, mode should be changed
|
||||
assertEquals(GL11.GL_FRONT_AND_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT_AND_BACK after GL_COMPILE_AND_EXECUTE");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
static Stream<Arguments> states() {
|
||||
return Stream.of(
|
||||
state("glEnable(GL_LIGHTING)", () -> GLStateManager.glEnable(GL11.GL_LIGHTING), () -> GLStateManager.glDisable(GL11.GL_LIGHTING),
|
||||
GLSM_DisplayList_UnitTest::lighting, 1.0),
|
||||
state("glCullFace", () -> GLStateManager.glCullFace(GL11.GL_FRONT), () -> GLStateManager.glCullFace(GL11.GL_BACK),
|
||||
() -> GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), GL11.GL_FRONT),
|
||||
state("glLogicOp", () -> GLStateManager.glLogicOp(GL11.GL_XOR), () -> GLStateManager.glLogicOp(GL11.GL_COPY),
|
||||
() -> GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), GL11.GL_XOR),
|
||||
state("glClearDepth", () -> GLStateManager.glClearDepth(0.5), () -> GLStateManager.glClearDepth(1.0),
|
||||
() -> GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.5),
|
||||
state("glBlendColor", () -> GLStateManager.glBlendColor(0.5f, 0.4f, 0.6f, 0.8f), () -> GLStateManager.glBlendColor(0f, 0f, 0f, 0f),
|
||||
GLSM_DisplayList_UnitTest::blendColorRed, 0.5),
|
||||
state("glFrontFace", () -> GLStateManager.glFrontFace(GL11.GL_CW), () -> GLStateManager.glFrontFace(GL11.GL_CCW),
|
||||
() -> GL11.glGetInteger(GL11.GL_FRONT_FACE), GL11.GL_CW),
|
||||
state("glHint", () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST), () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE),
|
||||
() -> GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), GL11.GL_NICEST));
|
||||
}
|
||||
|
||||
// ==================== LogicOp Tests ====================
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("states")
|
||||
void compiledStateChangeAppliesOnlyWhenTheListIsCalled(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
|
||||
final double initial = driver.getAsDouble();
|
||||
final int list = GL11.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
apply.run();
|
||||
GLStateManager.glEndList();
|
||||
assertEquals(initial, driver.getAsDouble(), 0.0001, name + " must not execute under GL_COMPILE");
|
||||
|
||||
@Test
|
||||
void testLogicOpInDisplayList() {
|
||||
// Get initial logic op
|
||||
int initialOp = GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glLogicOp(GL11.GL_XOR);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile, op should be unchanged
|
||||
assertEquals(initialOp, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be unchanged after GL_COMPILE");
|
||||
|
||||
// Call the display list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Now op should be GL_XOR
|
||||
assertEquals(GL11.GL_XOR, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_XOR after calling display list");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY); // Reset to default
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply when the list is called");
|
||||
} finally {
|
||||
reset.run();
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogicOpCompileAndExecute() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glLogicOp(GL11.GL_INVERT);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile and execute, op should be changed
|
||||
assertEquals(GL11.GL_INVERT, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_INVERT after GL_COMPILE_AND_EXECUTE");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("states")
|
||||
void compileAndExecuteAppliesStateChangeImmediately(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
|
||||
final int list = GL11.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
apply.run();
|
||||
GLStateManager.glEndList();
|
||||
assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply under GL_COMPILE_AND_EXECUTE");
|
||||
} finally {
|
||||
reset.run();
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleStateChangesInDisplayList() {
|
||||
// Test that multiple state commands work together
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
@@ -138,173 +96,15 @@ class GLSM_DisplayList_UnitTest {
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Verify all states changed
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace should be GL_FRONT");
|
||||
assertEquals(GL11.GL_AND, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_AND");
|
||||
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING should be enabled");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
// ==================== ClearDepth Tests ====================
|
||||
|
||||
@Test
|
||||
void testClearDepthInDisplayList() {
|
||||
// Get initial clear depth
|
||||
double initialDepth = GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glClearDepth(0.5);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile, depth should be unchanged
|
||||
assertEquals(initialDepth, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be unchanged after GL_COMPILE");
|
||||
|
||||
// Call the display list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Now depth should be 0.5
|
||||
assertEquals(0.5, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.5 after calling display list");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glClearDepth(1.0); // Reset to default
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearDepthCompileAndExecute() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glClearDepth(0.25);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile and execute, depth should be changed
|
||||
assertEquals(0.25, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.25 after GL_COMPILE_AND_EXECUTE");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glClearDepth(1.0);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
// ==================== BlendColor Tests ====================
|
||||
|
||||
@Test
|
||||
void testBlendColorInDisplayList() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBlendColor(0.2f, 0.4f, 0.6f, 0.8f);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Call the display list - should not throw
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glBlendColor(0f, 0f, 0f, 0f);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBlendColorCompileAndExecute() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glBlendColor(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Should not throw - display list was executed
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glBlendColor(0f, 0f, 0f, 0f);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
// ==================== FrontFace Tests ====================
|
||||
|
||||
@Test
|
||||
void testFrontFaceInDisplayList() {
|
||||
// Get initial front face
|
||||
int initialMode = GL11.glGetInteger(GL11.GL_FRONT_FACE);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glFrontFace(GL11.GL_CW);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile, mode should be unchanged
|
||||
assertEquals(initialMode, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be unchanged after GL_COMPILE");
|
||||
|
||||
// Call the display list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Now mode should be GL_CW
|
||||
assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after calling display list");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glFrontFace(GL11.GL_CCW); // Reset to default
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFrontFaceCompileAndExecute() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glFrontFace(GL11.GL_CW);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile and execute, mode should be changed
|
||||
assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after GL_COMPILE_AND_EXECUTE");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glFrontFace(GL11.GL_CCW);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
// ==================== Hint Tests ====================
|
||||
|
||||
@Test
|
||||
void testHintInDisplayList() {
|
||||
// Get initial hint value
|
||||
int initialHint = GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT);
|
||||
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile, hint should be unchanged
|
||||
assertEquals(initialHint, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be unchanged after GL_COMPILE");
|
||||
|
||||
// Call the display list
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
// Now hint should be GL_NICEST
|
||||
assertEquals(GL11.GL_NICEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_NICEST after calling display list");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE); // Reset to default
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHintCompileAndExecute() {
|
||||
int list = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_FASTEST);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// After compile and execute, hint should be changed
|
||||
assertEquals(GL11.GL_FASTEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_FASTEST after GL_COMPILE_AND_EXECUTE");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -20,37 +24,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@GLCompatTest
|
||||
public class GLSM_GLU_UnitTest {
|
||||
|
||||
private static final FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
private Matrix4f getModelviewMatrix() {
|
||||
buffer.clear();
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buffer);
|
||||
return new Matrix4f(buffer);
|
||||
return new Matrix4f(GlTestSupport.modelview());
|
||||
}
|
||||
|
||||
private Matrix4f getProjectionMatrix() {
|
||||
buffer.clear();
|
||||
GLStateManager.glGetFloat(GL11.GL_PROJECTION_MATRIX, buffer);
|
||||
return new Matrix4f(buffer);
|
||||
return new Matrix4f(GlTestSupport.matrix(GL11.GL_PROJECTION_MATRIX));
|
||||
}
|
||||
|
||||
private void assertMatrixEquals(Matrix4f expected, Matrix4f actual, String message) {
|
||||
assertTrue(expected.equals(actual, 0.001f), () -> message + "\nExpected:\n" + expected + "\nActual:\n" + actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluPerspective_matchesJOML() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
GLStateManager.gluPerspective(60.0f, 1.5f, 0.1f, 1000.0f);
|
||||
|
||||
Matrix4f expected = new Matrix4f().perspective((float) Math.toRadians(60.0f), 1.5f, 0.1f, 1000.0f);
|
||||
assertMatrixEquals(expected, getProjectionMatrix(), "gluPerspective standard");
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluPerspective_accumulates() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
@@ -67,40 +52,6 @@ public class GLSM_GLU_UnitTest {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluLookAt_matchesJOML() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
GLStateManager.gluLookAt(
|
||||
0.0f, 0.0f, 5.0f, // eye
|
||||
0.0f, 0.0f, 0.0f, // center
|
||||
0.0f, 1.0f, 0.0f); // up
|
||||
|
||||
Matrix4f expected = new Matrix4f().lookAt(
|
||||
0.0f, 0.0f, 5.0f,
|
||||
0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f);
|
||||
assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt simple");
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluLookAt_offAxis() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
GLStateManager.gluLookAt(
|
||||
3.0f, 4.0f, 5.0f,
|
||||
1.0f, 2.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f);
|
||||
|
||||
Matrix4f expected = new Matrix4f().lookAt(
|
||||
3.0f, 4.0f, 5.0f,
|
||||
1.0f, 2.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f);
|
||||
assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt off-axis");
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluLookAt_accumulates() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
@@ -208,25 +159,23 @@ public class GLSM_GLU_UnitTest {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluErrorString_glErrors() {
|
||||
assertEquals("No error", GLStateManager.gluErrorString(GL11.GL_NO_ERROR));
|
||||
assertEquals("Invalid enum", GLStateManager.gluErrorString(GL11.GL_INVALID_ENUM));
|
||||
assertEquals("Invalid value", GLStateManager.gluErrorString(GL11.GL_INVALID_VALUE));
|
||||
assertEquals("Invalid operation", GLStateManager.gluErrorString(GL11.GL_INVALID_OPERATION));
|
||||
assertEquals("Out of memory", GLStateManager.gluErrorString(GL11.GL_OUT_OF_MEMORY));
|
||||
static Stream<Arguments> errorStrings() {
|
||||
return Stream.of(
|
||||
Arguments.of(GL11.GL_NO_ERROR, "No error"),
|
||||
Arguments.of(GL11.GL_INVALID_ENUM, "Invalid enum"),
|
||||
Arguments.of(GL11.GL_INVALID_VALUE, "Invalid value"),
|
||||
Arguments.of(GL11.GL_INVALID_OPERATION, "Invalid operation"),
|
||||
Arguments.of(GL11.GL_OUT_OF_MEMORY, "Out of memory"),
|
||||
Arguments.of(100900, "Invalid enum (glu)"),
|
||||
Arguments.of(100901, "Invalid value (glu)"),
|
||||
Arguments.of(100902, "Out of memory (glu)"),
|
||||
Arguments.of(99999, "Unknown error: 99999"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluErrorString_gluErrors() {
|
||||
assertEquals("Invalid enum (glu)", GLStateManager.gluErrorString(100900));
|
||||
assertEquals("Invalid value (glu)", GLStateManager.gluErrorString(100901));
|
||||
assertEquals("Out of memory (glu)", GLStateManager.gluErrorString(100902));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluErrorString_unknownCode() {
|
||||
assertEquals("Unknown error: 99999", GLStateManager.gluErrorString(99999));
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@MethodSource("errorStrings")
|
||||
void gluErrorString(int code, String expected) {
|
||||
assertEquals(expected, GLStateManager.gluErrorString(code));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+12
-23
@@ -2,11 +2,13 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.ContextCapabilities;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -16,17 +18,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
@GLCoreTest
|
||||
public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void unbind(int unit) {
|
||||
GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, 0);
|
||||
GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindThenQueryReturnsFullTuple() {
|
||||
drainErrors();
|
||||
final int tex = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(2, tex, 1, false, 3, GL15.GL_READ_ONLY, GL30.GL_RGBA32UI);
|
||||
@@ -40,13 +37,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
} finally {
|
||||
unbind(2);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveRestoreRoundTripsTheWholeTuple() {
|
||||
drainErrors();
|
||||
final int a = GL11.glGenTextures();
|
||||
final int b = GL11.glGenTextures();
|
||||
try {
|
||||
@@ -55,7 +50,7 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
assertNotNull(live);
|
||||
final ImageUnitBinding saved = live.copy();
|
||||
|
||||
GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, 0);
|
||||
GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8);
|
||||
assertEquals(b, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
|
||||
|
||||
GLStateManager.glBindImageTexture(0, saved.getTexture(), saved.getLevel(), saved.isLayered(), saved.getLayer(), saved.getAccess(), saved.getFormat());
|
||||
@@ -68,30 +63,26 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
unbind(0);
|
||||
GLStateManager.glDeleteTextures(a);
|
||||
GLStateManager.glDeleteTextures(b);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetToZeroIsNotARestore() {
|
||||
drainErrors();
|
||||
final int tex = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(0, tex, 1, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI);
|
||||
GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, 0);
|
||||
GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8);
|
||||
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LEVEL, 0));
|
||||
} finally {
|
||||
unbind(0);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingTheBoundTextureClearsTheImageUnit() {
|
||||
drainErrors();
|
||||
final int tex = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(1, tex, 0, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI);
|
||||
@@ -101,13 +92,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), "GL unbinds a deleted texture from every image unit");
|
||||
} finally {
|
||||
unbind(1);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unitsAreIndependentAndUnitZeroIsNotSpecial() {
|
||||
drainErrors();
|
||||
final int a = GL11.glGenTextures();
|
||||
final int b = GL11.glGenTextures();
|
||||
try {
|
||||
@@ -123,13 +112,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
unbind(3);
|
||||
GLStateManager.glDeleteTextures(a);
|
||||
GLStateManager.glDeleteTextures(b);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeUnitDoesNotCorruptNeighbours() {
|
||||
drainErrors();
|
||||
final int tex = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(0, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
|
||||
@@ -137,11 +124,14 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
assertFalse(beyond == 0, "unit 0 must resolve for this test to mean anything");
|
||||
|
||||
GLStateManager.glBindImageTexture(beyond, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
|
||||
// Without image load/store the backend drops the call, so the driver never sees the unit.
|
||||
final ContextCapabilities caps = GLStateManager.capabilities;
|
||||
final boolean reachesDriver = caps.OpenGL42 || caps.GL_ARB_shader_image_load_store || caps.GL_EXT_shader_image_load_store;
|
||||
assertEquals(reachesDriver ? GL11.GL_INVALID_VALUE : GL11.GL_NO_ERROR, GL11.glGetError(), "driver must reject the out-of-range unit");
|
||||
assertEquals(tex, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
|
||||
} finally {
|
||||
unbind(0);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +141,9 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
drainErrors();
|
||||
final int tex = GL11.glGenTextures();
|
||||
try {
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GL42.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
|
||||
GLStateManager.glBindImageTexture(1, tex, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
|
||||
drainErrors();
|
||||
@@ -164,7 +154,6 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
} finally {
|
||||
unbind(1);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
+529
-241
@@ -1,22 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
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 com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.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 org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.HINT_TARGETS;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.legacyPnames;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.popAttribTo;
|
||||
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.setEnabled;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -27,44 +39,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
@GLCoreTest
|
||||
public class GLSM_LegacyStackQueries_GLTest {
|
||||
|
||||
private static final int[] SINGLE_VALUE_QUERIES = {
|
||||
GL11.GL_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_CLIENT_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_TEXTURE_STACK_DEPTH,
|
||||
GL11.GL_MAX_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_MAX_MODELVIEW_STACK_DEPTH,
|
||||
GL11.GL_MAX_PROJECTION_STACK_DEPTH,
|
||||
GL11.GL_MAX_TEXTURE_STACK_DEPTH,
|
||||
GL13.GL_MAX_TEXTURE_UNITS,
|
||||
GL11.GL_MAX_LIGHTS,
|
||||
GL11.GL_STENCIL_BITS,
|
||||
GL11.GL_DEPTH_BITS,
|
||||
};
|
||||
private static final int[] LEGACY_PNAMES = legacyPnames();
|
||||
|
||||
private static boolean positionArrayEnabled() {
|
||||
final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation());
|
||||
return position != null && position.enabled;
|
||||
}
|
||||
|
||||
private static boolean colorArrayEnabled() {
|
||||
return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0;
|
||||
}
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void drainClientAttribStack() {
|
||||
while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) {
|
||||
GLStateManager.glPopClientAttrib();
|
||||
}
|
||||
}
|
||||
|
||||
private static void drainAttribStack() {
|
||||
while (GLStateManager.getAttribDepth() > 0) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
private static void assumeDriverRejectsLegacyPnames() {
|
||||
drainErrors();
|
||||
GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing");
|
||||
}
|
||||
|
||||
private static int pushToLimit(int depthPname, int maxPname) {
|
||||
@@ -95,9 +75,7 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.MAX_ATTRIB_STACK_DEPTH, GLStateManager.glGetInteger(GL11.GL_MAX_ATTRIB_STACK_DEPTH));
|
||||
} finally {
|
||||
while (GLStateManager.getAttribDepth() > base) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
popAttribTo(base);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,42 +128,24 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxModelviewStackDepthIsExactlyReachable() {
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_MODELVIEW_STACK_DEPTH), pushToLimit(GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
popToBase(GL11.GL_MODELVIEW_STACK_DEPTH);
|
||||
}
|
||||
static Stream<Arguments> matrixStacks() {
|
||||
return Stream.of(
|
||||
Arguments.of("modelview", GL11.GL_MODELVIEW, GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH),
|
||||
Arguments.of("projection", GL11.GL_PROJECTION, GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH),
|
||||
Arguments.of("texture", GL11.GL_TEXTURE, GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxProjectionStackDepthIsExactlyReachable() {
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_PROJECTION_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH), pushToLimit(GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
popToBase(GL11.GL_PROJECTION_STACK_DEPTH);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxTextureStackDepthIsExactlyReachable() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("matrixStacks")
|
||||
void maxMatrixStackDepthIsExactlyReachable(String name, int mode, int depthPname, int maxPname) {
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH), pushToLimit(GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH));
|
||||
GLStateManager.glMatrixMode(mode);
|
||||
assertEquals(1, GLStateManager.glGetInteger(depthPname));
|
||||
assertEquals(GLStateManager.glGetInteger(maxPname), pushToLimit(depthPname, maxPname));
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
popToBase(GL11.GL_TEXTURE_STACK_DEPTH);
|
||||
GLStateManager.glMatrixMode(mode);
|
||||
popToBase(depthPname);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
@@ -203,14 +163,12 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
final int savedRead = glCtx.readFramebuffer;
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
drainErrors();
|
||||
assertEquals(
|
||||
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE),
|
||||
GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
assertEquals(
|
||||
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE),
|
||||
GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
|
||||
@@ -226,7 +184,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
final int depthOnly = GLStateManager.glGenRenderbuffers();
|
||||
final int depthStencil = GLStateManager.glGenRenderbuffers();
|
||||
try {
|
||||
drainErrors();
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthOnly);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT24, 16, 16);
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthStencil);
|
||||
@@ -247,7 +204,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil);
|
||||
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
assertEquals(24, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
|
||||
@@ -257,33 +213,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intBufferOverloadMatchesAndDriverIsNotAsked() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final IntBuffer buf = BufferUtils.createIntBuffer(16);
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
drainErrors();
|
||||
GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing");
|
||||
for (final int pname : SINGLE_VALUE_QUERIES) {
|
||||
final int direct = GLStateManager.glGetInteger(pname);
|
||||
buf.put(0, -12345);
|
||||
GLStateManager.glGetInteger(pname, buf);
|
||||
assertEquals(direct, buf.get(0), "IntBuffer overload for 0x" + Integer.toHexString(pname));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying 0x" + Integer.toHexString(pname));
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainClientAttribStack();
|
||||
while (GLStateManager.getAttribDepth() > base) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribDepthUnchangedWhileCompilingDisplayList() {
|
||||
final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
@@ -306,14 +235,12 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
|
||||
@Test
|
||||
void popAttribOnEmptyStackIsIgnored() {
|
||||
drainAttribStack();
|
||||
drainErrors();
|
||||
popAttribTo(0);
|
||||
assertEquals(0, GLStateManager.getAttribDepth());
|
||||
assertDoesNotThrow(GLStateManager::glPopAttrib);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
assertDoesNotThrow(GLStateManager::glPopAttrib);
|
||||
assertEquals(0, GLStateManager.getAttribDepth());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -322,8 +249,11 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
final int unpackBuffer = GLStateManager.glGenBuffers();
|
||||
final int packBuffer = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
// Pack and unpack start from different values, so a pop that crosses them is caught
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
@@ -332,31 +262,41 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 7);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
|
||||
assertEquals(1, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(2, glCtx.pixelPackState.alignment());
|
||||
assertEquals(4, glCtx.pixelPackState.alignment());
|
||||
assertEquals(6, glCtx.pixelUnpackState.skipRows());
|
||||
assertEquals(7, glCtx.pixelPackState.skipRows());
|
||||
assertEquals(7, glCtx.pixelPackState.rowLength());
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertSame(unpackBefore, glCtx.pixelUnpackState);
|
||||
assertSame(packBefore, glCtx.pixelPackState);
|
||||
assertEquals(4, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
assertEquals(4, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_PACK_ROW_LENGTH));
|
||||
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
@@ -428,122 +368,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void packAndUnpackRestoreToTheirOwnPriorValues() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7);
|
||||
assertEquals(1, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(4, glCtx.pixelPackState.alignment());
|
||||
assertEquals(6, glCtx.pixelUnpackState.skipRows());
|
||||
assertEquals(7, glCtx.pixelPackState.skipRows());
|
||||
assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertEquals(2, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(8, glCtx.pixelPackState.alignment());
|
||||
assertEquals(3, glCtx.pixelUnpackState.skipRows());
|
||||
assertEquals(5, glCtx.pixelPackState.skipRows());
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexArrayEnableRestoredOnlyWithVertexArrayBit() {
|
||||
final int savedVao = GLStateManager.getBoundVAO();
|
||||
final int vao = GLStateManager.glGenVertexArrays();
|
||||
final IntBuffer enabled = BufferUtils.createIntBuffer(16);
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
drainErrors();
|
||||
assertFalse(positionArrayEnabled());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
assertTrue(positionArrayEnabled());
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(positionArrayEnabled());
|
||||
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
|
||||
assertEquals(GL11.GL_FALSE, enabled.get(0));
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled());
|
||||
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
|
||||
assertEquals(GL11.GL_TRUE, enabled.get(0));
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindVertexArray(savedVao);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexArrayStateRestoredOnlyWithVertexArrayBit() {
|
||||
final int savedVao = GLStateManager.getBoundVAO();
|
||||
final int vao = GLStateManager.glGenVertexArrays();
|
||||
try {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags();
|
||||
assertFalse(colorArrayEnabled());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertTrue(colorArrayEnabled());
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(colorArrayEnabled());
|
||||
assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags());
|
||||
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(colorArrayEnabled());
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glBindVertexArray(savedVao);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intLightGettersConvertLikeMesa() {
|
||||
final FloatBuffer savedDiffuse = BufferUtils.createFloatBuffer(16);
|
||||
@@ -570,7 +394,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, values);
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, 45.75f);
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, 2.5f);
|
||||
drainErrors();
|
||||
|
||||
ints.position(2);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, ints);
|
||||
@@ -606,7 +429,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(-777, ints.get(0));
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SHININESS, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glLoadIdentity();
|
||||
savedDiffuse.limit(4);
|
||||
@@ -637,7 +459,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 12.6f);
|
||||
values.put(0, 2.5f).put(1, -2.5f).put(2, 7.4f).put(3, 0.0f);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, values);
|
||||
drainErrors();
|
||||
|
||||
ints.position(1);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, ints);
|
||||
@@ -665,7 +486,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(-777, ints.get(0));
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPOT_CUTOFF, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
savedSpecular.limit(4);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular);
|
||||
@@ -691,7 +511,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240.0f, 16.0f);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE2, 0.75f, 0.125f);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE3, 0.375f, 0.625f);
|
||||
drainErrors();
|
||||
|
||||
final float[][] expected = { { 0.25f, 0.5f }, { 240.0f, 16.0f }, { 0.75f, 0.125f }, { 0.375f, 0.625f } };
|
||||
for (int unit = 0; unit < 4; unit++) {
|
||||
@@ -702,7 +521,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(0.0f, buf.get(2), "r of unit " + unit);
|
||||
assertEquals(1.0f, buf.get(3), "q of unit " + unit);
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
for (int unit = 0; unit < 4; unit++) {
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0 + unit, saved[unit][0], saved[unit][1]);
|
||||
@@ -719,7 +537,6 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
drainErrors();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buf.put(i, -7.0f);
|
||||
}
|
||||
@@ -728,10 +545,481 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(0.0f, buf.get(1));
|
||||
assertEquals(0.0f, buf.get(2));
|
||||
assertEquals(1.0f, buf.get(3));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyQueriesNeverReachTheDriver() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
final FloatBuffer floats = BufferUtils.createFloatBuffer(16);
|
||||
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16);
|
||||
final ByteBuffer bytes = BufferUtils.createByteBuffer(16);
|
||||
final int[] intArray = new int[16];
|
||||
final float[] floatArray = new float[16];
|
||||
final double[] doubleArray = new double[16];
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assumeDriverRejectsLegacyPnames();
|
||||
for (final int pname : LEGACY_PNAMES) {
|
||||
drainErrors();
|
||||
final int scalar = GLStateManager.glGetInteger(pname);
|
||||
GLStateManager.glGetFloat(pname);
|
||||
GLStateManager.glGetBoolean(pname);
|
||||
GLStateManager.glIsEnabled(pname);
|
||||
ints.clear();
|
||||
ints.put(0, -12345);
|
||||
GLStateManager.glGetInteger(pname, ints);
|
||||
floats.clear();
|
||||
GLStateManager.glGetFloat(pname, floats);
|
||||
doubles.clear();
|
||||
GLStateManager.glGetDouble(pname, doubles);
|
||||
bytes.clear();
|
||||
GLStateManager.glGetBoolean(pname, bytes);
|
||||
intArray[0] = -12345;
|
||||
GLStateManager.glGetInteger(pname, intArray);
|
||||
GLStateManager.glGetFloat(pname, floatArray);
|
||||
GLStateManager.glGetDouble(pname, doubleArray);
|
||||
if (LegacyQueries.count(LegacyQueries.desc(pname)) == 1) {
|
||||
assertEquals(scalar, ints.get(0), "IntBuffer overload for " + hex(pname));
|
||||
assertEquals(scalar, intArray[0], "int[] overload for " + hex(pname));
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying " + hex(pname));
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainClientAttribStack();
|
||||
popAttribTo(base);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void typedEntryPointsAgreeUnderMesaConversions() {
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
final FloatBuffer floats = BufferUtils.createFloatBuffer(16);
|
||||
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16);
|
||||
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedNormal = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_NORMAL, savedNormal);
|
||||
final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC);
|
||||
final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF);
|
||||
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
|
||||
final float savedEnd = GLStateManager.glGetFloat(GL11.GL_FOG_END);
|
||||
final float savedDensity = GLStateManager.glGetFloat(GL11.GL_FOG_DENSITY);
|
||||
final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
|
||||
final boolean savedFog = GLStateManager.glIsEnabled(GL11.GL_FOG);
|
||||
final int savedShade = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
|
||||
final int savedHint = GLStateManager.glGetInteger(GL11.GL_FOG_HINT);
|
||||
final float savedMinSize = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f);
|
||||
GLStateManager.glFogf(GL11.GL_FOG_END, 2.5f);
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.25f);
|
||||
GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f);
|
||||
GLStateManager.glNormal3f(0f, -0.5f, 1f);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.glEnable(GL11.GL_FOG);
|
||||
GLStateManager.glShadeModel(GL11.GL_FLAT);
|
||||
GLStateManager.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 2.5f);
|
||||
GLStateManager.glTranslatef(1.5f, -2.5f, 3f);
|
||||
for (final int pname : LEGACY_PNAMES) {
|
||||
final int desc = LegacyQueries.desc(pname);
|
||||
final int type = LegacyQueries.type(desc);
|
||||
final int count = LegacyQueries.count(desc);
|
||||
final String name = hex(pname);
|
||||
if (count == 1) {
|
||||
if (type == LegacyQueries.BOOL) {
|
||||
final boolean b = GLStateManager.glGetBoolean(pname);
|
||||
assertEquals(b ? 1 : 0, GLStateManager.glGetInteger(pname), "int of bool " + name);
|
||||
assertEquals(b ? 1.0f : 0.0f, GLStateManager.glGetFloat(pname), "float of bool " + name);
|
||||
} else if (type == LegacyQueries.INT) {
|
||||
final int i = GLStateManager.glGetInteger(pname);
|
||||
assertEquals((float) i, GLStateManager.glGetFloat(pname), "float of int " + name);
|
||||
assertEquals(i != 0, GLStateManager.glGetBoolean(pname), "bool of int " + name);
|
||||
} else {
|
||||
final float f = GLStateManager.glGetFloat(pname);
|
||||
assertEquals(LegacyQueries.toInt(type, f), GLStateManager.glGetInteger(pname), "int of float " + name);
|
||||
assertEquals(f != 0.0f, GLStateManager.glGetBoolean(pname), "bool of float " + name);
|
||||
}
|
||||
} else {
|
||||
floats.clear();
|
||||
GLStateManager.glGetFloat(pname, floats);
|
||||
ints.clear();
|
||||
GLStateManager.glGetInteger(pname, ints);
|
||||
doubles.clear();
|
||||
GLStateManager.glGetDouble(pname, doubles);
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float f = floats.get(i);
|
||||
assertEquals(LegacyQueries.toInt(type, f), ints.get(i), "int element " + i + " of " + name);
|
||||
assertEquals((double) f, doubles.get(i), "double element " + i + " of " + name);
|
||||
}
|
||||
assertEquals(floats.get(0), GLStateManager.glGetFloat(pname), "scalar float of " + name);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, savedMinSize);
|
||||
GLStateManager.glHint(GL11.GL_FOG_HINT, savedHint);
|
||||
GLStateManager.glShadeModel(savedShade);
|
||||
setEnabled(GL11.GL_FOG, savedFog);
|
||||
setEnabled(GL11.GL_LIGHTING, savedLighting);
|
||||
GLStateManager.glNormal3f(savedNormal.get(0), savedNormal.get(1), savedNormal.get(2));
|
||||
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, savedDensity);
|
||||
GLStateManager.glFogf(GL11.GL_FOG_END, savedEnd);
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
|
||||
GLStateManager.glAlphaFunc(savedFunc, savedRef);
|
||||
}
|
||||
}
|
||||
|
||||
private static float driverMaxPointSize() {
|
||||
final FloatBuffer range = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
final float smooth = range.get(1);
|
||||
// A rejected query leaves the buffer alone, so the smooth maximum stands in for the missing aliased range
|
||||
GL11.glGetFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||
drainErrors();
|
||||
return Math.max(smooth, range.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void edgeFlagIsCompiledIntoDisplayLists() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glEdgeFlag(true);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glEdgeFlag(false);
|
||||
GLStateManager.glEndList();
|
||||
assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG), "compiling must not change the live edge flag");
|
||||
GLStateManager.glCallList(list);
|
||||
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
|
||||
} finally {
|
||||
GLStateManager.glEdgeFlag(true);
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textureBitRestoresPerUnitTextureEnables() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
final boolean cube = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
final boolean texture2d = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT);
|
||||
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, !cube);
|
||||
setEnabled(GL11.GL_TEXTURE_2D, !texture2d);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(cube, GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
assertEquals(texture2d, GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D));
|
||||
} finally {
|
||||
popAttribTo(base);
|
||||
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, cube);
|
||||
setEnabled(GL11.GL_TEXTURE_2D, texture2d);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void crossTypeQueriesConvert() {
|
||||
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
|
||||
final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC);
|
||||
final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF);
|
||||
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
|
||||
final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
|
||||
assertEquals(1073741823, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_REF));
|
||||
|
||||
assertEquals((float) GLStateManager.glGetInteger(GL11.GL_FOG_MODE), GLStateManager.glGetFloat(GL11.GL_FOG_MODE));
|
||||
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f);
|
||||
assertEquals(-2, GLStateManager.glGetInteger(GL11.GL_FOG_START));
|
||||
|
||||
GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_LIGHTING));
|
||||
GLStateManager.glDisable(GL11.GL_LIGHTING);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_LIGHTING));
|
||||
|
||||
GLStateManager.glColor4f(1f, 0.5f, 0f, 1f);
|
||||
ints.clear();
|
||||
GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints);
|
||||
assertEquals(2147483647, ints.get(0));
|
||||
assertEquals(1073741823, ints.get(1));
|
||||
assertEquals(0, ints.get(2));
|
||||
assertEquals(2147483647, ints.get(3));
|
||||
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX, ints);
|
||||
assertEquals(2147483647, ints.get(0), "matrix elements scale like normalized floats");
|
||||
assertEquals(0, ints.get(1));
|
||||
assertEquals(2147483647, ints.get(15));
|
||||
assertEquals(2147483647, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX));
|
||||
assertEquals(1.0f, GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
|
||||
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
|
||||
GLStateManager.glAlphaFunc(savedFunc, savedRef);
|
||||
setEnabled(GL11.GL_LIGHTING, savedLighting);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void conversionHelpersMatchMesa() {
|
||||
assertEquals(1, LegacyQueries.lround(0.5f));
|
||||
assertEquals(-1, LegacyQueries.lround(-0.5f));
|
||||
assertEquals(2, LegacyQueries.lround(2.4f));
|
||||
assertEquals(-3, LegacyQueries.lround(-2.6f));
|
||||
assertEquals(Integer.MAX_VALUE, LegacyQueries.floatToInt(1f));
|
||||
assertEquals(0, LegacyQueries.floatToInt(0f));
|
||||
assertEquals(-Integer.MAX_VALUE, LegacyQueries.floatToInt(-1f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void queriesWriteAtBufferPosition() {
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(32);
|
||||
final FloatBuffer floats = BufferUtils.createFloatBuffer(32);
|
||||
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(32);
|
||||
final FloatBuffer reference = BufferUtils.createFloatBuffer(32);
|
||||
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
|
||||
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
|
||||
try {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ints.put(i, -777);
|
||||
floats.put(i, -777f);
|
||||
doubles.put(i, -777.0);
|
||||
}
|
||||
|
||||
ints.position(4);
|
||||
GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE, ints);
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE), ints.get(4));
|
||||
assertEquals(-777, ints.get(0));
|
||||
|
||||
floats.position(4);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, floats);
|
||||
reference.clear();
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, reference);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(reference.get(i), floats.get(4 + i), "modelview element " + i);
|
||||
}
|
||||
assertEquals(-777f, floats.get(0));
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ints.put(i, -777);
|
||||
}
|
||||
GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f);
|
||||
ints.position(4);
|
||||
GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints);
|
||||
assertEquals(536870911, ints.get(4));
|
||||
assertEquals(1073741823, ints.get(5));
|
||||
assertEquals(1610612735, ints.get(6));
|
||||
assertEquals(2147483647, ints.get(7));
|
||||
assertEquals(-777, ints.get(0));
|
||||
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, 3.5f);
|
||||
doubles.position(4);
|
||||
GLStateManager.glGetDouble(GL11.GL_FOG_START, doubles);
|
||||
assertEquals(3.5, doubles.get(4));
|
||||
assertEquals(-777.0, doubles.get(0));
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
floats.put(i, -777f);
|
||||
}
|
||||
floats.position(4);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, floats);
|
||||
reference.clear();
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, reference);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
assertEquals(reference.get(i), floats.get(4 + i), "diffuse element " + i);
|
||||
}
|
||||
assertEquals(-777f, floats.get(0));
|
||||
} finally {
|
||||
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
|
||||
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void hintsRoundTripAndFollowHintBit() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final int[] saved = new int[HINT_TARGETS.length];
|
||||
for (int i = 0; i < HINT_TARGETS.length; i++) {
|
||||
saved[i] = GLStateManager.glGetInteger(HINT_TARGETS[i]);
|
||||
}
|
||||
try {
|
||||
for (final int target : HINT_TARGETS) {
|
||||
final String name = hex(target);
|
||||
assertEquals(GL11.GL_DONT_CARE, GLStateManager.glGetInteger(target), "default " + name);
|
||||
GLStateManager.glHint(target, GL11.GL_NICEST);
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "nicest " + name);
|
||||
GLStateManager.glHint(target, 12345);
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "invalid mode " + name);
|
||||
}
|
||||
GLStateManager.glPushAttrib(GL11.GL_HINT_BIT);
|
||||
for (final int target : HINT_TARGETS) {
|
||||
GLStateManager.glHint(target, GL11.GL_FASTEST);
|
||||
assertEquals(GL11.GL_FASTEST, GLStateManager.glGetInteger(target), "fastest " + hex(target));
|
||||
}
|
||||
GLStateManager.glPopAttrib();
|
||||
for (final int target : HINT_TARGETS) {
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "restored " + hex(target));
|
||||
}
|
||||
assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE));
|
||||
} finally {
|
||||
popAttribTo(base);
|
||||
for (int i = 0; i < HINT_TARGETS.length; i++) {
|
||||
GLStateManager.glHint(HINT_TARGETS[i], saved[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pointParametersRoundTripAndFollowPointBit() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final FloatBuffer attenuation = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedAttenuation = BufferUtils.createFloatBuffer(16);
|
||||
final float savedMin = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN);
|
||||
final float savedMax = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX);
|
||||
final float savedFade = GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE);
|
||||
final int savedOrigin = GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN);
|
||||
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, savedAttenuation);
|
||||
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
|
||||
try {
|
||||
assertEquals(0f, savedMin);
|
||||
assertEquals(driverMaxPointSize(), savedMax);
|
||||
assertEquals(1f, savedFade);
|
||||
assertEquals(1f, savedAttenuation.get(0));
|
||||
assertEquals(0f, savedAttenuation.get(1));
|
||||
assertEquals(0f, savedAttenuation.get(2));
|
||||
assertEquals(GL20.GL_UPPER_LEFT, savedOrigin);
|
||||
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 0.5f);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 2.5f);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 4.5f);
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
|
||||
assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, -1f);
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
|
||||
attenuation.put(0, 0.5f).put(1, 0.25f).put(2, 0.125f);
|
||||
attenuation.position(0).limit(3);
|
||||
GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
|
||||
attenuation.clear();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
attenuation.put(i, -777f);
|
||||
}
|
||||
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
|
||||
assertEquals(0.5f, attenuation.get(0));
|
||||
assertEquals(0.25f, attenuation.get(1));
|
||||
assertEquals(0.125f, attenuation.get(2));
|
||||
|
||||
GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_LOWER_LEFT);
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 1f);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 3f);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 2f);
|
||||
attenuation.put(0, 1f).put(1, 1f).put(2, 1f);
|
||||
attenuation.position(0).limit(3);
|
||||
GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
|
||||
GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_UPPER_LEFT);
|
||||
assertEquals(1f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(GL20.GL_UPPER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, Float.NaN);
|
||||
assertTrue(Float.isNaN(GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)));
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
|
||||
assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
attenuation.clear();
|
||||
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
|
||||
assertEquals(0.5f, attenuation.get(0));
|
||||
assertEquals(0.25f, attenuation.get(1));
|
||||
assertEquals(0.125f, attenuation.get(2));
|
||||
} finally {
|
||||
popAttribTo(base);
|
||||
assertEquals(savedMax, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cacheOnlyEnablesRoundTrip() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final boolean savedSprite = GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE);
|
||||
final boolean savedTwoSide = GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE);
|
||||
final boolean savedEdge = GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
final boolean savedCube0 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
final boolean savedCube1 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
try {
|
||||
assertEnableRoundTrip(GL20.GL_POINT_SPRITE, false);
|
||||
assertEnableRoundTrip(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false);
|
||||
assertEnableRoundTrip(GL13.GL_TEXTURE_CUBE_MAP, false);
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glEnable(GL20.GL_POINT_SPRITE);
|
||||
GLStateManager.glEnable(GL20.GL_VERTEX_PROGRAM_TWO_SIDE);
|
||||
GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
GLStateManager.glPopAttrib();
|
||||
assertFalse(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
assertFalse(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertTrue(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP));
|
||||
GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP);
|
||||
|
||||
assertTrue(savedEdge);
|
||||
GLStateManager.glEdgeFlag(false);
|
||||
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glEdgeFlag(true);
|
||||
assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
|
||||
GLStateManager.glPopAttrib();
|
||||
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
|
||||
} finally {
|
||||
popAttribTo(base);
|
||||
GLStateManager.glEdgeFlag(savedEdge);
|
||||
setEnabled(GL20.GL_POINT_SPRITE, savedSprite);
|
||||
setEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, savedTwoSide);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube1);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyLightState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyMaterialState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
@@ -15,9 +12,6 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
@GLCompatTest
|
||||
public class GLSM_Lighting_UnitTest {
|
||||
|
||||
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
||||
@Test
|
||||
void testLightEnablement() {
|
||||
verifyState(GL11.GL_LIGHT0, false, "GL_LIGHT0 Initial State");
|
||||
@@ -46,12 +40,10 @@ public class GLSM_Lighting_UnitTest {
|
||||
verifyLightState(GL11.GL_LIGHT1, GL11.GL_SPECULAR, new float[]{0F, 0F, 0F, 1F}, "GL_LIGHT1 Initial Specular");
|
||||
verifyLightState(GL11.GL_LIGHT1, GL11.GL_POSITION, new float[]{0F, 0F, 1F, 0F}, "GL_LIGHT1 Initial Position");
|
||||
|
||||
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.25F, 0.5F, 0.25F, 0.5F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Ambient Changed State");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.3F, 0.4F, 0.5F, 0.6F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
|
||||
@@ -64,33 +56,27 @@ public class GLSM_Lighting_UnitTest {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F);
|
||||
GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F);
|
||||
newf4b(0.25F, 0.5F, 0.25F, 0.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.25F, 0.5F, 0.25F, 0.0F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 Position Changed State");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.3F, 0.4F, 0.5F, 0.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.3F, 0.4F, 0.5F, 0.0F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.2824F, 0.4200F, 0.4937F, 0.0F}, "GL_LIGHT0 attrib push state changed check");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 attrib pop state check");
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.25F, 0.5F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Diffuse Changed State");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.3F, 0.4F, 0.5F, 0.6F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
|
||||
|
||||
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.25F, 0.5F, 0.25F, 0.5F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Specular Changed State");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.3F, 0.4F, 0.5F, 0.6F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
|
||||
@@ -99,12 +85,10 @@ public class GLSM_Lighting_UnitTest {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F);
|
||||
GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F);
|
||||
newf4b(0.25F, 0.5F, 0.25F, 0.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.25F, 0.5F, 0.25F, 0.0F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 Spot Direction Changed State");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.3F, 0.4F, 0.5F, 0.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.3F, 0.4F, 0.5F, 0.0F));
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.2824F, 0.4200F, 0.4937F}, "GL_LIGHT0 attrib push state changed check");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 attrib pop state check");
|
||||
@@ -157,8 +141,7 @@ public class GLSM_Lighting_UnitTest {
|
||||
void testLightModel() {
|
||||
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "GL_LIGHT_MODEL_AMBIENT initial state");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "GL_LIGHT_MODEL_AMBIENT changed state");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F});
|
||||
@@ -235,16 +218,14 @@ public class GLSM_Lighting_UnitTest {
|
||||
void testMaterial() {
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
|
||||
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
GLStateManager.glPopAttrib();
|
||||
@@ -254,8 +235,7 @@ public class GLSM_Lighting_UnitTest {
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, "");
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
@@ -268,16 +248,14 @@ public class GLSM_Lighting_UnitTest {
|
||||
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
|
||||
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, floats(0.8F, 0.6F, 0.4F, 0.2F));
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
|
||||
@@ -291,8 +269,7 @@ public class GLSM_Lighting_UnitTest {
|
||||
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, "");
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
newf4b(0.8F, 0.6F, 0.4F, 0.0F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, f4b);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, floats(0.8F, 0.6F, 0.4F, 0.0F));
|
||||
if (GLStateManager.vendorIsNVIDIA()) {
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.8F, 0.4F, 0.6F}, "");
|
||||
} else {
|
||||
@@ -302,16 +279,4 @@ public class GLSM_Lighting_UnitTest {
|
||||
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, "");
|
||||
|
||||
}
|
||||
|
||||
static void newf4b(float x, float y, float z, float w) {
|
||||
f4b.clear();
|
||||
f4b.put(x).put(y).put(z).put(w);
|
||||
f4b.flip();
|
||||
}
|
||||
|
||||
static void newf3b(float x, float y, float z) {
|
||||
f3b.clear();
|
||||
f3b.put(x).put(y).put(z);
|
||||
f3b.flip();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -12,6 +15,7 @@ import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -72,7 +76,6 @@ public class GLSM_LineStipple_GLTest {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (vao != 0) GLStateManager.glDeleteVertexArrays(vao);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private boolean[] renderLine(float x0, float x1) {
|
||||
@@ -117,35 +120,29 @@ public class GLSM_LineStipple_GLTest {
|
||||
GLStateManager.glLineStipple(factor, (short) pattern);
|
||||
}
|
||||
|
||||
@Test
|
||||
void solidPatternCoversEveryPixel() {
|
||||
stipple(1, 0xFFFF);
|
||||
assertArrayEquals(expected(i -> true), renderLine(0f, W));
|
||||
static Stream<Arguments> patterns() {
|
||||
return Stream.of(
|
||||
pattern("solid pattern covers every pixel", true, 1, 0xFFFF, i -> true),
|
||||
pattern("empty pattern covers nothing", true, 1, 0x0000, i -> false),
|
||||
pattern("alternating pattern dashes every other pixel", true, 1, 0xAAAA, i -> (i & 1) == 1),
|
||||
pattern("factor stretches each bit across that many pixels", true, 2, 0xAAAA, i -> ((i / 2) & 1) == 1),
|
||||
pattern("disabled stipple covers every pixel", false, 1, 0xAAAA, i -> true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyPatternCoversNothing() {
|
||||
stipple(1, 0x0000);
|
||||
assertArrayEquals(expected(i -> false), renderLine(0f, W));
|
||||
private static Arguments pattern(String name, boolean enabled, int factor, int pattern, IntPredicate on) {
|
||||
return Arguments.of(name, enabled, factor, pattern, on);
|
||||
}
|
||||
|
||||
@Test
|
||||
void alternatingPatternDashesEveryOtherPixel() {
|
||||
stipple(1, 0xAAAA);
|
||||
assertArrayEquals(expected(i -> (i & 1) == 1), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void factorStretchesEachBitAcrossThatManyPixels() {
|
||||
stipple(2, 0xAAAA);
|
||||
assertArrayEquals(expected(i -> ((i / 2) & 1) == 1), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disabledStippleCoversEveryPixel() {
|
||||
GLStateManager.glLineStipple(1, (short) 0xAAAA);
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
assertArrayEquals(expected(i -> true), renderLine(0f, W));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("patterns")
|
||||
void patternSelectsTheCoveredPixels(String name, boolean enabled, int factor, int pattern, IntPredicate on) {
|
||||
GLStateManager.glLineStipple(factor, (short) pattern);
|
||||
if (enabled) {
|
||||
GLStateManager.glEnable(GL11.GL_LINE_STIPPLE);
|
||||
} else {
|
||||
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
|
||||
}
|
||||
assertArrayEquals(expected(on), renderLine(0f, W));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -18,23 +19,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@GLCoreTest
|
||||
public class GLSM_PerThread_UnitTest {
|
||||
|
||||
private static void runThreaded(int n, ThreadBody body) throws Throwable {
|
||||
private static void runThreaded(int n, ThreadBody body) throws InterruptedException {
|
||||
final CyclicBarrier barrier = new CyclicBarrier(n);
|
||||
final Throwable[] errs = new Throwable[n];
|
||||
final Thread[] ts = new Thread[n];
|
||||
final TestThreads.Worker[] workers = new TestThreads.Worker[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int tid = i;
|
||||
ts[i] = new Thread(() -> {
|
||||
try {
|
||||
body.run(tid, barrier);
|
||||
} catch (Throwable t) {
|
||||
errs[tid] = t;
|
||||
}
|
||||
}, "glsm-perthread-" + i);
|
||||
workers[i] = TestThreads.start("glsm-perthread-" + i, () -> body.run(tid, barrier));
|
||||
}
|
||||
for (Thread t : ts) t.start();
|
||||
for (Thread t : ts) t.join();
|
||||
for (int i = 0; i < n; i++) if (errs[i] != null) throw errs[i];
|
||||
for (TestThreads.Worker worker : workers) worker.join();
|
||||
}
|
||||
|
||||
interface ThreadBody {
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
@@ -49,6 +50,24 @@ class GLSM_PushPop_UnitTest {
|
||||
public static final float[] FLOAT_ARRAY_3_1 = { 1f, 1f, 1f };
|
||||
public static final float[] FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT = { 0f, 0f, -1f };
|
||||
|
||||
private static void toggleAndVerify(List<GLBit> bits) {
|
||||
for (GLBit bit : bits) {
|
||||
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
|
||||
if (bit.initial()) {
|
||||
GLStateManager.glDisable(bit.glEnum());
|
||||
} else {
|
||||
GLStateManager.glEnable(bit.glEnum());
|
||||
}
|
||||
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyReset(List<GLBit> bits) {
|
||||
for (GLBit bit : bits) {
|
||||
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPushPopColorBufferBit() {
|
||||
|
||||
@@ -241,19 +260,11 @@ class GLSM_PushPop_UnitTest {
|
||||
bits.add(new GLBit(GL11.GL_TEXTURE_GEN_Q, "Texture Gen Q", false));
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
bits.forEach(bit -> {
|
||||
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
|
||||
if(bit.initial()) {
|
||||
GLStateManager.glDisable(bit.glEnum());
|
||||
} else {
|
||||
GLStateManager.glEnable(bit.glEnum());
|
||||
}
|
||||
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
|
||||
});
|
||||
toggleAndVerify(bits);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
|
||||
verifyReset(bits);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -329,15 +340,7 @@ class GLSM_PushPop_UnitTest {
|
||||
verifyState(GL11.GL_SHADE_MODEL, GL11.GL_FLAT, "Shade Model");
|
||||
|
||||
|
||||
bits.forEach(bit -> {
|
||||
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
|
||||
if(bit.initial()) {
|
||||
GLStateManager.glDisable(bit.glEnum());
|
||||
} else {
|
||||
GLStateManager.glEnable(bit.glEnum());
|
||||
}
|
||||
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
|
||||
});
|
||||
toggleAndVerify(bits);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyState(GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_FRONT_AND_BACK, "Color Material Face - Reset");
|
||||
@@ -353,7 +356,7 @@ class GLSM_PushPop_UnitTest {
|
||||
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT, "Light Spot Direction - Reset");
|
||||
verifyState(GL11.GL_SHADE_MODEL, GL11.GL_SMOOTH, "Shade Model - Reset");
|
||||
|
||||
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
|
||||
verifyReset(bits);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -388,15 +391,7 @@ class GLSM_PushPop_UnitTest {
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
|
||||
verifyState(GL11.GL_TEXTURE_BINDING_2D, tex2, "Texture Binding - Unit 1");
|
||||
|
||||
bits.forEach(bit -> {
|
||||
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
|
||||
if(bit.initial()) {
|
||||
GLStateManager.glDisable(bit.glEnum());
|
||||
} else {
|
||||
GLStateManager.glEnable(bit.glEnum());
|
||||
}
|
||||
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
|
||||
});
|
||||
toggleAndVerify(bits);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture Unit 0 - Reset");
|
||||
@@ -404,7 +399,7 @@ class GLSM_PushPop_UnitTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
verifyState(GL11.GL_TEXTURE_BINDING_2D, 0, "Texture Binding Unit 1 - Reset");
|
||||
|
||||
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
|
||||
verifyReset(bits);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(tex1);
|
||||
@@ -1156,30 +1151,6 @@ class GLSM_PushPop_UnitTest {
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPolygonStateReadBack() {
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glFrontFace(GL11.GL_CW);
|
||||
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
|
||||
|
||||
verifyState(GL11.GL_CULL_FACE_MODE, GL11.GL_FRONT, "Cull Face Mode");
|
||||
verifyState(GL11.GL_FRONT_FACE, GL11.GL_CW, "Front Face");
|
||||
verifyState(GL11.GL_POLYGON_MODE, new int[] { GL11.GL_LINE, GL11.GL_LINE }, "Polygon Mode");
|
||||
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glFrontFace(GL11.GL_CCW);
|
||||
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTextureReadBack() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture - Reset");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUntouchedPushPopRoundTripLeavesStateIdentical() {
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
|
||||
@@ -6,6 +6,8 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.driverTextureBinding;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -15,14 +17,6 @@ public class GLSM_RetainedState_GLTest {
|
||||
|
||||
private static final StateSet ALL = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
|
||||
|
||||
private static int driverTextureBinding(int unit) {
|
||||
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GL13.glActiveTexture(saved);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static void baseline() {
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.disableBlend();
|
||||
@@ -234,7 +228,7 @@ public class GLSM_RetainedState_GLTest {
|
||||
@Test
|
||||
void aDeletedProgramIsNotRebound() {
|
||||
final RetainedState retained = new RetainedState();
|
||||
final int program = GLSM_StateSet_GLTest.linkTrivialProgram();
|
||||
final int program = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
@@ -243,11 +237,10 @@ public class GLSM_RetainedState_GLTest {
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertEquals(0, GLStateManager.getActiveProgram());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
baseline();
|
||||
|
||||
@@ -24,7 +24,6 @@ class GLSM_SamplerBinding_GLTest {
|
||||
@BeforeEach
|
||||
void requireSamplerObjects() {
|
||||
assumeTrue(RenderSystem.supportsSamplerObjects());
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -35,11 +34,6 @@ class GLSM_SamplerBinding_GLTest {
|
||||
GL33.glBindSampler(unit, 0);
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainGlErrors();
|
||||
}
|
||||
|
||||
private static void drainGlErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static int driverBinding(int unit) {
|
||||
|
||||
+63
-77
@@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -12,6 +14,7 @@ import org.lwjgl.opengl.SharedDrawable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -43,21 +46,13 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
private boolean originalSplashComplete;
|
||||
private Thread originalDrawableGLHolder;
|
||||
|
||||
private static void setSplashComplete(boolean value) {
|
||||
SplashWindow.setSplashComplete(value);
|
||||
}
|
||||
|
||||
private static boolean getSplashComplete() {
|
||||
return SplashWindow.isSplashComplete();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setupSharedDrawable() throws Exception {
|
||||
originalSplashComplete = getSplashComplete();
|
||||
originalSplashComplete = SplashWindow.isSplashComplete();
|
||||
originalDrawableGLHolder = GLStateManager.getDrawableGLHolder();
|
||||
|
||||
// Reset splash state to simulate splash screen scenario
|
||||
setSplashComplete(false);
|
||||
SplashWindow.setSplashComplete(false);
|
||||
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
|
||||
@@ -149,7 +144,7 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
// Restore original state
|
||||
GLStateManager.setDrawableGL(null);
|
||||
GLStateManager.setDrawableGLHolder(originalDrawableGLHolder);
|
||||
setSplashComplete(originalSplashComplete);
|
||||
SplashWindow.setSplashComplete(originalSplashComplete);
|
||||
if (originalSplashComplete) {
|
||||
GLStateManager.markSplashComplete("test");
|
||||
}
|
||||
@@ -214,46 +209,81 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
GL11.glDeleteTextures(texShared[0]);
|
||||
}
|
||||
|
||||
/** One texture parameter through its typed GLSM setter and getter, plus the raw driver read. Values travel as float. */
|
||||
private static final class TexParam {
|
||||
final String name;
|
||||
final float initial;
|
||||
final float changed;
|
||||
final FloatConsumer set;
|
||||
final FloatSupplier glsm;
|
||||
final FloatSupplier driver;
|
||||
|
||||
TexParam(String name, float initial, float changed, FloatConsumer set, FloatSupplier glsm, FloatSupplier driver) {
|
||||
this.name = name;
|
||||
this.initial = initial;
|
||||
this.changed = changed;
|
||||
this.set = set;
|
||||
this.glsm = glsm;
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private interface FloatConsumer {
|
||||
void accept(float value);
|
||||
}
|
||||
|
||||
private interface FloatSupplier {
|
||||
float get();
|
||||
}
|
||||
|
||||
static Stream<TexParam> texParams() {
|
||||
return Stream.of(
|
||||
new TexParam("glTexParameteri MIN_FILTER", GL11.GL_LINEAR, GL11.GL_NEAREST,
|
||||
v -> GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, (int) v),
|
||||
() -> GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER),
|
||||
() -> GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER)),
|
||||
new TexParam("glTexParameterf LOD_BIAS", 0.0f, 1.5f,
|
||||
v -> GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, v),
|
||||
() -> GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS),
|
||||
() -> GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: SharedDrawable sets texture MIN_FILTER, verify both GLSM and actual GL state are correct on both contexts.
|
||||
* Test: SharedDrawable sets a texture parameter, verify both GLSM and actual GL state are correct on both contexts.
|
||||
*/
|
||||
@Test
|
||||
void testTexParameterFromSharedDrawable() throws InterruptedException {
|
||||
@ParameterizedTest
|
||||
@MethodSource("texParams")
|
||||
void testTexParameterFromSharedDrawable(TexParam param) throws InterruptedException {
|
||||
int texId = GL11.glGenTextures();
|
||||
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
param.set.accept(param.initial);
|
||||
|
||||
int initialGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_LINEAR, initialGLSM, "Initial GLSM should be GL_LINEAR");
|
||||
assertEquals(param.initial, param.glsm.get(), 0.001f, "Initial GLSM value");
|
||||
assertEquals(param.initial, param.driver.get(), 0.001f, "Initial GL value");
|
||||
|
||||
int initialGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_LINEAR, initialGL, "Initial GL should be GL_LINEAR");
|
||||
|
||||
// SharedDrawable: bind and change params to GL_NEAREST via GLSM
|
||||
executeOnSharedDrawable(() -> {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
|
||||
int boundTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
assertEquals(texId, boundTex, "SharedDrawable should have texId bound");
|
||||
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
param.set.accept(param.changed);
|
||||
|
||||
int glsmValue = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_NEAREST, glsmValue, "SharedDrawable GLSM should return GL_NEAREST");
|
||||
|
||||
int glValue = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_NEAREST, glValue, "SharedDrawable GL should be GL_NEAREST");
|
||||
assertEquals(param.changed, param.glsm.get(), 0.001f, "SharedDrawable GLSM value");
|
||||
assertEquals(param.changed, param.driver.get(), 0.001f, "SharedDrawable GL value");
|
||||
});
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
|
||||
int mainGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_NEAREST, mainGLSM, "Main thread GLSM should return GL_NEAREST");
|
||||
|
||||
int mainGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_NEAREST, mainGL, "Main thread GL should be GL_NEAREST");
|
||||
assertEquals(param.changed, param.glsm.get(), 0.001f, "Main thread GLSM value");
|
||||
assertEquals(param.changed, param.driver.get(), 0.001f, "Main thread GL value");
|
||||
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
@@ -358,50 +388,6 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test glTexParameterf (float parameters like LOD_BIAS).
|
||||
*/
|
||||
@Test
|
||||
void testTexParameterfFromSharedDrawable() throws InterruptedException {
|
||||
int texId = GL11.glGenTextures();
|
||||
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||
|
||||
float initialGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
float initialGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
assertEquals(0.0f, initialGLSM, 0.001f, "Initial GLSM LOD_BIAS should be 0.0");
|
||||
assertEquals(0.0f, initialGL, 0.001f, "Initial GL LOD_BIAS should be 0.0");
|
||||
|
||||
// SharedDrawable: change LOD_BIAS
|
||||
executeOnSharedDrawable(() -> {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
|
||||
int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
assertEquals(texId, bound, "SharedDrawable should have texId bound");
|
||||
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 1.5f);
|
||||
|
||||
float glsmValue = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
assertEquals(1.5f, glsmValue, 0.001f, "SharedDrawable GLSM should return 1.5");
|
||||
float glValue = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
assertEquals(1.5f, glValue, 0.001f, "SharedDrawable GL should be 1.5");
|
||||
});
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
|
||||
float finalGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
float finalGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
|
||||
assertEquals(1.5f, finalGLSM, 0.001f, "Main thread GLSM should return 1.5");
|
||||
assertEquals(1.5f, finalGL, 0.001f, "Main thread GL should be 1.5");
|
||||
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test glTexImage2D: SharedDrawable uploads texture data, verify dimensions cached correctly.
|
||||
*/
|
||||
@@ -478,7 +464,7 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
GL11.glDeleteTextures(deferredGen);
|
||||
assertTrue(GL11.glIsTexture(texId), "Delete should stay deferred while the splash is not complete");
|
||||
|
||||
setSplashComplete(true);
|
||||
SplashWindow.setSplashComplete(true);
|
||||
flushGen = GLStateManager.glGenTextures();
|
||||
assertFalse(GL11.glIsTexture(texId), "Texture should be gone after the post-splash flush");
|
||||
assertEquals(0, GLStateManager.getBoundTextureForServerState(), "Flush should scrub the main thread binding");
|
||||
|
||||
+19
-11
@@ -5,12 +5,17 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -30,13 +35,23 @@ public class GLSM_SplashCachingCoverage_UnitTest {
|
||||
void leaveSplashWindow() throws IllegalAccessException {
|
||||
SplashWindow.leave();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LESS);
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void depthFuncSetDuringSplashLandsInCache() {
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
static Stream<Arguments> stateSetDuringSplash() {
|
||||
return Stream.of(
|
||||
Arguments.of("depth func", (Runnable) () -> GLStateManager.glDepthFunc(GL11.GL_LEQUAL), GL11.GL_LEQUAL,
|
||||
(IntSupplier) () -> GLStateManager.getDepthState().getFunc()),
|
||||
Arguments.of("cull face", (Runnable) () -> GLStateManager.glCullFace(GL11.GL_FRONT), GL11.GL_FRONT,
|
||||
(IntSupplier) () -> GLStateManager.getPolygonState().getCullFaceMode()));
|
||||
}
|
||||
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache write dropped while caching=false");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("stateSetDuringSplash")
|
||||
void stateSetDuringSplashLandsInCache(String name, Runnable set, int expected, IntSupplier cached) {
|
||||
set.run();
|
||||
|
||||
assertEquals(expected, cached.getAsInt(), "cache write dropped while caching=false");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,11 +152,4 @@ public class GLSM_SplashCachingCoverage_UnitTest {
|
||||
assertFalse(GLStateManager.takeStateSeedPending(), "seed must be consumed exactly once, not once per frame");
|
||||
assertFalse(GLStateManager.takeStateSeedPending(), "seed must stay consumed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cullFaceSetDuringSplashLandsInCache() {
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache write dropped while caching=false");
|
||||
}
|
||||
}
|
||||
+3
-7
@@ -54,18 +54,14 @@ public class GLSM_SplashCompleteLatch_UnitTest {
|
||||
@Test
|
||||
void cachingIsThreadScopedBeforeTheLatchAndGlobalAfter() throws Exception {
|
||||
final AtomicBoolean cachingOffThread = new AtomicBoolean(true);
|
||||
final Runnable probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled());
|
||||
final TestThreads.Body probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled());
|
||||
|
||||
Thread t = new Thread(probe, "non-holder");
|
||||
t.start();
|
||||
t.join();
|
||||
TestThreads.run("non-holder", probe);
|
||||
assertFalse(cachingOffThread.get(), "caching must be holder-scoped during the dual-context window");
|
||||
|
||||
GLStateManager.markSplashComplete("test");
|
||||
|
||||
t = new Thread(probe, "non-holder");
|
||||
t.start();
|
||||
t.join();
|
||||
TestThreads.run("non-holder", probe);
|
||||
assertTrue(cachingOffThread.get(), "after the latch caching is global - this is what the latch buys");
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
-13
@@ -10,13 +10,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@GLCoreTest
|
||||
public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void flattenedCacheTracksNon2DTargets() {
|
||||
drainErrors();
|
||||
final int t3d = GL11.glGenTextures();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
@@ -38,13 +33,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyTargetBindIsNeitherForwardedNorCached() {
|
||||
drainErrors();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE5);
|
||||
@@ -60,13 +53,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribRestoresRecordedTarget() {
|
||||
drainErrors();
|
||||
final int t3d = GL11.glGenTextures();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
@@ -86,13 +77,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unbindIsNotElidedAcrossTargets() {
|
||||
drainErrors();
|
||||
final int t2d = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE4);
|
||||
@@ -100,11 +89,9 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "2D unbind after a 3D unbind must reach the driver");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
drainErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -79,6 +79,8 @@ public class GLSM_TextureUploadGeneration_GLTest {
|
||||
final int afterSub = info.getUploadGeneration();
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 1, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(4));
|
||||
assertEquals(afterSub, info.getUploadGeneration(), "only level 0 is tracked");
|
||||
// Level 1 was never allocated, so the driver rejects that upload.
|
||||
assertEquals(GL11.GL_INVALID_OPERATION, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.util.GLSMUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -15,6 +16,13 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
@GLCompatTest
|
||||
public class GLSM_VAO_UnitTest {
|
||||
|
||||
@AfterEach
|
||||
void resetClientArrayState() {
|
||||
glBindVertexArray(0);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultStates() {
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING Initial State");
|
||||
@@ -36,14 +44,14 @@ public class GLSM_VAO_UnitTest {
|
||||
final int vaoVertexType = GL11.GL_INT;
|
||||
final int vaoVertexStride = vaoVertexSize * 4;
|
||||
|
||||
GL13.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
verifyState(GL13.GL_CLIENT_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "GL_CLIENT_ACTIVE_TEXTURE - Setup");
|
||||
|
||||
// Set the vertex attrib to a defined state
|
||||
int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GL11.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0);
|
||||
GLStateManager.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0L);
|
||||
|
||||
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, true);
|
||||
verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Enabled");
|
||||
@@ -74,7 +82,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLSMUtil.setClientState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled);
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Enabled");
|
||||
|
||||
GL11.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0);
|
||||
GLStateManager.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0L);
|
||||
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Set");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Set");
|
||||
@@ -117,7 +125,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@@ -138,7 +146,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@@ -159,7 +167,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@@ -179,7 +187,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ public class GLSM_ValueStackCow_GLTest {
|
||||
if (fixtureTexA == -1) {
|
||||
fixtureTexA = GLStateManager.glGenTextures();
|
||||
fixtureTexB = GLStateManager.glGenTextures();
|
||||
final int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fixtureTexB);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, bound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,4 +43,15 @@ class GLVersionParseTest {
|
||||
assertFalse(RenderSystem.isContextValid(4, 4, -1, false));
|
||||
assertTrue(RenderSystem.isContextValid(3, 3, 33, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void probeCapAfterRejection() {
|
||||
assertEquals(41, RenderSystem.probeCapAfterRejection(46, 41));
|
||||
assertEquals(33, RenderSystem.probeCapAfterRejection(46, 11));
|
||||
assertEquals(33, RenderSystem.probeCapAfterRejection(46, 21));
|
||||
assertEquals(32, RenderSystem.probeCapAfterRejection(33, 11));
|
||||
assertEquals(45, RenderSystem.probeCapAfterRejection(46, 46));
|
||||
assertEquals(45, RenderSystem.probeCapAfterRejection(46, -1));
|
||||
assertEquals(33, RenderSystem.probeCapAfterRejection(46, 33));
|
||||
}
|
||||
}
|
||||
+19
-27
@@ -4,12 +4,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -29,10 +29,10 @@ class ImmediateModeThreadIsolationTest {
|
||||
final AtomicReference<DirectTessellator> bg1 = new AtomicReference<>();
|
||||
final AtomicReference<DirectTessellator> bg2 = new AtomicReference<>();
|
||||
|
||||
final Thread t1 = new Thread(() -> bg1.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-1");
|
||||
final Thread t2 = new Thread(() -> bg2.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-2");
|
||||
t1.start(); t2.start();
|
||||
t1.join(); t2.join();
|
||||
final TestThreads.Worker t1 = TestThreads.start("iso-test-1", () -> bg1.set(ImmediateModeRecorder.getInternalTessellator()));
|
||||
final TestThreads.Worker t2 = TestThreads.start("iso-test-2", () -> bg2.set(ImmediateModeRecorder.getInternalTessellator()));
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
assertNotNull(bg1.get());
|
||||
assertNotNull(bg2.get());
|
||||
@@ -43,28 +43,22 @@ class ImmediateModeThreadIsolationTest {
|
||||
|
||||
@Test
|
||||
void concurrentImmediateModeDoesNotCorruptRecording() throws Exception {
|
||||
final List<Throwable> failures = new CopyOnWriteArrayList<>();
|
||||
final CountDownLatch start = new CountDownLatch(1);
|
||||
final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
|
||||
final Runnable spam = () -> {
|
||||
try {
|
||||
start.await();
|
||||
while (!stop.get()) {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
failures.add(t);
|
||||
final TestThreads.Body spam = () -> {
|
||||
start.await();
|
||||
while (!stop.get()) {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
}
|
||||
};
|
||||
final Thread t1 = new Thread(spam, "iso-spam-1");
|
||||
final Thread t2 = new Thread(spam, "iso-spam-2");
|
||||
t1.start(); t2.start();
|
||||
final TestThreads.Worker t1 = TestThreads.start("iso-spam-1", spam);
|
||||
final TestThreads.Worker t2 = TestThreads.start("iso-spam-2", spam);
|
||||
start.countDown();
|
||||
|
||||
final List<Integer> lists = new ArrayList<>();
|
||||
@@ -83,10 +77,10 @@ class ImmediateModeThreadIsolationTest {
|
||||
}
|
||||
} finally {
|
||||
stop.set(true);
|
||||
t1.join(5000); t2.join(5000);
|
||||
}
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
assertTrue(failures.isEmpty(), "background immediate mode threw: " + failures);
|
||||
for (int list : lists) {
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(list);
|
||||
assertNotNull(compiled, "list " + list + " should be compiled");
|
||||
@@ -97,9 +91,7 @@ class ImmediateModeThreadIsolationTest {
|
||||
|
||||
@Test
|
||||
void sweepFreesDeadThreadTessellators() throws Exception {
|
||||
final Thread t = new Thread(() -> ImmediateModeRecorder.getInternalTessellator(), "iso-sweep");
|
||||
t.start();
|
||||
t.join();
|
||||
TestThreads.run("iso-sweep", ImmediateModeRecorder::getInternalTessellator);
|
||||
final int before = ImmediateModeRecorder.getThreadTessellatorCount();
|
||||
assertTrue(before >= 1);
|
||||
ImmediateModeRecorder.cleanupOrphanTessellators();
|
||||
|
||||
@@ -2,17 +2,19 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@GLCoreTest
|
||||
class InstancedQuadDrawGLTest {
|
||||
@@ -26,7 +28,7 @@ class InstancedQuadDrawGLTest {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
program = buildProgram();
|
||||
program = GlTestSupport.program(VS, FS);
|
||||
GLStateManager.glUseProgram(program);
|
||||
|
||||
final FloatBuffer verts = BufferUtils.createFloatBuffer(8);
|
||||
@@ -42,63 +44,39 @@ class InstancedQuadDrawGLTest {
|
||||
ebo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, idx, GL15.GL_STATIC_DRAW);
|
||||
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
if (program != 0) GL20.glDeleteProgram(program);
|
||||
if (program != 0) GLStateManager.glDeleteProgram(program);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
|
||||
if (ebo != 0) GLStateManager.glDeleteBuffers(ebo);
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawArraysInstanced_quads_noInvalidEnum() {
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced array draw must be emulated, not rejected");
|
||||
static Stream<Arguments> draws() {
|
||||
return Stream.of(
|
||||
draw("GL_QUADS instanced array draw must be emulated, not rejected", ebo -> GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3)),
|
||||
draw("non-quad instanced array draw passes through cleanly", ebo -> {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3);
|
||||
}),
|
||||
draw("GL_QUADS instanced element draw must be emulated, not rejected", ebo -> {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawArraysInstanced_triangles_fastPathClean() {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "non-quad instanced array draw passes through cleanly");
|
||||
private static Arguments draw(String name, IntConsumer body) {
|
||||
return Arguments.of(name, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawElementsInstanced_quads_noInvalidEnum() {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced element draw must be emulated, not rejected");
|
||||
}
|
||||
|
||||
private static int buildProgram() {
|
||||
final int vs = compile(GL20.GL_VERTEX_SHADER, VS);
|
||||
final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS);
|
||||
final int p = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(p, vs);
|
||||
GL20.glAttachShader(p, fs);
|
||||
GL20.glLinkProgram(p);
|
||||
assertNotEquals(0, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), "test shader must link: " + GL20.glGetProgramInfoLog(p, 1024));
|
||||
GL20.glDeleteShader(vs);
|
||||
GL20.glDeleteShader(fs);
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(s, src);
|
||||
GL20.glCompileShader(s);
|
||||
assertNotEquals(0, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), "test shader must compile: " + GL20.glGetShaderInfoLog(s, 1024));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) { /* clear */ }
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("draws")
|
||||
void instancedDrawRaisesNoGlError(String name, IntConsumer draw) {
|
||||
draw.accept(ebo);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), name);
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,6 @@ class QuadConverterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void triangulateQuads_zeroQuads_noop() {
|
||||
QuadConverter.triangulateQuads(0L, GL11.GL_UNSIGNED_INT, 0L, GL11.GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void triangulateQuads_unsupportedTypes_throw() {
|
||||
final ByteBuffer src = MemoryUtilities.memAlloc(16);
|
||||
@@ -240,8 +235,6 @@ class QuadConverterTest {
|
||||
}
|
||||
}
|
||||
|
||||
// === helpers ===
|
||||
|
||||
private static void assertUintIndices(ByteBuffer buf, int... expected) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], buf.getInt(i * 4), "uint index[" + i + "]");
|
||||
|
||||
@@ -1,72 +1,42 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@GLCoreTest
|
||||
class ReadbackGLTest {
|
||||
|
||||
@AfterEach
|
||||
void noErrors() {
|
||||
void resetPack() {
|
||||
ReadbackFixture.resetPack();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba8() {
|
||||
ReadbackFixture.rgba8();
|
||||
static Stream<Arguments> readbacks() {
|
||||
return Stream.of(
|
||||
format("rgba8", ReadbackFixture::rgba8),
|
||||
format("packState", ReadbackFixture::packState),
|
||||
format("pixelPackBuffer", ReadbackFixture::pixelPackBuffer),
|
||||
format("rgb8", ReadbackFixture::rgb8),
|
||||
format("rgba16f", ReadbackFixture::rgba16f),
|
||||
format("rgba32f", ReadbackFixture::rgba32f),
|
||||
format("r8", ReadbackFixture::r8),
|
||||
format("rg16f", ReadbackFixture::rg16f),
|
||||
format("rgb10a2", ReadbackFixture::rgb10a2),
|
||||
format("r11g11b10f", ReadbackFixture::r11g11b10f),
|
||||
format("depthStencil", ReadbackFixture::depthStencil));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packState() {
|
||||
ReadbackFixture.packState();
|
||||
private static Arguments format(String name, Runnable body) {
|
||||
return Arguments.of(name, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelPackBuffer() {
|
||||
ReadbackFixture.pixelPackBuffer();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb8() {
|
||||
ReadbackFixture.rgb8();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba16f() {
|
||||
ReadbackFixture.rgba16f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba32f() {
|
||||
ReadbackFixture.rgba32f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void r8() {
|
||||
ReadbackFixture.r8();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rg16f() {
|
||||
ReadbackFixture.rg16f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb10a2() {
|
||||
ReadbackFixture.rgb10a2();
|
||||
}
|
||||
|
||||
@Test
|
||||
void r11g11b10f() {
|
||||
ReadbackFixture.r11g11b10f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void depthStencil() {
|
||||
ReadbackFixture.depthStencil();
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("readbacks")
|
||||
void readback(String name, Runnable body) {
|
||||
body.run();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
@@ -71,8 +72,7 @@ public class DSAUnsupportedBindingTest {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, y);
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, x);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {
|
||||
}
|
||||
drainErrors();
|
||||
|
||||
dsa.texParameteri(t, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class FFPUniformBlockLayoutGLTest {
|
||||
void driverOffsetsMatchJavaConstants() {
|
||||
final VertexKey vk = VertexKey.fromState(false, false, false, false, 0);
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
final Program program = Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk), null);
|
||||
final Program program = Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk));
|
||||
try {
|
||||
final int programId = program.getProgramId();
|
||||
final int blockIndex = GL31.glGetUniformBlockIndex(programId, FFPUniformBlock.BLOCK_NAME);
|
||||
|
||||
@@ -42,7 +42,7 @@ class FFPUniformValuesTest {
|
||||
|
||||
final VertexKey vk = VertexKey.fromState(false, false, false, false, 0);
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
final Program program = Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk), null);
|
||||
final Program program = Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk));
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
try {
|
||||
GL20.glUseProgram(program.getProgramId());
|
||||
|
||||
+41
-43
@@ -7,15 +7,19 @@ import org.joml.Vector3f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -112,12 +116,6 @@ class FFPVertexLightingGLTest {
|
||||
+ " light1=" + GLStateManager.getLightStates()[1].isEnabled();
|
||||
}
|
||||
|
||||
private static FloatBuffer floats(float... values) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length);
|
||||
buf.put(values).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void buildTriangle() {
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
@@ -156,9 +154,8 @@ class FFPVertexLightingGLTest {
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "FFP draw must not raise a GL error");
|
||||
|
||||
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
|
||||
GL11.glReadPixels(400, 200, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
|
||||
return new float[] { (pixel.get(0) & 0xFF) / 255.0f, (pixel.get(1) & 0xFF) / 255.0f, (pixel.get(2) & 0xFF) / 255.0f };
|
||||
final int[] pixel = FfpFixture.readPixel(400, 200);
|
||||
return new float[] { pixel[0] / 255.0f, pixel[1] / 255.0f, pixel[2] / 255.0f };
|
||||
}
|
||||
|
||||
private void assertSolverMatchesShader(String context) {
|
||||
@@ -170,23 +167,20 @@ class FFPVertexLightingGLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void faceOnNormalMatchesGeneratedShader() {
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
assertSolverMatchesShader("face-on normal");
|
||||
static Stream<Arguments> normals() {
|
||||
return Stream.of(
|
||||
Arguments.of("face-on normal", 0.0f, 0.0f, 1.0f, Float.POSITIVE_INFINITY),
|
||||
// A 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light
|
||||
Arguments.of("sign normal", 0.0f, 0.0f, -0.010416667f, 0.45f),
|
||||
Arguments.of("tilted normal", 0.3f, 0.8f, 0.5f, Float.POSITIVE_INFINITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
void signNormalMatchesGeneratedShader() {
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, -0.010416667f);
|
||||
assertSolverMatchesShader("sign normal");
|
||||
assertTrue(factor.x < 0.45f, "a 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light, got " + factor.x);
|
||||
}
|
||||
|
||||
@Test
|
||||
void tiltedNormalMatchesGeneratedShader() {
|
||||
GLStateManager.glNormal3f(0.3f, 0.8f, 0.5f);
|
||||
assertSolverMatchesShader("tilted normal");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("normals")
|
||||
void normalMatchesGeneratedShader(String name, float x, float y, float z, float factorCeiling) {
|
||||
GLStateManager.glNormal3f(x, y, z);
|
||||
assertSolverMatchesShader(name);
|
||||
assertTrue(factor.x < factorCeiling, name + ": factor must stay below " + factorCeiling + ", got " + factor.x);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,27 +200,31 @@ class FFPVertexLightingGLTest {
|
||||
assertTrue(factor.x > 1.2f, "normalize restores the unit normal, which must pick up more diffuse than rescale's 0.5-long one, got " + factor.x);
|
||||
}
|
||||
|
||||
@Test
|
||||
void materialEmissionIsNotRepresentable() {
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f));
|
||||
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "emission is additive, so the lit colour is not vertexColor * factor");
|
||||
static Stream<Arguments> unrepresentableStates() {
|
||||
return Stream.of(
|
||||
unrepresentable("emission is additive, so the lit colour is not vertexColor * factor",
|
||||
() -> GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f))),
|
||||
unrepresentable("specular is additive, so the lit colour is not vertexColor * factor", () -> {
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}),
|
||||
unrepresentable("only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour",
|
||||
() -> GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION)));
|
||||
}
|
||||
|
||||
private static Arguments unrepresentable(String reason, Runnable state) {
|
||||
return Arguments.of(reason, state);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("unrepresentableStates")
|
||||
void additiveLightingIsNotRepresentable(String reason, Runnable state) {
|
||||
state.run();
|
||||
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), reason);
|
||||
assertEquals(IDENTITY_FACTOR, factor, "a rejected solve must leave an identity factor");
|
||||
}
|
||||
|
||||
@Test
|
||||
void specularContributionIsNotRepresentable() {
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "specular is additive, so the lit colour is not vertexColor * factor");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAmbientAndDiffuseColorMaterialIsNotRepresentable() {
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION);
|
||||
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightsBeyondTheFfpCountAreIgnored() {
|
||||
assertTrue(FFPVertexLighting.modulatesVertexColor(factor), FFPVertexLightingGLTest::gateState);
|
||||
|
||||
+1
-7
@@ -11,10 +11,10 @@ import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@@ -25,12 +25,6 @@ class FfpUniformBlockDedupTest {
|
||||
return GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_START, FFPUniformBlock.BINDING_POINT);
|
||||
}
|
||||
|
||||
private static FloatBuffer floats(float... values) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length);
|
||||
buf.put(values).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static DoubleBuffer doubles(double... values) {
|
||||
final DoubleBuffer buf = BufferUtils.createDoubleBuffer(values.length);
|
||||
buf.put(values).flip();
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.NORMAL_BIT;
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -100,7 +101,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLSMHooks.blendHandler = new LoggingBlendHandler();
|
||||
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
assertEquals(List.of("resolve", "flush"), log);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ class LogicOpGLTest {
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
LogicOpFixture.deleteResources();
|
||||
FfpFixture.resetFfpState();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -48,7 +47,6 @@ class LogicOpGLTest {
|
||||
assertRgb(op, src);
|
||||
}
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class ProgramUniformTrackingTest {
|
||||
private static Program buildProgram(boolean hasColor) {
|
||||
final VertexKey vk = VertexKey.fromState(hasColor, false, false, false, 0);
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
return Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk), null);
|
||||
return Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,15 +5,18 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class WideLineClipGLTest {
|
||||
@@ -23,7 +26,7 @@ class WideLineClipGLTest {
|
||||
private static final float LINE_WIDTH = 3.0f;
|
||||
private static final int BACKGROUND = 0xFF000000;
|
||||
|
||||
private static final float[] IN_FRONT = { 0f, -1f, -2f };
|
||||
private static final float[] IN_FRONT = { 0f, -0.9875f, -2f };
|
||||
private static final float[] BEHIND = { 0f, 0.8f, 2f };
|
||||
|
||||
private boolean savedEmulation;
|
||||
@@ -32,7 +35,7 @@ class WideLineClipGLTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
assumeTrue(GLStateManager.supportsGeometryShaders());
|
||||
FfpFixture.routeVertexFormats(true);
|
||||
savedEmulation = GLStateManager.wideLineEmulationEnabled;
|
||||
GLStateManager.wideLineEmulationEnabled = true;
|
||||
|
||||
@@ -51,6 +54,7 @@ class WideLineClipGLTest {
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
FfpFixture.routeVertexFormats(false);
|
||||
GLStateManager.wideLineEmulationEnabled = savedEmulation;
|
||||
GLStateManager.glLineWidth(1.0f);
|
||||
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
|
||||
@@ -59,17 +63,18 @@ class WideLineClipGLTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
FfpFixture.resetFfpState();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineEndingBehindTheCameraKeepsItsWidth() {
|
||||
assertConstantWidth(IN_FRONT, BEHIND);
|
||||
static Stream<Arguments> linesCrossingTheNearPlane() {
|
||||
return Stream.of(
|
||||
Arguments.of("line ending behind the camera", IN_FRONT, BEHIND),
|
||||
Arguments.of("line starting behind the camera", BEHIND, IN_FRONT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineStartingBehindTheCameraKeepsItsWidth() {
|
||||
assertConstantWidth(BEHIND, IN_FRONT);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("linesCrossingTheNearPlane")
|
||||
void lineCrossingTheNearPlaneKeepsItsWidth(String name, float[] start, float[] end) {
|
||||
assertConstantWidth(start, end);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,6 +85,60 @@ class WideLineClipGLTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static void orthographic() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
private static float pixelCenter(int pixel) {
|
||||
return (pixel + 0.5f) * 2.0f / SIZE - 1.0f;
|
||||
}
|
||||
|
||||
private static int litInColumn(int[] pixels, int x) {
|
||||
int lit = 0;
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
if (pixels[row * SIZE + x] != BACKGROUND) lit++;
|
||||
}
|
||||
return lit;
|
||||
}
|
||||
|
||||
private int[] drawLineOnce(float[] start, float[] end) {
|
||||
final int[] pixels = drawLine(start, end);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
vbo = 0;
|
||||
vao = 0;
|
||||
return pixels;
|
||||
}
|
||||
|
||||
@Test
|
||||
void wideLineCoversItsFirstPixelButNotItsLast() {
|
||||
orthographic();
|
||||
GLStateManager.glLineWidth(2.0f);
|
||||
final float y = pixelCenter(16);
|
||||
final int[] pixels = drawLineOnce(new float[] { pixelCenter(40), y, 0f }, new float[] { pixelCenter(20), y, 0f });
|
||||
assertEquals(2, litInColumn(pixels, 40), "first pixel of a right-to-left line");
|
||||
assertEquals(0, litInColumn(pixels, 20), "last pixel of a right-to-left line");
|
||||
assertEquals(2, litInColumn(pixels, 21), "pixel next to the last");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cullingKeepsWideLinesInBothDirections() {
|
||||
orthographic();
|
||||
GLStateManager.glLineWidth(2.0f);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
try {
|
||||
final int[] rightward = drawLineOnce(new float[] { -0.8f, 0f, 0f }, new float[] { 0.8f, 0f, 0f });
|
||||
final int[] leftward = drawLineOnce(new float[] { 0.8f, 0f, 0f }, new float[] { -0.8f, 0f, 0f });
|
||||
assertEquals(2, litInColumn(rightward, SIZE / 2), "left-to-right line with culling on");
|
||||
assertEquals(2, litInColumn(leftward, SIZE / 2), "right-to-left line with culling on");
|
||||
assertTrue(GLStateManager.getCullState().isEnabled(), "the game's cull state survives");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConstantWidth(float[] start, float[] end) {
|
||||
final int[] pixels = drawLine(start, end);
|
||||
for (int row = 0; row < SIZE / 4; row++) {
|
||||
@@ -90,7 +149,6 @@ class WideLineClipGLTest {
|
||||
final int y = row, count = lit;
|
||||
assertTrue(count > 0 && count <= LINE_WIDTH + 1, () -> "row " + y + " is " + count + " pixels wide");
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
private int[] drawLine(float[] start, float[] end) {
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
@@ -12,6 +15,7 @@ import org.objectweb.asm.tree.MethodNode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
@@ -71,14 +75,16 @@ class ModRedirectorTest {
|
||||
assertEquals(coreExclusions.length + 2, exclusions.size(), exclusions.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exclusionsCombineCoreLwjgl3AndExtra() {
|
||||
assertStandardExclusions(redirector().getTransformerExclusions());
|
||||
static Stream<Arguments> exclusionLists() {
|
||||
return Stream.of(
|
||||
Arguments.of("ModRedirector", redirector().getTransformerExclusions()),
|
||||
Arguments.of("EarlyRedirectorCore", new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void earlyRedirectorExclusionsCombineCoreLwjgl3AndExtra() {
|
||||
assertStandardExclusions(new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions());
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("exclusionLists")
|
||||
void exclusionsCombineCoreLwjgl3AndExtra(String name, String[] exclusions) {
|
||||
assertStandardExclusions(exclusions);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,22 +92,17 @@ class ModRedirectorTest {
|
||||
assertNull(redirector().transform("com.example.TestClass", null, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludedPrefixReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
assertSame(original, redirector().transform(EXTRA_EXCLUSION + ".TestClass", original, this));
|
||||
static Stream<Arguments> untouchedClasses() {
|
||||
return Stream.of(
|
||||
Arguments.of("extra exclusion prefix", EXTRA_EXCLUSION + ".TestClass", bytesWithGlCall()),
|
||||
Arguments.of("core exclusion prefix", "org.lwjgl.opengl.TestClass", bytesWithGlCall()),
|
||||
Arguments.of("no GL call", "com.example.TestClass", bytesWithoutGlCall()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void coreExcludedPrefixReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithGlCall();
|
||||
assertSame(original, redirector().transform("org.lwjgl.opengl.TestClass", original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedClassReturnsOriginalArray() {
|
||||
final byte[] original = bytesWithoutGlCall();
|
||||
assertSame(original, redirector().transform("com.example.TestClass", original, this));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("untouchedClasses")
|
||||
void untouchedClassReturnsOriginalArray(String name, String className, byte[] original) {
|
||||
assertSame(original, redirector().transform(className, original, this));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+20
-24
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -8,32 +10,26 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class TracyOptionsTest {
|
||||
|
||||
@Test
|
||||
void resolveBooleanFallsBackToConfigWhenNoOverride() {
|
||||
assertTrue(TracyOptions.resolveBoolean(null, true));
|
||||
assertFalse(TracyOptions.resolveBoolean(null, false));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource(value = {
|
||||
"no override falls back to config true, null, true, true",
|
||||
"no override falls back to config false, null, false, false",
|
||||
"override true wins, true, false, true",
|
||||
"override false wins, false, true, false"
|
||||
}, nullValues = "null")
|
||||
void resolveBoolean(String name, Boolean override, boolean config, boolean expected) {
|
||||
assertEquals(expected, TracyOptions.resolveBoolean(override, config));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveBooleanOverrideWinsEitherWay() {
|
||||
assertTrue(TracyOptions.resolveBoolean(Boolean.TRUE, false));
|
||||
assertFalse(TracyOptions.resolveBoolean(Boolean.FALSE, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsFallsBackToConfigWhenNoOverride() {
|
||||
assertEquals(4096, TracyOptions.resolveMaxSrcLocs(null, 4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsOverrideWins() {
|
||||
assertEquals(8192, TracyOptions.resolveMaxSrcLocs(8192, 4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsClampsBothConfigAndOverrideToTheMinimum() {
|
||||
assertEquals(16, TracyOptions.resolveMaxSrcLocs(null, 4));
|
||||
assertEquals(16, TracyOptions.resolveMaxSrcLocs(1, 4096));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource(value = {
|
||||
"no override falls back to config, null, 4096, 4096",
|
||||
"override wins, 8192, 4096, 8192",
|
||||
"config is clamped to the minimum, null, 4, 16",
|
||||
"override is clamped to the minimum, 1, 4096, 16"
|
||||
}, nullValues = "null")
|
||||
void resolveMaxSrcLocs(String name, Integer override, int config, int expected) {
|
||||
assertEquals(expected, TracyOptions.resolveMaxSrcLocs(override, config));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-24
@@ -62,25 +62,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
MemoryUtilities.memFree(read);
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileAndExecute_firesLiveAndBakes() {
|
||||
final int vbo = defaultQuadVbo();
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
final int vao = setupPosColorVao(vbo);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(3, cmd.getIndexCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void quads_triangulateAtRecord() {
|
||||
final int vbo = defaultQuadVbo();
|
||||
@@ -457,11 +438,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
assertNotEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "VAOs from separate batches must be distinct");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullListProbeCompiles() {
|
||||
assertNull(DisplayListManager.getDisplayList(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiLayoutDraws_emitInInsertionOrder() {
|
||||
// Two distinct layouts. The builder uses a LinkedHashMap, so the first
|
||||
|
||||
+18
-63
@@ -1,24 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
|
||||
|
||||
@Test
|
||||
void vaoEboMapSweep_clearsStaleEntryOnForeignVao() {
|
||||
@ParameterizedTest(name = "replayed before delete={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void listDelete_clearsSharedEboFromForeignVao(boolean replayFirst) {
|
||||
final int vbo = defaultQuadVbo();
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int vao = setupPosColorVao(vbo);
|
||||
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
|
||||
|
||||
if (replayFirst) compiledOf(list).render();
|
||||
|
||||
final IndexedDrawBatch batch = firstBatch(list);
|
||||
final int sharedEbo = batch.getSharedEBO();
|
||||
|
||||
@@ -31,32 +37,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
|
||||
untrackList(list);
|
||||
|
||||
GLStateManager.glBindVertexArray(foreignVao);
|
||||
assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO — non-gen crash imminent");
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO - non-gen crash imminent");
|
||||
|
||||
@Test
|
||||
void bindBufferCycle_afterListDelete_isGlNoError() {
|
||||
final int vbo = defaultQuadVbo();
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int vao = setupPosColorVao(vbo);
|
||||
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
|
||||
|
||||
final int sharedEbo = firstBatch(list).getSharedEBO();
|
||||
final int foreignVao = newVao();
|
||||
GLStateManager.glBindVertexArray(foreignVao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
untrackList(list);
|
||||
|
||||
GLStateManager.glBindVertexArray(foreignVao);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
final int err = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " — cache carried a freed id through a VAO rebind");
|
||||
assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " - cache carried a freed id through a VAO rebind");
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
@@ -85,8 +72,8 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
|
||||
|
||||
@Test
|
||||
void glDeleteProgram_zero_isNoOpAndLeavesActiveProgram() {
|
||||
final int prog = linkMinimalProgram();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
|
||||
drainErrors();
|
||||
|
||||
GLStateManager.glUseProgram(prog);
|
||||
assertEquals(prog, GLStateManager.getActiveProgram());
|
||||
@@ -97,13 +84,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
|
||||
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(prog);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
@Test
|
||||
void activeProgram_retainedAfterDelete_perGlSpec() {
|
||||
final int prog = linkMinimalProgram();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
|
||||
drainErrors();
|
||||
|
||||
GLStateManager.glUseProgram(prog);
|
||||
assertEquals(prog, GLStateManager.getActiveProgram());
|
||||
@@ -114,38 +101,6 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
|
||||
|
||||
GLStateManager.glUseProgram(0);
|
||||
assertEquals(0, GLStateManager.getActiveProgram(), "activeProgram clears once the deleted program is unbound");
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
}
|
||||
|
||||
private static int linkMinimalProgram() {
|
||||
final int vert = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER);
|
||||
GLStateManager.glShaderSource(vert, MIN_VERT_SRC);
|
||||
GLStateManager.glCompileShader(vert);
|
||||
if (GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||
throw new AssertionError("vertex shader failed to compile: " + GL20.glGetShaderInfoLog(vert, 4096));
|
||||
}
|
||||
|
||||
final int frag = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER);
|
||||
GLStateManager.glShaderSource(frag, MIN_FRAG_SRC);
|
||||
GLStateManager.glCompileShader(frag);
|
||||
if (GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||
throw new AssertionError("fragment shader failed to compile: " + GL20.glGetShaderInfoLog(frag, 4096));
|
||||
}
|
||||
|
||||
final int prog = GLStateManager.glCreateProgram();
|
||||
GLStateManager.glAttachShader(prog, vert);
|
||||
GLStateManager.glAttachShader(prog, frag);
|
||||
GLStateManager.glLinkProgram(prog);
|
||||
if (GL20.glGetProgrami(prog, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
throw new AssertionError("program failed to link: " + GL20.glGetProgramInfoLog(prog, 4096));
|
||||
}
|
||||
GLStateManager.glDetachShader(prog, vert);
|
||||
GLStateManager.glDetachShader(prog, frag);
|
||||
GLStateManager.glDeleteShader(vert);
|
||||
GLStateManager.glDeleteShader(frag);
|
||||
return prog;
|
||||
}
|
||||
|
||||
private static final String MIN_VERT_SRC = "#version 330 core\nvoid main(){gl_Position=vec4(0.0);}";
|
||||
private static final String MIN_FRAG_SRC = "#version 330 core\nout vec4 oC;void main(){oC=vec4(1.0);}";
|
||||
}
|
||||
+5
-33
@@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -36,7 +37,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
GL11.glViewport(0, 0, 16, 16);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
final int query = GL15.glGenQueries();
|
||||
GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, query);
|
||||
@@ -92,7 +93,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
GLStateManager.glBindVertexArray(distractorVao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
compiledOf(list).render();
|
||||
|
||||
@@ -103,35 +104,6 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayThenDelete_thenBindBufferCycle_isGlNoError() {
|
||||
final int vbo = defaultQuadVbo();
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int vao = setupPosColorVao(vbo);
|
||||
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
|
||||
|
||||
compiledOf(list).render();
|
||||
|
||||
|
||||
final int sharedEbo = firstBatch(list).getSharedEBO();
|
||||
final int foreignVao = newVao();
|
||||
GLStateManager.glBindVertexArray(foreignVao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
untrackList(list);
|
||||
|
||||
GLStateManager.glBindVertexArray(foreignVao);
|
||||
assertEquals(0, GLStateManager.getBoundEBO(), "batched EBO id survived list delete in vaoEboMap — non-gen crash imminent");
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
final int err = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, err, () -> "post-delete bindBuffer(" + prevEbo + ") hit GL 0x" + Integer.toHexString(err));
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_doesNotRestoreVAO_backToBackDraws() {
|
||||
final int vbo = defaultQuadVbo();
|
||||
@@ -153,7 +125,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
|
||||
final int distractor = newVao();
|
||||
GLStateManager.glBindVertexArray(distractor);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
compiledOf(list).render();
|
||||
|
||||
@@ -229,7 +201,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
drainErrors();
|
||||
|
||||
compiledOf(list).render();
|
||||
compiledOf(list).render();
|
||||
|
||||
-33
@@ -3,10 +3,8 @@ package com.gtnewhorizons.angelica.glsm.recording;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -33,37 +31,6 @@ class RecordingDataStructuresTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompiledDisplayListCreation() {
|
||||
// Create a display list with PushMatrix, Scale, PopMatrix
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glScalef(2.0f, 2.0f, 2.0f);
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify the compiled display list
|
||||
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled, "Display list should be compiled");
|
||||
assertNotNull(compiled.getCommandBuffer(), "Command buffer should not be null");
|
||||
|
||||
// Get command counts to verify structure
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
|
||||
// Should have PushMatrix and PopMatrix
|
||||
assertEquals(1, counts.getOrDefault(GLCommand.PUSH_MATRIX, 0), "Should have 1 PushMatrix");
|
||||
assertEquals(1, counts.getOrDefault(GLCommand.POP_MATRIX, 0), "Should have 1 PopMatrix");
|
||||
|
||||
// Scale should be collapsed into a MultMatrix (optimizer collapses transforms)
|
||||
// Or if in debug mode, might have individual commands - just check we have some transforms
|
||||
int transformCount = counts.getOrDefault(GLCommand.MULT_MATRIX, 0)
|
||||
+ counts.getOrDefault(GLCommand.SCALE, 0);
|
||||
assertTrue(transformCount >= 0, "Should have transform commands");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImmediateModeRecorder() {
|
||||
DirectTessellator recorder = ImmediateModeRecorder.getInternalTessellator();
|
||||
|
||||
+47
-51
@@ -1,8 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
@@ -15,59 +20,50 @@ class AttribLayoutKeyTest {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Test
|
||||
void standardPosColorUvNormal_isAlignedAndPacked() {
|
||||
final AttribLayoutKey k = new AttribLayoutKey(
|
||||
new int[]{0, 1, 2, 4},
|
||||
new int[]{3, 4, 2, 3},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE},
|
||||
new boolean[]{false, true, false, true});
|
||||
assertArrayEquals(new int[]{0, 12, 16, 24}, offsetsOf(k));
|
||||
assertEquals(28, k.stride());
|
||||
static Stream<Arguments> layouts() {
|
||||
return Stream.of(
|
||||
Arguments.of("standard pos/color/uv/normal is aligned and packed",
|
||||
new AttribLayoutKey(
|
||||
new int[]{0, 1, 2, 4},
|
||||
new int[]{3, 4, 2, 3},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE},
|
||||
new boolean[]{false, true, false, true}),
|
||||
new int[]{0, 12, 16, 24}, 28),
|
||||
Arguments.of("pos/uv/normal3 rounds the tail stride to 24",
|
||||
new AttribLayoutKey(
|
||||
new int[]{0, 2, 4},
|
||||
new int[]{3, 2, 3},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE},
|
||||
new boolean[]{false, false, true}),
|
||||
new int[]{0, 12, 20}, 24),
|
||||
Arguments.of("byte attrib in the middle rounds the next offset",
|
||||
new AttribLayoutKey(
|
||||
new int[]{0, 4, 2},
|
||||
new int[]{3, 3, 2},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT},
|
||||
new boolean[]{false, true, false}),
|
||||
new int[]{0, 12, 16}, 24),
|
||||
Arguments.of("all-byte layout packs tight",
|
||||
new AttribLayoutKey(
|
||||
new int[]{0, 1, 2},
|
||||
new int[]{3, 4, 2},
|
||||
new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE},
|
||||
new boolean[]{true, true, false}),
|
||||
new int[]{0, 3, 7}, 9),
|
||||
Arguments.of("shorts after a byte round offset and stride to even",
|
||||
new AttribLayoutKey(
|
||||
new int[]{0, 1},
|
||||
new int[]{1, 2},
|
||||
new int[]{GL11.GL_BYTE, GL11.GL_SHORT},
|
||||
new boolean[]{true, false}),
|
||||
new int[]{0, 2}, 6));
|
||||
}
|
||||
|
||||
@Test
|
||||
void posUvNormal3_roundsTailStrideTo24() {
|
||||
final AttribLayoutKey k = new AttribLayoutKey(
|
||||
new int[]{0, 2, 4},
|
||||
new int[]{3, 2, 3},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE},
|
||||
new boolean[]{false, false, true});
|
||||
assertArrayEquals(new int[]{0, 12, 20}, offsetsOf(k));
|
||||
assertEquals(24, k.stride());
|
||||
}
|
||||
|
||||
@Test
|
||||
void interiorHazardOrder_roundsMidAttribOffset() {
|
||||
final AttribLayoutKey k = new AttribLayoutKey(
|
||||
new int[]{0, 4, 2},
|
||||
new int[]{3, 3, 2},
|
||||
new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT},
|
||||
new boolean[]{false, true, false});
|
||||
assertArrayEquals(new int[]{0, 12, 16}, offsetsOf(k));
|
||||
assertEquals(24, k.stride());
|
||||
}
|
||||
|
||||
@Test
|
||||
void allByteLayout_packsTight() {
|
||||
final AttribLayoutKey k = new AttribLayoutKey(
|
||||
new int[]{0, 1, 2},
|
||||
new int[]{3, 4, 2},
|
||||
new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE},
|
||||
new boolean[]{true, true, false});
|
||||
assertArrayEquals(new int[]{0, 3, 7}, offsetsOf(k));
|
||||
assertEquals(9, k.stride());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shortsFollowingByte_roundToEvenAndStrideToEven() {
|
||||
final AttribLayoutKey k = new AttribLayoutKey(
|
||||
new int[]{0, 1},
|
||||
new int[]{1, 2},
|
||||
new int[]{GL11.GL_BYTE, GL11.GL_SHORT},
|
||||
new boolean[]{true, false});
|
||||
assertArrayEquals(new int[]{0, 2}, offsetsOf(k));
|
||||
assertEquals(6, k.stride());
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("layouts")
|
||||
void offsetsAndStride(String name, AttribLayoutKey key, int[] offsets, int stride) {
|
||||
assertArrayEquals(offsets, offsetsOf(key));
|
||||
assertEquals(stride, key.stride());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+34
-132
@@ -5,6 +5,8 @@ import com.gtnewhorizons.angelica.config.SystemProperties.UnmappedGLMode;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -16,7 +18,6 @@ import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
@@ -32,19 +33,8 @@ class GLSMRedirectorTest {
|
||||
|
||||
private static final String GLSM = GLSMRedirector.getTargetClassName();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Set<String> seenSet(String field) {
|
||||
try {
|
||||
final Field f = GLSMRedirector.class.getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
return (Set<String>) f.get(null);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> unmappedSeen() { return seenSet("UNMAPPED_SEEN"); }
|
||||
private static Set<String> debugSeen() { return seenSet("DEBUG_SEEN"); }
|
||||
private static Set<String> unmappedSeen() { return Reflect.getStatic(GLSMRedirector.class, "UNMAPPED_SEEN"); }
|
||||
private static Set<String> debugSeen() { return Reflect.getStatic(GLSMRedirector.class, "DEBUG_SEEN"); }
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
@@ -112,35 +102,25 @@ class GLSMRedirectorTest {
|
||||
return (Handle) dyn.bsmArgs[1];
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicGL11HandleIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"GL11, org/lwjgl/opengl/GL11, glPopMatrix, ()V, glPopMatrix",
|
||||
"extension class with a renamed target, org/lwjgl/opengl/EXTBlendFuncSeparate, glBlendFuncSeparateEXT, (IIII)V, tryBlendFuncSeparate",
|
||||
"GLxxC variant, org/lwjgl/opengl/GL11C, glDrawArrays, (III)V, glDrawArrays"
|
||||
})
|
||||
void invokeDynamicHandleIsRedirected(String name, String owner, String method, String desc, String expectedName) {
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(new Handle(Opcodes.H_INVOKESTATIC, owner, method, desc, false));
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("glPopMatrix", result.getName());
|
||||
assertEquals("()V", result.getDesc());
|
||||
assertEquals(expectedName, result.getName());
|
||||
assertEquals(desc, result.getDesc());
|
||||
assertEquals(Opcodes.H_INVOKESTATIC, result.getTag());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicNamedClassHandleIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/EXTBlendFuncSeparate", "glBlendFuncSeparateEXT", "(IIII)V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("tryBlendFuncSeparate", result.getName());
|
||||
assertEquals("(IIII)V", result.getDesc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicNonGLHandleIsNotTouched() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
|
||||
@@ -188,6 +168,7 @@ class GLSMRedirectorTest {
|
||||
@Test
|
||||
void nonAwareDebugCallbackIsRedirectedNotPassedThrough() {
|
||||
assertRedirected("org/lwjgl/opengl/GL43", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback");
|
||||
assertRedirected("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback");
|
||||
assertTrue(debugSeen().isEmpty());
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
@@ -240,54 +221,16 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void newlyMappedFramebufferAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glBindFramebufferEXT", "(II)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glBindFramebuffer", firstCall(cn).name);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void arbGenerateMipmapAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBFramebufferObject", "glGenerateMipmap", "(I)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glGenerateMipmap", firstCall(cn).name);
|
||||
assertEquals("(I)V", firstCall(cn).desc);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void extGenerateMipmapAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glGenerateMipmapEXT", "(I)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glGenerateMipmap", firstCall(cn).name);
|
||||
assertEquals("(I)V", firstCall(cn).desc);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void newlyMappedReadPixelsIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIILjava/nio/IntBuffer;)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glReadPixels", firstCall(cn).name);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glBindFramebufferEXT | (II)V | glBindFramebuffer",
|
||||
"org/lwjgl/opengl/ARBFramebufferObject | glGenerateMipmap | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glGenerateMipmapEXT | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/GL11 | glReadPixels | (IIIIIILjava/nio/IntBuffer;)V | glReadPixels",
|
||||
"org/lwjgl/opengl/ARBOcclusionQuery | glGenQueriesARB | (Ljava/nio/IntBuffer;)V | glGenQueries"
|
||||
})
|
||||
void aliasIsRedirected(String owner, String name, String desc, String expectedName) {
|
||||
assertEquals(desc, assertRedirected(owner, name, desc, expectedName).desc);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -299,47 +242,29 @@ class GLSMRedirectorTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void newlyMappedOcclusionQueryAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBOcclusionQuery", "glGenQueriesARB", "(Ljava/nio/IntBuffer;)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glGenQueries", firstCall(cn).name);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void utilCheckGLErrorIsRedirected() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{{"org/lwjgl/opengl/Util", "checkGLError", "()V"}});
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexedGettersHaveRealTargets() throws Exception {
|
||||
final Class<?> glsm = Class.forName(GLSM.replace('/', '.'), false, getClass().getClassLoader());
|
||||
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetInteger", int.class, int.class), "LWJGL2 GL30.glGetInteger(pname, index) redirects here");
|
||||
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri", int.class, int.class), "LWJGL3 GL31C.glGetIntegeri(pname, index) redirects here");
|
||||
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri_v", int.class, int.class, java.nio.IntBuffer.class));
|
||||
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetBooleani_v", int.class, int.class, java.nio.ByteBuffer.class));
|
||||
void indexedGettersHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL30", "glGetInteger", "(II)I"},
|
||||
{"org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I"},
|
||||
{"org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexedGettersAreRedirected() {
|
||||
assertRedirected("org/lwjgl/opengl/GL30", "glGetInteger", "(II)I", "glGetInteger");
|
||||
assertRedirected("org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I", "glGetIntegeri");
|
||||
assertRedirected("org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V", "glGetIntegeri_v");
|
||||
assertRedirected("org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V", "glGetBooleani_v");
|
||||
}
|
||||
|
||||
private void assertRedirected(String owner, String name, String desc, String expectedName) {
|
||||
private MethodInsnNode assertRedirected(String owner, String name, String desc, String expectedName) {
|
||||
final ClassNode cn = makeClassWithMethodCall(owner, name, desc);
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
assertTrue(changed, owner + "." + name + " should redirect");
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals(expectedName, firstCall(cn).name);
|
||||
assertTrue(unmappedSeen().isEmpty(), owner + "." + name + " should not be recorded");
|
||||
return firstCall(cn);
|
||||
}
|
||||
|
||||
private static boolean glsmDeclares(String name, String desc) throws ClassNotFoundException {
|
||||
@@ -465,20 +390,6 @@ class GLSMRedirectorTest {
|
||||
assertRedirected("org/lwjgl/opengl/APPLEVertexArrayObject", "glGenVertexArraysAPPLE", "()I", "glGenVertexArrays");
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicGLCVariantIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11C", "glDrawArrays", "(III)V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("glDrawArrays", result.getName());
|
||||
assertEquals("(III)V", result.getDesc());
|
||||
}
|
||||
|
||||
private static final String AWARE_SHIM = "com/gtnewhorizons/angelica/sdlgpu/compat/AwareGLDebugShim";
|
||||
private static final String CALLBACK_I = "Lorg/lwjgl/opengl/GLDebugMessageCallbackI;";
|
||||
|
||||
@@ -502,15 +413,6 @@ class GLSMRedirectorTest {
|
||||
assertEquals("glDebugMessageCallback", firstCall(cn).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAwareDebugMessageCallbackTargetsGLStateManager() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V");
|
||||
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, false));
|
||||
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareGLUtilSetupDebugMessageCallbackIsRedirected() {
|
||||
for (String desc : new String[]{"()Lorg/lwjgl/system/Callback;", "(Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;"}) {
|
||||
|
||||
+44
@@ -8,6 +8,7 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -15,12 +16,35 @@ import java.nio.file.Path;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Tag("lwjgl3")
|
||||
class SpirvCompilerDiskCacheTest {
|
||||
|
||||
private static final String SRC = "#version 460 core\nlayout(location = 0) in vec4 p;\nvoid main() { gl_Position = p; }\n";
|
||||
|
||||
static final String REDEFINES_CLAMP = """
|
||||
#version 330 core
|
||||
in vec4 uv;
|
||||
layout(location = 0) out vec4 color;
|
||||
float clamp(float min, float f, float max) {
|
||||
if (f > max) f = max;
|
||||
if (f < min) f = min;
|
||||
return f;
|
||||
}
|
||||
void main() {
|
||||
color = vec4(clamp(0.0, uv.x, 1.0), clamp(0.0, uv.y, 1.0), 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String BROKEN_WITH_HELPER = """
|
||||
#version 330 core
|
||||
layout(location = 0) out vec4 color;
|
||||
float helper(float t) { return t; }
|
||||
void main() { color = vec4(helper(undeclared_var)); }
|
||||
""";
|
||||
|
||||
@TempDir
|
||||
Path dir;
|
||||
|
||||
@@ -58,4 +82,24 @@ class SpirvCompilerDiskCacheTest {
|
||||
MemoryUtil.memFree(r.spirv());
|
||||
assertFalse(Files.exists(dir.resolve("spirv")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void redefinedBuiltinCompilesAndIsStoredUnderTheOriginalSource() {
|
||||
final SpirvCompiler.Options opts = SpirvCompiler.Options.vulkanForced460Core();
|
||||
ShaderDiskCache.configure(dir, "t");
|
||||
final SpirvCompiler.Result r = SpirvCompiler.compile(REDEFINES_CLAMP, Shaderc.shaderc_fragment_shader, "redefines-clamp", opts);
|
||||
assertNotNull(r.spirv(), r.error());
|
||||
MemoryUtil.memFree(r.spirv());
|
||||
assertNull(r.dumpPath());
|
||||
assertNotNull(ShaderDiskCache.get(SpirvCompiler.diskKey(REDEFINES_CLAMP, Shaderc.shaderc_fragment_shader, opts)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void brokenShaderWithHelperReportsTheOriginalError() throws IOException {
|
||||
final SpirvCompiler.Result r = SpirvCompiler.compile(BROKEN_WITH_HELPER, Shaderc.shaderc_fragment_shader, "broken-with-helper", SpirvCompiler.Options.vulkanForced460Core(), false);
|
||||
assertNull(r.spirv());
|
||||
assertTrue(r.error().contains("undeclared_var"), r.error());
|
||||
assertNotNull(r.dumpPath());
|
||||
assertFalse(Files.readString(r.dumpPath()).contains(GlslVulkanPreprocess.RESERVED_RENAMED_PREFIX));
|
||||
}
|
||||
}
|
||||
+5
@@ -131,6 +131,11 @@ class SpirvShaderTranslatorTest {
|
||||
"emitted uniform must not be `uint` - `glUniform1i` only accepts int/bool uniforms");
|
||||
}
|
||||
|
||||
@Test
|
||||
void redefinedBuiltin_translates() {
|
||||
assertNotNull(SpirvShaderTranslator.glslToGlslEs(SpirvCompilerDiskCacheTest.REDEFINES_CLAMP, GL20.GL_FRAGMENT_SHADER, "redefines_clamp.frag"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBoolWithInitializer_roundTripsAsBool() {
|
||||
final String src = """
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class UserFunctionRenameTest {
|
||||
|
||||
@Test
|
||||
void definitionForwardDeclarationAndCallsAreRenamed() {
|
||||
final String out = GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
float fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(fade(0.5)); }
|
||||
float fade(float t) { return t * t; }
|
||||
""");
|
||||
assertEquals("""
|
||||
#version 330 core
|
||||
float angelica_renamed_fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(angelica_renamed_fade(0.5)); }
|
||||
float angelica_renamed_fade(float t) { return t * t; }
|
||||
""", out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void overloadsOfOneNameAreRenamedTogether() {
|
||||
final String out = GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
vec2 clamp(vec2 f) { return vec2(clamp(0.0, f.x, 1.0), clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
""");
|
||||
assertEquals("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float angelica_renamed_clamp(float min, float f, float max) { return f; }
|
||||
vec2 angelica_renamed_clamp(vec2 f) { return vec2(angelica_renamed_clamp(0.0, f.x, 1.0), angelica_renamed_clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(angelica_renamed_clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
""", out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameUsedAsMacroArgumentIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
#define SAT(fn, x) fn(0.0, x, 1.0)
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
void main() { color = vec4(SAT(clamp, 0.5)); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameUsedInMacroBodyIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
#define FADE(x) fade(x)
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { color = vec4(FADE(0.5)); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameSharedWithVariableIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { float fade = 2.0; color = vec4(fade); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shaderWithOnlyMainHasNothingToRename() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class LineQuadsTest {
|
||||
|
||||
private static final int RED = 0xFF0000FF;
|
||||
private static final int GREEN = 0xFF00FF00;
|
||||
private static final int BLUE = 0xFFFF0000;
|
||||
|
||||
private static ByteBuffer positionColor(float[][] positions, int[] colors) {
|
||||
final ByteBuffer buf = ByteBuffer.allocateDirect(positions.length * 16).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < positions.length; i++) {
|
||||
buf.putFloat(positions[i][0]).putFloat(positions[i][1]).putFloat(positions[i][2]).putInt(colors[i]);
|
||||
}
|
||||
return buf.flip();
|
||||
}
|
||||
|
||||
private static ByteBuffer allocateQuads(int drawMode, int vertexCount) {
|
||||
final VertexFormat quadFormat = LineQuads.quadFormat(DefaultVertexFormat.POSITION_COLOR);
|
||||
final int quadVertices = LineQuads.segmentCount(drawMode, vertexCount) * LineQuads.VERTICES_PER_SEGMENT;
|
||||
return ByteBuffer.allocateDirect(quadVertices * quadFormat.getVertexSize()).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
private static ByteBuffer expand(ByteBuffer src, int drawMode, int vertexCount) {
|
||||
final ByteBuffer dst = allocateQuads(drawMode, vertexCount);
|
||||
LineQuads.expandWithDirection(memAddress0(src), DefaultVertexFormat.POSITION_COLOR, drawMode, vertexCount, memAddress0(dst));
|
||||
return dst;
|
||||
}
|
||||
|
||||
@Test
|
||||
void otherEndCornersCarryTheOppositeEndpoint() {
|
||||
final ByteBuffer src = positionColor(new float[][] {{0, 0, 0}, {2, 0, 3}}, new int[] {RED, GREEN});
|
||||
final ByteBuffer quads = allocateQuads(GL11.GL_LINES, 2);
|
||||
LineQuads.expandWithOtherEnd(memAddress0(src), DefaultVertexFormat.POSITION_COLOR, GL11.GL_LINES, 2, memAddress0(quads));
|
||||
|
||||
final float[] end = {2, 0, 3};
|
||||
final float[] start = {0, 0, 0};
|
||||
final float[] ownX = {0, 0, 2, 0, 2, 2};
|
||||
final int[] color = {RED, RED, GREEN, RED, GREEN, GREEN};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
final boolean isStart = i == 0 || i == 1 || i == 3;
|
||||
assertVertex(quads, i, ownX[i], isStart ? end : start, color[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int STRIDE = 28;
|
||||
|
||||
private static void assertVertex(ByteBuffer quads, int index, float x, float[] direction, int color) {
|
||||
final int base = index * STRIDE;
|
||||
assertEquals(x, quads.getFloat(base), "x of vertex " + index);
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
assertEquals(direction[axis], quads.getFloat(base + 12 + axis * 4), "direction of vertex " + index);
|
||||
}
|
||||
assertEquals(color, quads.getInt(base + 24), "color of vertex " + index);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stripSegmentsFollowTheGeometryShaderCornerOrder() {
|
||||
assertEquals(STRIDE, LineQuads.quadFormat(DefaultVertexFormat.POSITION_COLOR).getVertexSize());
|
||||
|
||||
final ByteBuffer src = positionColor(new float[][] {{0, 0, 0}, {2, 0, 0}, {2, 0, 3}}, new int[] {RED, GREEN, BLUE});
|
||||
final ByteBuffer quads = expand(src, GL11.GL_LINE_STRIP, 3);
|
||||
|
||||
final float[] alongX = {1, 0, 0};
|
||||
final float[] alongZ = {0, 0, 1};
|
||||
final float[] firstX = {0, 0, 2, 0, 2, 2};
|
||||
final int[] firstColor = {RED, RED, GREEN, RED, GREEN, GREEN};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertVertex(quads, i, firstX[i], alongX, firstColor[i]);
|
||||
}
|
||||
final int[] secondColor = {GREEN, GREEN, BLUE, GREEN, BLUE, BLUE};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertVertex(quads, 6 + i, 2, alongZ, secondColor[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void loopClosesBackToFirstVertex() {
|
||||
final ByteBuffer src = positionColor(new float[][] {{0, 0, 0}, {2, 0, 0}, {2, 0, 3}}, new int[] {RED, GREEN, BLUE});
|
||||
assertEquals(3, LineQuads.segmentCount(GL11.GL_LINE_LOOP, 3));
|
||||
final ByteBuffer quads = expand(src, GL11.GL_LINE_LOOP, 3);
|
||||
|
||||
final int[] closingColor = {BLUE, BLUE, RED, BLUE, RED, RED};
|
||||
for (int i = 0; i < 6; i++) {
|
||||
assertEquals(closingColor[i], quads.getInt((12 + i) * STRIDE + 24), "color of closing vertex " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 403 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user