mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 17:55:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a6b1d14e9 | ||
|
|
3c6b7e0fe9 | ||
|
|
6b9cb265f8 | ||
|
|
0d2e82fba5 | ||
|
|
c340a200a8 | ||
|
|
358f495471 | ||
|
|
785cd05a37 | ||
|
|
433d08bc5f | ||
|
|
12840f8953 | ||
|
|
3980599f7e | ||
|
|
299e9d835d | ||
|
|
30f0835a21 | ||
|
|
3717d2d46e | ||
|
|
d69fd20a8b | ||
|
|
9493d630a0 | ||
|
|
aee989d23b | ||
|
|
3876218405 | ||
|
|
a6d014a9eb | ||
|
|
52444510d0 | ||
|
|
8840c2109f | ||
|
|
e64a4e7214 |
No files matched your search
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Install a software Vulkan driver
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y xvfb mesa-vulkan-drivers
|
||||
sudo apt-get install -y xvfb mesa-vulkan-drivers libgl1-mesa-dri
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
25
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
- name: Run SDL-GPU tests
|
||||
run: xvfb-run -a --server-args="-screen 0 1366x768x24" ./gradlew --build-cache --stacktrace --continue :sdl-gpu:glsmSdlTest :sdl-gpu:sdlGpuDeviceTest
|
||||
run: xvfb-run -a --server-args="-screen 0 1366x768x24" ./gradlew --build-cache --stacktrace --continue :sdl-gpu:glsmSdlTest :sdl-gpu:sdlGpuDeviceTest :lwjgl3-backend:glesTest
|
||||
- name: Check LWJGL did not extract natives to the shared temp dir
|
||||
run: test ! -e "/tmp/lwjgl_$USER"
|
||||
- name: Attach SDL-GPU test results
|
||||
@@ -48,3 +48,6 @@ jobs:
|
||||
sdl-gpu/build/test-results/
|
||||
sdl-gpu/build/reports/tests/
|
||||
sdl-gpu/hs_err_pid*.log
|
||||
lwjgl3-backend/build/test-results/
|
||||
lwjgl3-backend/build/reports/tests/
|
||||
lwjgl3-backend/hs_err_pid*.log
|
||||
@@ -67,6 +67,22 @@ artifacts {
|
||||
}
|
||||
}
|
||||
|
||||
// LWJGL2-API stubs for test JVMs that strip the lwjgl-2 jars (glsmSdlTest, glesTest).
|
||||
val lwjgl2StubsSourceSet = sourceSets.create("lwjgl2Stubs") {
|
||||
java.srcDir("src/lwjgl2Stubs/java")
|
||||
}
|
||||
|
||||
val lwjgl2Stubs by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(lwjgl2Stubs.name, lwjgl2StubsSourceSet.java.classesDirectory) {
|
||||
builtBy(tasks.named("lwjgl2StubsClasses"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Mojang"
|
||||
@@ -111,6 +127,7 @@ val spirvTestRuntime by configurations.creating {
|
||||
dependencies {
|
||||
// MC compile stubs
|
||||
compileOnly(sourceSets["stubs"].output)
|
||||
"lwjgl2StubsCompileOnly"(libs.lwjgl3.opengl)
|
||||
|
||||
// LWJGL
|
||||
compileOnly(libs.lwjgl2)
|
||||
|
||||
File renamed without changes.
+19
@@ -1,5 +1,7 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ContextCapabilities {
|
||||
|
||||
public boolean OpenGL30 = true;
|
||||
@@ -21,11 +23,28 @@ public class ContextCapabilities {
|
||||
public boolean GL_ARB_sampler_objects = true;
|
||||
public boolean GL_ARB_shader_image_load_store = true;
|
||||
public boolean GL_ARB_shader_storage_buffer_object = true;
|
||||
public boolean GL_ARB_stencil_texturing = false;
|
||||
public boolean GL_ARB_tessellation_shader = false;
|
||||
public boolean GL_ARB_texture_storage = false;
|
||||
|
||||
public boolean GL_EXT_direct_state_access = false;
|
||||
public boolean GL_EXT_gpu_shader4 = true;
|
||||
public boolean GL_EXT_shader_image_load_store = true;
|
||||
|
||||
public boolean GL_NVX_gpu_memory_info = false;
|
||||
|
||||
public ContextCapabilities() {
|
||||
if ("lwjgl3".equals(System.getProperty("glsm.stubs.capabilities"))) copyFromCurrentContext();
|
||||
}
|
||||
|
||||
private void copyFromCurrentContext() {
|
||||
final GLCapabilities caps = GL.getCapabilities();
|
||||
for (Field field : ContextCapabilities.class.getFields()) {
|
||||
try {
|
||||
field.setBoolean(this, GLCapabilities.class.getField(field.getName()).getBoolean(caps));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException("LWJGL3 GLCapabilities has no " + field.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,13 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public final class GLContext {
|
||||
|
||||
private static ContextCapabilities capabilities;
|
||||
|
||||
private GLContext() {}
|
||||
|
||||
public static ContextCapabilities getCapabilities() {
|
||||
if (capabilities == null) capabilities = new ContextCapabilities();
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public final class SystemProperties {
|
||||
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
|
||||
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
public static final boolean DISABLE_LTW_WORKAROUND = Boolean.getBoolean("angelica.disableLtwWorkaround");
|
||||
public static final boolean EMULATE_CLIP_DISTANCES = Boolean.getBoolean("angelica.emulateClipDistances");
|
||||
|
||||
// Redirector
|
||||
public static final UnmappedGLMode UNMAPPED_GL = parseEnum("angelica.unmappedGL", UnmappedGLMode.WARN, UnmappedGLMode.WARN, UnmappedGLMode.class);
|
||||
|
||||
@@ -26,12 +26,20 @@ public final class CaptureGate {
|
||||
public static final boolean TOOL_ATTACHED = detectCaptureTool();
|
||||
public static boolean markersThisFrame = true;
|
||||
public static boolean tracyConnected;
|
||||
private static boolean toolAttached = TOOL_ATTACHED;
|
||||
|
||||
private CaptureGate() {}
|
||||
|
||||
public static void refresh() {
|
||||
tracyConnected = Tracy.isConnected();
|
||||
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, tracyConnected);
|
||||
markersThisFrame = gateValue(FORCED, toolAttached, tracyConnected);
|
||||
}
|
||||
|
||||
static void probeGlTool() {
|
||||
if (RenderSystem.hasExtension("GL_EXT_debug_tool")) {
|
||||
toolAttached = true;
|
||||
LOGGER.info("Capture tool detected via GL_EXT_debug_tool; instrumentation enabled");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
|
||||
@@ -39,7 +47,7 @@ public final class CaptureGate {
|
||||
}
|
||||
|
||||
public static boolean enabledAtStartup() {
|
||||
return FORCED || TOOL_ATTACHED;
|
||||
return FORCED || toolAttached;
|
||||
}
|
||||
|
||||
private static boolean detectCaptureTool() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
|
||||
public final class ClipPlaneBooleanState extends BooleanStateStack {
|
||||
@@ -13,12 +14,22 @@ public final class ClipPlaneBooleanState extends BooleanStateStack {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean forwardsToBackend() {
|
||||
return !RenderSystem.isClipDistanceEmulated();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabledChanged() {
|
||||
final int before = owner.clipPlaneEnabledMask;
|
||||
if (isEnabled()) {
|
||||
owner.clipPlaneEnabledMask |= (1 << index);
|
||||
} else {
|
||||
owner.clipPlaneEnabledMask &= ~(1 << index);
|
||||
}
|
||||
owner.clipPlaneGeneration++;
|
||||
if ((before == 0) != (owner.clipPlaneEnabledMask == 0) && RenderSystem.isClipDistanceEmulated() && GLSMHooks.CLIP_PLANES_IN_USE.hasListeners()) {
|
||||
GLSMHooks.CLIP_PLANES_IN_USE.post(GLSMHooks.clipPlanesInUseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ public class CompatUniformManager {
|
||||
final int cpGen = glCtx.clipPlaneGeneration;
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
uploadClipPlanes(locs, glCtx.clipPlaneEnabledMask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,16 +468,14 @@ public class CompatUniformManager {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_MAT_BASE + MF_SHININESS], mat.shininess);
|
||||
}
|
||||
|
||||
private static void uploadClipPlanes(int[] locs) {
|
||||
private static void uploadClipPlanes(int[] locs, int enabledMask) {
|
||||
if (locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], GLSMRasterizer.anyClipPlaneEnabled() ? 1 : 0);
|
||||
RENDER_BACKEND.uniform1i(locs[LOC_CLIP_PLANES_ENABLED], enabledMask != 0 ? 1 : 0);
|
||||
}
|
||||
if (locs[LOC_CLIP_PLANES] != -1) {
|
||||
final ClipPlaneState cps = GLSMRasterizer.getClipPlaneState();
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
cps.putEffectivePlanes(enabledMask, clipPlaneBuf);
|
||||
clipPlaneBuf.flip();
|
||||
RENDER_BACKEND.uniform4(locs[LOC_CLIP_PLANES], clipPlaneBuf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/// A draw between [#beginTracking] and [#endTracking] is left to the caller's replay instead of repeated. A replay
|
||||
/// ignores the depth mask, which a previous renderer may leak differently into the pass and the replay, and repeats
|
||||
/// set GL state through the backend, never the GLSM cache.
|
||||
public final class DepthEcho {
|
||||
|
||||
private static int framebuffer;
|
||||
private static int source;
|
||||
private static boolean echoed;
|
||||
private static boolean tracking;
|
||||
private static boolean hit;
|
||||
private static int immediate;
|
||||
private static boolean replaying;
|
||||
private static boolean replayDepthWrite;
|
||||
|
||||
private DepthEcho() {}
|
||||
|
||||
public static void arm(int framebuffer, int source) {
|
||||
DepthEcho.framebuffer = framebuffer;
|
||||
DepthEcho.source = source;
|
||||
}
|
||||
|
||||
public static void disarm() {
|
||||
framebuffer = 0;
|
||||
tracking = false;
|
||||
hit = false;
|
||||
endReplay();
|
||||
}
|
||||
|
||||
public static boolean armed() {
|
||||
return framebuffer != 0;
|
||||
}
|
||||
|
||||
public static void beginTracking() {
|
||||
tracking = framebuffer != 0;
|
||||
hit = false;
|
||||
}
|
||||
|
||||
public static boolean endTracking() {
|
||||
tracking = false;
|
||||
final boolean was = hit;
|
||||
hit = false;
|
||||
return was;
|
||||
}
|
||||
|
||||
public static void beginImmediate() {
|
||||
immediate++;
|
||||
}
|
||||
|
||||
public static void endImmediate() {
|
||||
immediate--;
|
||||
}
|
||||
|
||||
public static void beginReplay() {
|
||||
replaying = true;
|
||||
}
|
||||
|
||||
public static void endReplay() {
|
||||
replaying = false;
|
||||
restoreReplayDepthMask();
|
||||
}
|
||||
|
||||
public static boolean replaying() {
|
||||
return replaying;
|
||||
}
|
||||
|
||||
public static boolean takeEchoed() {
|
||||
final boolean was = echoed;
|
||||
echoed = false;
|
||||
return was;
|
||||
}
|
||||
|
||||
public static boolean skipsCall() {
|
||||
return replaySkips() && !DisplayListManager.isRecording();
|
||||
}
|
||||
|
||||
public static boolean skipsDraw() {
|
||||
if (!replaying) return false;
|
||||
if (replaySkips()) return true;
|
||||
if (!GLSMDepthStencilAlpha.getDepthState().isEnabled()) {
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
replayDepthWrite = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean replaySkips() {
|
||||
return replaying && !(blendsVisibly() && GLSMFramebuffer.getDrawFramebuffer() == framebuffer);
|
||||
}
|
||||
|
||||
public static boolean begin() {
|
||||
if (replayDepthWrite) {
|
||||
restoreReplayDepthMask();
|
||||
return false;
|
||||
}
|
||||
if (framebuffer == 0 || GLSMDepthStencilAlpha.getDepthState().isEnabled() || !blendsVisibly() || GLSMFramebuffer.getDrawFramebuffer() != source) return false;
|
||||
echoed = true;
|
||||
if (tracking && immediate == 0 && !BatchStateGuard.isSuspended()) {
|
||||
hit = true;
|
||||
return false;
|
||||
}
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
RENDER_BACKEND.depthMask(false);
|
||||
RENDER_BACKEND.bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, source);
|
||||
}
|
||||
|
||||
private static void restoreReplayDepthMask() {
|
||||
if (!replayDepthWrite) return;
|
||||
replayDepthWrite = false;
|
||||
RENDER_BACKEND.depthMask(GLSMDepthStencilAlpha.getDepthState().isEnabled());
|
||||
}
|
||||
|
||||
private static boolean blendsVisibly() {
|
||||
if (!GLSMDepthStencilAlpha.getDepthTest().isEnabled() || !GLSMBlend.getBlendMode().isEnabled()) return false;
|
||||
if (GLSMDepthStencilAlpha.getDepthState().getFunc() == GL11.GL_EQUAL) return false;
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
return mask.red || mask.green || mask.blue || mask.alpha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ColorMaskStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.DepthStateStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/// Per-draw state for depth peeling. Color passes skip the depth test but keep the draw's depth writes, so the target
|
||||
/// ends with the nearest peeled depth and the rest pass's own test keeps only what is in front of it. Depth and rest
|
||||
/// passes discard fragments that would not change the target under the draw's blend, so transparent texels do not take
|
||||
/// a peel. Draws with an application program cannot run [#GLSL], so they skip the peels and draw unsorted afterwards.
|
||||
public final class DepthPeel {
|
||||
|
||||
public static final int TEXTURE_UNIT = 15;
|
||||
public static final int SCENE_TEXTURE_UNIT = 14;
|
||||
|
||||
private static final int PASS_DEPTH = 0;
|
||||
private static final int PASS_COLOR = 1;
|
||||
private static final int PASS_REST = 2;
|
||||
private static final int UNBOUNDED = 4;
|
||||
private static final int COVER_ALPHA = 0;
|
||||
private static final int COVER_RGB = 8;
|
||||
private static final int COVER_RGB_ALPHA = 16;
|
||||
private static final int COVER_ALL = 24;
|
||||
|
||||
public static final String GLSL = "#define PEEL_PASS_COLOR " + PASS_COLOR + "\n"
|
||||
+ "#define PEEL_UNBOUNDED " + UNBOUNDED + "\n"
|
||||
+ "#define PEEL_COVER_MASK " + COVER_ALL + "\n"
|
||||
+ "#define PEEL_COVER_RGB " + COVER_RGB + "\n"
|
||||
+ "#define PEEL_COVER_RGB_ALPHA " + COVER_RGB_ALPHA + "\n"
|
||||
+ "#define PEEL_COVER_ALL " + COVER_ALL + "\n"
|
||||
+ "#define PEEL_SCENE_TOLERANCE 6.0e-8\n"
|
||||
+ "#define PEEL_WINDOW 2.4e-7\n"
|
||||
+ "#define PEEL_MIN_COVERAGE 0.004\n"
|
||||
+ """
|
||||
uniform sampler2D u_PeelDepth;
|
||||
uniform sampler2D u_PeelScene;
|
||||
uniform int u_PeelMode;
|
||||
void peelTest(vec4 c) {
|
||||
ivec2 texel = ivec2(gl_FragCoord.xy);
|
||||
float z = gl_FragCoord.z;
|
||||
if (z > texelFetch(u_PeelScene, texel, 0).r + PEEL_SCENE_TOLERANCE) discard;
|
||||
float bound = texelFetch(u_PeelDepth, texel, 0).r;
|
||||
if ((u_PeelMode & 3) == PEEL_PASS_COLOR) {
|
||||
if (z > bound || z < bound - PEEL_WINDOW) discard;
|
||||
return;
|
||||
}
|
||||
if ((u_PeelMode & PEEL_UNBOUNDED) == 0 && z >= bound - PEEL_WINDOW) discard;
|
||||
int cover = u_PeelMode & PEEL_COVER_MASK;
|
||||
float rgb = max(c.r, max(c.g, c.b));
|
||||
float coverage = cover == PEEL_COVER_ALL ? 1.0 : cover == PEEL_COVER_RGB ? rgb : cover == PEEL_COVER_RGB_ALPHA ? rgb * c.a : c.a;
|
||||
if (coverage < PEEL_MIN_COVERAGE) discard;
|
||||
}
|
||||
""";
|
||||
|
||||
public enum Pass { OFF, DEPTH, COLOR, REST, APPLICATION }
|
||||
|
||||
private static Pass pass = Pass.OFF;
|
||||
private static boolean unbounded;
|
||||
private static boolean overridden;
|
||||
private static boolean skippedApplicationDraws;
|
||||
|
||||
private DepthPeel() {}
|
||||
|
||||
public static void setPass(Pass next, boolean unbounded) {
|
||||
if (pass == Pass.OFF) skippedApplicationDraws = false;
|
||||
pass = next;
|
||||
DepthPeel.unbounded = unbounded;
|
||||
GLSMConfig.depthPeel = next == Pass.DEPTH || next == Pass.COLOR || next == Pass.REST;
|
||||
}
|
||||
|
||||
public static boolean active() {
|
||||
return pass != Pass.OFF;
|
||||
}
|
||||
|
||||
public static boolean skippedApplicationDraws() {
|
||||
return skippedApplicationDraws;
|
||||
}
|
||||
|
||||
public static int shaderMode() {
|
||||
return switch (pass) {
|
||||
case DEPTH -> PASS_DEPTH | coverage() | (unbounded ? UNBOUNDED : 0);
|
||||
case COLOR -> PASS_COLOR;
|
||||
default -> PASS_REST | coverage();
|
||||
};
|
||||
}
|
||||
|
||||
private static int coverage() {
|
||||
if (!GLSMBlend.getBlendMode().isEnabled()) return COVER_ALL;
|
||||
final BlendStateStack blend = GLStateManager.ctx().blendState;
|
||||
if (blend.getDstRgb() != GL11.GL_ONE) return COVER_ALPHA;
|
||||
return blend.getSrcRgb() == GL11.GL_SRC_ALPHA ? COVER_RGB_ALPHA : COVER_RGB;
|
||||
}
|
||||
|
||||
public static boolean begin() {
|
||||
if (pass == Pass.OFF) return true;
|
||||
final boolean fixedFunction = GLSMPrograms.getActiveProgram() == 0;
|
||||
if (pass == Pass.APPLICATION) return !fixedFunction;
|
||||
if (!fixedFunction) {
|
||||
skippedApplicationDraws = true;
|
||||
return false;
|
||||
}
|
||||
if (pass == Pass.DEPTH) {
|
||||
RENDER_BACKEND.colorMask(false, false, false, false);
|
||||
RENDER_BACKEND.depthMask(true);
|
||||
RENDER_BACKEND.depthFunc(GL11.GL_GREATER);
|
||||
overridden = true;
|
||||
} else if (pass == Pass.COLOR) {
|
||||
RENDER_BACKEND.depthFunc(GL11.GL_ALWAYS);
|
||||
overridden = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
if (!overridden) return;
|
||||
overridden = false;
|
||||
final ColorMaskStack mask = GLSMBlend.getColorMask();
|
||||
final DepthStateStack depth = GLSMDepthStencilAlpha.getDepthState();
|
||||
RENDER_BACKEND.colorMask(mask.red, mask.green, mask.blue, mask.alpha);
|
||||
RENDER_BACKEND.depthMask(depth.isEnabled());
|
||||
RENDER_BACKEND.depthFunc(depth.getFunc());
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,8 @@ public final class GLESCaps {
|
||||
|
||||
private GLESCaps() {}
|
||||
|
||||
public static boolean isCapAllowed(int cap, boolean isGLES, boolean hasClipCullDistance) {
|
||||
public static boolean isCapAllowed(int cap, boolean isGLES) {
|
||||
if (!isGLES) return true;
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + GLStateManager.MAX_CLIP_PLANES) {
|
||||
return hasClipCullDistance;
|
||||
}
|
||||
return switch (cap) {
|
||||
case GL11.GL_LINE_SMOOTH,
|
||||
GL11.GL_POLYGON_SMOOTH,
|
||||
|
||||
@@ -149,19 +149,46 @@ public final class GLSMBlend {
|
||||
}
|
||||
|
||||
static void issueBlendFunc(BlendStateStack blend) {
|
||||
if (GLSMConfig.hudCacheOverride) {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
}
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), emittedSrcAlpha(blend), emittedDstAlpha(blend));
|
||||
}
|
||||
|
||||
private static boolean addsWithoutCoverage(BlendStateStack blend) {
|
||||
return blend.getDstRgb() == GL11.GL_ONE;
|
||||
}
|
||||
|
||||
private static int emittedSrcAlpha(BlendStateStack blend) {
|
||||
if (GLSMConfig.layerBlending) return addsWithoutCoverage(blend) ? GL11.GL_ZERO : GL11.GL_ONE;
|
||||
return GLSMConfig.hudCacheOverride ? GL11.GL_ONE : blend.getSrcAlpha();
|
||||
}
|
||||
|
||||
private static int emittedDstAlpha(BlendStateStack blend) {
|
||||
if (GLSMConfig.layerBlending) return addsWithoutCoverage(blend) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
return GLSMConfig.hudCacheOverride ? GL11.GL_ONE_MINUS_SRC_ALPHA : blend.getDstAlpha();
|
||||
}
|
||||
|
||||
public static void setHudCacheOverride(boolean enabled) {
|
||||
if (GLSMConfig.hudCacheOverride == enabled) return;
|
||||
GLSMConfig.hudCacheOverride = enabled;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) return;
|
||||
reissueBlendFunc();
|
||||
}
|
||||
|
||||
public static void setLayerBlending(boolean enabled) {
|
||||
if (GLSMConfig.layerBlending == enabled) return;
|
||||
GLSMConfig.layerBlending = enabled;
|
||||
reissueBlendFunc();
|
||||
}
|
||||
|
||||
public static boolean isLayerBlending() {
|
||||
return GLSMConfig.layerBlending;
|
||||
}
|
||||
|
||||
private static void reissueBlendFunc() {
|
||||
final BlendStateStack blend = ctx().blendState;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) {
|
||||
blend.setFuncUnknownState();
|
||||
return;
|
||||
}
|
||||
issueBlendFunc(blend);
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
@@ -226,11 +253,10 @@ public final class GLSMBlend {
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
issueBlendFunc(glCtx.blendState);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
final boolean hudCache = GLSMConfig.hudCacheOverride;
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = hudCache ? GL11.GL_ONE : srcAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = hudCache ? GL11.GL_ONE_MINUS_SRC_ALPHA : dstAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = emittedSrcAlpha(glCtx.blendState);
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = emittedDstAlpha(glCtx.blendState);
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -76,6 +77,7 @@ public final class GLSMDraws {
|
||||
ImmediateModeRecorder.begin(mode);
|
||||
return;
|
||||
}
|
||||
BatchStateGuard.beforeDraw();
|
||||
ImmediateModeRecorder.beginLive(mode);
|
||||
}
|
||||
|
||||
@@ -141,6 +143,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -174,6 +177,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -207,6 +211,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -240,6 +245,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -273,6 +279,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
drawCalls++;
|
||||
@@ -329,12 +336,14 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
@@ -353,6 +362,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
final GLContextState glCtx = ctx();
|
||||
@@ -377,6 +387,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
@@ -393,11 +404,13 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.drawRangeElementsBaseVertex(mode, start, end, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
public static void glMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
@@ -423,6 +436,7 @@ public final class GLSMDraws {
|
||||
}
|
||||
|
||||
public static void glDrawArrays(int mode, int first, int count) {
|
||||
if (DepthEcho.skipsCall()) return;
|
||||
rejectExitCapture();
|
||||
drawCalls++;
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -521,6 +535,7 @@ public final class GLSMDraws {
|
||||
|
||||
public static void glCallList(int list) {
|
||||
rejectExitCapture();
|
||||
if (!DisplayListManager.isRecording()) BatchStateGuard.beforeDraw();
|
||||
if (!GLDebug.isActive()) {
|
||||
DisplayListManager.glCallList(list);
|
||||
return;
|
||||
|
||||
@@ -30,7 +30,6 @@ public final class GLSMEnable {
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
glCtx.clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].enable();
|
||||
glCtx.clipPlaneGeneration++;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,7 +118,6 @@ public final class GLSMEnable {
|
||||
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
|
||||
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
|
||||
glCtx.clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].disable();
|
||||
glCtx.clipPlaneGeneration++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ public final class GLSMFramebuffer {
|
||||
private static long firstClearBlockNanos;
|
||||
|
||||
public static void glClear(int mask) {
|
||||
if (DepthEcho.replaying() && !DisplayListManager.isRecording()) return;
|
||||
rejectExitCapture();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -315,6 +316,7 @@ public final class GLSMFramebuffer {
|
||||
}
|
||||
|
||||
public static void glBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
||||
if (DepthEcho.replaying() && !DisplayListManager.isRecording()) return;
|
||||
rejectExitCapture();
|
||||
RENDER_BACKEND.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
@@ -503,8 +503,8 @@ public final class GLSMQueries {
|
||||
return RENDER_BACKEND.genQueries();
|
||||
}
|
||||
public static void glDeleteQueries(int id) { rejectExitCapture(); RENDER_BACKEND.deleteQueries(id); }
|
||||
public static void glBeginQuery(int target, int id) { rejectExitCapture(); RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { rejectExitCapture(); RENDER_BACKEND.endQuery(target); }
|
||||
public static void glBeginQuery(int target, int id) { rejectExitCapture(); if (!DepthEcho.replaying()) RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { rejectExitCapture(); if (!DepthEcho.replaying()) RENDER_BACKEND.endQuery(target); }
|
||||
public static void glGetQueryObjectui(int id, int pname, IntBuffer params) { rejectExitCapture(); RENDER_BACKEND.getQueryObjectui(id, pname, params); }
|
||||
public static int glGetQueryObjecti(int id, int pname) { rejectExitCapture(); return RENDER_BACKEND.getQueryObjecti(id, pname); }
|
||||
public static void glQueryCounter(int id, int target) { rejectExitCapture(); RENDER_BACKEND.queryCounter(id, target); }
|
||||
|
||||
@@ -88,8 +88,11 @@ public final class GLSMRasterizer {
|
||||
static void probePointSizeRange(GLContextState glCtx) {
|
||||
final FloatBuffer range = glCtx.queryScratch;
|
||||
range.clear();
|
||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
final float smooth = range.get(1);
|
||||
float smooth = 0.0f;
|
||||
if (!RenderSystem.isGLES()) {
|
||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
smooth = range.get(1);
|
||||
}
|
||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||
maxPointSize = Math.max(smooth, range.get(1));
|
||||
}
|
||||
@@ -506,6 +509,14 @@ public final class GLSMRasterizer {
|
||||
return glCtx.clipPlaneEnabledMask != 0;
|
||||
}
|
||||
|
||||
public static boolean clipEmulationActive() {
|
||||
return RenderSystem.isClipDistanceEmulated() && anyClipPlaneEnabled();
|
||||
}
|
||||
|
||||
public static boolean clipEmulationActive(GLContextState glCtx) {
|
||||
return RenderSystem.isClipDistanceEmulated() && anyClipPlaneEnabled(glCtx);
|
||||
}
|
||||
|
||||
public static void glSampleCoverage(float value, boolean invert) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glSampleCoverage");
|
||||
|
||||
@@ -458,6 +458,7 @@ public class GLStateManager {
|
||||
static void init(Runnable initCallback) {
|
||||
final GLContextState glCtx = ctx();
|
||||
RenderSystem.initRenderer();
|
||||
CaptureGate.probeGlTool();
|
||||
|
||||
GLSMFramebuffer.queryDefaultDrawBuffer();
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public class RenderSystem {
|
||||
private static volatile boolean isGLES;
|
||||
private static volatile int glesVersion;
|
||||
private static volatile boolean glesDetected;
|
||||
private static volatile boolean hasClipCullDistance;
|
||||
private static boolean clipDistanceEmulated;
|
||||
|
||||
private static volatile boolean isLTW;
|
||||
private static volatile boolean ltwDetected;
|
||||
@@ -132,7 +132,12 @@ public class RenderSystem {
|
||||
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
|
||||
supportsStencilTexturing = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_stencil_texturing;
|
||||
supportsTextureStorage = isGLES ? glesVersion >= 300 : GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_texture_storage;
|
||||
hasClipCullDistance = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
|
||||
final boolean esClipExt = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
|
||||
final int maxClipDistances = esClipExt ? RENDER_BACKEND.getInteger(GL30.GL_MAX_CLIP_DISTANCES) : 0;
|
||||
clipDistanceEmulated = SystemProperties.EMULATE_CLIP_DISTANCES || maxClipDistances < GLStateManager.MAX_CLIP_PLANES;
|
||||
if (clipDistanceEmulated) {
|
||||
GLStateManager.LOGGER.info("Clip distances emulated (GL_MAX_CLIP_DISTANCES={}, forced={})", maxClipDistances, SystemProperties.EMULATE_CLIP_DISTANCES);
|
||||
}
|
||||
|
||||
// Cache maximum image units
|
||||
if (supportsImageLoadStore) {
|
||||
@@ -434,11 +439,11 @@ public class RenderSystem {
|
||||
return supportsImageLoadStore;
|
||||
}
|
||||
|
||||
public static boolean hasClipCullDistance() {
|
||||
return hasClipCullDistance;
|
||||
public static boolean isClipDistanceEmulated() {
|
||||
return clipDistanceEmulated;
|
||||
}
|
||||
|
||||
private static boolean hasExtension(String name) {
|
||||
static boolean hasExtension(String name) {
|
||||
final int count = GLSMQueries.glGetInteger(GL30.GL_NUM_EXTENSIONS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (name.equals(GLSMQueries.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
|
||||
|
||||
@@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class StateSet {
|
||||
|
||||
public static final int R_DEPTH = 1;
|
||||
@@ -79,28 +81,47 @@ public final class StateSet {
|
||||
public static final StateSet CUTOUT = buildCutout();
|
||||
public static final StateSet MASKS = buildMasks();
|
||||
public static final StateSet CULL = buildCull();
|
||||
public static final StateSet BLOCK_ENTITY_PASS = buildBlockEntityPass();
|
||||
|
||||
private static CowStateStack<?>[] batchMembers() {
|
||||
return new CowStateStack<?>[] {
|
||||
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
|
||||
GLSMBlend.getColorMask(), GLSMRasterizer.getPolygonState(),
|
||||
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
|
||||
GLSMTextures.getActiveTextureUnitStack(),
|
||||
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
|
||||
GLSMLighting.getLightingState(), GLSMRasterizer.getCullState(), GLSMRasterizer.getPolygonOffsetFillState(),
|
||||
GLSMTextures.getTextures().getTextureUnitStates(0), GLSMTextures.getTextures().getTextureUnitStates(1),
|
||||
GLSMLighting.getLightStates()[0], GLSMLighting.getLightStates()[1],
|
||||
GLSMLighting.getLightDataStates()[0], GLSMLighting.getLightDataStates()[1],
|
||||
GLSMLighting.getColorMaterial(), GLSMLighting.getColorMaterialFace(), GLSMLighting.getColorMaterialParameter(),
|
||||
GLSMLighting.getLightModel(), GLSMLighting.getFrontMaterial(), GLSMLighting.getBackMaterial(),
|
||||
GLSMLighting.getNormalizeState(), GLSMLighting.getRescaleNormalState()
|
||||
};
|
||||
}
|
||||
|
||||
private static StateSet buildBatch() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
GLSMBlend.getBlendState(), GLSMDepthStencilAlpha.getAlphaState(), GLSMDepthStencilAlpha.getDepthState(),
|
||||
GLSMBlend.getColorMask(), GLSMRasterizer.getPolygonState(),
|
||||
GLSMTextures.getTextures().getTextureUnitBindings(0), GLSMTextures.getTextures().getTextureUnitBindings(1),
|
||||
GLSMTextures.getActiveTextureUnitStack(),
|
||||
GLSMBlend.getBlendMode(), GLSMDepthStencilAlpha.getAlphaTest(), GLSMDepthStencilAlpha.getDepthTest(),
|
||||
GLSMLighting.getLightingState(), GLSMRasterizer.getCullState(), GLSMRasterizer.getPolygonOffsetFillState(),
|
||||
GLSMTextures.getTextures().getTextureUnitStates(0), GLSMTextures.getTextures().getTextureUnitStates(1),
|
||||
GLSMLighting.getLightStates()[0], GLSMLighting.getLightStates()[1],
|
||||
GLSMLighting.getLightDataStates()[0], GLSMLighting.getLightDataStates()[1],
|
||||
GLSMLighting.getColorMaterial(), GLSMLighting.getColorMaterialFace(), GLSMLighting.getColorMaterialParameter(),
|
||||
GLSMLighting.getLightModel(), GLSMLighting.getFrontMaterial(), GLSMLighting.getBackMaterial(),
|
||||
GLSMLighting.getNormalizeState(), GLSMLighting.getRescaleNormalState()
|
||||
),
|
||||
ids(batchMembers()),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF | B_LIGHTING,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildBlockEntityPass() {
|
||||
final CowStateStack<?>[] batch = batchMembers();
|
||||
final CowStateStack<?>[] all = Arrays.copyOf(batch, batch.length + 4);
|
||||
all[batch.length] = GLSMCurrentAttribs.getColor();
|
||||
all[batch.length + 1] = GLSMFog.getFogState();
|
||||
all[batch.length + 2] = GLSMFog.getFogMode();
|
||||
all[batch.length + 3] = GLSMRasterizer.getShadeModelState();
|
||||
return new StateSet(0,
|
||||
ids(all),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF | B_LIGHTING | B_CURRENT | B_FRAG_FOG,
|
||||
true);
|
||||
}
|
||||
|
||||
private static StateSet buildFont() {
|
||||
return new StateSet(0,
|
||||
ids(
|
||||
|
||||
+12
-12
@@ -323,42 +323,42 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArrays(int mode, int first, int count) {
|
||||
protected void issueDrawArrays(int mode, int first, int count) {
|
||||
GL11.glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, long indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
GL11.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, IntBuffer indices) {
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ShortBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {
|
||||
GL11.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
GL11.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
GL31.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GL43.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
@@ -378,12 +378,12 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
GL32.glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
for (int i = 0; i < drawcount; i++) {
|
||||
GL32.glDrawElementsBaseVertex(mode, memGetInt(pCount + 4L * i), type, memGetAddress(pIndices + (long) i * POINTER_SIZE), memGetInt(pBaseVertex + 4L * i));
|
||||
}
|
||||
@@ -1715,7 +1715,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
GL14.glMultiDrawArrays(mode, firsts, counts);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthEcho;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLESFormatRemap;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -321,28 +323,221 @@ public abstract class RenderBackend {
|
||||
public abstract void logicOp(int opcode);
|
||||
public abstract void hint(int target, int mode);
|
||||
|
||||
public abstract void drawArrays(int mode, int first, int count);
|
||||
public abstract void drawElements(int mode, int count, int type, long indices);
|
||||
public void drawElements(int mode, ByteBuffer indices) {}
|
||||
public void drawElements(int mode, IntBuffer indices) {}
|
||||
public void drawElements(int mode, ShortBuffer indices) {}
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {}
|
||||
public abstract void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
public void multiDrawArraysIndirectCount(int mode, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
public void multiDrawElementsIndirectCount(int mode, int type, long indirect, long drawcount, int maxdrawcount, int stride) { throw new UnsupportedOperationException(getName() + ": indirect draw-count buffers unsupported"); }
|
||||
public abstract void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size);
|
||||
public abstract void drawElementsInstanced(int mode, int count, int type, long indices, int primcount);
|
||||
public abstract void drawArraysInstanced(int mode, int first, int count, int primcount);
|
||||
public void provokingVertex(int provokeMode) {}
|
||||
public abstract void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
|
||||
public abstract void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
|
||||
|
||||
public final void drawArrays(int mode, int first, int count) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawArrays(mode, first, count);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawArrays(mode, first, count);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, int count, int type, long indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, IntBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, ShortBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawArraysInstanced(mode, first, count, primcount);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawArraysInstanced(mode, first, count, primcount);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (DepthEcho.skipsDraw() || !DepthPeel.begin()) return;
|
||||
try {
|
||||
issueMultiDrawArrays(mode, firsts, counts);
|
||||
} finally {
|
||||
DepthPeel.end();
|
||||
}
|
||||
if (DepthEcho.begin()) {
|
||||
try {
|
||||
issueMultiDrawArrays(mode, firsts, counts);
|
||||
} finally {
|
||||
DepthEcho.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void issueDrawArrays(int mode, int first, int count);
|
||||
protected abstract void issueDrawElements(int mode, int count, int type, long indices);
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {}
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {}
|
||||
protected abstract void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride);
|
||||
protected abstract void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount);
|
||||
protected abstract void issueDrawArraysInstanced(int mode, int first, int count, int primcount);
|
||||
protected abstract void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex);
|
||||
protected abstract void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex);
|
||||
protected abstract void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
|
||||
public void drawRangeElements(int mode, int start, int end, int count, int type, long indices) {
|
||||
drawElements(mode, count, type, indices);
|
||||
}
|
||||
public void drawRangeElementsBaseVertex(int mode, int start, int end, int count, int type, long indices, int baseVertex) {
|
||||
drawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
public abstract void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts);
|
||||
public abstract void primitiveRestartIndex(int index);
|
||||
public abstract void pointParameterf(int pname, float param);
|
||||
public abstract void pointParameteri(int pname, int param);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public final class ClipDistanceGlsl {
|
||||
|
||||
public static final String VS_OUT_DIST0 = "out vec4 angelica_ClipDist0;";
|
||||
public static final String VS_OUT_DIST1 = "out vec4 angelica_ClipDist1;";
|
||||
public static final String FS_IN_DIST0 = "in vec4 angelica_ClipDist0;";
|
||||
public static final String FS_IN_DIST1 = "in vec4 angelica_ClipDist1;";
|
||||
public static final String FS_DISCARD = "if (any(lessThan(angelica_ClipDist0, vec4(0.0))) || any(lessThan(angelica_ClipDist1, vec4(0.0)))) discard;\n";
|
||||
|
||||
private ClipDistanceGlsl() {}
|
||||
|
||||
public static void appendVsAssign(StringBuilder sb, String[] distances) {
|
||||
sb.append(" angelica_ClipDist0 = vec4(").append(distances[0]).append(", ").append(distances[1]).append(", ").append(distances[2]).append(", ").append(distances[3]).append(");\n");
|
||||
sb.append(" angelica_ClipDist1 = vec4(").append(distances[4]).append(", ").append(distances[5]).append(", ").append(distances[6]).append(", ").append(distances[7]).append(");\n");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
@@ -15,12 +16,75 @@ public final class FFPVertexLighting {
|
||||
private static final Vector4f scratchEyePos = new Vector4f();
|
||||
private static final Matrix3f scratchNormalMat = new Matrix3f();
|
||||
private static final Vector3f scratchVec = new Vector3f();
|
||||
private static final Vector3f scratchFactor = new Vector3f();
|
||||
private static final Vector3f base = new Vector3f();
|
||||
private static final Vector3f[] lightDirs = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
|
||||
private static final Vector3f[] lightDiffuse = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
|
||||
private static int litCount;
|
||||
private static boolean normalize;
|
||||
private static float rescale;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
|
||||
lightDirs[i] = new Vector3f();
|
||||
lightDiffuse[i] = new Vector3f();
|
||||
}
|
||||
}
|
||||
|
||||
private FFPVertexLighting() {}
|
||||
|
||||
public static boolean modulatesVertexColor(Vector3f destFactor) {
|
||||
destFactor.set(1.0f);
|
||||
if (!prepare(false)) return false;
|
||||
|
||||
factor(ShaderManager.getCurrentNormal(), scratchFactor);
|
||||
final float r = scratchFactor.x;
|
||||
final float g = scratchFactor.y;
|
||||
final float b = scratchFactor.z;
|
||||
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
|
||||
if (!Float.isFinite(r + g + b)) return false;
|
||||
destFactor.set(r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean bakeColors(int[] rawBuffer, int vertexCount, boolean hasNormals, boolean hasColor) {
|
||||
if (vertexCount <= 0 || !prepare(true)) return false;
|
||||
|
||||
if (!hasNormals) factor(ShaderManager.getCurrentNormal(), scratchFactor);
|
||||
float cr = 0.0f, cg = 0.0f, cb = 0.0f;
|
||||
int alphaBits = 0;
|
||||
if (!hasColor) {
|
||||
final var color = GLSMCurrentAttribs.getColor();
|
||||
cr = clamp01(color.getRed());
|
||||
cg = clamp01(color.getGreen());
|
||||
cb = clamp01(color.getBlue());
|
||||
alphaBits = toByte(clamp01(color.getAlpha())) << 24;
|
||||
}
|
||||
|
||||
boolean haveNormal = false;
|
||||
int lastNormal = 0;
|
||||
for (int v = 0, i = 0; v < vertexCount; v++, i += 8) {
|
||||
if (hasNormals) {
|
||||
final int packed = rawBuffer[i + 6];
|
||||
if (!haveNormal || packed != lastNormal) {
|
||||
factor((byte) packed / 127.0f, (byte) (packed >> 8) / 127.0f, (byte) (packed >> 16) / 127.0f, scratchFactor);
|
||||
lastNormal = packed;
|
||||
haveNormal = true;
|
||||
}
|
||||
}
|
||||
if (hasColor) {
|
||||
final int packed = rawBuffer[i + 5];
|
||||
cr = (packed & 0xFF) / 255.0f;
|
||||
cg = ((packed >>> 8) & 0xFF) / 255.0f;
|
||||
cb = ((packed >>> 16) & 0xFF) / 255.0f;
|
||||
alphaBits = packed & 0xFF000000;
|
||||
}
|
||||
rawBuffer[i + 5] = alphaBits | (toByte(clamp01(cb * scratchFactor.z)) << 16) | (toByte(clamp01(cg * scratchFactor.y)) << 8) | toByte(clamp01(cr * scratchFactor.x));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean prepare(boolean directionalOnly) {
|
||||
if (!GLSMLighting.getLightingState().isEnabled()) return false;
|
||||
if (!GLSMLighting.getColorMaterial().isEnabled()) return false;
|
||||
if (VertexKey.encodeColorMaterialMode(GLSMLighting.getColorMaterialParameter().getValue()) != VertexKey.CM_AMBIENT_AND_DIFFUSE) return false;
|
||||
@@ -32,44 +96,58 @@ public final class FFPVertexLighting {
|
||||
final boolean materialHasSpecular = material.shininess > 0.0f && (material.specular.x != 0.0f || material.specular.y != 0.0f || material.specular.z != 0.0f);
|
||||
|
||||
final Vector4f lightModelAmbient = GLSMLighting.getLightModel().ambient;
|
||||
float r = lightModelAmbient.x;
|
||||
float g = lightModelAmbient.y;
|
||||
float b = lightModelAmbient.z;
|
||||
base.set(lightModelAmbient.x, lightModelAmbient.y, lightModelAmbient.z);
|
||||
|
||||
final var lightEnables = GLSMLighting.getLightStates();
|
||||
final var lightData = GLSMLighting.getLightDataStates();
|
||||
boolean normalReady = false;
|
||||
litCount = 0;
|
||||
|
||||
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
|
||||
if (!lightEnables[i].isEnabled()) continue;
|
||||
final LightState light = lightData[i];
|
||||
if (materialHasSpecular && (light.specular.x != 0.0f || light.specular.y != 0.0f || light.specular.z != 0.0f))
|
||||
return false;
|
||||
if (!normalReady) {
|
||||
transformCurrentNormal();
|
||||
normalReady = true;
|
||||
}
|
||||
final float nDotVP = Math.max(scratchNormal.dot(lightDirection(light)), 0.0f);
|
||||
r += light.ambient.x + nDotVP * light.diffuse.x;
|
||||
g += light.ambient.y + nDotVP * light.diffuse.y;
|
||||
b += light.ambient.z + nDotVP * light.diffuse.z;
|
||||
if (directionalOnly && light.position.w != 0.0f) return false;
|
||||
base.add(light.ambient.x, light.ambient.y, light.ambient.z);
|
||||
lightDirs[litCount].set(lightDirection(light));
|
||||
lightDiffuse[litCount].set(light.diffuse.x, light.diffuse.y, light.diffuse.z);
|
||||
litCount++;
|
||||
}
|
||||
|
||||
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
|
||||
if (!Float.isFinite(r + g + b)) return false;
|
||||
destFactor.set(r, g, b);
|
||||
if (litCount > 0) {
|
||||
GLSMMatrices.getModelViewMatrix().normal(scratchNormalMat);
|
||||
normalize = GLSMLighting.getNormalizeState().isEnabled();
|
||||
rescale = !normalize && GLSMLighting.getRescaleNormalState().isEnabled() ? Uniforms.rescaleFactor(scratchNormalMat, scratchVec) : 1.0f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void transformCurrentNormal() {
|
||||
GLSMMatrices.getModelViewMatrix().normal(scratchNormalMat);
|
||||
scratchNormal.set(ShaderManager.getCurrentNormal());
|
||||
private static void factor(Vector3f normal, Vector3f dest) {
|
||||
factor(normal.x, normal.y, normal.z, dest);
|
||||
}
|
||||
|
||||
private static void factor(float nx, float ny, float nz, Vector3f dest) {
|
||||
dest.set(base);
|
||||
if (litCount == 0) return;
|
||||
scratchNormal.set(nx, ny, nz);
|
||||
scratchNormalMat.transform(scratchNormal);
|
||||
if (GLSMLighting.getNormalizeState().isEnabled()) {
|
||||
if (normalize) {
|
||||
scratchNormal.normalize();
|
||||
} else if (GLSMLighting.getRescaleNormalState().isEnabled()) {
|
||||
scratchNormal.mul(Uniforms.rescaleFactor(scratchNormalMat, scratchVec));
|
||||
} else {
|
||||
scratchNormal.mul(rescale);
|
||||
}
|
||||
for (int i = 0; i < litCount; i++) {
|
||||
final float nDotVP = Math.max(scratchNormal.dot(lightDirs[i]), 0.0f);
|
||||
dest.fma(nDotVP, lightDiffuse[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static float clamp01(float value) {
|
||||
return Math.min(Math.max(value, 0.0f), 1.0f);
|
||||
}
|
||||
|
||||
private static int toByte(float unit) {
|
||||
return (int) (unit * 255.0f + 0.5f);
|
||||
}
|
||||
|
||||
private static Vector3f lightDirection(LightState light) {
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexEnvState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
@@ -15,24 +17,24 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (14) + unit 0 (47 bits at offset 14)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* long[0]: global bits (14) + unit 0 (45 bits at offset 14), depth peel (60), combined glint (61), glint alpha (62), clip discard (63)
|
||||
* long[1..3]: unit 1..3 (45 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
* Per-unit packing (47 bits):
|
||||
* Per-unit packing (45 bits):
|
||||
* 0: enabled
|
||||
* 1-3: mode (MODULATE=0..COMBINE=5)
|
||||
* 4: needsEnvColor
|
||||
* 5-8: combineRgb (encoded 0-7)
|
||||
* 9-12: combineAlpha (encoded 0-7)
|
||||
* 13-14: scaleShiftRgb
|
||||
* 15-16: scaleShiftAlpha
|
||||
* 17-21: arg0 RGB (source:3 + operand:2)
|
||||
* 22-26: arg1 RGB
|
||||
* 27-31: arg2 RGB
|
||||
* 32-36: arg0 Alpha
|
||||
* 37-41: arg1 Alpha
|
||||
* 42-46: arg2 Alpha
|
||||
* 5-7: combineRgb (encoded 0-7)
|
||||
* 8-10: combineAlpha (encoded 0-7)
|
||||
* 11-12: scaleShiftRgb
|
||||
* 13-14: scaleShiftAlpha
|
||||
* 15-19: arg0 RGB (source:3 + operand:2)
|
||||
* 20-24: arg1 RGB
|
||||
* 25-29: arg2 RGB
|
||||
* 30-34: arg0 Alpha
|
||||
* 35-39: arg1 Alpha
|
||||
* 40-44: arg2 Alpha
|
||||
*/
|
||||
public final class FragmentKey {
|
||||
public static final int TEX_ENV_MODULATE = 0;
|
||||
@@ -77,8 +79,10 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_DEPTH_PEEL = 60;
|
||||
private static final int BIT_COMBINED_GLINT = 61;
|
||||
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
|
||||
private static final int BIT_CLIP_DISCARD = 63;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
@@ -92,16 +96,16 @@ public final class FragmentKey {
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
private static final int U_NEEDS_ENV_COLOR = 4; // 1 bit
|
||||
private static final int U_COMBINE_RGB = 5; // 4 bits
|
||||
private static final int U_COMBINE_ALPHA = 9; // 4 bits
|
||||
private static final int U_SCALE_RGB = 13; // 2 bits
|
||||
private static final int U_SCALE_ALPHA = 15; // 2 bits
|
||||
private static final int U_ARG0_RGB = 17; // 5 bits (source:3 + operand:2)
|
||||
private static final int U_ARG1_RGB = 22; // 5 bits
|
||||
private static final int U_ARG2_RGB = 27; // 5 bits
|
||||
private static final int U_ARG0_ALPHA = 32; // 5 bits
|
||||
private static final int U_ARG1_ALPHA = 37; // 5 bits
|
||||
private static final int U_ARG2_ALPHA = 42; // 5 bits
|
||||
private static final int U_COMBINE_RGB = 5; // 3 bits
|
||||
private static final int U_COMBINE_ALPHA = 8; // 3 bits
|
||||
private static final int U_SCALE_RGB = 11; // 2 bits
|
||||
private static final int U_SCALE_ALPHA = 13; // 2 bits
|
||||
private static final int U_ARG0_RGB = 15; // 5 bits (source:3 + operand:2)
|
||||
private static final int U_ARG1_RGB = 20; // 5 bits
|
||||
private static final int U_ARG2_RGB = 25; // 5 bits
|
||||
private static final int U_ARG0_ALPHA = 30; // 5 bits
|
||||
private static final int U_ARG1_ALPHA = 35; // 5 bits
|
||||
private static final int U_ARG2_ALPHA = 40; // 5 bits
|
||||
|
||||
static final int MAX_UNITS = 4;
|
||||
|
||||
@@ -125,7 +129,9 @@ public final class FragmentKey {
|
||||
public static int packFromState(long[] out, GLContextState glCtx) {
|
||||
long global = 0;
|
||||
if (CombinedGlint.isActive()) global |= 1L << BIT_COMBINED_GLINT;
|
||||
if (GLSMConfig.depthPeel) global |= 1L << BIT_DEPTH_PEEL;
|
||||
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
|
||||
if (GLSMRasterizer.clipEmulationActive(glCtx)) global |= 1L << BIT_CLIP_DISCARD;
|
||||
|
||||
// Fog
|
||||
global |= activeFogMode(glCtx);
|
||||
@@ -197,8 +203,8 @@ public final class FragmentKey {
|
||||
if (mode == TEX_ENV_COMBINE) {
|
||||
final int combRgb = encodeCombineFunc(envState.combineRgb);
|
||||
final int combAlpha = encodeCombineFunc(envState.combineAlpha);
|
||||
bits |= ((long) (combRgb & 0xF)) << U_COMBINE_RGB;
|
||||
bits |= ((long) (combAlpha & 0xF)) << U_COMBINE_ALPHA;
|
||||
bits |= ((long) (combRgb & 0x7)) << U_COMBINE_RGB;
|
||||
bits |= ((long) (combAlpha & 0x7)) << U_COMBINE_ALPHA;
|
||||
bits |= ((long) (encodeScale(envState.scaleRgb) & 0x3)) << U_SCALE_RGB;
|
||||
bits |= ((long) (encodeScale(envState.scaleAlpha) & 0x3)) << U_SCALE_ALPHA;
|
||||
|
||||
@@ -259,7 +265,9 @@ public final class FragmentKey {
|
||||
|
||||
public int fogMode() { return (int) (packed[0] & 0x3); }
|
||||
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
|
||||
public boolean clipDiscard() { return (packed[0] & (1L << BIT_CLIP_DISCARD)) != 0; }
|
||||
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
|
||||
public boolean depthPeel() { return (packed[0] & (1L << BIT_DEPTH_PEEL)) != 0; }
|
||||
public int textureSupersamplingSamples() { return 1 << (int) ((packed[0] >> BIT_TEXTURE_SUPERSAMPLING) & 0x7); }
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
@@ -277,8 +285,8 @@ public final class FragmentKey {
|
||||
public boolean unitEnabled(int i) { return (unitBits(i) & 1) != 0; }
|
||||
public int unitMode(int i) { return (int) ((unitBits(i) >> U_MODE) & 0x7); }
|
||||
public boolean unitNeedsEnvColor(int i) { return ((unitBits(i) >> U_NEEDS_ENV_COLOR) & 1) != 0; }
|
||||
public int unitCombineRgb(int i) { return (int) ((unitBits(i) >> U_COMBINE_RGB) & 0xF); }
|
||||
public int unitCombineAlpha(int i) { return (int) ((unitBits(i) >> U_COMBINE_ALPHA) & 0xF); }
|
||||
public int unitCombineRgb(int i) { return (int) ((unitBits(i) >> U_COMBINE_RGB) & 0x7); }
|
||||
public int unitCombineAlpha(int i) { return (int) ((unitBits(i) >> U_COMBINE_ALPHA) & 0x7); }
|
||||
public int unitScaleShiftRgb(int i) { return (int) ((unitBits(i) >> U_SCALE_RGB) & 0x3); }
|
||||
public int unitScaleShiftAlpha(int i) { return (int) ((unitBits(i) >> U_SCALE_ALPHA) & 0x3); }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
|
||||
@@ -24,6 +25,7 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("#define GLINT_ALPHA_FUNC ").append(key.alphaTestEnabled() ? key.alphaTestFunc() : 7).append('\n');
|
||||
sb.append("#define GLINT_OVERLAY ").append(key.overlayInstanced() ? 2 : key.overlayEnabled() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_LIGHTMAP ").append(key.nrEnabledUnits() > 1 && key.unitEnabled(1) ? 1 : 0).append('\n');
|
||||
if (key.depthPeel()) sb.append("#define DEPTH_PEEL 1\n");
|
||||
sb.append(ShaderLoader.getShaderSource("angelica:combined_glint.frag.glsl"));
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -34,12 +36,16 @@ public final class FragmentShaderGenerator {
|
||||
}
|
||||
|
||||
sb.append("void main() {\n");
|
||||
if (key.clipDiscard()) {
|
||||
sb.append(ClipDistanceGlsl.FS_DISCARD);
|
||||
}
|
||||
emitLineStipple(sb, key);
|
||||
emitTextureSampling(sb, key);
|
||||
emitTexEnvChain(sb, key);
|
||||
emitSpecularAdd(sb, key);
|
||||
emitAlphaTest(sb, key);
|
||||
emitFog(sb, key);
|
||||
emitDepthPeel(sb, key);
|
||||
sb.append(" fragColor = color;\n");
|
||||
sb.append("}\n");
|
||||
|
||||
@@ -49,6 +55,9 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitInputs(StringBuilder sb, FragmentKey key) {
|
||||
sb.append("// Inputs from vertex shader\n");
|
||||
sb.append("in vec4 v_Color;\n");
|
||||
if (key.clipDiscard()) {
|
||||
sb.append(ClipDistanceGlsl.FS_IN_DIST0).append('\n').append(ClipDistanceGlsl.FS_IN_DIST1).append('\n');
|
||||
}
|
||||
if (key.overlayInstanced()) {
|
||||
sb.append("in vec4 v_Overlay;\n");
|
||||
}
|
||||
@@ -83,10 +92,15 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("uniform sampler2D u_Sampler").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.depthPeel()) sb.append(DepthPeel.GLSL);
|
||||
sb.append(FFPUniformBlock.GLSL_DECL);
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
private static void emitDepthPeel(StringBuilder sb, FragmentKey key) {
|
||||
if (key.depthPeel()) sb.append(" peelTest(color);\n");
|
||||
}
|
||||
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatShaderTransformer;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -35,6 +36,10 @@ public class Program {
|
||||
@Getter private final FragmentKey fragmentKey;
|
||||
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
public final int locPeelDepth;
|
||||
public final int locPeelScene;
|
||||
private final int locPeelMode;
|
||||
private int peelMode = -1;
|
||||
|
||||
Program(int programId, VertexKey vertexKey, FragmentKey fragmentKey) {
|
||||
this.programId = programId;
|
||||
@@ -43,6 +48,19 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locSampler[i] = RENDER_BACKEND.getUniformLocation(programId, "u_Sampler" + i);
|
||||
}
|
||||
locPeelDepth = RENDER_BACKEND.getUniformLocation(programId, "u_PeelDepth");
|
||||
locPeelScene = RENDER_BACKEND.getUniformLocation(programId, "u_PeelScene");
|
||||
locPeelMode = RENDER_BACKEND.getUniformLocation(programId, "u_PeelMode");
|
||||
}
|
||||
|
||||
boolean peels() {
|
||||
return locPeelMode != -1;
|
||||
}
|
||||
|
||||
void syncPeelMode(int mode) {
|
||||
if (peelMode == mode) return;
|
||||
RENDER_BACKEND.uniform1i(locPeelMode, mode);
|
||||
peelMode = mode;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
@@ -99,6 +117,8 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ffpProgram.locSampler[i] != -1) backend.uniform1i(ffpProgram.locSampler[i], i);
|
||||
}
|
||||
if (ffpProgram.locPeelDepth != -1) backend.uniform1i(ffpProgram.locPeelDepth, DepthPeel.TEXTURE_UNIT);
|
||||
if (ffpProgram.locPeelScene != -1) backend.uniform1i(ffpProgram.locPeelScene, DepthPeel.SCENE_TEXTURE_UNIT);
|
||||
if (previousProgram != 0) {
|
||||
backend.useProgram(previousProgram);
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
|
||||
import com.gtnewhorizons.angelica.glsm.DepthPeel;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -188,6 +189,7 @@ public final class ShaderManager {
|
||||
private void uploadUniforms(GLContextState glCtx) {
|
||||
if (currentProgram != null) {
|
||||
uniforms.upload(glCtx);
|
||||
if (currentProgram.peels()) currentProgram.syncPeelMode(DepthPeel.shaderMode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
@@ -330,9 +329,7 @@ public class Uniforms {
|
||||
private void stageClipPlanes(GLContextState glCtx) {
|
||||
final ClipPlaneState cps = glCtx.clipPlaneState;
|
||||
clipPlaneBuf.clear();
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
cps.putEyePlane(i, clipPlaneBuf);
|
||||
}
|
||||
cps.putEffectivePlanes(glCtx.clipPlaneEnabledMask, clipPlaneBuf);
|
||||
final long src = memAddress0(clipPlaneBuf);
|
||||
for (int i = 0; i < 32 * 4; i += 4) {
|
||||
putInt(FFPUniformBlock.CLIP_PLANES + i, memGetInt(src + i));
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -53,6 +54,7 @@ public final class VertexKey {
|
||||
private static final int BIT_UNIT23_UV_FROM_UNIT0 = BIT_UNIT_TEXMAT_BASE + MAX_UNITS;
|
||||
private static final int BIT_LINE_STIPPLE = BIT_UNIT23_UV_FROM_UNIT0 + 1;
|
||||
private static final int BIT_INSTANCING = BIT_LINE_STIPPLE + 1;
|
||||
private static final int BIT_CLIP_EMULATED = BIT_INSTANCING + 3;
|
||||
private static final long INSTANCING_MASK = 0x7L;
|
||||
|
||||
static {
|
||||
@@ -105,6 +107,7 @@ 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 clipEmulated() { return bit(BIT_CLIP_EMULATED); }
|
||||
public boolean lineStipple() { return bit(BIT_LINE_STIPPLE); }
|
||||
public boolean lineQuads() { return bit(BIT_LINE_QUADS); }
|
||||
public Instancing instancing() { return Instancing.VALUES[(int) ((packed >> BIT_INSTANCING) & INSTANCING_MASK)]; }
|
||||
@@ -238,6 +241,7 @@ public final class VertexKey {
|
||||
|
||||
if (GLSMRasterizer.anyClipPlaneEnabled(glCtx)) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
if (RenderSystem.isClipDistanceEmulated()) bits |= 1L << BIT_CLIP_EMULATED;
|
||||
}
|
||||
|
||||
if (glCtx.lineStippleActive) {
|
||||
@@ -279,6 +283,6 @@ public final class VertexKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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());
|
||||
return String.format("FFPVertexKey[0x%012X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b clipEmu=%b lquads=%b inst=%s]", packed,lightingEnabled(), lightEnabled(0), lightEnabled(1), colorMaterialEnabled(), fogEnabled(), unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0, unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0, hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(), texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), clipEmulated(), lineQuads(), instancing());
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,9 @@ public final class VertexShaderGenerator {
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
}
|
||||
if (key.clipEmulated()) {
|
||||
sb.append(ClipDistanceGlsl.VS_OUT_DIST0).append('\n').append(ClipDistanceGlsl.VS_OUT_DIST1).append('\n');
|
||||
}
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
@@ -398,10 +401,19 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitClipDistances(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Clip distances\n");
|
||||
final boolean emulated = key.clipEmulated();
|
||||
final String[] distances = emulated ? new String[8] : null;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
final String own = "dot(u_ClipPlane[" + i + "], eyePos)";
|
||||
final String distance = key.lineQuads() ? acrossLine(own, "dot(u_ClipPlane[" + i + "], lineEyeOther)") : own;
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = ").append(distance).append(";\n");
|
||||
if (emulated) {
|
||||
distances[i] = distance;
|
||||
} else {
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = ").append(distance).append(";\n");
|
||||
}
|
||||
}
|
||||
if (emulated) {
|
||||
ClipDistanceGlsl.appendVsAssign(sb, distances);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
/** Drains deferred geometry before changing state that is not stored with each batch. */
|
||||
public final class BatchStateGuard {
|
||||
public static Runnable flush;
|
||||
public static Runnable drawFlush;
|
||||
private static int suspended;
|
||||
|
||||
private BatchStateGuard() {}
|
||||
@@ -16,12 +17,20 @@ public final class BatchStateGuard {
|
||||
public static boolean isSuspended() { return suspended != 0; }
|
||||
|
||||
public static void beforeChange() {
|
||||
if (flush == null || suspended != 0 || !GLStateManager.isMainThread()
|
||||
run(flush);
|
||||
}
|
||||
|
||||
public static void beforeDraw() {
|
||||
run(drawFlush);
|
||||
}
|
||||
|
||||
private static void run(Runnable action) {
|
||||
if (action == null || suspended != 0 || !GLStateManager.isMainThread()
|
||||
|| DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.COMPILE) return;
|
||||
final CommandRecorder recorder = DisplayListManager.isRecording() ? DisplayListManager.pauseRecording() : null;
|
||||
suspend();
|
||||
try {
|
||||
flush.run();
|
||||
action.run();
|
||||
} finally {
|
||||
resume();
|
||||
if (recorder != null) DisplayListManager.resumeRecording(recorder);
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextureLifetime;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
@@ -16,11 +17,18 @@ public final class FrameHooks {
|
||||
private static boolean gameLoopStarted;
|
||||
private static long frameCounter;
|
||||
private static boolean frameGroupActive;
|
||||
private static String framePrefix = "frame:";
|
||||
private static String buildLabel = "";
|
||||
|
||||
public static void bootstrapFirstFrame() {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
if (!gameLoopStarted) {
|
||||
gameLoopStarted = true;
|
||||
final GLSMInitConfig config = GLStateManager.getInitConfig();
|
||||
if (config != null && !config.getBuildLabel().isEmpty()) {
|
||||
buildLabel = config.getBuildLabel();
|
||||
framePrefix = buildLabel + " frame:";
|
||||
}
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
pushFrameGroup();
|
||||
}
|
||||
@@ -48,8 +56,10 @@ public final class FrameHooks {
|
||||
}
|
||||
|
||||
private static void pushFrameGroup() {
|
||||
final boolean wasConnected = CaptureGate.tracyConnected;
|
||||
CaptureGate.refresh();
|
||||
GLDebug.pushGroup("frame:", frameCounter);
|
||||
if (CaptureGate.tracyConnected && !wasConnected && !buildLabel.isEmpty()) Tracy.message(buildLabel);
|
||||
GLDebug.pushGroup(framePrefix, frameCounter);
|
||||
frameGroupActive = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ public final class GLSMConfig {
|
||||
}
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
public static boolean layerBlending;
|
||||
public static boolean depthPeel;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
@@ -31,6 +31,7 @@ public final class GLSMHooks {
|
||||
public static final EventBus<ProgramDeleteEvent> PROGRAM_DELETE = EventBus.create(ProgramDeleteEvent.class);
|
||||
public static final EventBus<BlendFuncChangeEvent> BLEND_FUNC_CHANGE = EventBus.create(BlendFuncChangeEvent.class);
|
||||
public static final EventBus<FogStateChangeEvent> FOG_STATE_CHANGE = EventBus.create(FogStateChangeEvent.class);
|
||||
public static final EventBus<ClipPlanesInUseEvent> CLIP_PLANES_IN_USE = EventBus.create(ClipPlanesInUseEvent.class);
|
||||
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
|
||||
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
|
||||
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
|
||||
@@ -46,6 +47,7 @@ public final class GLSMHooks {
|
||||
public static final ProgramDeleteEvent programDeleteEvent = new ProgramDeleteEvent();
|
||||
public static final BlendFuncChangeEvent blendFuncChangeEvent = new BlendFuncChangeEvent();
|
||||
public static final FogStateChangeEvent fogStateChangeEvent = new FogStateChangeEvent();
|
||||
public static final ClipPlanesInUseEvent clipPlanesInUseEvent = new ClipPlanesInUseEvent();
|
||||
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
|
||||
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
|
||||
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
|
||||
|
||||
@@ -17,6 +17,7 @@ public final class GLSMInitConfig {
|
||||
@Getter private final Runnable postInitCallback;
|
||||
private final boolean dsaEnabled;
|
||||
@Getter private final boolean noErrorChecks;
|
||||
@Getter private final String buildLabel;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
this.lwjglDebug = builder.lwjglDebug;
|
||||
@@ -27,6 +28,7 @@ public final class GLSMInitConfig {
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.dsaEnabled = builder.dsaEnabled;
|
||||
this.buildLabel = builder.buildLabel;
|
||||
}
|
||||
|
||||
public boolean isDSAEnabled() {
|
||||
@@ -46,6 +48,7 @@ public final class GLSMInitConfig {
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean dsaEnabled = true;
|
||||
private boolean noErrorChecks;
|
||||
private String buildLabel = "";
|
||||
|
||||
private Builder() {}
|
||||
|
||||
@@ -85,6 +88,11 @@ public final class GLSMInitConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder buildLabel(String buildLabel) {
|
||||
this.buildLabel = buildLabel == null ? "" : buildLabel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GLSMInitConfig build() {
|
||||
return new GLSMInitConfig(this);
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ClipPlanesInUseEvent extends MutableEvent {}
|
||||
+15
@@ -1,9 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
public final class IndexedDrawBatch {
|
||||
@@ -34,17 +37,29 @@ public final class IndexedDrawBatch {
|
||||
GLSMVertexInput.glBindVertexArray(v);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, sharedVBO);
|
||||
final int stride = layout.stride();
|
||||
int vertexFlags = 0;
|
||||
for (int i = 0; i < layout.locations.length; i++) {
|
||||
final int loc = layout.locations[i];
|
||||
GLSMVertexInput.glVertexAttribPointer(loc, layout.sizes[i], layout.types[i], layout.normalized[i], stride, layout.offset(i));
|
||||
GLSMVertexInput.glEnableVertexAttribArray(loc);
|
||||
vertexFlags |= vertexFlag(loc);
|
||||
}
|
||||
// glVertexAttribPointer doesn't set these, and the FFP shader only reads the inputs they name. Saved with the VAO.
|
||||
VAOManager.setCurrentVertexFlags(vertexFlags);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEBO);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
vao = v;
|
||||
vaoCtx = glCtx;
|
||||
}
|
||||
|
||||
private static int vertexFlag(int location) {
|
||||
if (location == Usage.COLOR.getAttributeLocation()) return VertexFlags.COLOR_BIT;
|
||||
if (location == Usage.NORMAL.getAttributeLocation()) return VertexFlags.NORMAL_BIT;
|
||||
if (location == Usage.PRIMARY_UV.getAttributeLocation()) return VertexFlags.TEXTURE_BIT;
|
||||
if (location == Usage.SECONDARY_UV.getAttributeLocation()) return VertexFlags.BRIGHTNESS_BIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (deleted) return;
|
||||
deleted = true;
|
||||
|
||||
@@ -12,7 +12,8 @@ final class GLSMOwners {
|
||||
"getColorLogicOpState", "getColorMask", "getEffectiveBlendState", "getEffectiveColorMask",
|
||||
"getIndexLogicOpState", "getLogicOpMode", "glBlendColor", "glBlendEquation", "glBlendEquationSeparate",
|
||||
"glBlendFunc", "glBlendFuncSeparate", "glBlendFuncSeparatei", "glClearColor", "glColorMask", "glLogicOp",
|
||||
"isEffectiveBlendEnabled", "setHudCacheOverride", "tryBlendFuncSeparate"},
|
||||
"isEffectiveBlendEnabled", "isLayerBlending", "setHudCacheOverride", "setLayerBlending",
|
||||
"tryBlendFuncSeparate"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMBuffers",
|
||||
"getBoundCopyReadBuffer", "getBoundCopyWriteBuffer", "getBoundVBO", "getIndexedBufferBinding",
|
||||
"glBindBuffer", "glBindBufferBase", "glBindBufferRange", "glBufferData", "glBufferStorage",
|
||||
@@ -106,22 +107,22 @@ final class GLSMOwners {
|
||||
"glGetIntegeri", "glGetIntegeri_v", "glGetQueryObjecti", "glGetQueryObjectui", "glGetQueryObjectui64",
|
||||
"glGetString", "glGetStringi", "glIsQuery", "glQueryCounter"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMRasterizer",
|
||||
"anyClipPlaneEnabled", "disableCull", "disableScissorTest", "enableCull", "enableScissorTest",
|
||||
"getAutoNormalState", "getClipPlaneState", "getClipPlaneStates", "getCullState", "getDitherState",
|
||||
"getEdgeFlagState", "getHintState", "getLineSmoothState", "getLineState", "getLineStippleState",
|
||||
"getMap1Color4State", "getMap1IndexState", "getMap1NormalState", "getMap1TextureCoord1State",
|
||||
"getMap1TextureCoord2State", "getMap1TextureCoord3State", "getMap1TextureCoord4State",
|
||||
"getMap1Vertex3State", "getMap1Vertex4State", "getMap2Color4State", "getMap2IndexState",
|
||||
"getMap2NormalState", "getMap2TextureCoord1State", "getMap2TextureCoord2State", "getMap2TextureCoord3State",
|
||||
"getMap2TextureCoord4State", "getMap2Vertex3State", "getMap2Vertex4State", "getMultisampleState",
|
||||
"getPointSmoothState", "getPointSpriteState", "getPointState", "getPolygonOffsetFillState",
|
||||
"getPolygonOffsetLineState", "getPolygonOffsetPointState", "getPolygonSmoothState", "getPolygonState",
|
||||
"getPolygonStippleState", "getSampleAlphaToCoverageState", "getSampleAlphaToOneState",
|
||||
"getSampleCoverageState", "getScissorState", "getScissorTest", "getShadeModelState", "getViewportState",
|
||||
"glClipPlane", "glCullFace", "glFrontFace", "glGetClipPlane", "glHint", "glLineStipple", "glLineWidth",
|
||||
"glPointParameter", "glPointParameterf", "glPointParameteri", "glPointSize", "glPolygonMode",
|
||||
"glPolygonOffset", "glPolygonOffsetClamp", "glPolygonStipple", "glSampleCoverage", "glScissor",
|
||||
"glShadeModel", "glViewport"},
|
||||
"anyClipPlaneEnabled", "clipEmulationActive", "disableCull", "disableScissorTest", "enableCull",
|
||||
"enableScissorTest", "getAutoNormalState", "getClipPlaneState", "getClipPlaneStates", "getCullState",
|
||||
"getDitherState", "getEdgeFlagState", "getHintState", "getLineSmoothState", "getLineState",
|
||||
"getLineStippleState", "getMap1Color4State", "getMap1IndexState", "getMap1NormalState",
|
||||
"getMap1TextureCoord1State", "getMap1TextureCoord2State", "getMap1TextureCoord3State",
|
||||
"getMap1TextureCoord4State", "getMap1Vertex3State", "getMap1Vertex4State", "getMap2Color4State",
|
||||
"getMap2IndexState", "getMap2NormalState", "getMap2TextureCoord1State", "getMap2TextureCoord2State",
|
||||
"getMap2TextureCoord3State", "getMap2TextureCoord4State", "getMap2Vertex3State", "getMap2Vertex4State",
|
||||
"getMultisampleState", "getPointSmoothState", "getPointSpriteState", "getPointState",
|
||||
"getPolygonOffsetFillState", "getPolygonOffsetLineState", "getPolygonOffsetPointState",
|
||||
"getPolygonSmoothState", "getPolygonState", "getPolygonStippleState", "getSampleAlphaToCoverageState",
|
||||
"getSampleAlphaToOneState", "getSampleCoverageState", "getScissorState", "getScissorTest",
|
||||
"getShadeModelState", "getViewportState", "glClipPlane", "glCullFace", "glFrontFace", "glGetClipPlane",
|
||||
"glHint", "glLineStipple", "glLineWidth", "glPointParameter", "glPointParameterf", "glPointParameteri",
|
||||
"glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonOffsetClamp", "glPolygonStipple",
|
||||
"glSampleCoverage", "glScissor", "glShadeModel", "glViewport"},
|
||||
{"com/gtnewhorizons/angelica/glsm/GLSMTexEnvGen",
|
||||
"glGetTexEnv", "glGetTexEnvf", "glGetTexEnvi", "glGetTexGen", "glGetTexGend", "glGetTexGenf",
|
||||
"glGetTexGeni", "glTexEnv", "glTexEnvf", "glTexEnvi", "glTexGen", "glTexGend", "glTexGenf", "glTexGeni"},
|
||||
|
||||
+17
-4
@@ -15,8 +15,10 @@ import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class GlslVulkanPreprocess {
|
||||
@@ -56,7 +58,9 @@ public final class GlslVulkanPreprocess {
|
||||
private GlslVulkanPreprocess() {}
|
||||
|
||||
private record CacheKey(String source, int glShaderType, boolean separateReadOnlyImages) {}
|
||||
public record Result(String rewrittenSource, Set<String> boolUniforms, Set<String> explicitVsInputs) {}
|
||||
public record Result(String rewrittenSource, Set<String> boolUniforms, Set<String> explicitVsInputs, Map<String, UniformBlock> uniformBlocks) {}
|
||||
|
||||
public record UniformBlock(String qualifierText, @Nullable String instanceName) {}
|
||||
|
||||
/** Replace {@code [startIdx, stopIdx]} with {@code replacement}. */
|
||||
public record Edit(int startIdx, int stopIdx, String replacement) {}
|
||||
@@ -109,7 +113,7 @@ public final class GlslVulkanPreprocess {
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
final Metadata meta = collectEdits(new ShaderIndex(root, source), glShaderType, debugName, separateReadOnlyImages, edits);
|
||||
|
||||
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs());
|
||||
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs(), meta.uniformBlocks());
|
||||
synchronized (CACHE) {
|
||||
CACHE.putAndMoveToFirst(key, out);
|
||||
while (CACHE.size() > CACHE_MAX) CACHE.removeLast();
|
||||
@@ -117,11 +121,12 @@ public final class GlslVulkanPreprocess {
|
||||
return out;
|
||||
}
|
||||
|
||||
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless) {}
|
||||
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless, Map<String, UniformBlock> uniformBlocks) {}
|
||||
|
||||
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<>();
|
||||
Map<String, UniformBlock> uniformBlocks = Map.of();
|
||||
final Set<String> explicitInputs = new HashSet<>();
|
||||
final Set<String> readOnlyImages = new HashSet<>();
|
||||
boolean needsSamplerless = false;
|
||||
@@ -130,6 +135,14 @@ public final class GlslVulkanPreprocess {
|
||||
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
|
||||
|
||||
for (GLSLParser.DeclarationContext declaration : index.globalDeclarations()) {
|
||||
if (declaration.LEFT_BRACE() != null && ShaderIndex.hasStorageQualifier(declaration.type_qualifier(), "uniform")) {
|
||||
final GLSLParser.Type_qualifierContext blockQualifier = declaration.type_qualifier();
|
||||
final String qualifierText = index.source().substring(startIdx(blockQualifier), stopIdx(blockQualifier) + 1);
|
||||
final String instanceName = declaration.IDENTIFIER().size() > 1 ? declaration.IDENTIFIER(1).getText() : null;
|
||||
if (uniformBlocks.isEmpty()) uniformBlocks = new HashMap<>();
|
||||
uniformBlocks.put(declaration.IDENTIFIER(0).getText(), new UniformBlock(qualifierText, instanceName));
|
||||
continue;
|
||||
}
|
||||
final GLSLParser.Init_declarator_listContext list = declaration.init_declarator_list();
|
||||
if (list == null) continue;
|
||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||
@@ -223,7 +236,7 @@ public final class GlslVulkanPreprocess {
|
||||
}
|
||||
}
|
||||
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless);
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless, uniformBlocks);
|
||||
}
|
||||
|
||||
public static boolean renamesIdentifier(String name) {
|
||||
|
||||
+74
-37
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.Edit;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.UniformBlock;
|
||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
@@ -26,8 +27,10 @@ import org.taumc.glsl.grammar.GLSLParserBaseListener;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.startIdx;
|
||||
@@ -65,13 +68,13 @@ public final class SpirvShaderTranslator {
|
||||
try {
|
||||
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
|
||||
if (emitted == null) return null;
|
||||
return postProcessEsOutput(emitted, pre.boolUniforms());
|
||||
return postProcessEsOutput(emitted, pre.boolUniforms(), pre.uniformBlocks());
|
||||
} finally {
|
||||
memFree(cr.spirv());
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String postProcessEsOutput(String glslEs, Set<String> boolUniformNames) {
|
||||
private static @Nullable String postProcessEsOutput(String glslEs, Set<String> boolUniformNames, Map<String, UniformBlock> uniformBlocks) {
|
||||
final GLSLParser.Translation_unitContext root;
|
||||
try {
|
||||
root = GlslTransformUtils.parseFullQuiet(glslEs);
|
||||
@@ -80,21 +83,42 @@ public final class SpirvShaderTranslator {
|
||||
return null;
|
||||
}
|
||||
|
||||
final DefaultBlockInfo block = findDefaultBlock(root);
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
final BlockFinder finder = new BlockFinder(glslEs, uniformBlocks);
|
||||
ParseTreeWalker.DEFAULT.walk(finder, root);
|
||||
|
||||
if (block != null) {
|
||||
edits.add(new Edit(startIdx(block.structExternalDecl), stopIdx(block.structExternalDecl), buildLooseUniforms(block.members, boolUniformNames)));
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
BlockInfo defaultBlock = null;
|
||||
final Map<String, Set<String>> bareMembersByVar = new HashMap<>();
|
||||
for (BlockInfo block : finder.blocks) {
|
||||
final String structReplacement;
|
||||
if (block.source == null) {
|
||||
defaultBlock = block;
|
||||
structReplacement = buildLooseUniforms(block.members, boolUniformNames);
|
||||
bareMembersByVar.put(block.varName, block.memberNames());
|
||||
} else if (block.source.instanceName() == null) {
|
||||
structReplacement = block.source.qualifierText() + " " + block.structName + " " + block.body + ";";
|
||||
bareMembersByVar.put(block.varName, block.memberNames());
|
||||
} else {
|
||||
structReplacement = block.source.qualifierText() + " " + block.structName + " " + block.body + " " + block.declaratorText + ";";
|
||||
}
|
||||
edits.add(new Edit(startIdx(block.structExternalDecl), stopIdx(block.structExternalDecl), structReplacement));
|
||||
edits.add(new Edit(startIdx(block.uniformExternalDecl), stopIdx(block.uniformExternalDecl), ""));
|
||||
}
|
||||
|
||||
final UseSiteRewriter rewriter = new UseSiteRewriter(block, boolUniformNames, edits);
|
||||
final Set<String> defaultMembers = defaultBlock == null ? Set.of() : bareMembersByVar.get(defaultBlock.varName);
|
||||
final UseSiteRewriter rewriter = new UseSiteRewriter(defaultBlock, defaultMembers, boolUniformNames, bareMembersByVar, edits);
|
||||
ParseTreeWalker.DEFAULT.walk(rewriter, root);
|
||||
|
||||
return GlslVulkanPreprocess.applyEdits(glslEs, edits);
|
||||
}
|
||||
|
||||
private record DefaultBlockInfo(ParserRuleContext structExternalDecl, ParserRuleContext uniformExternalDecl, String blockVarName, List<MemberInfo> members) {}
|
||||
private record BlockInfo(String structName, @Nullable UniformBlock source, ParserRuleContext structExternalDecl, String body, List<MemberInfo> members, ParserRuleContext uniformExternalDecl, String varName, String declaratorText) {
|
||||
Set<String> memberNames() {
|
||||
final Set<String> names = new HashSet<>();
|
||||
for (MemberInfo m : members) names.add(m.name);
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
private record MemberInfo(String typePrefix, String name) {}
|
||||
|
||||
@@ -111,31 +135,38 @@ public final class SpirvShaderTranslator {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static @Nullable DefaultBlockInfo findDefaultBlock(GLSLParser.Translation_unitContext root) {
|
||||
final DefaultBlockFinder finder = new DefaultBlockFinder();
|
||||
ParseTreeWalker.DEFAULT.walk(finder, root);
|
||||
if (finder.structExternalDecl == null || finder.uniformExternalDecl == null) return null;
|
||||
return new DefaultBlockInfo(finder.structExternalDecl, finder.uniformExternalDecl, finder.blockVarName, finder.members);
|
||||
private static boolean isDefaultBlockStruct(String structName) {
|
||||
return structName.startsWith("_RESERVED_IDENTIFIER_FIXUP_") && structName.endsWith("DefaultUniformBlock");
|
||||
}
|
||||
|
||||
private static final class DefaultBlockFinder extends GLSLParserBaseListener {
|
||||
String structName;
|
||||
ParserRuleContext structExternalDecl;
|
||||
ParserRuleContext uniformExternalDecl;
|
||||
String blockVarName;
|
||||
final List<MemberInfo> members = new ArrayList<>();
|
||||
private static final class BlockFinder extends GLSLParserBaseListener {
|
||||
private record PendingStruct(@Nullable UniformBlock source, ParserRuleContext externalDecl, String body, List<MemberInfo> members) {}
|
||||
|
||||
private final String glslEs;
|
||||
private final Map<String, UniformBlock> sourceBlocks;
|
||||
private final Map<String, PendingStruct> pending = new HashMap<>();
|
||||
final List<BlockInfo> blocks = new ArrayList<>();
|
||||
|
||||
BlockFinder(String glslEs, Map<String, UniformBlock> sourceBlocks) {
|
||||
this.glslEs = glslEs;
|
||||
this.sourceBlocks = sourceBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterStruct_specifier(GLSLParser.Struct_specifierContext ctx) {
|
||||
if (structExternalDecl != null) return;
|
||||
final TerminalNode id = ctx.IDENTIFIER();
|
||||
if (id == null) return;
|
||||
final String idText = id.getText();
|
||||
if (!idText.startsWith("_RESERVED_IDENTIFIER_FIXUP_") || !idText.endsWith("DefaultUniformBlock")) return;
|
||||
final UniformBlock source = sourceBlocks.get(idText);
|
||||
if (source == null && !isDefaultBlockStruct(idText)) return;
|
||||
|
||||
structName = idText;
|
||||
structExternalDecl = findExternalDeclaration(ctx);
|
||||
final List<MemberInfo> members = source != null && source.instanceName() != null ? List.of() : collectMembers(ctx);
|
||||
final String body = glslEs.substring(ctx.LEFT_BRACE().getSymbol().getStartIndex(), ctx.RIGHT_BRACE().getSymbol().getStopIndex() + 1);
|
||||
pending.putIfAbsent(idText, new PendingStruct(source, findExternalDeclaration(ctx), body, members));
|
||||
}
|
||||
|
||||
private static List<MemberInfo> collectMembers(GLSLParser.Struct_specifierContext ctx) {
|
||||
final List<MemberInfo> members = new ArrayList<>();
|
||||
for (GLSLParser.Struct_declarationContext sd : ctx.struct_declaration_list().struct_declaration()) {
|
||||
final StringBuilder typePrefix = new StringBuilder();
|
||||
if (sd.type_qualifier() != null) typePrefix.append(textWithSpaces(sd.type_qualifier())).append(' ');
|
||||
@@ -146,11 +177,12 @@ public final class SpirvShaderTranslator {
|
||||
members.add(new MemberInfo(typePrefix + arraySuffix, decl.IDENTIFIER().getText()));
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
|
||||
if (structName == null || uniformExternalDecl != null) return;
|
||||
if (pending.isEmpty()) return;
|
||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||
if (single == null || single.typeless_declaration() == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
@@ -159,10 +191,14 @@ public final class SpirvShaderTranslator {
|
||||
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
|
||||
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
|
||||
if (ts.type_name() == null) return;
|
||||
if (!structName.equals(ts.type_name().getText())) return;
|
||||
final String structName = ts.type_name().getText();
|
||||
final PendingStruct struct = pending.remove(structName);
|
||||
if (struct == null) return;
|
||||
|
||||
blockVarName = single.typeless_declaration().IDENTIFIER().getText();
|
||||
uniformExternalDecl = findExternalDeclaration(ctx);
|
||||
final GLSLParser.Typeless_declarationContext declarator = single.typeless_declaration();
|
||||
final String declaratorText = glslEs.substring(startIdx(declarator), stopIdx(declarator) + 1);
|
||||
blocks.add(new BlockInfo(structName, struct.source, struct.externalDecl, struct.body, struct.members,
|
||||
findExternalDeclaration(ctx), declarator.IDENTIFIER().getText(), declaratorText));
|
||||
}
|
||||
|
||||
private static String textWithSpaces(ParseTree tree) {
|
||||
@@ -192,17 +228,18 @@ public final class SpirvShaderTranslator {
|
||||
}
|
||||
|
||||
private static final class UseSiteRewriter extends GLSLParserBaseListener {
|
||||
private final @Nullable DefaultBlockInfo block;
|
||||
private final @Nullable BlockInfo block;
|
||||
private final Set<String> boolNames;
|
||||
private final Map<String, Set<String>> bareMembersByVar;
|
||||
private final List<Edit> edits;
|
||||
private final Set<String> memberNames;
|
||||
|
||||
UseSiteRewriter(@Nullable DefaultBlockInfo block, Set<String> boolNames, List<Edit> edits) {
|
||||
UseSiteRewriter(@Nullable BlockInfo block, Set<String> memberNames, Set<String> boolNames, Map<String, Set<String>> bareMembersByVar, List<Edit> edits) {
|
||||
this.block = block;
|
||||
this.memberNames = memberNames;
|
||||
this.boolNames = boolNames;
|
||||
this.bareMembersByVar = bareMembersByVar;
|
||||
this.edits = edits;
|
||||
this.memberNames = new HashSet<>();
|
||||
if (block != null) for (MemberInfo m : block.members) memberNames.add(m.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,11 +277,11 @@ public final class SpirvShaderTranslator {
|
||||
}
|
||||
}
|
||||
|
||||
if (block != null && ctx.getChildCount() == 3 && ctx.DOT() != null && ctx.field_selection() != null) {
|
||||
final ParseTree inner = ctx.getChild(0);
|
||||
if (inner.getText().equals(block.blockVarName)) {
|
||||
if (ctx.getChildCount() == 3 && ctx.DOT() != null && ctx.field_selection() != null) {
|
||||
final Set<String> bareMembers = bareMembersByVar.get(ctx.getChild(0).getText());
|
||||
if (bareMembers != null) {
|
||||
final String member = ctx.field_selection().getText();
|
||||
if (memberNames.contains(member) && !coveredByAncestorBoolCompare(ctx)) {
|
||||
if (bareMembers.contains(member) && !coveredByAncestorBoolCompare(ctx)) {
|
||||
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), member));
|
||||
}
|
||||
}
|
||||
@@ -254,8 +291,8 @@ public final class SpirvShaderTranslator {
|
||||
private @Nullable String resolveBoolOperand(ParseTree operand) {
|
||||
final String text = operand.getText();
|
||||
if (boolNames.contains(text)) return text;
|
||||
if (block != null && text.startsWith(block.blockVarName + ".")) {
|
||||
final String member = text.substring(block.blockVarName.length() + 1);
|
||||
if (block != null && text.startsWith(block.varName + ".")) {
|
||||
final String member = text.substring(block.varName.length() + 1);
|
||||
if (boolNames.contains(member) && memberNames.contains(member)) return member;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -23,8 +23,12 @@ public class BooleanState implements ISettableState<BooleanState> {
|
||||
this.ffpStateOnly = ffpStateOnly;
|
||||
}
|
||||
|
||||
protected boolean forwardsToBackend() {
|
||||
return !ffpStateOnly;
|
||||
}
|
||||
|
||||
public void applyToBackend() {
|
||||
if (ffpStateOnly) return;
|
||||
if (!forwardsToBackend()) return;
|
||||
if (enabled) {
|
||||
RENDER_BACKEND.enable(glCap);
|
||||
} else {
|
||||
@@ -51,7 +55,7 @@ public class BooleanState implements ISettableState<BooleanState> {
|
||||
stateUnknown = false;
|
||||
this.enabled = enabled;
|
||||
onEnabledChanged();
|
||||
if (!ffpStateOnly) {
|
||||
if (forwardsToBackend()) {
|
||||
if (enabled) {
|
||||
RENDER_BACKEND.enable(this.glCap);
|
||||
} else {
|
||||
|
||||
@@ -27,10 +27,16 @@ public class ClipPlaneState {
|
||||
dest[3] = tempPlane.w;
|
||||
}
|
||||
|
||||
/** Writes the 4-component eye-space plane equation into {@code buf} at its current position. */
|
||||
public void putEyePlane(int index, FloatBuffer buf) {
|
||||
final float[] p = eyePlanes[index];
|
||||
buf.put(p[0]).put(p[1]).put(p[2]).put(p[3]);
|
||||
// Vulkan and clip emulation have no per-distance enable: a disabled plane must read as distance 0.
|
||||
public void putEffectivePlanes(int enabledMask, FloatBuffer buf) {
|
||||
for (int i = 0; i < eyePlanes.length; i++) {
|
||||
if ((enabledMask & (1 << i)) != 0) {
|
||||
final float[] p = eyePlanes[i];
|
||||
buf.put(p[0]).put(p[1]).put(p[2]).put(p[3]);
|
||||
} else {
|
||||
buf.put(0f).put(0f).put(0f).put(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void getEyePlane(int index, DoubleBuffer buf) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
/** Emulates GL proxy textures for backends that lack them. */
|
||||
public final class ProxyTexture {
|
||||
|
||||
private final int maxTextureSize;
|
||||
private final int max3DTextureSize;
|
||||
private final int maxCubeMapTextureSize;
|
||||
private final int maxArrayTextureLayers;
|
||||
|
||||
private int target;
|
||||
private int width;
|
||||
private int height;
|
||||
private int depth;
|
||||
private int internalFormat;
|
||||
|
||||
public ProxyTexture(int maxTextureSize, int max3DTextureSize, int maxCubeMapTextureSize, int maxArrayTextureLayers) {
|
||||
this.maxTextureSize = maxTextureSize;
|
||||
this.max3DTextureSize = max3DTextureSize;
|
||||
this.maxCubeMapTextureSize = maxCubeMapTextureSize;
|
||||
this.maxArrayTextureLayers = maxArrayTextureLayers;
|
||||
}
|
||||
|
||||
public void record(int target, int level, int internalFormat, int width, int height, int depth, boolean formatSupported) {
|
||||
final boolean layersInHeight = layersInHeight(target);
|
||||
final boolean layersInDepth = layersInDepth(target);
|
||||
final int dimLimit = dimensionLimit(target);
|
||||
final int w = levelZeroEquivalentSize(width, level);
|
||||
final int h = layersInHeight ? height : levelZeroEquivalentSize(height, level);
|
||||
final int d = layersInDepth ? depth : levelZeroEquivalentSize(depth, level);
|
||||
final int hLimit = layersInHeight ? maxArrayTextureLayers : dimLimit;
|
||||
final int dLimit = layersInDepth ? maxArrayTextureLayers : dimLimit;
|
||||
final boolean fits = w >= 1 && w <= dimLimit && h >= 1 && h <= hLimit && d >= 1 && d <= dLimit && formatSupported;
|
||||
if (fits) {
|
||||
accept(target, w, h, d, internalFormat);
|
||||
} else {
|
||||
reject(target);
|
||||
}
|
||||
}
|
||||
|
||||
public void reject(int target) {
|
||||
accept(target, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void accept(int target, int width, int height, int depth, int internalFormat) {
|
||||
this.target = target;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.internalFormat = internalFormat;
|
||||
}
|
||||
|
||||
public int levelParameter(int target, int level, int pname) {
|
||||
if (this.target != target) return 0;
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(width, level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> layersInHeight(target) ? height : TextureInfo.mipLevelSize(height, level);
|
||||
case GL12.GL_TEXTURE_DEPTH -> layersInDepth(target) ? depth : TextureInfo.mipLevelSize(depth, level);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> internalFormat;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public int dimensionLimit(int target) {
|
||||
return switch (target) {
|
||||
case GL12.GL_PROXY_TEXTURE_3D -> max3DTextureSize;
|
||||
case GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> maxCubeMapTextureSize;
|
||||
default -> maxTextureSize;
|
||||
};
|
||||
}
|
||||
|
||||
public static int levelZeroEquivalentSize(int size, int level) {
|
||||
if (size <= 0 || level < 0 || level >= Integer.SIZE - 1) return 0;
|
||||
final long scaled = (long) size << level;
|
||||
return scaled > Integer.MAX_VALUE ? 0 : (int) scaled;
|
||||
}
|
||||
|
||||
private static boolean layersInHeight(int target) {
|
||||
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY;
|
||||
}
|
||||
|
||||
private static boolean layersInDepth(int target) {
|
||||
return target == GL30.GL_PROXY_TEXTURE_2D_ARRAY || target == GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY || target == GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@ public class TextureInfo {
|
||||
private static final int UNSET_INT = Integer.MIN_VALUE;
|
||||
private static final float UNSET_FLOAT = Float.NEGATIVE_INFINITY;
|
||||
|
||||
public static int mipLevelSize(int base, int level) {
|
||||
if (base <= 0 || level < 0) return 0;
|
||||
if (level >= Integer.SIZE - 1) return 1;
|
||||
return Math.max(1, base >> level);
|
||||
}
|
||||
|
||||
@Getter protected final int id;
|
||||
@Getter @Setter protected int target = GL11.GL_TEXTURE_2D;
|
||||
protected int internalFormat = -1;
|
||||
|
||||
@@ -53,4 +53,7 @@ void main() {
|
||||
#if GLINT_REPLACE_ALPHA
|
||||
fragColor.a = visible1 ? b.a : a.a;
|
||||
#endif
|
||||
#ifdef DEPTH_PEEL
|
||||
peelTest(fragColor);
|
||||
#endif
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class GLESCapsTest {
|
||||
};
|
||||
|
||||
private static boolean onGles(int cap) {
|
||||
return GLESCaps.isCapAllowed(cap, true, true);
|
||||
return GLESCaps.isCapAllowed(cap, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,25 +60,10 @@ class GLESCapsTest {
|
||||
@Test
|
||||
void desktopContextAllowsEveryCap() {
|
||||
for (int cap : DESKTOP_ONLY) {
|
||||
assertTrue(GLESCaps.isCapAllowed(cap, false, false), () -> "cap 0x" + Integer.toHexString(cap) + " is legal on desktop core and the gate must never suppress it there");
|
||||
assertTrue(GLESCaps.isCapAllowed(cap, false), () -> "cap 0x" + Integer.toHexString(cap) + " is legal on desktop core and the gate must never suppress it there");
|
||||
}
|
||||
for (int cap : VALID_ON_GLES) {
|
||||
assertTrue(GLESCaps.isCapAllowed(cap, false, false));
|
||||
assertTrue(GLESCaps.isCapAllowed(cap, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clipPlanesFollowClipCullDistance() {
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
final int cap = GL11.GL_CLIP_PLANE0 + i;
|
||||
assertTrue(GLESCaps.isCapAllowed(cap, true, true), () -> "clip plane " + cap + " works where EXT_clip_cull_distance is present and must not be suppressed");
|
||||
assertFalse(GLESCaps.isCapAllowed(cap, true, false), () -> "clip plane " + cap + " is invalid on GLES without EXT_clip_cull_distance");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void capsOutsideTheClipPlaneRangeAreUnaffectedByClipCullDistance() {
|
||||
assertTrue(GLESCaps.isCapAllowed(GL11.GL_BLEND, true, false));
|
||||
assertTrue(GLESCaps.isCapAllowed(GL11.GL_CLIP_PLANE0 + GLStateManager.MAX_CLIP_PLANES, true, false));
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
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.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_BlendAlphaOverride_GLTest {
|
||||
|
||||
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void resetOverrides() {
|
||||
GLSMHooks.blendHandler = null;
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.setLayerBlending(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideChangesTheDriverFuncButNotTheCache() {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
static Stream<Arguments> layerBlendRows() {
|
||||
return Stream.of(
|
||||
Arguments.of("translucent over", GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA),
|
||||
Arguments.of("alpha additive", GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("layerBlendRows")
|
||||
void layerBlendingAccumulatesCoverageAndKeepsAdditiveCoverageFree(String name, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
GLSMBlend.setLayerBlending(true);
|
||||
GLSMBlend.glBlendFunc(srcRgb, dstRgb);
|
||||
assertCachedFunc(srcRgb, dstRgb, srcRgb, dstRgb);
|
||||
assertDriverFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
GLSMBlend.setLayerBlending(false);
|
||||
assertDriverFunc(srcRgb, dstRgb, srcRgb, dstRgb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void togglingWhileABlendOverrideIsHeldReemitsOnTheNextUnchangedFunc() {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMHooks.blendHandler = new HeldOverride();
|
||||
GLSMBlend.setLayerBlending(true);
|
||||
GLSMHooks.blendHandler = null;
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
private static final class HeldOverride implements DeferredBlendHandler {
|
||||
@Override public boolean isBlendLocked() { return false; }
|
||||
@Override public boolean isOverrideHeld() { return true; }
|
||||
@Override public void deferBlendModeToggle(boolean enabled) {}
|
||||
@Override public void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {}
|
||||
@Override public void flushDeferredBlend() {}
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_HudCacheBlendOverride_GLTest {
|
||||
|
||||
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GLSMQueries.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideChangesTheDriverFuncButNotTheCache() {
|
||||
try {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
|
||||
try {
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLEND);
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
|
||||
try {
|
||||
GLSMBlend.setHudCacheOverride(true);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} finally {
|
||||
GLSMBlend.setHudCacheOverride(false);
|
||||
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,33 @@ public class GLSM_StateSet_GLTest {
|
||||
|| GLSMBlend.getColorMask().blue || GLSMBlend.getColorMask().alpha, "cache color mask - not a HAND member, must stay toggled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void blockEntityPassRestoresColorFogAndShadeModelOnTopOfBatchButNotStencil() {
|
||||
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLSMFog.disableFog();
|
||||
GLSMFog.glFogf(GL11.GL_FOG_END, 64.0f);
|
||||
GLSMRasterizer.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
GLSMEnable.glDisable(GL11.GL_STENCIL_TEST);
|
||||
|
||||
final int d = GLSMAttribStack.pushState(StateSet.BLOCK_ENTITY_PASS);
|
||||
GLSMCurrentAttribs.glColor4f(0.2f, 0.4f, 0.6f, 0.8f);
|
||||
GLSMFog.enableFog();
|
||||
GLSMFog.glFogf(GL11.GL_FOG_END, 8.0f);
|
||||
GLSMRasterizer.glShadeModel(GL11.GL_FLAT);
|
||||
GLSMDepthStencilAlpha.glDepthMask(false);
|
||||
GLSMEnable.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLSMAttribStack.popStateTo(d);
|
||||
|
||||
assertEquals(1.0f, GLSMCurrentAttribs.getColor().getRed(), "cache color red");
|
||||
assertEquals(1.0f, GLSMCurrentAttribs.getColor().getAlpha(), "cache color alpha");
|
||||
assertFalse(GLSMFog.getFogMode().isEnabled(), "fog enable restored by BLOCK_ENTITY_PASS pop");
|
||||
assertEquals(64.0f, GLSMFog.getFogState().getEnd(), "cache fog end");
|
||||
assertEquals(GL11.GL_SMOOTH, GLSMRasterizer.getShadeModelState().getValue(), "cache shade model");
|
||||
assertTrue(GLSMDepthStencilAlpha.getDepthState().isEnabled(), "depth mask restored, as in BATCH");
|
||||
verifyIsEnabled(GL11.GL_STENCIL_TEST, true, "Stencil test - not a BLOCK_ENTITY_PASS member, must stay toggled");
|
||||
}
|
||||
|
||||
@Test
|
||||
void masksRestoresDepthMaskAndColorMaskButNotDepthTestEnable() {
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMAttribStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.ClipTestSupport;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
class ClipPlaneGLTest {
|
||||
|
||||
private static final int SIZE = 64;
|
||||
private static final int BACKGROUND = 0xFF000000;
|
||||
private static final int QUAD = 0xFF0000FF;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FfpFixture.resetFfpState();
|
||||
FfpFixture.activateFfp();
|
||||
GLSMRasterizer.glViewport(0, 0, SIZE, SIZE);
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLSMMatrices.glLoadIdentity();
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glLoadIdentity();
|
||||
GLSMEnable.glDisable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
for (int i = 0; i < GLStateManager.MAX_CLIP_PLANES; i++) {
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE0 + i);
|
||||
}
|
||||
FfpFixture.resetFfpState();
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "emulated={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void disabledPlaneDoesNotClip(boolean emulated) {
|
||||
try (var ignored = ClipTestSupport.emulation(emulated)) {
|
||||
ClipTestSupport.setPlane(1, 0, 0, 0, -1);
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE1);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
ClipTestSupport.setPlane(0, 1, 0, 0, 0);
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
assertRightHalfOnly(drawQuad(), "plane1 disabled, plane0 enabled");
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "emulated={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void attribPopRestoresClipping(boolean emulated) {
|
||||
try (var ignored = ClipTestSupport.emulation(emulated)) {
|
||||
ClipTestSupport.setPlane(0, 1, 0, 0, 0);
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
assertDriverClipPlane0(!emulated, "after glEnable");
|
||||
GLSMAttribStack.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
final int[] unclipped = drawQuad();
|
||||
GLSMAttribStack.glPopAttrib();
|
||||
assertDriverClipPlane0(!emulated, "after glPopAttrib");
|
||||
GLSMAttribStack.replayStateToBackend();
|
||||
assertDriverClipPlane0(!emulated, "after replayStateToBackend");
|
||||
final int[] restored = drawQuad();
|
||||
|
||||
for (int i = 0; i < unclipped.length; i++) {
|
||||
final int px = i;
|
||||
assertEquals(QUAD, unclipped[i], () -> "plane disabled inside the pushed scope, pixel " + px % SIZE + "," + px / SIZE);
|
||||
}
|
||||
assertRightHalfOnly(restored, "after glPopAttrib");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDriverClipPlane0(boolean expected, String when) {
|
||||
assertEquals(expected, GL11.glIsEnabled(GL11.GL_CLIP_PLANE0), () -> when + ": clip plane reaches the driver only when clip distances are native");
|
||||
}
|
||||
|
||||
private static void assertRightHalfOnly(int[] pixels, String what) {
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
final int x = i % SIZE, y = i / SIZE;
|
||||
assertEquals(x < SIZE / 2 ? BACKGROUND : QUAD, pixels[i], () -> what + ": first mismatch at pixel " + x + "," + y);
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] drawQuad() {
|
||||
final int vao = GLSMVertexInput.glGenVertexArrays();
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
final int vbo = GLSMBuffers.glGenBuffers();
|
||||
try {
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
final FloatBuffer positions = BufferUtils.createFloatBuffer(18);
|
||||
positions.put(new float[] { -1, -1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0 }).flip();
|
||||
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, positions, GL15.GL_STATIC_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
|
||||
FfpFixture.clear();
|
||||
GLSMCurrentAttribs.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLSMDraws.glDrawArrays(GL11.GL_TRIANGLES, 0, 6);
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
return FfpFixture.readRegion(SIZE);
|
||||
} finally {
|
||||
GLSMBuffers.glDeleteBuffers(vbo);
|
||||
GLSMVertexInput.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -7,6 +7,7 @@ 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.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
@@ -55,6 +56,30 @@ class FFPUniformBlockSpirvLayoutTest {
|
||||
MemoryUtil.memFree(result.spirv());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "emulated={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void clipDistanceCapabilityFollowsEmulation(boolean emulated) {
|
||||
long vbits = bit("BIT_CLIP_PLANES");
|
||||
if (emulated) vbits |= bit("BIT_CLIP_EMULATED");
|
||||
final String vs = VertexShaderGenerator.generate(VertexKey.fromPacked(vbits));
|
||||
assertEquals(!emulated, declaresClipDistance(vs, Shaderc.shaderc_vertex_shader), "vertex ClipDistance capability");
|
||||
}
|
||||
|
||||
private static boolean declaresClipDistance(String glsl, int shaderKind) {
|
||||
final SpirvCompiler.Result result = SpirvCompiler.compile(glsl, shaderKind, "clip-emulation-test", SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(result.spirv(), () -> "SPIR-V compile failed: " + result.error() + "\n" + glsl);
|
||||
try {
|
||||
final IntBuffer words = result.spirv().asIntBuffer();
|
||||
for (int i = 5; i < words.limit(); i += Math.max(1, words.get(i) >>> 16)) {
|
||||
final int word = words.get(i);
|
||||
if ((word & 0xFFFF) == 17 && words.get(i + 1) == 32) return true;
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
MemoryUtil.memFree(result.spirv());
|
||||
}
|
||||
}
|
||||
|
||||
private static VertexKey cubeKey(boolean lightmapUnit, boolean unit0TextureMatrix, boolean texGen) {
|
||||
long bits = bit("BIT_HAS_VERTEX_COLOR") | bit("BIT_HAS_VERTEX_NORMAL") | bit("BIT_HAS_VERTEX_TEX") | bit("BIT_UNIT_TEX_BASE");
|
||||
if (lightmapUnit) bits |= 1L << (position("BIT_UNIT_TEX_BASE") + 1);
|
||||
|
||||
+74
-5
@@ -27,10 +27,12 @@ import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
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.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -82,6 +84,7 @@ class FFPVertexLightingGLTest {
|
||||
GLSMEnable.glDisable(GL11.GL_LIGHT1);
|
||||
GLSMEnable.glDisable(GL11.GL_LIGHT2);
|
||||
GLSMEnable.glDisable(GL11.GL_COLOR_MATERIAL);
|
||||
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
@@ -126,7 +129,7 @@ class FFPVertexLightingGLTest {
|
||||
+ " light1=" + GLSMLighting.getLightStates()[1].isEnabled();
|
||||
}
|
||||
|
||||
private void buildTriangle() {
|
||||
private void buildTriangle(boolean withColor) {
|
||||
vao = GLSMVertexInput.glGenVertexArrays();
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
|
||||
@@ -141,9 +144,13 @@ class FFPVertexLightingGLTest {
|
||||
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
GLSMVertexInput.glEnableVertexAttribArray(POSITION_LOC);
|
||||
GLSMVertexInput.glVertexAttribPointer(POSITION_LOC, 3, GL11.GL_FLOAT, false, STRIDE, 0L);
|
||||
GLSMVertexInput.glEnableVertexAttribArray(COLOR_LOC);
|
||||
GLSMVertexInput.glVertexAttribPointer(COLOR_LOC, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, 12L);
|
||||
VAOManager.setCurrentVertexFlags(COLOR_BIT);
|
||||
if (withColor) {
|
||||
GLSMVertexInput.glEnableVertexAttribArray(COLOR_LOC);
|
||||
GLSMVertexInput.glVertexAttribPointer(COLOR_LOC, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, 12L);
|
||||
VAOManager.setCurrentVertexFlags(COLOR_BIT);
|
||||
} else {
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putVertex(ByteBuffer buf, float x, float y) {
|
||||
@@ -154,7 +161,11 @@ class FFPVertexLightingGLTest {
|
||||
}
|
||||
|
||||
private float[] drawAndReadCenter() {
|
||||
buildTriangle();
|
||||
return drawAndReadCenter(true);
|
||||
}
|
||||
|
||||
private float[] drawAndReadCenter(boolean withColor) {
|
||||
buildTriangle(withColor);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
@@ -233,6 +244,64 @@ class FFPVertexLightingGLTest {
|
||||
state.run();
|
||||
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), reason);
|
||||
assertEquals(IDENTITY_FACTOR, factor, "a rejected solve must leave an identity factor");
|
||||
assertBakeRejected(reason);
|
||||
}
|
||||
|
||||
private static void assertBakeRejected(String reason) {
|
||||
final int[] raw = new int[8];
|
||||
Arrays.fill(raw, 0x5A3C1E0F);
|
||||
final int[] before = raw.clone();
|
||||
assertFalse(FFPVertexLighting.bakeColors(raw, 1, true, true), reason + ": bake must be rejected");
|
||||
assertArrayEquals(before, raw, reason + ": a rejected bake must leave the buffer untouched");
|
||||
}
|
||||
|
||||
@Test
|
||||
void positionalLightIsNotBaked() {
|
||||
GLSMLighting.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
assertBakeRejected("a positional light has a per-vertex direction");
|
||||
assertTrue(FFPVertexLighting.modulatesVertexColor(factor), "text keeps the object-origin approximation for positional lights");
|
||||
}
|
||||
|
||||
static Stream<Arguments> bakeCases() {
|
||||
return Stream.of(
|
||||
Arguments.of("flattened face-on", -1.0e-4f, 0.0f, 0.0f, 1.0f, true, true),
|
||||
Arguments.of("flattened top", -1.0e-4f, 0.0f, 1.0f, 0.0f, true, true),
|
||||
Arguments.of("flattened side", -1.0e-4f, 1.0f, 0.0f, 0.0f, true, true),
|
||||
Arguments.of("unflattened tilted", 1.0f, 0.3f, 0.8f, 0.5f, true, true),
|
||||
Arguments.of("current color", -1.0e-4f, 0.0f, 1.0f, 0.0f, true, false),
|
||||
Arguments.of("current normal", -1.0e-4f, 0.0f, 1.0f, 0.0f, false, true));
|
||||
}
|
||||
|
||||
private static int packNormal(float x, float y, float z) {
|
||||
return ((byte) (x * 127.0f) & 0xFF) | (((byte) (y * 127.0f) & 0xFF) << 8) | (((byte) (z * 127.0f) & 0xFF) << 16);
|
||||
}
|
||||
|
||||
private static float unpackNormal(int packed, int shift) {
|
||||
return (byte) (packed >> shift) / 127.0f;
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("bakeCases")
|
||||
void bakedColorMatchesGeneratedShader(String name, float zScale, float nx, float ny, float nz, boolean hasNormals, boolean hasColor) {
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLSMMatrices.glLoadIdentity();
|
||||
GLSMMatrices.glScalef(1.0f, 1.0f, zScale);
|
||||
standardItemLighting();
|
||||
if (!hasColor) GLSMCurrentAttribs.glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
final int packedNormal = packNormal(nx, ny, nz);
|
||||
final int[] raw = new int[8];
|
||||
raw[5] = VERTEX_COLOR;
|
||||
if (hasNormals) raw[6] = packedNormal;
|
||||
GLSMCurrentAttribs.glNormal3f(unpackNormal(packedNormal, 0), unpackNormal(packedNormal, 8), unpackNormal(packedNormal, 16));
|
||||
assertTrue(FFPVertexLighting.bakeColors(raw, 1, hasNormals, hasColor), name + ": expected a bakeable state, " + gateState());
|
||||
|
||||
final float[] actual = drawAndReadCenter(hasColor);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
final int baked = (raw[5] >>> (c * 8)) & 0xFF;
|
||||
final int shader = Math.round(actual[c] * 255.0f);
|
||||
assertEquals(baked, shader, name + ": channel " + c + " baked=" + baked + " shader=" + shader);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-10
@@ -3,21 +3,21 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMFog;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTexEnvGen;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.ClipTestSupport;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -32,12 +32,6 @@ class FfpUniformBlockDedupTest {
|
||||
return GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_START, FFPUniformBlock.BINDING_POINT);
|
||||
}
|
||||
|
||||
private static DoubleBuffer doubles(double... values) {
|
||||
final DoubleBuffer buf = BufferUtils.createDoubleBuffer(values.length);
|
||||
buf.put(values).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static void resetState() {
|
||||
GLSMMatrices.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLSMMatrices.glLoadIdentity();
|
||||
@@ -58,7 +52,8 @@ class FfpUniformBlockDedupTest {
|
||||
GLSMLighting.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
GLSMLighting.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 0.0f);
|
||||
GLSMTexEnvGen.glTexGen(GL11.GL_S, GL11.GL_OBJECT_PLANE, floats(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
GLSMRasterizer.glClipPlane(GL11.GL_CLIP_PLANE0, doubles(0.0, 0.0, 0.0, 0.0));
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
ClipTestSupport.setPlane(0, 0.0, 0.0, 0.0, 0.0);
|
||||
GLSMRasterizer.glLineStipple(1, (short) 0xFFFF);
|
||||
GLSMRasterizer.glViewport(0, 0, 854, 480);
|
||||
}
|
||||
@@ -149,7 +144,10 @@ class FfpUniformBlockDedupTest {
|
||||
addCategory(tests, "currentTexCoord", () -> GLSMCurrentAttribs.glTexCoord2f(0.125f, 0.375f));
|
||||
addCategory(tests, "lightmapCoord", () -> GLStateManager.ctx().lastBrightnessX = 96.0f);
|
||||
addCategory(tests, "texGenPlane", () -> GLSMTexEnvGen.glTexGen(GL11.GL_S, GL11.GL_OBJECT_PLANE, floats(0.5f, 0.0f, 0.0f, 0.25f)));
|
||||
addCategory(tests, "clipPlane", () -> GLSMRasterizer.glClipPlane(GL11.GL_CLIP_PLANE0, doubles(0.0, 1.0, 0.0, 7.0)));
|
||||
addCategory(tests, "clipPlane", () -> {
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
ClipTestSupport.setPlane(0, 0.0, 1.0, 0.0, 7.0);
|
||||
});
|
||||
addCategory(tests, "alphaRef", () -> GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.35f));
|
||||
addCategory(tests, "fogColor", () -> GLSMFog.glFog(GL11.GL_FOG_COLOR, floats(0.1f, 0.2f, 0.3f, 0.4f)));
|
||||
addCategory(tests, "fogDensity", () -> GLSMFog.glFogf(GL11.GL_FOG_DENSITY, 0.75f));
|
||||
|
||||
+33
@@ -277,6 +277,39 @@ class SpirvShaderTranslatorTest {
|
||||
assertFalse(es.contains("!= 0u"), "no bool use-site should still be comparing against 0u");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBlock_staysABlockUnderGLES() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
layout(std140) uniform Blk {
|
||||
mat4 u_M;
|
||||
vec4 u_C;
|
||||
};
|
||||
layout(std140) uniform Named {
|
||||
vec4 u_N;
|
||||
} named;
|
||||
uniform float u_Loose;
|
||||
struct L { vec4 c; };
|
||||
uniform L u_Light;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = u_M * color * u_C * u_Loose + u_Light.c + named.u_N;
|
||||
}
|
||||
""";
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "uniform_block.frag");
|
||||
dumpOnFailure("uniform_block.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertTrue(Pattern.compile("layout\\s*\\(\\s*std140\\s*\\)\\s*uniform\\s+Blk\\s*\\{").matcher(es).find(), "a source uniform block must reach GLSL ES as a block, so glUniformBlockBinding finds it by name\n\n" + es);
|
||||
assertFalse(Pattern.compile("uniform\\s+Blk\\s+_").matcher(es).find(), "the block must not be left as a plain struct uniform\n\n" + es);
|
||||
assertFalse(Pattern.compile("_[0-9]+\\.u_M").matcher(es).find(), "an unnamed source block's members must be accessed bare\n\n" + es);
|
||||
assertTrue(Pattern.compile("layout\\s*\\(\\s*std140\\s*\\)\\s*uniform\\s+Named\\s*\\{[^}]*\\}\\s*named\\s*;").matcher(es).find(), "an instance-named block keeps its instance name\n\n" + es);
|
||||
assertTrue(es.contains("named.u_N"), "members of an instance-named block stay qualified\n\n" + es);
|
||||
assertContainsDecl(es, "float", "u_Loose");
|
||||
assertTrue(Pattern.compile("uniform\\s+L\\s+u_Light\\s*;").matcher(es).find(), "a struct uniform is not a block and must stay a plain uniform\n\n" + es);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readonlyImage3D_staysAnImageUnderGLES() {
|
||||
final String src = """
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.gtnewhorizons.angelica.glsm.testutil;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
|
||||
public final class ClipTestSupport {
|
||||
|
||||
private ClipTestSupport() {}
|
||||
|
||||
public static void setPlane(int index, double a, double b, double c, double d) {
|
||||
final DoubleBuffer eq = BufferUtils.createDoubleBuffer(4);
|
||||
eq.put(a).put(b).put(c).put(d).flip();
|
||||
GLSMRasterizer.glClipPlane(GL11.GL_CLIP_PLANE0 + index, eq);
|
||||
}
|
||||
|
||||
public static Emulation emulation(boolean emulated) {
|
||||
return new Emulation(emulated);
|
||||
}
|
||||
|
||||
public static final class Emulation implements AutoCloseable {
|
||||
private final boolean previous;
|
||||
|
||||
private Emulation(boolean emulated) {
|
||||
previous = Reflect.getStatic(RenderSystem.class, "clipDistanceEmulated");
|
||||
Reflect.setStatic(RenderSystem.class, "clipDistanceEmulated", emulated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Reflect.setStatic(RenderSystem.class, "clipDistanceEmulated", previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configur
|
||||
for (name in forwardedRunProperties) {
|
||||
jvmArgs("-D$name=${System.getProperty(name)}")
|
||||
}
|
||||
jvmArgs("-Dangelica.buildVersion=${project.version}")
|
||||
}
|
||||
|
||||
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import java.time.Duration
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
@@ -75,6 +77,80 @@ dependencies {
|
||||
compileOnly(libs.annotations)
|
||||
}
|
||||
|
||||
val glesTag = "gles"
|
||||
|
||||
val glsmRedirectAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmLwjgl2Stubs: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||
glsmLwjgl2Stubs(project(":glsm", "lwjgl2Stubs"))
|
||||
|
||||
testImplementation(platform(libs.junit.bom))
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(testFixtures(project(":glsm")))
|
||||
testImplementation(project(":glsm")) {
|
||||
capabilities { requireCapability("${project.group}:glsm-stubs") }
|
||||
}
|
||||
listOf(libs.lwjgl3.sdl, libs.lwjgl3.shaderc, libs.lwjgl3.spvc).forEach {
|
||||
testImplementation(it)
|
||||
testRuntimeOnly(it) { artifact { classifier = lwjglNatives } }
|
||||
}
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testRuntimeOnly(libs.log4j.core)
|
||||
testRuntimeOnly(libs.fastutil)
|
||||
testRuntimeOnly(libs.joml)
|
||||
testRuntimeOnly(libs.commons.lang3)
|
||||
testRuntimeOnly(libs.guava)
|
||||
testRuntimeOnly(libs.celeritas.common) { isTransitive = false }
|
||||
testRuntimeOnly(libs.asm.tree.test)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
excludeTags(glesTag)
|
||||
}
|
||||
}
|
||||
|
||||
val unpackLwjgl3Natives = registerLwjgl3NativesUnpack()
|
||||
|
||||
val glesTest by tasks.registering(Test::class) {
|
||||
description = "Boots GLSM on the LWJGL3 GL backend in a real OpenGL ES 3.2 context."
|
||||
group = "verification"
|
||||
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||
classpath = glsmLwjgl2Stubs.plus(
|
||||
sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
)
|
||||
useJUnitPlatform {
|
||||
includeTags(glesTag)
|
||||
}
|
||||
systemProperty("glsm.stubs.capabilities", "lwjgl3")
|
||||
javaAgent(glsmRedirectAgent)
|
||||
loadLwjgl3NativesFrom(unpackLwjgl3Natives)
|
||||
outputs.cacheIf { false }
|
||||
timeout = Duration.ofMinutes(5)
|
||||
systemProperty("junit.jupiter.execution.timeout.default", "60s")
|
||||
systemProperty("junit.jupiter.execution.timeout.threaddump.enabled", "true")
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed")
|
||||
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
}
|
||||
mustRunAfter(tasks.test)
|
||||
}
|
||||
|
||||
if (!providers.environmentVariable("CI").isPresent) {
|
||||
tasks.check { dependsOn(glesTest) }
|
||||
}
|
||||
|
||||
tasks.named<JavaCompile>("compileJava") {
|
||||
val classesDir = destinationDirectory
|
||||
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
|
||||
|
||||
+72
-22
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.GLScreenCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
@@ -48,6 +51,7 @@ import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLEvents;
|
||||
import org.lwjgl.sdl.SDLTimer;
|
||||
import org.lwjgl.sdl.SDLVideo;
|
||||
import org.lwjgl.system.FunctionProvider;
|
||||
import org.lwjgl.system.JNI;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
@@ -97,14 +101,45 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|| normalizedVendor.startsWith("advanced micro devices");
|
||||
}
|
||||
}
|
||||
final FunctionProvider functions = GL.getFunctionProvider();
|
||||
if (RenderSystem.isGLES()) {
|
||||
pfnClearDepthf = GL.getFunctionProvider().getFunctionAddress("glClearDepthf");
|
||||
pfnDepthRangef = GL.getFunctionProvider().getFunctionAddress("glDepthRangef");
|
||||
pfnClearDepthf = functions.getFunctionAddress("glClearDepthf");
|
||||
pfnDepthRangef = functions.getFunctionAddress("glDepthRangef");
|
||||
proxyTexture = new ProxyTexture(GL11C.glGetInteger(GL11C.GL_MAX_TEXTURE_SIZE), GL11C.glGetInteger(GL12C.GL_MAX_3D_TEXTURE_SIZE),
|
||||
GL11C.glGetInteger(GL13C.GL_MAX_CUBE_MAP_TEXTURE_SIZE), GL11C.glGetInteger(GL30C.GL_MAX_ARRAY_TEXTURE_LAYERS));
|
||||
}
|
||||
pfnGenSamplers = requireFunction(functions, "glGenSamplers");
|
||||
pfnDeleteSamplers = requireFunction(functions, "glDeleteSamplers");
|
||||
pfnBindSampler = requireFunction(functions, "glBindSampler");
|
||||
pfnSamplerParameteri = requireFunction(functions, "glSamplerParameteri");
|
||||
pfnSamplerParameterf = requireFunction(functions, "glSamplerParameterf");
|
||||
pfnIsSampler = requireFunction(functions, "glIsSampler");
|
||||
pfnVertexAttribDivisor = requireFunction(functions, "glVertexAttribDivisor");
|
||||
}
|
||||
|
||||
private static long requireFunction(FunctionProvider functions, String name) {
|
||||
final long address = functions.getFunctionAddress(name);
|
||||
if (address == 0L) throw new IllegalStateException(name + " is not exported by the GL driver");
|
||||
return address;
|
||||
}
|
||||
|
||||
private long pfnClearDepthf;
|
||||
private long pfnDepthRangef;
|
||||
private @Nullable ProxyTexture proxyTexture;
|
||||
// GL33C slots stay unloaded on an ES 3.2 context: LWJGL builds desktop caps only up to the reported 3.2.
|
||||
private long pfnGenSamplers;
|
||||
private long pfnDeleteSamplers;
|
||||
private long pfnBindSampler;
|
||||
private long pfnSamplerParameteri;
|
||||
private long pfnSamplerParameterf;
|
||||
private long pfnIsSampler;
|
||||
private long pfnVertexAttribDivisor;
|
||||
|
||||
private boolean recordProxy(int target, int level, int internalFormat, int width, int height, int depth) {
|
||||
if (proxyTexture == null || !TextureInfoCache.isProxyTarget(target)) return false;
|
||||
proxyTexture.record(target, level, internalFormat, width, height, depth, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
@@ -253,7 +288,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
public void finish() {GL11C.glFinish();}
|
||||
|
||||
private static boolean capAllowed(int cap) {
|
||||
return GLESCaps.isCapAllowed(cap, RenderSystem.isGLES(), RenderSystem.hasClipCullDistance());
|
||||
return GLESCaps.isCapAllowed(cap, RenderSystem.isGLES());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -458,42 +493,42 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArrays(int mode, int first, int count) {
|
||||
protected void issueDrawArrays(int mode, int first, int count) {
|
||||
GL11C.glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, long indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
GL11C.glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ByteBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, IntBuffer indices) {
|
||||
protected void issueDrawElements(int mode, IntBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, ShortBuffer indices) {
|
||||
protected void issueDrawElements(int mode, ShortBuffer indices) {
|
||||
GL11C.glDrawElements(mode, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
protected void issueDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
GL11C.glDrawElements(mode, count, type, MemoryUtil.memAddress(indices));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
GL31C.glDrawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
GL31C.glDrawArraysInstanced(mode, first, count, primcount);
|
||||
}
|
||||
|
||||
@@ -504,7 +539,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
GL43C.glMultiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
@@ -514,12 +549,12 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
GL32C.glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
GL32C.nglMultiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
}
|
||||
|
||||
@@ -565,26 +600,31 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, long pixelBufferOffset) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixelBufferOffset);
|
||||
}
|
||||
|
||||
@@ -605,16 +645,19 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void texImage1D(int target, int level, int internalFormat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, 1, 1)) return;
|
||||
GL11C.glTexImage1D(target, level, internalFormat, width, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, depth)) return;
|
||||
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
|
||||
if (recordProxy(target, level, internalFormat, width, height, depth)) return;
|
||||
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
||||
}
|
||||
|
||||
@@ -686,6 +729,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public int getTexLevelParameteri(int target, int level, int pname) {
|
||||
if (proxyTexture != null && TextureInfoCache.isProxyTarget(target)) return proxyTexture.levelParameter(target, level, pname);
|
||||
return GL11C.glGetTexLevelParameteri(target, level, pname);
|
||||
}
|
||||
|
||||
@@ -706,27 +750,33 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public int genSamplers() {
|
||||
return GL33C.glGenSamplers();
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final IntBuffer ids = stack.mallocInt(1);
|
||||
JNI.callPV(1, MemoryUtil.memAddress(ids), pfnGenSamplers);
|
||||
return ids.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSamplers(int sampler) {
|
||||
GL33C.glDeleteSamplers(sampler);
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
JNI.callPV(1, MemoryUtil.memAddress(stack.ints(sampler)), pfnDeleteSamplers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindSampler(int unit, int sampler) {
|
||||
GL33C.glBindSampler(unit, sampler);
|
||||
JNI.callV(unit, sampler, pfnBindSampler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void samplerParameteri(int sampler, int pname, int param) {
|
||||
GL33C.glSamplerParameteri(sampler, pname, param);
|
||||
JNI.callV(sampler, pname, param, pfnSamplerParameteri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void samplerParameterf(int sampler, int pname, float param) {
|
||||
GL33C.glSamplerParameterf(sampler, pname, param);
|
||||
JNI.callV(sampler, pname, param, pfnSamplerParameterf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1371,7 +1421,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override public boolean isFramebuffer(int framebuffer) { return GL30C.glIsFramebuffer(framebuffer); }
|
||||
@Override public boolean isRenderbuffer(int renderbuffer) { return GL30C.glIsRenderbuffer(renderbuffer); }
|
||||
@Override public boolean isSampler(int sampler) { return GL33C.glIsSampler(sampler); }
|
||||
@Override public boolean isSampler(int sampler) { return JNI.callZ(sampler, pfnIsSampler); }
|
||||
@Override public boolean isQuery(int query) { return GL15C.glIsQuery(query); }
|
||||
|
||||
@Override
|
||||
@@ -1441,7 +1491,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void vertexAttribDivisor(int index, int divisor) {
|
||||
GL33C.glVertexAttribDivisor(index, divisor);
|
||||
JNI.callV(index, divisor, pfnVertexAttribDivisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1834,7 +1884,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
GL14C.glMultiDrawArrays(mode, firsts, counts);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package com.gtnewhorizons.angelica.lwjgl3;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.ClipTestSupport;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11C;
|
||||
import org.lwjgl.opengl.GL12C;
|
||||
import org.lwjgl.opengl.GL20C;
|
||||
import org.lwjgl.opengl.GL30C;
|
||||
import org.lwjgl.opengl.GL33C;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLInit;
|
||||
import org.lwjgl.sdl.SDLVideo;
|
||||
import org.lwjgl.system.Configuration;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
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.assertTrue;
|
||||
|
||||
@Tag("gles")
|
||||
@DisabledOnOs(OS.MAC)
|
||||
class GlesSmokeTest {
|
||||
|
||||
private static final int SIZE = 64;
|
||||
private static int fbo;
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
Configuration.OPENGL_EXPLICIT_INIT.set(true);
|
||||
assertTrue(SDLInit.SDL_Init(SDLInit.SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_PROFILE_MASK, SDLVideo.SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_DEPTH_SIZE, 24);
|
||||
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_STENCIL_SIZE, 8);
|
||||
final long window = SDLVideo.SDL_CreateWindow("glesSmoke", SIZE, SIZE, SDLVideo.SDL_WINDOW_OPENGL | SDLVideo.SDL_WINDOW_HIDDEN);
|
||||
assertNotEquals(0L, window, () -> "SDL_CreateWindow failed: " + SDLError.SDL_GetError());
|
||||
final long context = SDLVideo.SDL_GL_CreateContext(window);
|
||||
assertNotEquals(0L, context, () -> "SDL_GL_CreateContext failed: " + SDLError.SDL_GetError());
|
||||
assertTrue(SDLVideo.SDL_GL_MakeCurrent(window, context), () -> "SDL_GL_MakeCurrent failed: " + SDLError.SDL_GetError());
|
||||
|
||||
GL.create(SDLVideo::SDL_GL_GetProcAddress);
|
||||
GL.createCapabilities();
|
||||
// LWJGL's GL_CONTEXT_PROFILE_MASK probe in createCapabilities leaves INVALID_ENUM on an ES context.
|
||||
while (GL11C.glGetError() != GL11C.GL_NO_ERROR) {}
|
||||
|
||||
final String version = GL11C.glGetString(GL11C.GL_VERSION);
|
||||
assertTrue(version != null && version.startsWith("OpenGL ES 3."), () -> "not an ES 3.x context: " + version);
|
||||
assertTrue(BackendManager.RENDER_BACKEND instanceof Lwjgl3GLRenderBackend, () -> "selected backend is " + BackendManager.RENDER_BACKEND.getName());
|
||||
|
||||
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(SIZE, SIZE).build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete("glesSmoke");
|
||||
|
||||
assertTrue(RenderSystem.isGLES());
|
||||
assertTrue(RenderSystem.supportsSamplerObjects());
|
||||
fbo = createTarget();
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerLifecycle() {
|
||||
final int sampler = RenderSystem.genSampler();
|
||||
assertNotEquals(0, sampler);
|
||||
RenderSystem.samplerParameteri(sampler, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_LINEAR);
|
||||
RenderSystem.samplerParameterf(sampler, GL12C.GL_TEXTURE_MAX_LOD, 4.0f);
|
||||
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||
assertEquals(sampler, GL11C.glGetInteger(GL33C.GL_SAMPLER_BINDING));
|
||||
assertTrue(BackendManager.RENDER_BACKEND.isSampler(sampler));
|
||||
|
||||
RenderSystem.destroySampler(sampler);
|
||||
assertFalse(BackendManager.RENDER_BACKEND.isSampler(sampler));
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexAttribDivisor() {
|
||||
final int vao = GLSMVertexInput.glGenVertexArrays();
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
GLSMVertexInput.glVertexAttribDivisor(5, 1);
|
||||
assertEquals(1, GL20C.glGetVertexAttribi(5, GL33C.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ffpQuadRenders() {
|
||||
bindClearedTarget();
|
||||
GLSMDraws.glBegin(GL11C.GL_QUADS);
|
||||
GLSMCurrentAttribs.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, -1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, -1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
GLSMDraws.glEnd();
|
||||
|
||||
assertEquals(0xFF00FF00, readCenterPixel());
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ffpTexturedQuadRenders() {
|
||||
final int texel = 0xFF2060A0;
|
||||
final int texture = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, texture);
|
||||
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
|
||||
final IntBuffer pixels = MemoryUtil.memAllocInt(16);
|
||||
try {
|
||||
for (int i = 0; i < 16; i++) pixels.put(i, texel);
|
||||
GLSMTextures.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, GL11C.GL_RGBA, 4, 4, 0, GL12C.GL_BGRA, GL12C.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||
} finally {
|
||||
MemoryUtil.memFree(pixels);
|
||||
}
|
||||
|
||||
bindClearedTarget();
|
||||
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, texture);
|
||||
GLSMEnable.glEnable(GL11C.GL_TEXTURE_2D);
|
||||
GLSMDraws.glBegin(GL11C.GL_QUADS);
|
||||
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLSMCurrentAttribs.glTexCoord2f(0.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, -1.0f, 0.0f);
|
||||
GLSMCurrentAttribs.glTexCoord2f(1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, -1.0f, 0.0f);
|
||||
GLSMCurrentAttribs.glTexCoord2f(1.0f, 1.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLSMCurrentAttribs.glTexCoord2f(0.0f, 1.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
GLSMDraws.glEnd();
|
||||
GLSMEnable.glDisable(GL11C.GL_TEXTURE_2D);
|
||||
|
||||
assertEquals(texel, readCenterPixel());
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "emulated={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void clipPlaneKeepsOnlyTheEnabledHalfSpace(boolean emulated) {
|
||||
try (var ignored = ClipTestSupport.emulation(emulated)) {
|
||||
try {
|
||||
bindClearedTarget();
|
||||
ClipTestSupport.setPlane(1, 0.0, 0.0, 0.0, -1.0);
|
||||
ClipTestSupport.setPlane(0, 1.0, 0.0, 0.0, 0.0);
|
||||
GLSMEnable.glEnable(GL30C.GL_CLIP_DISTANCE0);
|
||||
GLSMDraws.glBegin(GL11C.GL_QUADS);
|
||||
GLSMCurrentAttribs.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, -1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, -1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLSMDraws.glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
GLSMDraws.glEnd();
|
||||
|
||||
assertEquals(0xFF000000, readPixel(SIZE / 4, SIZE / 2), "x < 0 must be clipped by plane 0");
|
||||
assertEquals(0xFF00FF00, readPixel(3 * SIZE / 4, SIZE / 2), "x >= 0 must survive; plane 1 set but not enabled must not clip");
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
} finally {
|
||||
GLSMEnable.glDisable(GL30C.GL_CLIP_DISTANCE0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void proxyTextureProbeFindsTheMaxTextureSize() {
|
||||
final int maxSize = Integer.highestOneBit(GL11C.glGetInteger(GL11C.GL_MAX_TEXTURE_SIZE));
|
||||
int found = -1;
|
||||
for (int size = maxSize * 2; size > 0 && found < 0; size >>= 1) {
|
||||
GLSMTextures.glTexImage2D(GL11C.GL_PROXY_TEXTURE_2D, 0, GL11C.GL_RGBA, size, size, 0, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
if (GLSMTextures.glGetTexLevelParameteri(GL11C.GL_PROXY_TEXTURE_2D, 0, GL11C.GL_TEXTURE_WIDTH) != 0) found = size;
|
||||
}
|
||||
assertEquals(maxSize, found);
|
||||
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||
}
|
||||
|
||||
private static void bindClearedTarget() {
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, fbo);
|
||||
GLSMRasterizer.glViewport(0, 0, SIZE, SIZE);
|
||||
GLSMBlend.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLSMFramebuffer.glClear(GL11C.GL_COLOR_BUFFER_BIT | GL11C.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private static int readCenterPixel() {
|
||||
return readPixel(SIZE / 2, SIZE / 2);
|
||||
}
|
||||
|
||||
private static int readPixel(int x, int y) {
|
||||
final ByteBuffer pixel = MemoryUtil.memAlloc(4);
|
||||
try {
|
||||
GLSMFramebuffer.glReadPixels(x, y, 1, 1, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, pixel);
|
||||
return (pixel.get(3) & 0xFF) << 24 | (pixel.get(0) & 0xFF) << 16 | (pixel.get(1) & 0xFF) << 8 | pixel.get(2) & 0xFF;
|
||||
} finally {
|
||||
MemoryUtil.memFree(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
private static int createTarget() {
|
||||
final int color = GLSMTextures.glGenTextures();
|
||||
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, color);
|
||||
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
|
||||
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
|
||||
GLSMTextures.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, GL11C.GL_RGBA8, SIZE, SIZE, 0, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
final int depth = GLSMFramebuffer.glGenRenderbuffers();
|
||||
GLSMFramebuffer.glBindRenderbuffer(GL30C.GL_RENDERBUFFER, depth);
|
||||
GLSMFramebuffer.glRenderbufferStorage(GL30C.GL_RENDERBUFFER, GL30C.GL_DEPTH24_STENCIL8, SIZE, SIZE);
|
||||
final int target = GLSMFramebuffer.glGenFramebuffers();
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, target);
|
||||
GLSMFramebuffer.glFramebufferTexture2D(GL30C.GL_FRAMEBUFFER, GL30C.GL_COLOR_ATTACHMENT0, GL11C.GL_TEXTURE_2D, color, 0);
|
||||
GLSMFramebuffer.glFramebufferRenderbuffer(GL30C.GL_FRAMEBUFFER, GL30C.GL_DEPTH_STENCIL_ATTACHMENT, GL30C.GL_RENDERBUFFER, depth);
|
||||
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, 0);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Clip-plane scene for -Dangelica.debug.testBlocks=true. ~ is the flight start, yaw 0 faces south (+z).
|
||||
# Each angelica:test_clip_plane TESR keeps only the lower half of a gold cube (plane 0) and leaves a reject-all plane 1 disabled.
|
||||
|
||||
gamerule doMobSpawning false
|
||||
gamerule doDaylightCycle false
|
||||
time set 6000
|
||||
|
||||
fill ~-2 ~-1 ~4 ~2 ~0 ~4 angelica:test_clip_plane
|
||||
@@ -0,0 +1,83 @@
|
||||
# Translucent TESR ordering scene for a generated superflat (windows-flyby-kit/make_world.py). Needs
|
||||
# -Dangelica.debug.testBlocks=true. $x $y $z is the flight start; ground-standing level is $y-2. Start facing south (yaw 0),
|
||||
# pitch 15. Blocks: test_tesr_provider (0 translucent red, 1 additive purple), test_tesr_model (0 box pane,
|
||||
# 1 Tessellator pane, 2 stacked A/B/A panes), test_tesr_decal, test_tesr_glow; metadata + 8 draws in the solid pass.
|
||||
|
||||
gamerule doMobSpawning false
|
||||
|
||||
# translucent pass: three rows, materials shifted per row so every column stacks three different ones
|
||||
setblock $x-2 $y $z+6 angelica:test_tesr_provider 0 replace
|
||||
setblock $x-1 $y $z+6 angelica:test_tesr_provider 1 replace
|
||||
setblock $x+0 $y $z+6 angelica:test_tesr_model 0 replace
|
||||
setblock $x+1 $y $z+6 angelica:test_tesr_model 1 replace
|
||||
setblock $x-2 $y $z+8 angelica:test_tesr_provider 1 replace
|
||||
setblock $x-1 $y $z+8 angelica:test_tesr_model 0 replace
|
||||
setblock $x+0 $y $z+8 angelica:test_tesr_model 1 replace
|
||||
setblock $x+1 $y $z+8 angelica:test_tesr_provider 0 replace
|
||||
setblock $x-2 $y $z+10 angelica:test_tesr_model 0 replace
|
||||
setblock $x-1 $y $z+10 angelica:test_tesr_model 1 replace
|
||||
setblock $x+0 $y $z+10 angelica:test_tesr_provider 0 replace
|
||||
setblock $x+1 $y $z+10 angelica:test_tesr_provider 1 replace
|
||||
|
||||
# solid pass: the same rows two blocks higher
|
||||
setblock $x-2 $y+2 $z+6 angelica:test_tesr_provider 8 replace
|
||||
setblock $x-1 $y+2 $z+6 angelica:test_tesr_provider 9 replace
|
||||
setblock $x+0 $y+2 $z+6 angelica:test_tesr_model 8 replace
|
||||
setblock $x+1 $y+2 $z+6 angelica:test_tesr_model 9 replace
|
||||
setblock $x-2 $y+2 $z+8 angelica:test_tesr_provider 9 replace
|
||||
setblock $x-1 $y+2 $z+8 angelica:test_tesr_model 8 replace
|
||||
setblock $x+0 $y+2 $z+8 angelica:test_tesr_model 9 replace
|
||||
setblock $x+1 $y+2 $z+8 angelica:test_tesr_provider 8 replace
|
||||
setblock $x-2 $y+2 $z+10 angelica:test_tesr_model 8 replace
|
||||
setblock $x-1 $y+2 $z+10 angelica:test_tesr_model 9 replace
|
||||
setblock $x+0 $y+2 $z+10 angelica:test_tesr_provider 8 replace
|
||||
setblock $x+1 $y+2 $z+10 angelica:test_tesr_provider 9 replace
|
||||
|
||||
# one tile entity submitting translucent A, additive B, translucent A; a depth-equal decal
|
||||
setblock $x-7 $y $z+8 angelica:test_tesr_model 2 replace
|
||||
setblock $x-7 $y+1 $z+8 angelica:test_tesr_decal 0 replace
|
||||
|
||||
# light blue glass wall: glow and provider panes in front, a provider pane behind. The panes behind the wall and
|
||||
# under the water show only when block entities draw before translucent terrain.
|
||||
setblock $x+5 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+5 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+5 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+7 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+8 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y-1 $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y $z+14 stained_glass 3 replace
|
||||
setblock $x+9 $y+1 $z+14 stained_glass 3 replace
|
||||
setblock $x+6 $y $z+13 angelica:test_tesr_glow 0 replace
|
||||
setblock $x+8 $y $z+13 angelica:test_tesr_provider 0 replace
|
||||
setblock $x+7 $y $z+15 angelica:test_tesr_provider 0 replace
|
||||
|
||||
# water pool with a provider pane under the surface
|
||||
setblock $x-10 $y-4 $z+12 water 0 replace
|
||||
setblock $x-10 $y-3 $z+12 water 0 replace
|
||||
setblock $x-10 $y-4 $z+13 water 0 replace
|
||||
setblock $x-10 $y-3 $z+13 water 0 replace
|
||||
setblock $x-10 $y-4 $z+14 water 0 replace
|
||||
setblock $x-10 $y-3 $z+14 water 0 replace
|
||||
setblock $x-9 $y-4 $z+12 water 0 replace
|
||||
setblock $x-9 $y-3 $z+12 water 0 replace
|
||||
setblock $x-9 $y-4 $z+13 water 0 replace
|
||||
setblock $x-9 $y-3 $z+13 water 0 replace
|
||||
setblock $x-9 $y-4 $z+14 water 0 replace
|
||||
setblock $x-9 $y-3 $z+14 water 0 replace
|
||||
setblock $x-8 $y-4 $z+12 water 0 replace
|
||||
setblock $x-8 $y-3 $z+12 water 0 replace
|
||||
setblock $x-8 $y-4 $z+13 water 0 replace
|
||||
setblock $x-8 $y-3 $z+13 water 0 replace
|
||||
setblock $x-8 $y-4 $z+14 water 0 replace
|
||||
setblock $x-8 $y-3 $z+14 water 0 replace
|
||||
setblock $x-9 $y-4 $z+13 angelica:test_tesr_provider 0 replace
|
||||
|
||||
# control: a lone pane with sky behind it
|
||||
setblock $x+5 $y+2 $z+8 angelica:test_tesr_provider 0 replace
|
||||
@@ -0,0 +1,58 @@
|
||||
# Translucency layer scene for a generated superflat (windows-flyby-kit/make_world.py). $x $y $z is the flight start;
|
||||
# ground-standing level is $y-2. Start facing south (yaw 0), pitch 15. Use with -Dangelica.flyby.weather=RAIN. Torch
|
||||
# flame and smoke and rain fall in front of a light blue glass wall and a water pool; one torch row stands behind the glass.
|
||||
|
||||
gamerule doMobSpawning false
|
||||
|
||||
# glass wall
|
||||
setblock $x-1 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x-1 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x-1 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+0 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+1 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+2 $y+0 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y-2 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y-1 $z+9 stained_glass 3 replace
|
||||
setblock $x+3 $y+0 $z+9 stained_glass 3 replace
|
||||
|
||||
# torches in front of and behind the glass
|
||||
setblock $x-1 $y-2 $z+7 torch 5 replace
|
||||
setblock $x-1 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+0 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+0 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+1 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+1 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+2 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+2 $y-2 $z+11 torch 5 replace
|
||||
setblock $x+3 $y-2 $z+7 torch 5 replace
|
||||
setblock $x+3 $y-2 $z+11 torch 5 replace
|
||||
|
||||
# water pool with torches in front of it
|
||||
setblock $x-6 $y-3 $z+7 water 0 replace
|
||||
setblock $x-6 $y-3 $z+8 water 0 replace
|
||||
setblock $x-6 $y-3 $z+9 water 0 replace
|
||||
setblock $x-6 $y-3 $z+10 water 0 replace
|
||||
setblock $x-5 $y-3 $z+7 water 0 replace
|
||||
setblock $x-5 $y-3 $z+8 water 0 replace
|
||||
setblock $x-5 $y-3 $z+9 water 0 replace
|
||||
setblock $x-5 $y-3 $z+10 water 0 replace
|
||||
setblock $x-4 $y-3 $z+7 water 0 replace
|
||||
setblock $x-4 $y-3 $z+8 water 0 replace
|
||||
setblock $x-4 $y-3 $z+9 water 0 replace
|
||||
setblock $x-4 $y-3 $z+10 water 0 replace
|
||||
setblock $x-3 $y-3 $z+7 water 0 replace
|
||||
setblock $x-3 $y-3 $z+8 water 0 replace
|
||||
setblock $x-3 $y-3 $z+9 water 0 replace
|
||||
setblock $x-3 $y-3 $z+10 water 0 replace
|
||||
setblock $x-2 $y-3 $z+7 water 0 replace
|
||||
setblock $x-2 $y-3 $z+8 water 0 replace
|
||||
setblock $x-2 $y-3 $z+9 water 0 replace
|
||||
setblock $x-2 $y-3 $z+10 water 0 replace
|
||||
setblock $x-5 $y-2 $z+6 torch 5 replace
|
||||
setblock $x-3 $y-2 $z+6 torch 5 replace
|
||||
@@ -106,7 +106,13 @@ val glsmSdlAgent = sourceSets.create("glsmSdlAgent") {
|
||||
java.srcDir("src/glsmSdlAgent/java")
|
||||
}
|
||||
|
||||
val glsmLwjgl2Stubs: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmLwjgl2Stubs(project(":glsm", "lwjgl2Stubs"))
|
||||
"glsmSdlAgentCompileOnly"(project(":glsm"))
|
||||
"glsmSdlAgentCompileOnly"(testFixtures(project(":glsm")))
|
||||
"glsmSdlAgentCompileOnly"(libs.asm.tree.test)
|
||||
@@ -148,7 +154,7 @@ val glsmSdlTest by tasks.registering(Test::class) {
|
||||
description = "Runs the headless GLSM-on-SDL FFP rig in its own JVM, with the SDL GPU gate engaged."
|
||||
group = "verification"
|
||||
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||
classpath = glsmSdlStubs.output.classesDirs.plus(
|
||||
classpath = glsmSdlStubs.output.classesDirs.plus(glsmLwjgl2Stubs).plus(
|
||||
sourceSets["test"].runtimeClasspath.filter { file ->
|
||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.lwjgl.opengl;
|
||||
|
||||
public final class GLContext {
|
||||
|
||||
private static final ContextCapabilities CAPABILITIES = new ContextCapabilities();
|
||||
|
||||
private GLContext() {}
|
||||
|
||||
public static ContextCapabilities getCapabilities() {
|
||||
return CAPABILITIES;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureStaging;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.compute.ComputeBinder;
|
||||
@@ -113,6 +114,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.lwjgl.util.spvc.Spvc;
|
||||
import org.lwjgl.sdl.SDLGPU;
|
||||
import org.lwjgl.sdl.SDL_GPUBlitInfo;
|
||||
import org.lwjgl.sdl.SDLTimer;
|
||||
@@ -1226,7 +1228,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void drawArrays(int mode, int first, int count) {
|
||||
@Override protected void issueDrawArrays(int mode, int first, int count) {
|
||||
if (count <= 0) return;
|
||||
final ContextState st = s();
|
||||
if (st.rasterizerDiscard) return;
|
||||
@@ -1248,7 +1250,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
captureVertexWrites(st, first, count, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElements(int mode, int count, int type, long indices) {
|
||||
@Override protected void issueDrawElements(int mode, int count, int type, long indices) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElements");
|
||||
if (rp == 0) return;
|
||||
@@ -1258,7 +1260,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
captureVertexWrites(st, 0, count, 1, type, indices, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
@Override protected void issueDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElementsInstanced");
|
||||
if (rp == 0) return;
|
||||
@@ -1270,7 +1272,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
|
||||
@Override public void drawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
@Override protected void issueDrawElementsBaseVertex(int mode, int count, int type, long indices, int baseVertex) {
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "drawElementsBaseVertex");
|
||||
if (rp == 0) return;
|
||||
@@ -1295,7 +1297,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return rp;
|
||||
}
|
||||
|
||||
@Override public void multiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
@Override protected void issueMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
if (drawcount <= 0) return;
|
||||
final ContextState st = s();
|
||||
final long rp = prepareIndexedDrawBind(st, mode, type, "multiDrawElementsBaseVertex");
|
||||
@@ -1315,7 +1317,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void multiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
@Override protected void issueMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
final ContextState cs = s();
|
||||
if (drawcount <= 0) return;
|
||||
Tracy.beginZone(Z_SDL_INDIRECT_DRAW);
|
||||
@@ -1507,52 +1509,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return TextureInfoCache.isProxyTarget(target);
|
||||
}
|
||||
|
||||
private static boolean proxyLayersInHeight(int target) {
|
||||
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY;
|
||||
}
|
||||
|
||||
private static boolean proxyLayersInDepth(int target) {
|
||||
return isProxyArrayTarget(target) && !proxyLayersInHeight(target);
|
||||
}
|
||||
|
||||
private static boolean isProxyArrayTarget(int target) {
|
||||
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY || target == GL30.GL_PROXY_TEXTURE_2D_ARRAY || target == GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY || target == GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY;
|
||||
}
|
||||
|
||||
public static int proxyLevelSize(int base, int level) {
|
||||
return PixelOps.mipLevelSize(base, level);
|
||||
}
|
||||
|
||||
static int proxyDimensionLimit(int target) {
|
||||
return switch (target) {
|
||||
case GL12.GL_PROXY_TEXTURE_3D -> MAX_3D_TEXTURE_SIZE;
|
||||
case GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> MAX_CUBE_MAP_TEXTURE_SIZE;
|
||||
default -> MAX_TEXTURE_SIZE;
|
||||
};
|
||||
}
|
||||
|
||||
static int levelZeroEquivalentSize(int size, int level) {
|
||||
if (size <= 0 || level < 0 || level >= Integer.SIZE - 1) return 0;
|
||||
final long scaled = (long) size << level;
|
||||
return scaled > Integer.MAX_VALUE ? 0 : (int) scaled;
|
||||
}
|
||||
|
||||
private void recordProxyTexImage(ContextState st, int target, int level, int internalFormat, int width, int height, int depth) {
|
||||
final boolean layersInHeight = proxyLayersInHeight(target);
|
||||
final boolean layersInDepth = proxyLayersInDepth(target);
|
||||
final int dimLimit = proxyDimensionLimit(target);
|
||||
final int w = levelZeroEquivalentSize(width, level);
|
||||
final int h = layersInHeight ? height : levelZeroEquivalentSize(height, level);
|
||||
final int d = layersInDepth ? depth : levelZeroEquivalentSize(depth, level);
|
||||
final int hLimit = layersInHeight ? MAX_ARRAY_TEXTURE_LAYERS : dimLimit;
|
||||
final int dLimit = layersInDepth ? MAX_ARRAY_TEXTURE_LAYERS : dimLimit;
|
||||
final boolean fits = w >= 1 && w <= dimLimit && h >= 1 && h <= hLimit && d >= 1 && d <= dLimit
|
||||
&& resourceManager.isMappableTextureFormat(internalFormat);
|
||||
if (fits) {
|
||||
st.proxyTexture.accept(target, w, h, d, internalFormat);
|
||||
} else {
|
||||
st.proxyTexture.reject(target);
|
||||
}
|
||||
st.proxyTexture.record(target, level, internalFormat, width, height, depth, resourceManager.isMappableTextureFormat(internalFormat));
|
||||
}
|
||||
|
||||
private static int boundTextureFor(ContextState st, int target) {
|
||||
@@ -1816,23 +1774,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
@Override public int getTexLevelParameteri(int target, int level, int pname) {
|
||||
final ContextState cs = s();
|
||||
if (isProxyTarget(target)) {
|
||||
final ContextState.ProxyTextureState proxy = cs.proxyTexture;
|
||||
if (proxy.target != target) return 0;
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> proxyLevelSize(proxy.width, level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> proxyLayersInHeight(target) ? proxy.height : proxyLevelSize(proxy.height, level);
|
||||
case GL12.GL_TEXTURE_DEPTH -> proxyLayersInDepth(target) ? proxy.depth : proxyLevelSize(proxy.depth, level);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> proxy.internalFormat;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
if (isProxyTarget(target)) return cs.proxyTexture.levelParameter(target, level, pname);
|
||||
final int glId = cs.boundTextures[cs.activeTextureUnit];
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
if (meta == null) return 0;
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> PixelOps.mipLevelSize(meta.width(), level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> PixelOps.mipLevelSize(meta.height(), level);
|
||||
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(meta.width(), level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> TextureInfo.mipLevelSize(meta.height(), level);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||
final int stored = meta.glFormat();
|
||||
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
||||
@@ -1987,8 +1935,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (meta != null) {
|
||||
fbo.colorFormats[colorIdx] = meta.sdlFormat();
|
||||
if (colorIdx == 0) {
|
||||
fbo.width = PixelOps.mipLevelSize(meta.width(), level);
|
||||
fbo.height = PixelOps.mipLevelSize(meta.height(), level);
|
||||
fbo.width = TextureInfo.mipLevelSize(meta.width(), level);
|
||||
fbo.height = TextureInfo.mipLevelSize(meta.height(), level);
|
||||
}
|
||||
}
|
||||
fbo.colorAttachmentCount = Math.max(fbo.colorAttachmentCount, colorIdx + 1);
|
||||
@@ -2216,8 +2164,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
out.level = fbo.colorLevels[fbo.readBufferIndex];
|
||||
out.sdlFormat = meta != null ? meta.sdlFormat() : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
out.glBaseFormat = meta != null ? PixelPack.glBaseFormat(meta.glFormat()) : GL11.GL_RGBA;
|
||||
out.width = meta != null ? PixelOps.mipLevelSize(meta.width(), out.level) : fbo.width;
|
||||
out.height = meta != null ? PixelOps.mipLevelSize(meta.height(), out.level) : fbo.height;
|
||||
out.width = meta != null ? TextureInfo.mipLevelSize(meta.width(), out.level) : fbo.width;
|
||||
out.height = meta != null ? TextureInfo.mipLevelSize(meta.height(), out.level) : fbo.height;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -2283,8 +2231,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int glId = boundTextureFor(cs, target);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
if (meta == null || level < 0 || level >= meta.levels()) return;
|
||||
final int width = PixelOps.mipLevelSize(meta.width(), level);
|
||||
final int height = PixelOps.mipLevelSize(meta.height(), level);
|
||||
final int width = TextureInfo.mipLevelSize(meta.width(), level);
|
||||
final int height = TextureInfo.mipLevelSize(meta.height(), level);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
checkReadbackCapacity(format, type, width, height, loadPackState(cs), capacity);
|
||||
final int kind = readbackKind(format);
|
||||
@@ -2600,17 +2548,23 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (length != null && length.remaining() > 0) length.put(0, 0);
|
||||
return;
|
||||
}
|
||||
if (s == null) return;
|
||||
final byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
final int n = Math.min(bytes.length, out.remaining());
|
||||
out.put(bytes, 0, n);
|
||||
final byte[] bytes = (s == null ? "" : s).getBytes(StandardCharsets.UTF_8);
|
||||
final int pos = out.position();
|
||||
final int max = out.remaining();
|
||||
final int n = Math.max(0, Math.min(bytes.length, max - 1));
|
||||
for (int i = 0; i < n; i++) out.put(pos + i, bytes[i]);
|
||||
if (max > 0) out.put(pos + n, (byte) 0);
|
||||
if (length != null && length.remaining() > 0) length.put(0, n);
|
||||
}
|
||||
private static int glStringLength(String s) {
|
||||
return s == null || s.isEmpty() ? 0 : s.getBytes(StandardCharsets.UTF_8).length + 1;
|
||||
}
|
||||
@Override public int getShaderi(int shader, int pname) {
|
||||
return switch (pname) {
|
||||
case GL20.GL_COMPILE_STATUS -> shaderManager.isCompiled(shader) ? 1 : 0;
|
||||
case GL20.GL_SHADER_TYPE -> shaderManager.getShaderType(shader);
|
||||
case GL20.GL_INFO_LOG_LENGTH -> shaderManager.getShaderInfoLog(shader).length();
|
||||
case GL20.GL_INFO_LOG_LENGTH -> glStringLength(shaderManager.getShaderInfoLog(shader));
|
||||
case GL20.GL_SHADER_SOURCE_LENGTH -> glStringLength(shaderManager.getShaderSource(shader));
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
@@ -2618,7 +2572,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return switch (pname) {
|
||||
case GL20.GL_LINK_STATUS -> shaderManager.isLinked(program) ? 1 : 0;
|
||||
case GL20.GL_VALIDATE_STATUS -> shaderManager.isLinked(program) ? 1 : 0;
|
||||
case GL20.GL_INFO_LOG_LENGTH -> shaderManager.getProgramInfoLog(program).length();
|
||||
case GL20.GL_INFO_LOG_LENGTH -> glStringLength(shaderManager.getProgramInfoLog(program));
|
||||
case GL20.GL_ATTACHED_SHADERS -> shaderManager.getAttachedShaderCount(program);
|
||||
case GL20.GL_ACTIVE_UNIFORMS -> {
|
||||
final var entries = getOrBuildUniformEntries(program);
|
||||
@@ -2658,6 +2612,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int loc = e.getValue();
|
||||
int glType;
|
||||
int arrayLen = 1;
|
||||
boolean array = false;
|
||||
if (prog.allSamplerNames.contains(name)) {
|
||||
glType = GL20.GL_SAMPLER_2D;
|
||||
} else {
|
||||
@@ -2666,6 +2621,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (umi != null) {
|
||||
glType = FormatMap.mapMemberInfoToGlType(umi);
|
||||
arrayLen = Math.max(1, umi.arrayLen());
|
||||
array = umi.arrayStride() > 0;
|
||||
} else {
|
||||
glType = GL20.GL_FLOAT_VEC4;
|
||||
}
|
||||
@@ -2673,7 +2629,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
glType = FormatMap.remapUnsignedIntToBool(glType);
|
||||
}
|
||||
}
|
||||
result.add(new UniformEntry(name, glType, arrayLen));
|
||||
result.add(new UniformEntry(array ? name + "[0]" : name, glType, arrayLen));
|
||||
}
|
||||
result.sort(Comparator.comparing(UniformEntry::name));
|
||||
final List<UniformEntry> immutable = Collections.unmodifiableList(result);
|
||||
@@ -2732,6 +2688,78 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public int getUniformLocation(int program, ByteBuffer name) {
|
||||
return shaderManager.getUniformLocation(program, MemoryUtil.memASCII(name));
|
||||
}
|
||||
@Override public void getUniformfv(int program, int location, FloatBuffer params) {
|
||||
final Readback rb = readback(program, location);
|
||||
if (rb == null) return;
|
||||
final int pos = params.position();
|
||||
final int count = Math.min(rb.count(), params.remaining());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float raw = rb.raw(i);
|
||||
final int bits = Float.floatToRawIntBits(raw);
|
||||
params.put(pos + i, switch (rb.type()) {
|
||||
case FLOAT -> raw;
|
||||
case INT -> (float) bits;
|
||||
case UINT -> (float) (bits & 0xFFFFFFFFL);
|
||||
case BOOL -> bits != 0 ? 1f : 0f;
|
||||
case OPAQUE -> rb.opaqueValue();
|
||||
});
|
||||
}
|
||||
}
|
||||
@Override public void getUniformiv(int program, int location, IntBuffer params) {
|
||||
final Readback rb = readback(program, location);
|
||||
if (rb == null) return;
|
||||
final int pos = params.position();
|
||||
final int count = Math.min(rb.count(), params.remaining());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float raw = rb.raw(i);
|
||||
final int bits = Float.floatToRawIntBits(raw);
|
||||
params.put(pos + i, switch (rb.type()) {
|
||||
case FLOAT -> roundHalfAwayFromZero(raw);
|
||||
case BOOL -> bits != 0 ? 1 : 0;
|
||||
case INT, UINT -> bits;
|
||||
case OPAQUE -> rb.opaqueValue();
|
||||
});
|
||||
}
|
||||
}
|
||||
private enum ReadType { FLOAT, INT, UINT, BOOL, OPAQUE }
|
||||
private record Readback(float[] storage, int start, int count, ReadType type, int opaqueValue) {
|
||||
float raw(int i) {
|
||||
return storage != null && start + i < storage.length ? storage[start + i] : 0f;
|
||||
}
|
||||
}
|
||||
private Readback readback(int program, int location) {
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(program);
|
||||
if (prog == null || !prog.linked || location < 0 || location >= prog.uniformSlotCount) return null;
|
||||
final int kind = prog.locationKind[location];
|
||||
if (kind != ShaderManager.ProgramObject.LOCATION_KIND_PLAIN) {
|
||||
return new Readback(null, 0, 1, ReadType.OPAQUE, opaqueUnit(prog, location, kind));
|
||||
}
|
||||
final ShaderManager.UniformMemberInfo info = prog.memberInfo(location);
|
||||
if (info == null) return null;
|
||||
final int perElement = info.vectorSize() * info.columns();
|
||||
final int index = prog.arrayIndexBySlot[location];
|
||||
final int base = location - index;
|
||||
final float[] storage = s().uniformStaging(prog).uniformDataBySlot[base];
|
||||
return new Readback(storage, index * perElement, perElement, readType(prog, base, info), 0);
|
||||
}
|
||||
private static ReadType readType(ShaderManager.ProgramObject prog, int base, ShaderManager.UniformMemberInfo info) {
|
||||
if (info.baseType() == Spvc.SPVC_BASETYPE_FP32) return ReadType.FLOAT;
|
||||
if (info.baseType() != Spvc.SPVC_BASETYPE_UINT32) return ReadType.INT;
|
||||
final String name = prog.locationName[base];
|
||||
return name != null && prog.boolUniforms.contains(name) ? ReadType.BOOL : ReadType.UINT;
|
||||
}
|
||||
private static int roundHalfAwayFromZero(float f) {
|
||||
final double r = Math.floor(Math.abs((double) f) + 0.5);
|
||||
return (int) (f < 0 ? -r : r);
|
||||
}
|
||||
private static int opaqueUnit(ShaderManager.ProgramObject prog, int location, int kind) {
|
||||
final String name = prog.locationName[location];
|
||||
if (name == null) return 0;
|
||||
final int unit = kind == ShaderManager.ProgramObject.LOCATION_KIND_SAMPLER
|
||||
? prog.samplerTextureUnits.getInt(name)
|
||||
: prog.imageTextureUnits.getInt(name);
|
||||
return Math.max(unit, 0);
|
||||
}
|
||||
@Override public void getShaderSource(int shader, IntBuffer length, ByteBuffer source) {
|
||||
writeUtf8(shaderManager.getShaderSource(shader), length, source);
|
||||
}
|
||||
@@ -4166,8 +4194,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
||||
if (meta == null) return 0;
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> PixelOps.mipLevelSize(meta.width(), level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> PixelOps.mipLevelSize(meta.height(), level);
|
||||
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(meta.width(), level);
|
||||
case GL11.GL_TEXTURE_HEIGHT -> TextureInfo.mipLevelSize(meta.height(), level);
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||
final int stored = meta.glFormat();
|
||||
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
||||
@@ -4184,6 +4212,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
case GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS -> 32;
|
||||
case GL20.GL_MAX_VERTEX_ATTRIBS -> 16;
|
||||
case GL20.GL_MAX_DRAW_BUFFERS -> 8;
|
||||
case GL30.GL_MAX_CLIP_DISTANCES -> device.hasClipDistance() ? GLStateManager.MAX_CLIP_PLANES : 0;
|
||||
case GL30.GL_MAX_COLOR_ATTACHMENTS -> 8;
|
||||
case GL15.GL_ARRAY_BUFFER_BINDING -> cs.boundArrayBuffer;
|
||||
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> cs.currentVao.elementBuffer;
|
||||
@@ -4246,10 +4275,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
static final int MAX_UNIFORM_COMPONENTS_PER_STAGE = 4096 / Float.BYTES;
|
||||
static final int MAX_ARRAY_TEXTURE_LAYERS = 256;
|
||||
static final int MAX_TEXTURE_SIZE = 16384;
|
||||
static final int MAX_CUBE_MAP_TEXTURE_SIZE = 16384;
|
||||
static final int MAX_3D_TEXTURE_SIZE = 2048;
|
||||
public static final int MAX_ARRAY_TEXTURE_LAYERS = 256;
|
||||
public static final int MAX_TEXTURE_SIZE = 16384;
|
||||
public static final int MAX_CUBE_MAP_TEXTURE_SIZE = 16384;
|
||||
public static final int MAX_3D_TEXTURE_SIZE = 2048;
|
||||
/** D3D12 and Metal both cap anisotropy at 16, and every desktop Vulkan driver reports 16. */
|
||||
static final float MAX_ANISOTROPY = 16.0f;
|
||||
|
||||
@@ -4412,9 +4441,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (texHandle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
||||
if (meta == null || level >= meta.levels()) return;
|
||||
final int mipW = PixelOps.mipLevelSize(meta.width(), level);
|
||||
final int mipH = PixelOps.mipLevelSize(meta.height(), level);
|
||||
final int mipD = PixelOps.mipLevelSize(meta.depth(), level);
|
||||
final int mipW = TextureInfo.mipLevelSize(meta.width(), level);
|
||||
final int mipH = TextureInfo.mipLevelSize(meta.height(), level);
|
||||
final int mipD = TextureInfo.mipLevelSize(meta.depth(), level);
|
||||
final int bpp = PixelOps.sdlFormatTexelBytes(meta.sdlFormat());
|
||||
if (mipW <= 0 || mipH <= 0 || bpp <= 0) return;
|
||||
|
||||
@@ -4641,7 +4670,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
protected void issueDrawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (count <= 0 || primcount <= 0) return;
|
||||
final ContextState st = s();
|
||||
if (st.rasterizerDiscard) return;
|
||||
@@ -4659,11 +4688,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
protected void issueMultiDrawArrays(int mode, IntBuffer firsts, IntBuffer counts) {
|
||||
if (firsts == null || counts == null) return;
|
||||
final int n = Math.min(firsts.remaining(), counts.remaining());
|
||||
for (int i = 0; i < n; i++) {
|
||||
drawArrays(mode, firsts.get(firsts.position() + i), counts.get(counts.position() + i));
|
||||
issueDrawArrays(mode, firsts.get(firsts.position() + i), counts.get(counts.position() + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public final class Device {
|
||||
|
||||
private long device;
|
||||
private boolean claimed;
|
||||
private boolean clipDistance = true;
|
||||
private int supportedShaderFormats;
|
||||
private String driverName;
|
||||
private boolean fencePollEnabled;
|
||||
@@ -116,6 +117,10 @@ public final class Device {
|
||||
}
|
||||
|
||||
public boolean createDevice() {
|
||||
return createDevice(!SystemProperties.EMULATE_CLIP_DISTANCES);
|
||||
}
|
||||
|
||||
boolean createDevice(boolean requestClipDistance) {
|
||||
if (device != 0) return true;
|
||||
|
||||
installLogCallback();
|
||||
@@ -130,9 +135,9 @@ public final class Device {
|
||||
}
|
||||
|
||||
final boolean gpuDebug = SystemProperties.LWJGL_DEBUG || SystemProperties.SDL_GPU_DEBUG;
|
||||
device = createGPUDevice(gpuDebug, candidates);
|
||||
device = createGPUDevice(gpuDebug, candidates, requestClipDistance);
|
||||
if (device == 0 && VideoDriverRecovery.shouldRetryOnX11(SDL_GetCurrentVideoDriver(), vulkanLoaderAvailable(), System.getenv("DISPLAY"))) {
|
||||
device = VideoDriverRecovery.retryUnderX11(() -> createGPUDevice(gpuDebug, candidates));
|
||||
device = VideoDriverRecovery.retryUnderX11(() -> createGPUDevice(gpuDebug, candidates, requestClipDistance));
|
||||
}
|
||||
if (device == 0) {
|
||||
logDeviceCreationFailure(REQUESTED_SHADER_FORMATS);
|
||||
@@ -206,13 +211,22 @@ public final class Device {
|
||||
}
|
||||
}
|
||||
|
||||
private long createGPUDevice(boolean gpuDebug, String[] candidates) {
|
||||
private long createGPUDevice(boolean gpuDebug, String[] candidates, boolean requestClipDistance) {
|
||||
final boolean macos = Platform.get() == Platform.MACOSX;
|
||||
for (final String driver : candidates) {
|
||||
final boolean macVulkan = macos && isVulkan(driver);
|
||||
if (macVulkan) useMoltenVk();
|
||||
final long dev = SDL_CreateGPUDevice(REQUESTED_SHADER_FORMATS, gpuDebug, driver);
|
||||
if (dev != 0) return dev;
|
||||
long dev = createWithProperties(gpuDebug, driver, requestClipDistance);
|
||||
boolean clip = requestClipDistance;
|
||||
if (dev == 0 && clip && isVulkan(driver)) {
|
||||
LOG.info("SDL GPU Vulkan device creation failed with shaderClipDistance ({}); the device may lack it, retrying without it, clip distances will be emulated", SDLError.SDL_GetError());
|
||||
dev = createWithProperties(gpuDebug, driver, false);
|
||||
clip = false;
|
||||
}
|
||||
if (dev != 0) {
|
||||
clipDistance = clip;
|
||||
return dev;
|
||||
}
|
||||
LOG.warn("Failed to create a '{}' SDL GPU device: {}", driver, SDLError.SDL_GetError());
|
||||
if (macVulkan && MoltenVK.locate() == null) {
|
||||
LOG.warn("Vulkan on macOS needs MoltenVK. Add {} to the mods folder or install MoltenVK.", MoltenVK.JAR_NAME);
|
||||
@@ -221,6 +235,22 @@ public final class Device {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static long createWithProperties(boolean gpuDebug, String driver, boolean clipDistance) {
|
||||
final int props = SDLProperties.SDL_CreateProperties();
|
||||
try {
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, (REQUESTED_SHADER_FORMATS & SDL_GPU_SHADERFORMAT_SPIRV) != 0);
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, (REQUESTED_SHADER_FORMATS & SDL_GPU_SHADERFORMAT_DXBC) != 0);
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, (REQUESTED_SHADER_FORMATS & SDL_GPU_SHADERFORMAT_DXIL) != 0);
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, (REQUESTED_SHADER_FORMATS & SDL_GPU_SHADERFORMAT_MSL) != 0);
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, gpuDebug);
|
||||
SDLProperties.SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, driver);
|
||||
SDLProperties.SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN, clipDistance);
|
||||
return SDL_CreateGPUDeviceWithProperties(props);
|
||||
} finally {
|
||||
SDLProperties.SDL_DestroyProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
public void claimWindow(long window) {
|
||||
if (device == 0) {
|
||||
throw new IllegalStateException("SDL GPU device not created");
|
||||
@@ -298,6 +328,7 @@ public final class Device {
|
||||
driverVersion = null;
|
||||
driverInfo = null;
|
||||
supportedShaderFormats = 0;
|
||||
clipDistance = true;
|
||||
}
|
||||
|
||||
private void logWindowDiagnostics(long window) {
|
||||
@@ -351,6 +382,10 @@ public final class Device {
|
||||
return device;
|
||||
}
|
||||
|
||||
public boolean hasClipDistance() {
|
||||
return clipDistance;
|
||||
}
|
||||
|
||||
public String getDriverName() {
|
||||
return driverName;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ColorSource;
|
||||
@@ -10,6 +11,7 @@ import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.UniformStaging;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.Long2FloatOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
@@ -67,27 +69,8 @@ public final class ContextState {
|
||||
public final PackState packState = new PackState();
|
||||
public final ColorSource readColor = new ColorSource();
|
||||
|
||||
public static final class ProxyTextureState {
|
||||
public int target;
|
||||
public int width;
|
||||
public int height;
|
||||
public int depth;
|
||||
public int internalFormat;
|
||||
|
||||
public void accept(int target, int width, int height, int depth, int internalFormat) {
|
||||
this.target = target;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.internalFormat = internalFormat;
|
||||
}
|
||||
|
||||
public void reject(int target) {
|
||||
accept(target, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public final ProxyTextureState proxyTexture = new ProxyTextureState();
|
||||
public final ProxyTexture proxyTexture = new ProxyTexture(SDLGPURenderBackend.MAX_TEXTURE_SIZE, SDLGPURenderBackend.MAX_3D_TEXTURE_SIZE,
|
||||
SDLGPURenderBackend.MAX_CUBE_MAP_TEXTURE_SIZE, SDLGPURenderBackend.MAX_ARRAY_TEXTURE_LAYERS);
|
||||
|
||||
public static final class VAOState {
|
||||
public final boolean[] attribEnabled = new boolean[MAX_VERTEX_ATTRIBS];
|
||||
@@ -201,6 +184,7 @@ public final class ContextState {
|
||||
private final Reference2ObjectOpenHashMap<ShaderManager.ProgramObject, UniformStaging> uniformStagingByProgram = new Reference2ObjectOpenHashMap<>();
|
||||
private ShaderManager.ProgramObject stagingProg;
|
||||
private UniformStaging currentUniformStaging;
|
||||
public final Int2ObjectOpenHashMap<float[]> uniformArrayScratch = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public UniformStaging uniformStaging(ShaderManager.ProgramObject prog) {
|
||||
if (prog == stagingProg) return currentUniformStaging;
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpFormats;
|
||||
@@ -121,13 +121,13 @@ public final class LogicOpEmulator {
|
||||
if (fbo == null) return frameManager.getFbo0Width();
|
||||
final int db = drawBuffer(fbo, loc);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
||||
return meta != null ? PixelOps.mipLevelSize(meta.width(), fbo.colorLevels[db]) : fbo.width;
|
||||
return meta != null ? TextureInfo.mipLevelSize(meta.width(), fbo.colorLevels[db]) : fbo.width;
|
||||
}
|
||||
|
||||
private int targetHeight(FboState fbo, int loc) {
|
||||
if (fbo == null) return frameManager.getFbo0Height();
|
||||
final int db = drawBuffer(fbo, loc);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
||||
return meta != null ? PixelOps.mipLevelSize(meta.height(), fbo.colorLevels[db]) : fbo.height;
|
||||
return meta != null ? TextureInfo.mipLevelSize(meta.height(), fbo.colorLevels[db]) : fbo.height;
|
||||
}
|
||||
}
|
||||
+92
-6
@@ -754,16 +754,32 @@ public final class PipelineApplier {
|
||||
public float[] reuseOrAlloc(ContextState st, int location, int length) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog == null || location < 0 || location >= prog.uniformSlotCount) return null;
|
||||
if (prog.floatLimitBySlot[location] < 0) return arrayScratch(st, length);
|
||||
final float[] existing = st.uniformStaging(prog).uniformDataBySlot[location];
|
||||
if (existing != null && existing.length == length) return existing;
|
||||
return new float[length];
|
||||
}
|
||||
|
||||
private static float[] arrayScratch(ContextState st, int length) {
|
||||
float[] scratch = st.uniformArrayScratch.get(length);
|
||||
if (scratch == null) {
|
||||
scratch = new float[length];
|
||||
st.uniformArrayScratch.put(length, scratch);
|
||||
}
|
||||
return scratch;
|
||||
}
|
||||
|
||||
public void putUniformFv(ContextState st, int location, FloatBuffer value) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog == null || location < 0 || location >= prog.uniformSlotCount) return;
|
||||
final int n = value.remaining();
|
||||
final int pos = value.position();
|
||||
if (prog.floatLimitBySlot[location] < 0) {
|
||||
final float[] v = arrayScratch(st, n);
|
||||
value.get(pos, v);
|
||||
putUniform(st, location, v);
|
||||
return;
|
||||
}
|
||||
final float[] existing = st.uniformStaging(prog).uniformDataBySlot[location];
|
||||
if (existing != null && existing.length == n) {
|
||||
if (contentEquals(existing, value, pos, n)) {
|
||||
@@ -824,6 +840,7 @@ public final class PipelineApplier {
|
||||
if (Tracy.ENABLED) frameManager.noteUniformBlockFlush(b, size);
|
||||
}
|
||||
|
||||
private static final long ARRAY_HASH_SEED = 0x9E3779B97F4A7C15L;
|
||||
private static final boolean VERIFY_PER_FRAME_BLOCK = SystemProperties.SDL_VERIFY_PER_FRAME_UNIFORM_BLOCK;
|
||||
private final HashSet<String> perFrameBlockDriftWarned = new HashSet<>();
|
||||
|
||||
@@ -837,6 +854,12 @@ public final class PipelineApplier {
|
||||
public void putUniform(ContextState st, int location, float[] data) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog == null || location < 0 || location >= prog.uniformSlotCount) return;
|
||||
final int limit = prog.floatLimitBySlot[location];
|
||||
if (limit < 0) {
|
||||
putArrayElement(st, prog, st.uniformStaging(prog), location - prog.arrayIndexBySlot[location], location, data);
|
||||
return;
|
||||
}
|
||||
if (data.length > limit) return;
|
||||
final UniformStaging us = st.uniformStaging(prog);
|
||||
final int len = data.length;
|
||||
final long valueKey;
|
||||
@@ -884,6 +907,62 @@ public final class PipelineApplier {
|
||||
}
|
||||
return;
|
||||
}
|
||||
writeStages(st, prog, us, location, data);
|
||||
}
|
||||
|
||||
private void putArrayElement(ContextState st, ShaderManager.ProgramObject prog, UniformStaging us, int base, int location, float[] data) {
|
||||
final ShaderManager.UniformMemberInfo baseInfo = prog.memberInfo(base);
|
||||
if (baseInfo == null) return;
|
||||
final int len = prog.arrayLenBySlot[location];
|
||||
final int index = prog.arrayIndexBySlot[location];
|
||||
final int count = Math.min(elementCount(baseInfo, data), len - index);
|
||||
if (count <= 0) return;
|
||||
final int floatsPerElement = baseInfo.vectorSize() * baseInfo.columns();
|
||||
if (Tracy.ENABLED) st.slotWrites++;
|
||||
final int start = index * floatsPerElement;
|
||||
final int floats = count * floatsPerElement;
|
||||
float[] storage = us.uniformDataBySlot[base];
|
||||
final boolean fresh = storage == null;
|
||||
long storageHash;
|
||||
if (fresh) {
|
||||
storage = us.uniformDataBySlot[base] = new float[len * floatsPerElement];
|
||||
storageHash = ARRAY_HASH_SEED;
|
||||
} else {
|
||||
storageHash = us.uniformValueHashBySlot[base];
|
||||
}
|
||||
boolean changed = fresh;
|
||||
for (int i = 0; i < floats; i++) {
|
||||
final int oldBits = Float.floatToRawIntBits(storage[start + i]);
|
||||
final int newBits = Float.floatToRawIntBits(data[i]);
|
||||
if (oldBits == newBits) continue;
|
||||
storageHash ^= arrayHashTerm(start + i, oldBits) ^ arrayHashTerm(start + i, newBits);
|
||||
storage[start + i] = data[i];
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) {
|
||||
if (Tracy.ENABLED) st.slotWritesElided++;
|
||||
return;
|
||||
}
|
||||
us.uniformValueHashBySlot[base] = storageHash;
|
||||
for (int b = 0; b < ShaderManager.BLOCK_COUNT; b++) {
|
||||
final ShaderManager.UniformMemberInfo info = prog.blockInfoBySlot[b][location];
|
||||
if (info == null) continue;
|
||||
final ContextState.UniformBlockState block = st.uniformBlocks[b];
|
||||
if (prog.blockSize[b] > block.size) block.size = prog.blockSize[b];
|
||||
final FloatBuffer blockFb = block.staging(prog.blockSize[b]);
|
||||
final int hashSlot = block.hashSlot(prog.blockInfoBySlot[b][base].offset());
|
||||
if (hashSlot >= 0) block.setHash(hashSlot, storageHash);
|
||||
writeMember(st, prog, blockFb, prog.blockSize[b], info, data, location, false);
|
||||
block.dirty = true;
|
||||
if (VERIFY_PER_FRAME_BLOCK && b == ShaderManager.BLOCK_PER_FRAME && block.flushedThisFrame) {
|
||||
notePerFrameBlockDrift(prog, base);
|
||||
}
|
||||
return;
|
||||
}
|
||||
writeStages(st, prog, us, location, data);
|
||||
}
|
||||
|
||||
private void writeStages(ContextState st, ShaderManager.ProgramObject prog, UniformStaging us, int location, float[] data) {
|
||||
final ShaderManager.UniformMemberInfo vs = prog.vsInfoBySlot[location];
|
||||
if (vs != null && us.vsUniformFb != null) {
|
||||
writeMember(st, prog, us.vsUniformFb, prog.vertexUboSize, vs, data, location, true);
|
||||
@@ -896,6 +975,10 @@ public final class PipelineApplier {
|
||||
}
|
||||
}
|
||||
|
||||
private static long arrayHashTerm(int index, int bits) {
|
||||
return bits == 0 ? 0L : Hashing.fmix64(ARRAY_HASH_SEED, Hashing.packHiLo(index, bits));
|
||||
}
|
||||
|
||||
public void storeMatrix(ContextState st, int location, boolean transpose, FloatBuffer value, int size) {
|
||||
if (location < 0) return;
|
||||
final int n = value.remaining();
|
||||
@@ -914,16 +997,19 @@ public final class PipelineApplier {
|
||||
putUniform(st, location, out);
|
||||
}
|
||||
|
||||
private void writeMember(ContextState st, ShaderManager.ProgramObject prog, FloatBuffer ubo, int uboSize, ShaderManager.UniformMemberInfo info, float[] data, int loc, boolean isVs) {
|
||||
private static int elementCount(ShaderManager.UniformMemberInfo info, float[] data) {
|
||||
final int floatsPerElement = info.vectorSize() * info.columns();
|
||||
if (floatsPerElement <= 0) return;
|
||||
final int srcCount = data.length / floatsPerElement;
|
||||
if (srcCount <= 0) return;
|
||||
final int dataBytes = srcCount * floatsPerElement * 4;
|
||||
return floatsPerElement <= 0 ? 0 : data.length / floatsPerElement;
|
||||
}
|
||||
|
||||
private void writeMember(ContextState st, ShaderManager.ProgramObject prog, FloatBuffer ubo, int uboSize, ShaderManager.UniformMemberInfo info, float[] data, int loc, boolean isVs) {
|
||||
final int count = Math.min(elementCount(info, data), info.arrayLen());
|
||||
if (count <= 0) return;
|
||||
final int dataBytes = count * info.vectorSize() * info.columns() * 4;
|
||||
final int avail = uboSize - info.offset();
|
||||
if (dataBytes > avail) {
|
||||
throw new IllegalStateException("Uniform overflows UBO: prog=" + st.boundProgram + " loc=" + loc + " name='" + prog.locationToName.getOrDefault(loc, "?") + "' dataBytes=" + dataBytes + " availFromOffset=" + avail + " stage=" + (isVs ? "VS" : "FS"));
|
||||
}
|
||||
Std140Writer.write(ubo, info, data, srcCount);
|
||||
Std140Writer.write(ubo, info, data, count);
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,6 @@ import static org.lwjgl.sdl.SDLGPU.*;
|
||||
public final class PixelOps {
|
||||
private PixelOps() {}
|
||||
|
||||
public static int mipLevelSize(int base, int level) {
|
||||
if (base <= 0 || level < 0) return 0;
|
||||
if (level >= Integer.SIZE - 1) return 1;
|
||||
return Math.max(1, base >> level);
|
||||
}
|
||||
|
||||
public static int defaultMipLevels(int width, int height) {
|
||||
final int max = Math.max(1, Math.max(width, height));
|
||||
return Math.max(1, 32 - Integer.numberOfLeadingZeros(max));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.Submits;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
@@ -156,11 +157,11 @@ public final class TextureOps {
|
||||
}
|
||||
|
||||
public static int levelWidth(ResourceManager.TextureMeta meta, int level) {
|
||||
return meta != null ? PixelOps.mipLevelSize(meta.width(), level) : 0;
|
||||
return meta != null ? TextureInfo.mipLevelSize(meta.width(), level) : 0;
|
||||
}
|
||||
|
||||
public static int levelHeight(ResourceManager.TextureMeta meta, int level) {
|
||||
return meta != null ? PixelOps.mipLevelSize(meta.height(), level) : 0;
|
||||
return meta != null ? TextureInfo.mipLevelSize(meta.height(), level) : 0;
|
||||
}
|
||||
|
||||
private void copyFromDefaultFramebuffer(ContextState st, int destGlId, long destTex, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
|
||||
|
||||
@@ -777,7 +777,7 @@ public final class ShaderManager {
|
||||
else prog.fragmentUboSize = refl.uboSize();
|
||||
}
|
||||
for (UboMember m : refl.uboMembers()) {
|
||||
final int loc = ensureLocation(prog, m.name());
|
||||
final int loc = ensureLocation(prog, m.name(), m.arrayLen());
|
||||
final UniformMemberInfo info = new UniformMemberInfo(
|
||||
m.offset(), m.size(), m.arrayStride(), isVertex,
|
||||
m.vectorSize(), m.columns(), m.baseType(), m.arrayLen()
|
||||
@@ -795,7 +795,7 @@ public final class ShaderManager {
|
||||
prog.blockSize[b] = Math.max(prog.blockSize[b], block.size());
|
||||
prog.blockBinding[b] = block.binding();
|
||||
for (UboMember m : block.members()) {
|
||||
final int loc = ensureLocation(prog, m.name());
|
||||
final int loc = ensureLocation(prog, m.name(), m.arrayLen());
|
||||
if (prog.blockMemberInfo[b].containsKey(loc)) continue;
|
||||
prog.blockMemberInfo[b].put(loc, new UniformMemberInfo(
|
||||
m.offset(), m.size(), m.arrayStride(), false,
|
||||
@@ -809,14 +809,34 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
private static int ensureLocation(ProgramObject prog, String name) {
|
||||
return ensureLocation(prog, name, 1);
|
||||
}
|
||||
|
||||
private static int ensureLocation(ProgramObject prog, String name, int arrayLen) {
|
||||
final int existing = prog.nameToLocation.getInt(name);
|
||||
if (existing != -1) return existing;
|
||||
final int loc = prog.nextUniformLocation++;
|
||||
if (existing != -1) {
|
||||
final int registered = Math.max(1, prog.arrayLenByLocation.get(existing));
|
||||
if (registered == arrayLen) return existing;
|
||||
retireLocations(prog, existing, registered);
|
||||
}
|
||||
final int loc = prog.nextUniformLocation;
|
||||
prog.nextUniformLocation += arrayLen;
|
||||
prog.nameToLocation.put(name, loc);
|
||||
prog.locationToName.put(loc, name);
|
||||
for (int i = 1; i < arrayLen; i++) prog.locationToName.put(loc + i, name + "[" + i + "]");
|
||||
if (arrayLen > 1) prog.arrayLenByLocation.put(loc, arrayLen);
|
||||
return loc;
|
||||
}
|
||||
|
||||
private static void retireLocations(ProgramObject prog, int base, int len) {
|
||||
prog.arrayLenByLocation.remove(base);
|
||||
for (int i = 0; i < len; i++) prog.locationToName.remove(base + i);
|
||||
prog.locationToMemberInfo.remove(base);
|
||||
prog.vertexMemberInfo.remove(base);
|
||||
prog.fragmentMemberInfo.remove(base);
|
||||
for (int b = 0; b < BLOCK_COUNT; b++) prog.blockMemberInfo[b].remove(base);
|
||||
}
|
||||
|
||||
private static void applyVaryingMatch(ByteBuffer fragmentSpirv, StageReflection vs, StageReflection fs) {
|
||||
if (vs.vsOutputs().isEmpty() || fs.fsInputs().isEmpty() || fragmentSpirv == null) return;
|
||||
final HashMap<String, Integer> vsOutLocs = new HashMap<>(vs.vsOutputs().size() * 2);
|
||||
@@ -1268,7 +1288,13 @@ public final class ShaderManager {
|
||||
public int getUniformLocation(int program, String name) {
|
||||
final ProgramObject prog = programObjects.get(program);
|
||||
if (prog == null || !prog.linked) return -1;
|
||||
return prog.nameToLocation.getInt(name);
|
||||
final int exact = prog.nameToLocation.getInt(name);
|
||||
if (exact != -1) return exact;
|
||||
final int index = UniformNames.arrayIndex(name);
|
||||
if (index < 0) return -1;
|
||||
final int base = prog.nameToLocation.getInt(name.substring(0, name.lastIndexOf('[')));
|
||||
if (base < 0 || base >= prog.uniformSlotCount || index >= prog.arrayLenBySlot[base]) return -1;
|
||||
return base + index;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1645,7 +1671,12 @@ public final class ShaderManager {
|
||||
public record UniformMemberInfo(
|
||||
int offset, int size, int arrayStride, boolean isVertex,
|
||||
int vectorSize, int columns, int baseType, int arrayLen
|
||||
) {}
|
||||
) {
|
||||
public UniformMemberInfo element(int i) {
|
||||
return new UniformMemberInfo(offset + i * arrayStride, size - i * arrayStride, arrayStride, isVertex,
|
||||
vectorSize, columns, baseType, arrayLen - i);
|
||||
}
|
||||
}
|
||||
|
||||
public record UboMember(
|
||||
String name, int offset, int size, int arrayStride,
|
||||
@@ -2069,11 +2100,24 @@ public final class ShaderManager {
|
||||
return m != null ? m : blockMemberInfo[BLOCK_PER_PASS].get(loc);
|
||||
}
|
||||
|
||||
public UniformMemberInfo memberInfo(int slot) {
|
||||
for (int b = 0; b < BLOCK_COUNT; b++) {
|
||||
final UniformMemberInfo info = blockInfoBySlot[b][slot];
|
||||
if (info != null) return info;
|
||||
}
|
||||
final UniformMemberInfo vs = vsInfoBySlot[slot];
|
||||
return vs != null ? vs : fsInfoBySlot[slot];
|
||||
}
|
||||
|
||||
private static final UniformMemberInfo[] EMPTY_INFO = new UniformMemberInfo[0];
|
||||
|
||||
public UniformMemberInfo[] vsInfoBySlot;
|
||||
public UniformMemberInfo[] fsInfoBySlot;
|
||||
public int uniformSlotCount;
|
||||
public final Int2IntOpenHashMap arrayLenByLocation = new Int2IntOpenHashMap();
|
||||
public int[] arrayIndexBySlot = EMPTY_UNITS;
|
||||
public int[] arrayLenBySlot = EMPTY_UNITS;
|
||||
public int[] floatLimitBySlot = EMPTY_UNITS;
|
||||
public final int[] vertexInputBaseType = new int[16];
|
||||
public final int[] vertexInputVecSize = new int[16];
|
||||
public final String[] vertexInputName = new String[16];
|
||||
@@ -2200,24 +2244,51 @@ public final class ShaderManager {
|
||||
locationKind[loc] = LOCATION_KIND_PLAIN;
|
||||
}
|
||||
}
|
||||
arrayIndexBySlot = new int[n];
|
||||
arrayLenBySlot = new int[n];
|
||||
Arrays.fill(arrayLenBySlot, 1);
|
||||
for (var it = arrayLenByLocation.int2IntEntrySet().fastIterator(); it.hasNext(); ) {
|
||||
final var e = it.next();
|
||||
final int base = e.getIntKey();
|
||||
final int len = e.getIntValue();
|
||||
for (int i = 0; i < len; i++) {
|
||||
arrayLenBySlot[base + i] = len;
|
||||
arrayIndexBySlot[base + i] = i;
|
||||
}
|
||||
}
|
||||
for (int b = 0; b < BLOCK_COUNT; b++) {
|
||||
blockInfoBySlot[b] = new UniformMemberInfo[n];
|
||||
for (var it = blockMemberInfo[b].int2ObjectEntrySet().fastIterator(); it.hasNext(); ) {
|
||||
final var e = it.next();
|
||||
final int k = e.getIntKey();
|
||||
if (k >= 0 && k < n) blockInfoBySlot[b][k] = e.getValue();
|
||||
fillSlots(blockInfoBySlot[b], e.getIntKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
for (var it = vertexMemberInfo.int2ObjectEntrySet().fastIterator(); it.hasNext(); ) {
|
||||
final var e = it.next();
|
||||
final int k = e.getIntKey();
|
||||
if (k >= 0 && k < n) vsInfoBySlot[k] = e.getValue();
|
||||
fillSlots(vsInfoBySlot, e.getIntKey(), e.getValue());
|
||||
}
|
||||
for (var it = fragmentMemberInfo.int2ObjectEntrySet().fastIterator(); it.hasNext(); ) {
|
||||
final var e = it.next();
|
||||
final int k = e.getIntKey();
|
||||
if (k >= 0 && k < n) fsInfoBySlot[k] = e.getValue();
|
||||
fillSlots(fsInfoBySlot, e.getIntKey(), e.getValue());
|
||||
}
|
||||
floatLimitBySlot = new int[n];
|
||||
for (int slot = 0; slot < n; slot++) {
|
||||
if (arrayLenBySlot[slot] > 1) {
|
||||
floatLimitBySlot[slot] = -1;
|
||||
continue;
|
||||
}
|
||||
final UniformMemberInfo info = memberInfo(slot);
|
||||
final int floats = info == null || info.arrayStride() > 0 ? 0 : info.vectorSize() * info.columns();
|
||||
floatLimitBySlot[slot] = floats > 0 ? floats : Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private void fillSlots(UniformMemberInfo[] bySlot, int base, UniformMemberInfo info) {
|
||||
final int n = bySlot.length;
|
||||
if (base < 0 || base >= n) return;
|
||||
bySlot[base] = info;
|
||||
final int len = arrayLenBySlot[base];
|
||||
for (int i = 1; i < len; i++) bySlot[base + i] = info.element(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
public final class UniformNames {
|
||||
|
||||
private UniformNames() {}
|
||||
|
||||
public static int arrayIndex(String name) {
|
||||
final int len = name.length();
|
||||
if (len == 0 || name.charAt(len - 1) != ']') return -1;
|
||||
int i = len - 1;
|
||||
while (i > 0 && isDigit(name.charAt(i - 1))) i--;
|
||||
if (i == 0 || name.charAt(i - 1) != '[') return -1;
|
||||
if (name.charAt(i) == '0' && i + 1 != len - 1) return -1;
|
||||
long index = 0;
|
||||
for (int c = i; c < len - 1; c++) {
|
||||
index = index * 10 + (name.charAt(c) - '0');
|
||||
if (index > Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
||||
}
|
||||
return (int) index;
|
||||
}
|
||||
|
||||
private static boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
}
|
||||
+13
-12
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -47,11 +48,11 @@ class ProxyTextureLimitsTest {
|
||||
assertEquals(2048, SDLGPURenderBackend.deviceLimit(GL12.GL_MAX_3D_TEXTURE_SIZE));
|
||||
assertEquals(16384, SDLGPURenderBackend.deviceLimit(GL13.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
|
||||
|
||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL11.GL_PROXY_TEXTURE_2D));
|
||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL11.GL_PROXY_TEXTURE_1D));
|
||||
assertEquals(2048, SDLGPURenderBackend.proxyDimensionLimit(GL12.GL_PROXY_TEXTURE_3D));
|
||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL13.GL_PROXY_TEXTURE_CUBE_MAP));
|
||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY));
|
||||
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL11.GL_PROXY_TEXTURE_2D));
|
||||
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL11.GL_PROXY_TEXTURE_1D));
|
||||
assertEquals(2048, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL12.GL_PROXY_TEXTURE_3D));
|
||||
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL13.GL_PROXY_TEXTURE_CUBE_MAP));
|
||||
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,13 +100,13 @@ class ProxyTextureLimitsTest {
|
||||
|
||||
@Test
|
||||
void levelZeroEquivalentSizeRejectsOverflowAndNonsense() {
|
||||
assertEquals(4, SDLGPURenderBackend.levelZeroEquivalentSize(4, 0));
|
||||
assertEquals(32, SDLGPURenderBackend.levelZeroEquivalentSize(4, 3));
|
||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, -1));
|
||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(0, 2));
|
||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, 31));
|
||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, 30));
|
||||
assertEquals(1 << 30, SDLGPURenderBackend.levelZeroEquivalentSize(1, 30));
|
||||
assertEquals(4, ProxyTexture.levelZeroEquivalentSize(4, 0));
|
||||
assertEquals(32, ProxyTexture.levelZeroEquivalentSize(4, 3));
|
||||
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, -1));
|
||||
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(0, 2));
|
||||
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, 31));
|
||||
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, 30));
|
||||
assertEquals(1 << 30, ProxyTexture.levelZeroEquivalentSize(1, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -81,7 +82,7 @@ class ProxyTextureTargetTest {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("levelSizeCases")
|
||||
void levelSizeMatchesExpectation(String label, int base, int level, int expected) {
|
||||
assertEquals(expected, SDLGPURenderBackend.proxyLevelSize(base, level), label);
|
||||
assertEquals(expected, TextureInfo.mipLevelSize(base, level), label);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+10
@@ -2,11 +2,13 @@ package com.gtnewhorizons.angelica.sdlgpu.device;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
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.assertTrue;
|
||||
|
||||
@@ -46,4 +48,12 @@ class DeviceLifecycleGpuTest {
|
||||
device.destroyDevice();
|
||||
assertEquals(0L, device.getDevice());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsVulkanDeviceWithoutClipDistance() {
|
||||
device = new Device();
|
||||
assertTrue(device.createDevice(false), "no SDL GPU device on this machine");
|
||||
Assumptions.assumeTrue(device.isVulkan(), "test needs the vulkan driver, got " + device.getDriverName());
|
||||
assertFalse(device.hasClipDistance());
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeParityFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ParticleParityFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.ClipTestSupport;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
@@ -127,6 +128,30 @@ class GlsmSdlFfpTest {
|
||||
assertTexelEverywhere("two units, glCallLists");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void disabledClipPlaneDoesNotClip(boolean emulated) {
|
||||
try (var ignored = ClipTestSupport.emulation(emulated)) {
|
||||
bindBaseTexture();
|
||||
try {
|
||||
ClipTestSupport.setPlane(1, 0.0, 0.0, 0.0, -1.0);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
ClipTestSupport.setPlane(0, 1.0, 0.0, 0.0, 0.0);
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
quadUnit0Only();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
final int left = pixelAt(pixels, SIZE, SIZE / 4, SIZE / 2);
|
||||
final int right = pixelAt(pixels, SIZE, 3 * SIZE / 4, SIZE / 2);
|
||||
assertTrue(left != TEXEL, () -> "left quarter must be clipped by enabled plane 0: " + describe(left));
|
||||
assertEquals(TEXEL, right, () -> "right quarter must survive; disabled plane 1 must not clip: " + describe(right));
|
||||
} finally {
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void litTexturedQuadIsShaded() {
|
||||
bindBaseTexture();
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlUniformArrayTest {
|
||||
|
||||
private static final int COUNT = 8;
|
||||
private static final int[][] A = {
|
||||
{10, 20, 30}, {40, 50, 60}, {70, 80, 90}, {100, 110, 120},
|
||||
{130, 140, 150}, {160, 170, 180}, {190, 200, 210}, {220, 230, 240}
|
||||
};
|
||||
private static final int[] X = {250, 5, 15};
|
||||
private static final int[][] B = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {11, 12, 13}};
|
||||
|
||||
private int program;
|
||||
private int vao;
|
||||
private int arr;
|
||||
private int single;
|
||||
private int pick;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
program = GlTestSupport.program(GlsmSdlHeadlessRig.FULLSCREEN_VERTEX, """
|
||||
#version 330 core
|
||||
uniform vec4 arr[8];
|
||||
uniform vec4 single;
|
||||
uniform ivec4 iarr[2];
|
||||
uniform vec4 one[1];
|
||||
uniform int pick;
|
||||
out vec4 color;
|
||||
void main() { color = pick == -2 ? vec4(iarr[1]) / 255.0 : pick == -3 ? one[0] : pick < 0 ? single : arr[pick]; }
|
||||
""");
|
||||
vao = GLSMVertexInput.glGenVertexArrays();
|
||||
GLSMVertexInput.glBindVertexArray(vao);
|
||||
GLSMPrograms.glUseProgram(program);
|
||||
arr = loc("arr");
|
||||
single = loc("single");
|
||||
pick = loc("pick");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLSMPrograms.glUseProgram(0);
|
||||
GLSMVertexInput.glBindVertexArray(0);
|
||||
GLSMVertexInput.glDeleteVertexArrays(vao);
|
||||
GLSMPrograms.glDeleteProgram(program);
|
||||
}
|
||||
|
||||
private int loc(String name) {
|
||||
return GLSMPrograms.glGetUniformLocation(program, name);
|
||||
}
|
||||
|
||||
private static int argb(int[] rgb) {
|
||||
return 0xFF000000 | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
||||
}
|
||||
|
||||
private static float[] vec(int[] rgb) {
|
||||
return new float[] {rgb[0] / 255f, rgb[1] / 255f, rgb[2] / 255f, 1f};
|
||||
}
|
||||
|
||||
private static FloatBuffer pack(int[]... colors) {
|
||||
final FloatBuffer buf = MemoryUtil.memAllocFloat(colors.length * 4);
|
||||
for (int[] c : colors) buf.put(vec(c));
|
||||
buf.flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void writeAt(int location, int[]... colors) {
|
||||
final FloatBuffer buf = pack(colors);
|
||||
try {
|
||||
GLSMPrograms.glUniform4(location, buf);
|
||||
} finally {
|
||||
MemoryUtil.memFree(buf);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeElement(int location, int[] rgb) {
|
||||
final float[] v = vec(rgb);
|
||||
GLSMPrograms.glUniform4f(location, v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
|
||||
private void writeAll() {
|
||||
writeAt(arr, A);
|
||||
}
|
||||
|
||||
private int drawPick(int index) {
|
||||
GlsmSdlHeadlessRig.beginFrame();
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GLSMPrograms.glUniform1i(pick, index);
|
||||
GLSMDraws.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
return GlsmSdlHeadlessRig.readTarget(GlsmSdlHeadlessRig.SIZE / 2, GlsmSdlHeadlessRig.SIZE / 2, 1, 1)[0];
|
||||
}
|
||||
|
||||
private void assertPick(int index, int[] expected, String label) {
|
||||
final int got = drawPick(index);
|
||||
assertEquals(Integer.toHexString(argb(expected)), Integer.toHexString(got), label + ": " + GlsmSdlHeadlessRig.describe(got));
|
||||
}
|
||||
|
||||
private float[] readUniform(int location) {
|
||||
final FloatBuffer buf = MemoryUtil.memAllocFloat(4);
|
||||
try {
|
||||
GLSMPrograms.glGetUniform(program, location, buf);
|
||||
final float[] out = new float[4];
|
||||
buf.get(out);
|
||||
return out;
|
||||
} finally {
|
||||
MemoryUtil.memFree(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayElementLocations() {
|
||||
assertNotEquals(-1, arr, "arr");
|
||||
assertNotEquals(-1, single, "single");
|
||||
assertEquals(arr, loc("arr[0]"), "arr[0]");
|
||||
assertEquals(arr + 3, loc("arr[3]"), "arr[3]");
|
||||
assertEquals(arr + 7, loc("arr[7]"), "arr[7]");
|
||||
assertEquals(-1, loc("arr[8]"), "arr[8]");
|
||||
assertEquals(-1, loc("arr[03]"), "arr[03]");
|
||||
assertEquals(-1, loc("arr[-1]"), "arr[-1]");
|
||||
assertEquals(arr, loc("arr[]"), "arr[] is index 0, as Mesa parses it");
|
||||
assertEquals(single, loc("single[0]"), "single[0]");
|
||||
assertEquals(-1, loc("single[1]"), "single[1]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void elementWriteLandsInTheArray() {
|
||||
writeAll();
|
||||
final int element3 = loc("arr[3]");
|
||||
writeElement(element3, X);
|
||||
|
||||
assertPick(3, X, "pick=3 shows the element write");
|
||||
assertPick(2, A[2], "pick=2 keeps the bulk write");
|
||||
assertArrayEquals(vec(X), readUniform(element3), 0f, "glGetUniform arr[3]");
|
||||
assertArrayEquals(vec(A[2]), readUniform(loc("arr[2]")), 0f, "glGetUniform arr[2]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void elementWriteClampsToTheArrayEnd() {
|
||||
writeAll();
|
||||
writeAt(loc("arr[6]"), B);
|
||||
|
||||
assertPick(6, B[0], "pick=6");
|
||||
assertPick(7, B[1], "pick=7");
|
||||
assertPick(5, A[5], "pick=5");
|
||||
}
|
||||
|
||||
@Test
|
||||
void countBeyondTheDeclarationFollowsMesa() {
|
||||
writeElement(single, A[0]);
|
||||
writeAt(single, B);
|
||||
|
||||
assertPick(-1, A[0], "count 2 on a non-array is ignored");
|
||||
assertArrayEquals(vec(A[0]), readUniform(single), 0f, "glGetUniform single keeps its value");
|
||||
|
||||
writeAt(loc("one"), B);
|
||||
assertPick(-3, B[0], "count 2 on a size-1 array clamps");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullWriteAfterElementWriteIsNotElided() {
|
||||
writeAll();
|
||||
writeElement(loc("arr[3]"), X);
|
||||
writeAll();
|
||||
|
||||
assertPick(3, A[3], "pick=3 after the repeated full write");
|
||||
}
|
||||
|
||||
@Test
|
||||
void intElementReadsBackAsInts() {
|
||||
final int element1 = loc("iarr[1]");
|
||||
assertEquals(loc("iarr") + 1, element1, "iarr[1]");
|
||||
GLSMPrograms.glUniform4i(loc("iarr"), 1, 2, 3, 4);
|
||||
GLSMPrograms.glUniform4i(element1, X[0], X[1], X[2], 255);
|
||||
|
||||
assertPick(-2, X, "pick=-2 shows iarr[1]");
|
||||
assertArrayEquals(new int[] {X[0], X[1], X[2], 255}, readIntUniform(element1), "glGetUniformiv iarr[1]");
|
||||
assertArrayEquals(new int[] {1, 2, 3, 4}, readIntUniform(loc("iarr")), "glGetUniformiv iarr[0]");
|
||||
}
|
||||
|
||||
private int[] readIntUniform(int location) {
|
||||
final IntBuffer buf = MemoryUtil.memAllocInt(4);
|
||||
try {
|
||||
GLSMPrograms.glGetUniform(program, location, buf);
|
||||
final int[] out = new int[4];
|
||||
buf.get(out);
|
||||
return out;
|
||||
} finally {
|
||||
MemoryUtil.memFree(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void activeUniformReportsArrayNameWithIndexZero() {
|
||||
final Map<String, Integer> sizes = activeUniformSizes();
|
||||
assertEquals(COUNT, sizes.getOrDefault("arr[0]", -1), "size of arr[0] in " + sizes);
|
||||
assertEquals(1, sizes.getOrDefault("single", -1), "size of single in " + sizes);
|
||||
assertEquals(1, sizes.getOrDefault("one[0]", -1), "size of one[0] in " + sizes);
|
||||
}
|
||||
|
||||
private Map<String, Integer> activeUniformSizes() {
|
||||
final int active = GLSMPrograms.glGetProgrami(program, GL20.GL_ACTIVE_UNIFORMS);
|
||||
final Map<String, Integer> sizes = new HashMap<>();
|
||||
final IntBuffer length = MemoryUtil.memAllocInt(1);
|
||||
final IntBuffer size = MemoryUtil.memAllocInt(1);
|
||||
final IntBuffer type = MemoryUtil.memAllocInt(1);
|
||||
final ByteBuffer name = MemoryUtil.memAlloc(128);
|
||||
try {
|
||||
for (int i = 0; i < active; i++) {
|
||||
GLSMPrograms.glGetActiveUniform(program, i, length, size, type, name);
|
||||
final byte[] bytes = new byte[length.get(0)];
|
||||
for (int c = 0; c < bytes.length; c++) bytes[c] = name.get(c);
|
||||
sizes.put(new String(bytes, java.nio.charset.StandardCharsets.US_ASCII), size.get(0));
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(name);
|
||||
MemoryUtil.memFree(type);
|
||||
MemoryUtil.memFree(size);
|
||||
MemoryUtil.memFree(length);
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.api.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlowReplayExclusions;
|
||||
|
||||
/// Under Fancy translucency, block entities drawing glows (blended draws without depth writes) in the translucent
|
||||
/// pass are rendered a second time. Renderers that advance state per call opt out here.
|
||||
public final class TesrGlowDepth {
|
||||
|
||||
private TesrGlowDepth() {}
|
||||
|
||||
/// Excludes block entities of `type`, or rendered by a renderer of `type`, including subclasses.
|
||||
public static void excludeFromReplay(Class<?> type) {
|
||||
GlowReplayExclusions.exclude(type);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/** TESR mesh cache/batch API (Render thread only) */
|
||||
@API(owner = "angelica", provides = "angelica|tesr", apiVersion = "3")
|
||||
@API(owner = "angelica", provides = "angelica|tesr", apiVersion = "4")
|
||||
package com.gtnewhorizons.angelica.api.tesr;
|
||||
|
||||
import cpw.mods.fml.common.API;
|
||||
@@ -28,6 +28,7 @@ import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchDrawDefaults;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.FlattenedItemLighting;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -1194,6 +1195,7 @@ public class BatchingFontRenderer {
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
int lastTexture = INVALID_TEXTURE;
|
||||
boolean drawing = false;
|
||||
FlattenedItemLighting.setPreLitText(true);
|
||||
try {
|
||||
for (int i = from; i < to; i++) {
|
||||
final FontDrawCmd cmd = cmdsData[i];
|
||||
@@ -1219,8 +1221,12 @@ public class BatchingFontRenderer {
|
||||
owner.emitCommand(tessellator, cmd);
|
||||
}
|
||||
} finally {
|
||||
if (drawing) {
|
||||
tessellator.draw();
|
||||
try {
|
||||
if (drawing) {
|
||||
tessellator.draw();
|
||||
}
|
||||
} finally {
|
||||
FlattenedItemLighting.setPreLitText(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockClipPlaneTest extends BlockContainer {
|
||||
|
||||
public BlockClipPlaneTest() {
|
||||
super(Material.iron);
|
||||
setHardness(0.7f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int meta) {
|
||||
return new TileEntityClipPlaneTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renderAsNormalBlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BlockTesrTranslucentTest extends Block {
|
||||
|
||||
private final Supplier<TileEntity> tile;
|
||||
|
||||
public BlockTesrTranslucentTest(Supplier<TileEntity> tile) {
|
||||
super(Material.glass);
|
||||
this.tile = tile;
|
||||
setHardness(0.3f);
|
||||
setLightOpacity(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueCube() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renderAsNormalBlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderType() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(int metadata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createTileEntity(World world, int metadata) {
|
||||
return tile.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
public class TileEntityClipPlaneTest extends TileEntity {
|
||||
|
||||
@Override
|
||||
public boolean canUpdate() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.common;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
public abstract class TileTesrTranslucentTest extends TileEntity {
|
||||
|
||||
private static final int SOLID_PASS = 8;
|
||||
|
||||
public int variant() {
|
||||
return getBlockMetadata() & ~SOLID_PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass) {
|
||||
return pass == ((getBlockMetadata() & SOLID_PASS) != 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Provider extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Model extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Decal extends TileTesrTranslucentTest {}
|
||||
|
||||
public static class Glow extends TileTesrTranslucentTest {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizons.angelica.compat.spark;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class SparkKnownSources {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static final String PLUGIN = "me/lucko/spark/forge/plugin/Forge1710SparkPlugin";
|
||||
private static final String SOURCE_METADATA = "me.lucko.spark.common.sampler.source.SourceMetadata";
|
||||
|
||||
private static MethodHandle constructor;
|
||||
private static boolean failed;
|
||||
|
||||
private SparkKnownSources() {}
|
||||
|
||||
public static boolean mixinApplies() {
|
||||
try (InputStream stream = SparkKnownSources.class.getResourceAsStream("/" + PLUGIN + ".class")) {
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
final boolean[] declared = new boolean[1];
|
||||
new ClassReader(stream).accept(new ClassVisitor(Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if ("getKnownSources".equals(name) && "()Ljava/util/Collection;".equals(desc)) {
|
||||
declared[0] = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
if (declared[0]) {
|
||||
LOGGER.info("spark already reports mod sources; skipping");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.warn("Could not inspect spark plugin class; not adding mod sources", t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<Object> gather(ClassLoader loader) {
|
||||
if (failed) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
if (constructor == null) constructor = MethodHandles.publicLookup().findConstructor(Class.forName(SOURCE_METADATA, false, loader),
|
||||
MethodType.methodType(void.class, String.class, String.class, String.class, String.class));
|
||||
final List<ModContainer> mods = Loader.instance().getActiveModList();
|
||||
final List<Object> sources = new ArrayList<>(mods.size());
|
||||
for (int i = 0; i < mods.size(); i++) {
|
||||
final ModContainer mod = mods.get(i);
|
||||
sources.add(constructor.invoke(mod.getModId(), mod.getVersion(), (String) null, (String) null));
|
||||
}
|
||||
return sources;
|
||||
} catch (Throwable t) {
|
||||
failed = true;
|
||||
LOGGER.warn("Could not build spark mod sources; profiles will omit mod versions", t);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,6 +177,11 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] entityShadowReplayExcludedClasses;
|
||||
|
||||
@Config.Comment("Tile entity or renderer classes whose glows Fancy translucency records as they draw instead of rendering the tile entity a second time. For renderers that advance an animation or other state on each render.")
|
||||
@Config.DefaultStringList({})
|
||||
@Config.RequiresMcRestart
|
||||
public static String[] glowDepthReplayExcludedClasses;
|
||||
|
||||
@Config.Comment("Skip the end-of-frame shader buffer copy by ping-ponging buffers. Disable if a shader pack misrenders.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
|
||||
@@ -34,9 +34,6 @@ public final class PositionEntries {
|
||||
}
|
||||
}
|
||||
|
||||
private WorldProvider cachedProvider;
|
||||
private String dimensionName = "";
|
||||
|
||||
@Override
|
||||
public void display(DebugScreenDisplayer displayer, DebugContext context) {
|
||||
final EntityClientPlayerMP player = context.player();
|
||||
@@ -52,10 +49,7 @@ public final class PositionEntries {
|
||||
facing.value(DIRECTION_NAMES[heading]).text(" (").text(TOWARDS[heading]).text(") (");
|
||||
facing.value(MathHelper.wrapAngleTo180_float(player.rotationYaw), 1).text(SEPARATOR).value(MathHelper.wrapAngleTo180_float(player.rotationPitch), 1).text(')');
|
||||
final WorldProvider provider = context.world().provider;
|
||||
if (provider != cachedProvider) {
|
||||
cachedProvider = provider;
|
||||
dimensionName = provider.getDimensionName();
|
||||
}
|
||||
final String dimensionName = provider.getDimensionName();
|
||||
displayer.fact(DebugGroup.POSITION, "Dimension").value(dimensionName).text(" (").value(provider.dimensionId).text(')');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,13 @@ public class IrisGLSMBridge {
|
||||
refreshBlendCondition();
|
||||
});
|
||||
|
||||
GLSMHooks.CLIP_PLANES_IN_USE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp) {
|
||||
drp.onClipPlanesInUseChanged();
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (programRestoreDeferred) {
|
||||
@@ -336,12 +343,13 @@ public class IrisGLSMBridge {
|
||||
WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline instanceof DeferredWorldRenderingPipeline drp) {
|
||||
DeferredWorldRenderingPipeline.Pass activePass = drp.getActivePassProgram();
|
||||
if (activePass != null && activePass.getProgram() != null && activePass.getProgram().getProgramId() == event.newProgram) {
|
||||
final Program bound = activePass != null ? activePass.boundProgram() : null;
|
||||
if (bound != null && bound.getProgramId() == event.newProgram) {
|
||||
final int frame = SystemTimeUniforms.COUNTER.getAsInt();
|
||||
if (programLastUpdatedFrame.get(event.newProgram) != frame
|
||||
|| !ProgramUniforms.isActiveProgramBound()
|
||||
|| ProgramUniforms.activeHasDeferredUploads()) {
|
||||
activePass.getProgram().getUniforms().update();
|
||||
bound.getUniforms().update();
|
||||
programLastUpdatedFrame.put(event.newProgram, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.mixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.compat.spark.SparkKnownSources;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
@@ -300,6 +301,15 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_GLOW_DEPTH_REPLAY(new MixinBuilder("Drop particles and sounds a block entity renderer starts while Fancy translucency replays it into the glow depth")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins(
|
||||
"angelica.tesr.MixinEffectRenderer_GlowDepthReplay",
|
||||
"angelica.tesr.MixinSoundHandler_GlowDepthReplay"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_TESR_PROVIDER_DISPATCH(new MixinBuilder("Route TesrMeshProvider renderers through the batched mesh cache, and observe renderers that mix model parts with their own draws")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRProviderDispatch || AngelicaConfig.enableEntityBatching)
|
||||
@@ -550,6 +560,8 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinEntityRenderer"
|
||||
, "shaders.MixinGuiIngameForge"
|
||||
, "shaders.MixinFramebuffer"
|
||||
, "angelica.rendering.MixinEntityRenderer_TranslucencyLayers"
|
||||
, "angelica.rendering.MixinFramebuffer_TranslucencyLayers"
|
||||
, "shaders.MixinItem"
|
||||
, "shaders.MixinItemRenderer_ItemId"
|
||||
, "shaders.MixinLocale"
|
||||
@@ -641,6 +653,13 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
SPARK_MOD_SOURCES(new MixinBuilder("Mod versions in spark 1.7.10 profiles")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.SPARK)
|
||||
.setApplyIf(SparkKnownSources::mixinApplies)
|
||||
.addClientMixins("spark.MixinForge1710SparkPlugin")
|
||||
),
|
||||
|
||||
ANGELICA_TEXTURE(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
|
||||
@@ -32,6 +32,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
OPENBLOCKS(null, "OpenBlocks"),
|
||||
RAILCRAFT(null, "Railcraft"),
|
||||
SECURITYCRAFT(null, "securitycraft"),
|
||||
SPARK(null, "spark"),
|
||||
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
|
||||
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
|
||||
THAUMCRAFT(null, "Thaumcraft"),
|
||||
|
||||
@@ -39,6 +39,8 @@ import com.gtnewhorizons.angelica.commands.AngelicaCommand;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.common.BlockIsbrhTest;
|
||||
import com.gtnewhorizons.angelica.common.TileEntityClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
@@ -70,9 +72,11 @@ import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import com.gtnewhorizons.angelica.render.EmissiveTextureAutoloader;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.ClipPlaneTestRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.FpsReducer;
|
||||
import com.gtnewhorizons.angelica.rendering.FramePacer;
|
||||
import com.gtnewhorizons.angelica.rendering.IsbrhTestRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.TesrTranslucentTestRenderers;
|
||||
import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
@@ -86,6 +90,7 @@ import com.gtnewhorizons.angelica.utils.AngelicaJar;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import com.gtnewhorizons.angelica.utils.ManagedEnum;
|
||||
import com.gtnewhorizons.angelica.zoom.Zoom;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
@@ -146,6 +151,11 @@ public final class ClientProxy extends CommonProxy {
|
||||
if (AngelicaConfig.enableTestBlocks) {
|
||||
BlockIsbrhTest.renderId = RenderingRegistry.getNextAvailableRenderId();
|
||||
RenderingRegistry.registerBlockHandler(BlockIsbrhTest.renderId, new IsbrhTestRenderer());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityClipPlaneTest.class, new ClipPlaneTestRenderer());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Provider.class, new TesrTranslucentTestRenderers.Provider());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Model.class, new TesrTranslucentTestRenderers.Model());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Decal.class, new TesrTranslucentTestRenderers.Decal());
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileTesrTranslucentTest.Glow.class, new TesrTranslucentTestRenderers.Glow());
|
||||
}
|
||||
AngelicaJar.configureShaderDiskCache(event.getSourceFile());
|
||||
if (AngelicaConfig.enableIris) {
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.proxy;
|
||||
|
||||
import com.gtnewhorizons.angelica.common.BlockClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockIsbrhTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.common.BlockTest;
|
||||
import com.gtnewhorizons.angelica.common.TileEntityClipPlaneTest;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CommonProxy {
|
||||
|
||||
@@ -14,9 +21,20 @@ public class CommonProxy {
|
||||
if (AngelicaConfig.enableTestBlocks) {
|
||||
GameRegistry.registerBlock(new BlockTest(), "test_block");
|
||||
GameRegistry.registerBlock(new BlockIsbrhTest(), "test_isbrh");
|
||||
GameRegistry.registerBlock(new BlockClipPlaneTest(), "test_clip_plane");
|
||||
GameRegistry.registerTileEntity(TileEntityClipPlaneTest.class, "angelica:test_clip_plane");
|
||||
registerTesrTest("test_tesr_provider", TileTesrTranslucentTest.Provider.class, TileTesrTranslucentTest.Provider::new);
|
||||
registerTesrTest("test_tesr_model", TileTesrTranslucentTest.Model.class, TileTesrTranslucentTest.Model::new);
|
||||
registerTesrTest("test_tesr_decal", TileTesrTranslucentTest.Decal.class, TileTesrTranslucentTest.Decal::new);
|
||||
registerTesrTest("test_tesr_glow", TileTesrTranslucentTest.Glow.class, TileTesrTranslucentTest.Glow::new);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTesrTest(String name, Class<? extends TileEntity> tile, Supplier<TileEntity> factory) {
|
||||
GameRegistry.registerBlock(new BlockTesrTranslucentTest(factory), name);
|
||||
GameRegistry.registerTileEntity(tile, "angelica:" + name);
|
||||
}
|
||||
|
||||
public void init(FMLInitializationEvent event) {}
|
||||
|
||||
public void postInit(FMLPostInitializationEvent event) {}
|
||||
|
||||
@@ -103,8 +103,12 @@ public class PanoramaRenderer {
|
||||
}
|
||||
|
||||
public static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, Function<ShaderBindingContext, T> factory) {
|
||||
return loadProgram(name, vertBase, fragBase, ShaderConstants.EMPTY, factory);
|
||||
}
|
||||
|
||||
public static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, ShaderConstants fragConstants, Function<ShaderBindingContext, T> factory) {
|
||||
final GlShader vert = ShaderLoader.loadShader(ShaderType.VERTEX, vertBase + ".vert", ShaderConstants.EMPTY);
|
||||
final GlShader frag = ShaderLoader.loadShader(ShaderType.FRAGMENT, fragBase + ".frag", ShaderConstants.EMPTY);
|
||||
final GlShader frag = ShaderLoader.loadShader(ShaderType.FRAGMENT, fragBase + ".frag", fragConstants);
|
||||
try {
|
||||
final GlProgram<T> program = GlProgram.builder(name).attachShader(vert).attachShader(frag).link(factory);
|
||||
program.bind();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
|
||||
public class ClipPlaneTestRenderer extends TileEntitySpecialRenderer {
|
||||
|
||||
private static final DoubleBuffer KEEP_LOWER_HALF = plane(0.0, -1.0, 0.0, 0.5);
|
||||
private static final DoubleBuffer REJECT_ALL = plane(0.0, 0.0, 0.0, -1.0);
|
||||
|
||||
private static DoubleBuffer plane(double a, double b, double c, double d) {
|
||||
final DoubleBuffer buf = BufferUtils.createDoubleBuffer(4);
|
||||
buf.put(a).put(b).put(c).put(d).flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
GLSMMatrices.glPushMatrix();
|
||||
GLSMMatrices.glTranslated(x, y, z);
|
||||
GLSMRasterizer.glClipPlane(GL11.GL_CLIP_PLANE1, REJECT_ALL);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
GLSMRasterizer.glClipPlane(GL11.GL_CLIP_PLANE0, KEEP_LOWER_HALF);
|
||||
GLSMEnable.glEnable(GL11.GL_CLIP_PLANE0);
|
||||
GLSMMatrices.glTranslated(0.5, 0.5, 0.5);
|
||||
bindTexture(TextureMap.locationBlocksTexture);
|
||||
RenderBlocks.getInstance().renderBlockAsItem(Blocks.gold_block, 0, 1.0f);
|
||||
GLSMEnable.glDisable(GL11.GL_CLIP_PLANE0);
|
||||
GLSMMatrices.glPopMatrix();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMeshProvider;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMeshSink;
|
||||
import com.gtnewhorizons.angelica.common.TileTesrTranslucentTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMDepthStencilAlpha;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
|
||||
import com.gtnewhorizons.angelica.utils.WorkaroundUtils;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class TesrTranslucentTestRenderers {
|
||||
|
||||
private static final double HALF = 0.4375;
|
||||
|
||||
private TesrTranslucentTestRenderers() {}
|
||||
|
||||
private static void pane(Tessellator t, double half) {
|
||||
rect(t, -half, -half, half, half);
|
||||
}
|
||||
|
||||
private static void rect(Tessellator t, double x0, double y0, double x1, double y1) {
|
||||
t.setNormal(0f, 0f, -1f);
|
||||
t.addVertexWithUV(0.5 + x0, 0.5 + y0, 0.5, 0, 1);
|
||||
t.addVertexWithUV(0.5 + x0, 0.5 + y1, 0.5, 0, 0);
|
||||
t.addVertexWithUV(0.5 + x1, 0.5 + y1, 0.5, 1, 0);
|
||||
t.addVertexWithUV(0.5 + x1, 0.5 + y0, 0.5, 1, 1);
|
||||
}
|
||||
|
||||
private static void drawPane(double half) {
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
pane(t, half);
|
||||
t.draw();
|
||||
}
|
||||
|
||||
private static void apply(TesrMaterial material) {
|
||||
if (material.transparency() == TesrMaterial.Transparency.ADDITIVE) {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
} else {
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
GLSMCurrentAttribs.glColor4f(material.colorRed(), material.colorGreen(), material.colorBlue(), material.colorAlpha());
|
||||
}
|
||||
|
||||
private abstract static class PaneRenderer extends TileEntitySpecialRenderer {
|
||||
|
||||
void begin(double x, double y, double z) {
|
||||
bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
GLSMMatrices.glTranslated(x, y, z);
|
||||
GLSMEnable.glDisable(GL11.GL_CULL_FACE);
|
||||
GLSMEnable.glEnable(GL11.GL_BLEND);
|
||||
}
|
||||
|
||||
static void end() {
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLSMEnable.glDisable(GL11.GL_BLEND);
|
||||
GLSMEnable.glEnable(GL11.GL_CULL_FACE);
|
||||
GLSMMatrices.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Provider extends PaneRenderer implements TesrMeshProvider {
|
||||
|
||||
private static final TesrMaterial[] MATERIALS = {
|
||||
TesrMaterial.builder().color(1f, 0f, 0f, 0.5f).translucent().noCull().build(),
|
||||
TesrMaterial.builder().color(0.5f, 0f, 0.5f, 1f).additive().noCull().build() };
|
||||
private static final Object[] KEYS = { new Object(), new Object() };
|
||||
|
||||
private static int kind(TileEntity te) {
|
||||
return ((TileTesrTranslucentTest) te).variant() == 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object angelica$meshKey(TileEntity te) {
|
||||
return KEYS[kind(te)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$build(TesrMeshSink sink, TileEntity te) {
|
||||
pane(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, MATERIALS[kind(te)]), HALF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(MATERIALS[kind(te)]);
|
||||
drawPane(HALF);
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Model extends PaneRenderer {
|
||||
|
||||
private static final TesrMaterial TRANSLUCENT_GREEN = TesrMaterial.builder().color(0f, 1f, 0f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial TRANSLUCENT_YELLOW = TesrMaterial.builder().color(1f, 1f, 0f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial TRANSLUCENT_MAGENTA = TesrMaterial.builder().color(1f, 0f, 1f, 0.5f).translucent().build();
|
||||
private static final TesrMaterial ADDITIVE_CYAN = TesrMaterial.builder().color(0f, 0.6f, 0.6f, 1f).additive().build();
|
||||
|
||||
private final ModelBase model = new ModelBase() {};
|
||||
private final ModelRenderer base = new ModelRenderer(model, 0, 0);
|
||||
private final ModelRenderer pane = paneAt(0f);
|
||||
private final ModelRenderer paneFar = paneAt(0.16f);
|
||||
private final ModelRenderer paneNear = paneAt(-0.16f);
|
||||
|
||||
public Model() {
|
||||
base.addBox(-1f, 7f, -1f, 2, 1, 2);
|
||||
}
|
||||
|
||||
private ModelRenderer paneAt(float z) {
|
||||
final ModelRenderer renderer = new ModelRenderer(model, 0, 0);
|
||||
renderer.addBox(-7f, -7f, z, 14, 14, 0);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
final float scale = 1f / 16f;
|
||||
bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
GLSMMatrices.glPushMatrix();
|
||||
GLSMMatrices.glTranslated(x + 0.5, y + 0.5, z + 0.5);
|
||||
GLSMCurrentAttribs.glColor4f(1f, 1f, 1f, 1f);
|
||||
base.render(scale);
|
||||
GLSMEnable.glEnable(GL11.GL_BLEND);
|
||||
switch (((TileTesrTranslucentTest) te).variant()) {
|
||||
case 0 -> {
|
||||
apply(TRANSLUCENT_GREEN);
|
||||
pane.render(scale);
|
||||
}
|
||||
case 1 -> {
|
||||
apply(TRANSLUCENT_YELLOW);
|
||||
GLSMMatrices.glTranslated(-0.5, -0.5, -0.5);
|
||||
drawPane(HALF);
|
||||
}
|
||||
default -> {
|
||||
apply(TRANSLUCENT_MAGENTA);
|
||||
paneFar.render(scale);
|
||||
apply(ADDITIVE_CYAN);
|
||||
pane.render(scale);
|
||||
apply(TRANSLUCENT_GREEN);
|
||||
paneNear.render(scale);
|
||||
}
|
||||
}
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Decal extends PaneRenderer implements TesrMeshProvider {
|
||||
|
||||
private static final TesrMaterial WRITER = TesrMaterial.builder().color(1f, 1f, 1f, 0.5f).translucent().noCull().build();
|
||||
private static final TesrMaterial DECAL = TesrMaterial.builder().color(0f, 0f, 0f, 0.8f).translucent().depthEqual().noCull().build();
|
||||
private static final Object KEY = new Object();
|
||||
private static final double INNER = HALF / 2;
|
||||
|
||||
private static void writer(Tessellator t) {
|
||||
rect(t, -HALF, INNER, HALF, HALF);
|
||||
rect(t, -HALF, -HALF, HALF, -INNER);
|
||||
rect(t, -HALF, -INNER, -INNER, INNER);
|
||||
rect(t, INNER, -INNER, HALF, INNER);
|
||||
pane(t, INNER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object angelica$meshKey(TileEntity te) {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$build(TesrMeshSink sink, TileEntity te) {
|
||||
writer(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, WRITER));
|
||||
pane(sink.angelica$bucket(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND, DECAL), INNER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(WRITER);
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
writer(t);
|
||||
t.draw();
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_EQUAL);
|
||||
apply(DECAL);
|
||||
drawPane(INNER);
|
||||
GLSMDepthStencilAlpha.glDepthFunc(GL11.GL_LEQUAL);
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Glow extends PaneRenderer {
|
||||
|
||||
private static final TesrMaterial TRANSLUCENT_ORANGE = TesrMaterial.builder().color(1f, 0.5f, 0f, 0.6f).translucent().build();
|
||||
|
||||
@Override
|
||||
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTicks) {
|
||||
begin(x, y, z);
|
||||
apply(TRANSLUCENT_ORANGE);
|
||||
GLSMDepthStencilAlpha.glDepthMask(false);
|
||||
drawPane(HALF);
|
||||
GLSMDepthStencilAlpha.glDepthMask(true);
|
||||
end();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -13,6 +13,7 @@ import com.gtnewhorizons.angelica.rendering.TextureSamplers;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import com.gtnewhorizons.angelica.rendering.translucency.TranslucencyLayers;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import com.gtnewhorizons.angelica.rendering.voxelization.SdlShadowVoxelizationSink;
|
||||
@@ -196,6 +197,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
if (program != null) {
|
||||
program.bind();
|
||||
program.getInterface().setupState(pass);
|
||||
if (pass == AngelicaRenderPassConfiguration.TRANSLUCENT_PASS) TranslucencyLayers.INSTANCE.beginTerrainSplit(program.handle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +206,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
if (irisProgram != null) {
|
||||
endIrisPass(pass);
|
||||
} else {
|
||||
if (pass == AngelicaRenderPassConfiguration.TRANSLUCENT_PASS) TranslucencyLayers.INSTANCE.endTerrainSplit();
|
||||
super.end(pass);
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 157 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user