mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab3bf17696 | ||
|
|
cfc6e12e00 | ||
|
|
8537835d0c | ||
|
|
8e1809622c | ||
|
|
f81ee4b03a |
No files matched your search
@@ -34,6 +34,7 @@ import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -104,6 +105,8 @@ public class GLContextState {
|
||||
public boolean dirtyNormalAttrib;
|
||||
public boolean dirtyTexCoordAttrib;
|
||||
public boolean dirtyLightmapAttrib = true;
|
||||
public long entityIds = InstancedAttribs.DEFAULT_ENTITY_IDS;
|
||||
public boolean dirtyEntityIdsAttrib = true;
|
||||
public boolean unit23TexCoordSetDuringDraw = false;
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
@@ -236,6 +239,8 @@ public class GLContextState {
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final int[] uniformBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public final int[] storageBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public PixelStoreState pixelUnpackState = PixelStoreState.DEFAULT;
|
||||
public PixelStoreState pixelPackState = PixelStoreState.DEFAULT;
|
||||
public final int[] clientAttribSavedMask = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
@@ -245,7 +250,6 @@ public class GLContextState {
|
||||
public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public float maxPointSize = Float.NaN;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
@@ -25,6 +26,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -68,6 +70,7 @@ import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.LineQuads;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorEntityIds;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
@@ -93,7 +96,6 @@ import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.Drawable;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
@@ -104,6 +106,8 @@ import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
import org.lwjgl.opengl.KHRDebugCallback;
|
||||
@@ -328,6 +332,7 @@ public class GLStateManager {
|
||||
public static final int MAX_PROJECTION_STACK_DEPTH = 4;
|
||||
public static final int MAX_TEXTURE_STACK_DEPTH = 4;
|
||||
public static final int MAX_CLIP_PLANES = 8;
|
||||
public static final int INDEXED_BUFFER_SLOTS = 64;
|
||||
public static final int MAX_TEXTURE_UNITS = RENDER_BACKEND.getInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
|
||||
public static final int MAX_LEGACY_TEXTURE_UNITS = Math.min(VertexKey.MAX_UNITS, MAX_TEXTURE_UNITS);
|
||||
public static final int MAX_TEXTURE_COORDS = Math.min(VAOManager.TEX_COORD_UNITS, MAX_TEXTURE_UNITS);
|
||||
@@ -394,6 +399,26 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setEntityIds(int entity, int blockEntity, int item) {
|
||||
if (!isMainThread()) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
final long ids = InstancedAttribs.packEntityInfo(entity, blockEntity, item);
|
||||
if (ids == glCtx.entityIds) return;
|
||||
TessellatorEntityIds.onIdsChanged(glCtx.entityIds, ids);
|
||||
glCtx.entityIds = ids;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
|
||||
public static void flushEntityIds(GLContextState glCtx) {
|
||||
if (!glCtx.dirtyEntityIdsAttrib || glCtx.activeProgram == 0) return;
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
if (handler == null || !handler.wantsEntityIds()) return;
|
||||
final long ids = glCtx.entityIds;
|
||||
RENDER_BACKEND.vertexAttrib4f(ImmediateExtendedAttribHandler.LOC_ENTITY_IDS,
|
||||
InstancedAttribs.normalizedId(ids, 0), InstancedAttribs.normalizedId(ids, 16), InstancedAttribs.normalizedId(ids, 32), 0.0f);
|
||||
glCtx.dirtyEntityIdsAttrib = false;
|
||||
}
|
||||
|
||||
|
||||
public static int getMaxBoundTextureUnit() {
|
||||
return ctx().maxBoundTextureUnit;
|
||||
@@ -415,6 +440,7 @@ public class GLStateManager {
|
||||
|
||||
// This setting varies depending on driver, so it gets queried at runtime
|
||||
public static int DEFAULT_DRAW_BUFFER = GL11.GL_BACK;
|
||||
private static float maxPointSize;
|
||||
|
||||
private static volatile Thread CurrentThread = MainThread;
|
||||
@Setter @Getter private static boolean runningSplash = true;
|
||||
@@ -517,18 +543,6 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static float maxPointSize(GLContextState glCtx) {
|
||||
if (Float.isNaN(glCtx.maxPointSize)) {
|
||||
final FloatBuffer range = glCtx.queryScratch;
|
||||
range.clear();
|
||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
final float smooth = range.get(1);
|
||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||
glCtx.maxPointSize = Math.max(smooth, range.get(1));
|
||||
}
|
||||
return glCtx.maxPointSize;
|
||||
}
|
||||
|
||||
private static <T extends CowStateStack<?>> T mod(T s) {
|
||||
s.beforeModify();
|
||||
return s;
|
||||
@@ -777,6 +791,13 @@ public class GLStateManager {
|
||||
initCallback.run();
|
||||
}
|
||||
|
||||
final FloatBuffer range = glCtx.queryScratch;
|
||||
range.clear();
|
||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||
final float smooth = range.get(1);
|
||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||
maxPointSize = Math.max(smooth, range.get(1));
|
||||
|
||||
// Drain any pending GL errors from initialization. In core profile, some legacy queries may generate GL_INVALID_ENUM. The splash thread inherits the
|
||||
// DrawableGL context and its error state, so stale errors here would cause SplashProgress.checkGLError() to fail.
|
||||
int err;
|
||||
@@ -1482,7 +1503,7 @@ public class GLStateManager {
|
||||
case GL11.GL_LINE_WIDTH -> glCtx.lineState.getWidth();
|
||||
case GL11.GL_POINT_SIZE -> glCtx.pointState.getSize();
|
||||
case GL14.GL_POINT_SIZE_MIN -> glCtx.pointState.getMinSize();
|
||||
case GL14.GL_POINT_SIZE_MAX -> Float.isNaN(glCtx.pointState.getMaxSize()) ? maxPointSize(glCtx) : glCtx.pointState.getMaxSize();
|
||||
case GL14.GL_POINT_SIZE_MAX -> Float.isNaN(glCtx.pointState.getMaxSize()) ? maxPointSize : glCtx.pointState.getMaxSize();
|
||||
case GL14.GL_POINT_FADE_THRESHOLD_SIZE -> glCtx.pointState.getFadeThreshold();
|
||||
default -> {
|
||||
final int desc = isCachingEnabled() ? LegacyQueries.desc(pname) : 0;
|
||||
@@ -4825,6 +4846,7 @@ public class GLStateManager {
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
glCtx.dirtyLightmapAttrib = true;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
|
||||
private static void applyPolygonOffset(GLContextState glCtx) {
|
||||
@@ -7267,6 +7289,10 @@ public class GLStateManager {
|
||||
e.setValue(0);
|
||||
RENDER_BACKEND.bindBuffer(e.getIntKey(), 0);
|
||||
}
|
||||
for (int i = 0; i < INDEXED_BUFFER_SLOTS; i++) {
|
||||
if (glCtx.uniformBufferBindings[i] == buffer) glCtx.uniformBufferBindings[i] = 0;
|
||||
if (glCtx.storageBufferBindings[i] == buffer) glCtx.storageBufferBindings[i] = 0;
|
||||
}
|
||||
writeMappedBuffers.remove(buffer);
|
||||
|
||||
glCtx.vaos.onDeleteBuffer(buffer);
|
||||
@@ -7881,7 +7907,9 @@ public class GLStateManager {
|
||||
public static void glBindBufferBase(int target, int index, int buffer) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
final GLContextState glCtx = ctx();
|
||||
trackBufferBinding(glCtx, target, buffer);
|
||||
trackIndexedBufferBinding(glCtx, target, index, buffer);
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
@@ -7891,13 +7919,35 @@ public class GLStateManager {
|
||||
public static void glBindBufferRange(int target, int index, int buffer, long offset, long size) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
final GLContextState glCtx = ctx();
|
||||
trackBufferBinding(glCtx, target, buffer);
|
||||
trackIndexedBufferBinding(glCtx, target, index, buffer);
|
||||
RENDER_BACKEND.bindBufferRange(target, index, buffer, offset, size);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] indexedBufferBindings(GLContextState glCtx, int target, int index) {
|
||||
if (index < 0 || index >= INDEXED_BUFFER_SLOTS) return null;
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) return glCtx.uniformBufferBindings;
|
||||
if (target == GL43.GL_SHADER_STORAGE_BUFFER) return glCtx.storageBufferBindings;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void trackIndexedBufferBinding(GLContextState glCtx, int target, int index, int buffer) {
|
||||
final int[] bindings = indexedBufferBindings(glCtx, target, index);
|
||||
if (bindings != null) bindings[index] = buffer;
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
if (isCachingEnabled()) {
|
||||
final int[] bindings = indexedBufferBindings(ctx(), target, index);
|
||||
if (bindings != null) return bindings[index];
|
||||
}
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
public static int glGetUniformBlockIndex(int program, CharSequence name) { return RENDER_BACKEND.getUniformBlockIndex(program, name); }
|
||||
public static void glUniformBlockBinding(int program, int blockIndex, int binding) { RENDER_BACKEND.uniformBlockBinding(program, blockIndex, binding); }
|
||||
public static void glBindFragDataLocation(int program, int colorNumber, CharSequence name) { RENDER_BACKEND.bindFragDataLocation(program, colorNumber, name); }
|
||||
@@ -8656,7 +8706,9 @@ public class GLStateManager {
|
||||
return ctx().imageUnits.get(unit);
|
||||
}
|
||||
|
||||
private static int imageBindingOrBackend(int pname, int index) {
|
||||
private static int indexedIntegerOrBackend(int pname, int index) {
|
||||
if (pname == GL31.GL_UNIFORM_BUFFER_BINDING) return getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index);
|
||||
if (pname == GL43.GL_SHADER_STORAGE_BUFFER_BINDING) return getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, index);
|
||||
final ImageUnitBinding binding = !isCachingEnabled() ? null : ctx().imageUnits.get(index);
|
||||
if (binding == null) return RENDER_BACKEND.getIntegerIndexed(pname, index);
|
||||
return switch (pname) {
|
||||
@@ -8671,11 +8723,11 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGetInteger(int pname, int index) {
|
||||
return imageBindingOrBackend(pname, index);
|
||||
return indexedIntegerOrBackend(pname, index);
|
||||
}
|
||||
|
||||
public static int glGetIntegeri(int pname, int index) {
|
||||
return imageBindingOrBackend(pname, index);
|
||||
return indexedIntegerOrBackend(pname, index);
|
||||
}
|
||||
|
||||
public static void glGetIntegeri(int pname, int index, IntBuffer params) {
|
||||
@@ -8683,11 +8735,11 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glGetIntegeri_v(int pname, int index, IntBuffer params) {
|
||||
params.put(params.position(), imageBindingOrBackend(pname, index));
|
||||
params.put(params.position(), indexedIntegerOrBackend(pname, index));
|
||||
}
|
||||
|
||||
public static void glGetBooleani_v(int pname, int index, ByteBuffer params) {
|
||||
params.put(params.position(), (byte) (imageBindingOrBackend(pname, index) != 0 ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(params.position(), (byte) (indexedIntegerOrBackend(pname, index) != 0 ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
|
||||
public static void glGetBooleani(int pname, int index, ByteBuffer params) {
|
||||
|
||||
@@ -556,7 +556,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
return GLStateManager.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
|
||||
@@ -21,7 +21,11 @@ public final class BackendOptions {
|
||||
}
|
||||
|
||||
public static String sdlGpuDriver() {
|
||||
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : configDriver;
|
||||
return sdlGpuDriver(configDriver);
|
||||
}
|
||||
|
||||
public static String sdlGpuDriver(String driver) {
|
||||
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : driver;
|
||||
}
|
||||
|
||||
public static boolean configSdlGpu() {
|
||||
|
||||
@@ -1,55 +1,96 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public final class BackendStartGuard {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/BackendStartGuard");
|
||||
|
||||
private static File pending;
|
||||
static final int STRIKE_LIMIT = 3;
|
||||
|
||||
private static File marker;
|
||||
private static File verified;
|
||||
private static String key;
|
||||
private static int strikes;
|
||||
private static boolean pending;
|
||||
private static boolean tripped;
|
||||
|
||||
private BackendStartGuard() {}
|
||||
|
||||
public static boolean begin(File marker, boolean sdlGpuConfigured, boolean flagForced) {
|
||||
pending = null;
|
||||
tripped = false;
|
||||
if (!sdlGpuConfigured || flagForced) {
|
||||
public static boolean begin(File dir, boolean sdlGpuConfigured, boolean flagForced, int requirementsRevision, String requestedDriver) {
|
||||
marker = new File(dir, "sdlgpu-starting");
|
||||
verified = new File(dir, "sdlgpu-verified");
|
||||
key = requirementsRevision + "/" + requestedDriver;
|
||||
pending = tripped = false;
|
||||
if (flagForced) return false;
|
||||
if (!sdlGpuConfigured) {
|
||||
delete(marker);
|
||||
return false;
|
||||
}
|
||||
if (marker.isFile()) {
|
||||
if (key.equals(read(verified))) return false;
|
||||
final String count = read(marker);
|
||||
final Integer parsed = count == null ? null : Ints.tryParse(count.trim());
|
||||
strikes = parsed == null ? 0 : parsed;
|
||||
if (strikes >= STRIKE_LIMIT) {
|
||||
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; resetting the render backend to OpenGL", strikes);
|
||||
delete(marker);
|
||||
tripped = true;
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
final File dir = marker.getParentFile();
|
||||
if (dir != null && !dir.isDirectory() && !dir.mkdirs()) throw new IOException("cannot create " + dir);
|
||||
if (marker.createNewFile()) pending = marker;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write {}: {}", marker, e.toString());
|
||||
if (strikes == STRIKE_LIMIT - 1) {
|
||||
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; one more and the render backend switches to OpenGL", strikes);
|
||||
} else if (strikes > 0) {
|
||||
LOGGER.info("The last start ended while bringing up the SDL GPU device");
|
||||
}
|
||||
pending = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void finish() {
|
||||
if (pending == null) return;
|
||||
delete(pending);
|
||||
pending = null;
|
||||
public static void arm() {
|
||||
if (pending) write(marker, Integer.toString(strikes + 1));
|
||||
}
|
||||
|
||||
public static void disarm() {
|
||||
if (pending) delete(marker);
|
||||
}
|
||||
|
||||
public static void latch() {
|
||||
if (!pending) return;
|
||||
pending = false;
|
||||
write(verified, key);
|
||||
LOGGER.info("SDL GPU booted; the startup guard stays off until the requirements revision or driver changes");
|
||||
}
|
||||
|
||||
public static boolean tripped() {
|
||||
return tripped;
|
||||
}
|
||||
|
||||
private static void delete(File marker) {
|
||||
if (marker.isFile() && !marker.delete()) {
|
||||
LOGGER.warn("Could not delete {}", marker);
|
||||
private static String read(File file) {
|
||||
try {
|
||||
return file.isFile() ? new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8) : null;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not read {}: {}", file, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void write(File file, String content) {
|
||||
try {
|
||||
Files.createDirectories(file.toPath().getParent());
|
||||
Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write {}: {}", file, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void delete(File file) {
|
||||
if (file.isFile() && !file.delete()) LOGGER.warn("Could not delete {}", file);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ public final class InstancedAttribs {
|
||||
public static final int OFFSET_LIGHTMAP = HEAD_SIZE;
|
||||
public static final int STRIDE = 72;
|
||||
|
||||
public static final long DEFAULT_ENTITY_IDS = packEntityInfo(-1, 0, -1);
|
||||
|
||||
public static void writeHead(long ptr, float[] colMajor, int off, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
writeRows(ptr, colMajor, off);
|
||||
writeTail(ptr, colorABGR, overlayABGR, entityInfo);
|
||||
@@ -60,13 +62,21 @@ public final class InstancedAttribs {
|
||||
return id == 0xFFFF ? -1 : id;
|
||||
}
|
||||
|
||||
public static float normalizedId(long info, int shift) {
|
||||
return ((info >>> shift) & 0xFFFF) / 65535.0f;
|
||||
}
|
||||
|
||||
public static void pointHead(long base, int stride) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
|
||||
}
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_UNSIGNED_SHORT, stride, base + OFFSET_ENTITY);
|
||||
pointEntityIds(LOC_ENTITY, stride, base + OFFSET_ENTITY);
|
||||
}
|
||||
|
||||
public static void pointEntityIds(int location, int stride, long offset) {
|
||||
GLStateManager.glVertexAttribPointer(location, 4, GL11.GL_UNSIGNED_SHORT, true, stride, offset);
|
||||
}
|
||||
|
||||
public static void pointTemplate(long base) {
|
||||
|
||||
@@ -139,6 +139,7 @@ public final class ShaderManager {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
GLStateManager.flushEntityIds(glCtx);
|
||||
|
||||
// Handle FFP & Iris uniforms
|
||||
final int currentProgramId = glCtx.activeProgram;
|
||||
|
||||
+10
@@ -3,15 +3,18 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
int EXT_STRIDE = 12;
|
||||
int ENTITY_IDS_STRIDE = 8;
|
||||
int LOC_MC_ENTITY = 11;
|
||||
int LOC_MID_TEX = 12;
|
||||
int LOC_TANGENT = 13;
|
||||
int LOC_MIDBLOCK = 14;
|
||||
int LOC_ENTITY_IDS = 15;
|
||||
|
||||
int RAW_NORMAL_INDEX = 6;
|
||||
|
||||
@@ -19,6 +22,8 @@ public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
boolean wantsTangent();
|
||||
|
||||
boolean wantsEntityIds();
|
||||
|
||||
boolean wantsExtendedCapture();
|
||||
|
||||
boolean wantsFaceNormals();
|
||||
@@ -65,4 +70,9 @@ public interface ImmediateExtendedAttribHandler {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_TANGENT);
|
||||
GLStateManager.glVertexAttribPointer(LOC_TANGENT, 4, GL11.GL_BYTE, true, stride, baseOffset + 8);
|
||||
}
|
||||
|
||||
static void setupEntityIdsPointer(long offset, int stride) {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_ENTITY_IDS);
|
||||
InstancedAttribs.pointEntityIds(LOC_ENTITY_IDS, stride, offset);
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,25 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
public final class StreamingVaos {
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE;
|
||||
|
||||
static final int MID_TEX_AND_TANGENT = 1;
|
||||
static final int ENTITY_IDS = 2;
|
||||
private static final int EXTENDED_LAYOUTS = MID_TEX_AND_TANGENT | ENTITY_IDS;
|
||||
|
||||
final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
// An extended draw always carries at least one layout, so row 0 is layout 1
|
||||
private final int[][] extendedPersistentVAOs = new int[EXTENDED_LAYOUTS][FORMAT_COUNT];
|
||||
private final int[][] extendedOrphanVAOs = new int[EXTENDED_LAYOUTS][FORMAT_COUNT];
|
||||
|
||||
final int[] lineQuadPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] lineQuadOrphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
int[] extendedPersistent(int layout) {
|
||||
return extendedPersistentVAOs[layout - 1];
|
||||
}
|
||||
|
||||
int[] extendedOrphan(int layout) {
|
||||
return extendedOrphanVAOs[layout - 1];
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutLong;
|
||||
|
||||
/**
|
||||
* Entity, block entity, and item ids of the vertices in the main Tessellator batch.
|
||||
*/
|
||||
public final class TessellatorEntityIds {
|
||||
|
||||
private static Tessellator owner;
|
||||
private static int runCount;
|
||||
private static int[] runStarts = new int[8];
|
||||
private static long[] runIds = new long[8];
|
||||
|
||||
private TessellatorEntityIds() {}
|
||||
|
||||
public static void onIdsChanged(long previous, long next) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
if (tess == null || !tess.isDrawing || tess.vertexCount == 0) return;
|
||||
final int start = tess.vertexCount;
|
||||
if (owner != tess) {
|
||||
owner = tess;
|
||||
runCount = 0;
|
||||
}
|
||||
if (runCount == 0) addRun(0, previous);
|
||||
if (runStarts[runCount - 1] == start) {
|
||||
runIds[runCount - 1] = next;
|
||||
if (runCount > 1 && runIds[runCount - 2] == next) runCount--;
|
||||
} else {
|
||||
addRun(start, next);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onReset(Tessellator tess) {
|
||||
if (tess != owner) return;
|
||||
owner = null;
|
||||
runCount = 0;
|
||||
}
|
||||
|
||||
public static boolean hasIdChanges(Tessellator tess) {
|
||||
return tess == owner && runCount > 1;
|
||||
}
|
||||
|
||||
static int runsFor(Tessellator tess, int vertexCount) {
|
||||
if (tess != owner) return 0;
|
||||
int runs = runCount;
|
||||
while (runs > 0 && runStarts[runs - 1] >= vertexCount) runs--;
|
||||
return runs;
|
||||
}
|
||||
|
||||
static long firstRunIds() {
|
||||
return runIds[0];
|
||||
}
|
||||
|
||||
static void write(long dst, int stride, int vertexCount, int runs) {
|
||||
for (int run = 0; run < runs; run++) {
|
||||
final int end = run + 1 < runs ? runStarts[run + 1] : vertexCount;
|
||||
final long ids = runIds[run];
|
||||
for (int vertex = runStarts[run]; vertex < end; vertex++) {
|
||||
memPutLong(dst + (long) vertex * stride, ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addRun(int start, long ids) {
|
||||
if (runCount == runStarts.length) {
|
||||
runStarts = Arrays.copyOf(runStarts, runCount * 2);
|
||||
runIds = Arrays.copyOf(runIds, runCount * 2);
|
||||
}
|
||||
runStarts[runCount] = start;
|
||||
runIds[runCount] = ids;
|
||||
runCount++;
|
||||
}
|
||||
}
|
||||
+64
-28
@@ -130,20 +130,34 @@ public class TessellatorStreamingDrawer {
|
||||
final int extPrim = extHandler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount);
|
||||
final boolean wantsExt = extHandler != null && extHandler.wantsExtended();
|
||||
final boolean doExt = extPrim != 0 && tess.hasTexture && wantsExt;
|
||||
if (doExt) {
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
final int idRuns = TessellatorEntityIds.runsFor(tess, vertexCount);
|
||||
final boolean doIds = idRuns > 1 && extHandler != null && extHandler.wantsEntityIds() && !LineQuads.isLineMode(tess.drawMode);
|
||||
final GLContextState swappedCtx = idRuns >= 1 && !doIds ? GLStateManager.ctx() : null;
|
||||
final long restoreIds = swappedCtx != null ? swapEntityIds(swappedCtx, TessellatorEntityIds.firstRunIds()) : 0L;
|
||||
try {
|
||||
if (doExt || doIds) {
|
||||
final int layout = (doExt ? StreamingVaos.MID_TEX_AND_TANGENT : 0) | (doIds ? StreamingVaos.ENTITY_IDS : 0);
|
||||
final int combinedStride = combinedStride(format, layout);
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
long extAddress = extScratchAddress + vertexSize;
|
||||
if (doExt) {
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extAddress, combinedStride);
|
||||
extAddress += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
}
|
||||
if (doIds) TessellatorEntityIds.write(extAddress, combinedStride, vertexCount, idRuns);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, layout, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
if (swappedCtx != null) swapEntityIds(swappedCtx, restoreIds);
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -253,7 +267,7 @@ public class TessellatorStreamingDrawer {
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedStride = combinedStride(format, StreamingVaos.MID_TEX_AND_TANGENT);
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
@@ -262,7 +276,7 @@ public class TessellatorStreamingDrawer {
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
uploadAndDrawExtended(extScratch, flags, format, StreamingVaos.MID_TEX_AND_TANGENT, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -458,11 +472,11 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int layout, int combinedStride, int drawMode, int vertexCount) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos.persistentVAOs, vaos.orphanVAOs, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format, layout);
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
@@ -471,9 +485,9 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(vaos.extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedPersistent(layout)[flags]);
|
||||
} else {
|
||||
GLStateManager.glBindVertexArray(vaos.extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedOrphan(layout)[flags]);
|
||||
orphanBuffers[flags].upload(combined);
|
||||
firstVertex = 0;
|
||||
}
|
||||
@@ -482,14 +496,14 @@ public class TessellatorStreamingDrawer {
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureExtendedVAOs(StreamingVaos vaos, int flags, VertexFormat format) {
|
||||
final int[] extendedOrphanVAOs = vaos.extendedOrphanVAOs;
|
||||
final int[] extendedPersistentVAOs = vaos.extendedPersistentVAOs;
|
||||
private static void ensureExtendedVAOs(StreamingVaos vaos, int flags, VertexFormat format, int layout) {
|
||||
final int[] extendedOrphanVAOs = vaos.extendedOrphan(layout);
|
||||
final int[] extendedPersistentVAOs = vaos.extendedPersistent(layout);
|
||||
if (extendedOrphanVAOs[flags] == 0) {
|
||||
extendedOrphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
setupExtendedBufferState(format, layout);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
@@ -498,20 +512,42 @@ public class TessellatorStreamingDrawer {
|
||||
extendedPersistentVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, persistentBuffer.getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
setupExtendedBufferState(format, layout);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format) {
|
||||
final int combinedStride = format.getVertexSize() + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
private static long swapEntityIds(GLContextState glCtx, long ids) {
|
||||
final long previous = glCtx.entityIds;
|
||||
if (previous != ids) {
|
||||
glCtx.entityIds = ids;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
return previous;
|
||||
}
|
||||
|
||||
private static int combinedStride(VertexFormat format, int layout) {
|
||||
int stride = format.getVertexSize();
|
||||
if ((layout & StreamingVaos.MID_TEX_AND_TANGENT) != 0) stride += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
if ((layout & StreamingVaos.ENTITY_IDS) != 0) stride += ImmediateExtendedAttribHandler.ENTITY_IDS_STRIDE;
|
||||
return stride;
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format, int layout) {
|
||||
final int combinedStride = combinedStride(format, layout);
|
||||
long offset = 0;
|
||||
for (final VertexFormatElement element : format.elementsArray) {
|
||||
element.setupBufferState(offset, combinedStride);
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
if ((layout & StreamingVaos.MID_TEX_AND_TANGENT) != 0) {
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
offset += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
}
|
||||
if ((layout & StreamingVaos.ENTITY_IDS) != 0) {
|
||||
ImmediateExtendedAttribHandler.setupEntityIdsPointer(offset, combinedStride);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureExtScratch(int requiredBytes) {
|
||||
|
||||
+13
@@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -61,6 +62,18 @@ public class GLSM_DeletedBufferBinding_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedIndexedUniformBufferIsNotRestoredFromCache() {
|
||||
final int index = 3;
|
||||
final int ubo = makeBoundBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
GLStateManager.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, index, ubo);
|
||||
assertEquals(ubo, GLStateManager.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index));
|
||||
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
assertEquals(0, GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_BINDING, index));
|
||||
assertEquals(0, GLStateManager.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedElementArrayBufferDoesNotBlockClientDrawElements() {
|
||||
final int ebo = makeBoundBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
+38
-39
@@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -12,54 +11,54 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BackendStartGuardTest {
|
||||
|
||||
private static File marker(Path dir) {
|
||||
return dir.resolve("angelica").resolve("sdlgpu-starting").toFile();
|
||||
@TempDir
|
||||
Path dir;
|
||||
|
||||
private boolean boot() {
|
||||
return boot(1);
|
||||
}
|
||||
|
||||
private boolean boot(int revision) {
|
||||
return BackendStartGuard.begin(dir.toFile(), true, false, revision, "");
|
||||
}
|
||||
|
||||
private File marker() {
|
||||
return dir.resolve("sdlgpu-starting").toFile();
|
||||
}
|
||||
|
||||
@Test
|
||||
void firstStartCreatesTheMarkerAndFinishRemovesIt(@TempDir Path dir) {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
assertTrue(marker.isFile());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker.exists());
|
||||
void bootsThatReachTheBackendNeverCountAsStrikes() {
|
||||
for (int i = 0; i < BackendStartGuard.STRIKE_LIMIT; i++) {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
BackendStartGuard.disarm();
|
||||
}
|
||||
assertFalse(boot());
|
||||
}
|
||||
|
||||
@Test
|
||||
void leftoverMarkerTripsOnceAndIsRemoved(@TempDir Path dir) {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertTrue(BackendStartGuard.begin(marker, true, false));
|
||||
void thirdDeathInTheDeviceWindowResets() {
|
||||
for (int i = 0; i < BackendStartGuard.STRIKE_LIMIT; i++) {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
}
|
||||
assertTrue(boot());
|
||||
assertTrue(BackendStartGuard.tripped());
|
||||
assertFalse(marker.exists());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker.exists());
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker().exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void openGlConfigWritesNoMarkerAndClearsAStaleOne(@TempDir Path dir) throws IOException {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, false, false));
|
||||
assertFalse(marker.exists());
|
||||
assertTrue(marker.getParentFile().mkdirs());
|
||||
assertTrue(marker.createNewFile());
|
||||
assertFalse(BackendStartGuard.begin(marker, false, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
assertFalse(marker.exists());
|
||||
}
|
||||
void latchStopsArmingUntilTheRequirementsRevisionChanges() {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
BackendStartGuard.disarm();
|
||||
BackendStartGuard.latch();
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
assertFalse(marker().exists());
|
||||
|
||||
@Test
|
||||
void forcedFlagNeverTripsOrWrites(@TempDir Path dir) throws IOException {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, true));
|
||||
assertFalse(marker.exists());
|
||||
assertTrue(marker.getParentFile().mkdirs());
|
||||
assertTrue(marker.createNewFile());
|
||||
assertFalse(BackendStartGuard.begin(marker, true, true));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
assertFalse(boot(2));
|
||||
BackendStartGuard.arm();
|
||||
assertTrue(marker().isFile());
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLSMHooks.pendingProgramSelection = null;
|
||||
GLSMHooks.blendHandler = null;
|
||||
log.clear();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
FfpFixture.EntityIdInstances.delete();
|
||||
FfpFixture.resetFfpState();
|
||||
CubeParityFixture.disableDirectionalLight();
|
||||
CubeParityFixture.deleteResources();
|
||||
@@ -196,14 +196,14 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
FfpFixture.IntegerInstances.build(0.66f);
|
||||
FfpFixture.EntityIdInstances.build(0.66f);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
FfpFixture.IntegerInstances.draw();
|
||||
FfpFixture.EntityIdInstances.draw();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced ivec4 draw must not raise a GL error");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced entity id draw must not raise a GL error");
|
||||
|
||||
assertPixel(51, 50, 255, 0, 0, "entity -1 must render red");
|
||||
assertPixel(150, 50, 0, 255, 0, "entity 7 must render green");
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.EntityIdsOnlyHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
@GLCompatTest
|
||||
class TessellatorEntityIdsGLTest {
|
||||
|
||||
private static final int[] GREEN = { 0, 255, 0, 255 };
|
||||
private static final int[] BLUE = { 0, 0, 255, 255 };
|
||||
|
||||
private ImmediateExtendedAttribHandler previousHandler;
|
||||
private int program;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
previousHandler = EntityIdsOnlyHandler.install();
|
||||
program = GlTestSupport.program(TessellatorEntityIdsFixture.VERTEX, TessellatorEntityIdsFixture.FRAGMENT);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glViewport(0, 0, 8, 8);
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.glClearColor(0, 0, 0, 1);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLSMHooks.immediateExtendedHandler = previousHandler;
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedMidBatchApplyToTheVerticesAddedAfterTheChange() {
|
||||
TessellatorEntityIdsFixture.drawIdsChangedMidBatch();
|
||||
|
||||
assertColor(1, GREEN, "left quad was added under item 7");
|
||||
assertColor(6, BLUE, "right quad was added under item 50020");
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedAfterTheLastVertexLeaveTheBatchAlone() {
|
||||
TessellatorEntityIdsFixture.drawIdsChangedAfterTheLastVertex();
|
||||
|
||||
assertColor(1, GREEN, "every vertex was added under item 7");
|
||||
assertColor(6, GREEN, "every vertex was added under item 7");
|
||||
}
|
||||
|
||||
private static void assertColor(int x, int[] expected, String message) {
|
||||
final int[] pixel = FfpFixture.readPixel(x, 4);
|
||||
assertArrayEquals(expected, pixel, message + ", got " + Arrays.toString(pixel));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
|
||||
public final class EntityIdsOnlyHandler implements ImmediateExtendedAttribHandler {
|
||||
|
||||
public static ImmediateExtendedAttribHandler install() {
|
||||
final ImmediateExtendedAttribHandler previous = GLSMHooks.immediateExtendedHandler;
|
||||
GLSMHooks.immediateExtendedHandler = new EntityIdsOnlyHandler();
|
||||
GLStateManager.ctx().dirtyEntityIdsAttrib = true;
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override public boolean wantsExtended() { return false; }
|
||||
@Override public boolean wantsTangent() { return false; }
|
||||
@Override public boolean wantsEntityIds() { return GLStateManager.getActiveProgram() != 0; }
|
||||
@Override public boolean wantsExtendedCapture() { return false; }
|
||||
@Override public boolean wantsFaceNormals() { return false; }
|
||||
@Override public void writeFaceNormals(int[] rawBuffer, int vertexCount) {}
|
||||
@Override public void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride) {}
|
||||
@Override public void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride) {}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public final class FfpFixture {
|
||||
|
||||
private FfpFixture() {}
|
||||
|
||||
public static final class IntegerInstances {
|
||||
public static final class EntityIdInstances {
|
||||
|
||||
private static final int INSTANCES = 3;
|
||||
|
||||
@@ -31,16 +31,17 @@ public final class FfpFixture {
|
||||
layout(location = 5) in vec4 a_InstRow0;
|
||||
layout(location = 6) in vec4 a_InstRow1;
|
||||
layout(location = 7) in vec4 a_InstRow2;
|
||||
layout(location = 11) in uvec4 iris_Entity;
|
||||
layout(location = 11) in vec4 iris_InstEntity;
|
||||
flat out vec3 v_Color;
|
||||
void main() {
|
||||
""";
|
||||
|
||||
private static final String VS_TAIL = """
|
||||
gl_Position = instMV * vec4(a_Position, 1.0);
|
||||
if (iris_Entity.x == 65535u) { v_Color = vec3(1.0, 0.0, 0.0); }
|
||||
else if (iris_Entity.x == 7u) { v_Color = vec3(0.0, 1.0, 0.0); }
|
||||
else if (iris_Entity.x == 50020u) { v_Color = vec3(0.0, 0.0, 1.0); }
|
||||
int entity = int(round(iris_InstEntity.x * 65535.0));
|
||||
if (entity == 65535) { v_Color = vec3(1.0, 0.0, 0.0); }
|
||||
else if (entity == 7) { v_Color = vec3(0.0, 1.0, 0.0); }
|
||||
else if (entity == 50020) { v_Color = vec3(0.0, 0.0, 1.0); }
|
||||
else { v_Color = vec3(1.0, 0.0, 1.0); }
|
||||
}
|
||||
""";
|
||||
@@ -59,7 +60,7 @@ public final class FfpFixture {
|
||||
private static int templateVbo;
|
||||
private static int instanceVbo;
|
||||
|
||||
private IntegerInstances() {}
|
||||
private EntityIdInstances() {}
|
||||
|
||||
public static void build(float spacing) {
|
||||
program = buildProgram();
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class TessellatorEntityIdsFixture {
|
||||
|
||||
public static final String VERTEX = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = %d) in vec4 iris_Entity;
|
||||
flat out vec3 v_Color;
|
||||
void main() {
|
||||
gl_Position = vec4(a_Position, 1.0);
|
||||
int item = int(round(iris_Entity.z * 65535.0));
|
||||
v_Color = item == 7 ? vec3(0.0, 1.0, 0.0) : item == 50020 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
}
|
||||
""".formatted(ImmediateExtendedAttribHandler.LOC_ENTITY_IDS);
|
||||
|
||||
public static final String FRAGMENT = """
|
||||
#version 330 core
|
||||
flat in vec3 v_Color;
|
||||
out vec4 fragColor;
|
||||
void main() { fragColor = vec4(v_Color, 1.0); }
|
||||
""";
|
||||
|
||||
private TessellatorEntityIdsFixture() {}
|
||||
|
||||
public static void drawIdsChangedMidBatch() {
|
||||
draw(() -> {
|
||||
GLStateManager.setEntityIds(-1, 0, 7);
|
||||
startQuads();
|
||||
addQuad(-1.0f, 0.0f);
|
||||
GLStateManager.setEntityIds(-1, 0, 50020);
|
||||
addQuad(0.0f, 1.0f);
|
||||
GLStateManager.setEntityIds(-1, 0, -1);
|
||||
});
|
||||
}
|
||||
|
||||
public static void drawIdsChangedAfterTheLastVertex() {
|
||||
draw(() -> {
|
||||
GLStateManager.setEntityIds(-1, 0, 7);
|
||||
startQuads();
|
||||
addQuad(-1.0f, 1.0f);
|
||||
GLStateManager.setEntityIds(-1, 0, 50020);
|
||||
});
|
||||
}
|
||||
|
||||
private static void draw(Runnable fill) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final int[] savedRawBuffer = tess.rawBuffer;
|
||||
final int savedRawBufferSize = tess.rawBufferSize;
|
||||
final int savedRawBufferIndex = tess.rawBufferIndex;
|
||||
final int savedVertexCount = tess.vertexCount;
|
||||
final int savedDrawMode = tess.drawMode;
|
||||
final boolean savedIsDrawing = tess.isDrawing;
|
||||
final boolean savedHasTexture = tess.hasTexture;
|
||||
final boolean savedHasColor = tess.hasColor;
|
||||
final boolean savedHasNormals = tess.hasNormals;
|
||||
final boolean savedHasBrightness = tess.hasBrightness;
|
||||
try {
|
||||
fill.run();
|
||||
TessellatorStreamingDrawer.draw(tess);
|
||||
} finally {
|
||||
TessellatorEntityIds.onReset(tess);
|
||||
GLStateManager.setEntityIds(-1, 0, -1);
|
||||
tess.rawBuffer = savedRawBuffer;
|
||||
tess.rawBufferSize = savedRawBufferSize;
|
||||
tess.rawBufferIndex = savedRawBufferIndex;
|
||||
tess.vertexCount = savedVertexCount;
|
||||
tess.drawMode = savedDrawMode;
|
||||
tess.isDrawing = savedIsDrawing;
|
||||
tess.hasTexture = savedHasTexture;
|
||||
tess.hasColor = savedHasColor;
|
||||
tess.hasNormals = savedHasNormals;
|
||||
tess.hasBrightness = savedHasBrightness;
|
||||
}
|
||||
}
|
||||
|
||||
private static void startQuads() {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
tess.rawBuffer = new int[64];
|
||||
tess.rawBufferSize = tess.rawBuffer.length;
|
||||
tess.rawBufferIndex = 0;
|
||||
tess.vertexCount = 0;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasTexture = false;
|
||||
tess.hasColor = false;
|
||||
tess.hasNormals = false;
|
||||
tess.hasBrightness = false;
|
||||
tess.isDrawing = true;
|
||||
}
|
||||
|
||||
private static void addQuad(float left, float right) {
|
||||
addVertex(left, -1.0f);
|
||||
addVertex(right, -1.0f);
|
||||
addVertex(right, 1.0f);
|
||||
addVertex(left, 1.0f);
|
||||
}
|
||||
|
||||
private static void addVertex(float x, float y) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
final int base = tess.rawBufferIndex;
|
||||
tess.rawBuffer[base] = Float.floatToRawIntBits(x);
|
||||
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(y);
|
||||
tess.rawBuffer[base + 2] = Float.floatToRawIntBits(0.0f);
|
||||
tess.rawBufferIndex += 8;
|
||||
tess.vertexCount++;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import static org.lwjgl.sdl.SDLVideo.SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendOptions;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendStartGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier.Check;
|
||||
@@ -55,6 +56,7 @@ public final class SDLGPUGate {
|
||||
}
|
||||
|
||||
public static final String LWJGL3IFY_MIN_VERSION = "3.0.25";
|
||||
public static final int REQUIREMENTS_REVISION = 1;
|
||||
|
||||
private static final List<Check> REQUIREMENTS = List.of(
|
||||
new Check("/org/lwjgl/sdl/SDLGPU.class", "no lwjgl-sdl on the classpath"),
|
||||
@@ -90,12 +92,16 @@ public final class SDLGPUGate {
|
||||
boolean ok;
|
||||
try {
|
||||
Sys.initialize();
|
||||
BackendStartGuard.arm();
|
||||
ok = MainThreadExec.runOnMainThread(device()::createDevice);
|
||||
} catch (Throwable t) {
|
||||
LOG.error("SDL GPU device probe failed", t);
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) resetSdlLogging();
|
||||
if (!ok) {
|
||||
resetSdlLogging();
|
||||
BackendStartGuard.disarm();
|
||||
}
|
||||
deviceReady = ok;
|
||||
return ok;
|
||||
}
|
||||
@@ -182,6 +188,7 @@ public final class SDLGPUGate {
|
||||
throw new IllegalStateException("Celeritas already selected the SDL GPU LWJGL service; falling back to OpenGL would leave it bound to a dead backend");
|
||||
}
|
||||
SDLGPULWJGLService.unbind();
|
||||
BackendStartGuard.disarm();
|
||||
disarmed = true;
|
||||
engaged = false;
|
||||
deviceReady = false;
|
||||
|
||||
@@ -369,6 +369,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
LOG.info("Splash offscreen target {}x{} (window {}x{}, max desktop {}x{})", splashTexW, splashTexH, splashW, splashH, maxDesktop[0], maxDesktop[1]);
|
||||
}
|
||||
|
||||
Lwjgl3GLCapabilitiesShim.installFunctionProvider();
|
||||
Lwjgl3GLCapabilitiesShim.installOnCurrentThread(advertisedCapabilities());
|
||||
|
||||
LOG.info("SDLGPURenderBackend initialized: renderer={}, version={}", cachedRenderer, cachedVersion);
|
||||
|
||||
+10
@@ -31,6 +31,16 @@ public final class Lwjgl3GLCapabilitiesShim {
|
||||
LOG.info("Cleared fabricated LWJGL3 GLCapabilities on {}", Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Aware mods probe extension entry points through {@code GL.getFunctionProvider()}, which is null with no GL context.
|
||||
* Every name resolves to 0 so those probes see the extension as absent and use the core path, which GLSM routes.
|
||||
*/
|
||||
public static void installFunctionProvider() {
|
||||
if (GL.getFunctionProvider() != null) return;
|
||||
GL.create(name -> 0L);
|
||||
LOG.info("Installed null-address LWJGL3 GL function provider");
|
||||
}
|
||||
|
||||
public static GLCapabilities create(Set<String> advertised) {
|
||||
final FunctionProvider provider = name -> SENTINEL_ADDRESS;
|
||||
final IntFunction<PointerBuffer> bufferFactory = BufferUtils::createPointerBuffer;
|
||||
|
||||
+15
@@ -87,6 +87,21 @@ class Lwjgl3GLCapabilitiesShimTest {
|
||||
assertThrows(IllegalStateException.class, GL::getCapabilities, "clearing on release must stop the thread answering yes to runningOnRenderThread()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void functionProviderResolvesEveryNameToAbsent() {
|
||||
final boolean preinstalled = GL.getFunctionProvider() != null;
|
||||
Lwjgl3GLCapabilitiesShim.installFunctionProvider();
|
||||
try {
|
||||
assertNotNull(GL.getFunctionProvider());
|
||||
if (!preinstalled) {
|
||||
assertEquals(0L, GL.getFunctionProvider().getFunctionAddress("glBindVertexArrayAPPLE"));
|
||||
}
|
||||
Lwjgl3GLCapabilitiesShim.installFunctionProvider();
|
||||
} finally {
|
||||
if (!preinstalled) GL.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void advertisedSetHasNoDuplicatesOrBlanks() {
|
||||
final Set<String> advertised = SDLGPURenderBackend.advertisedCapabilities();
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CubeInstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlAttribBindingTest {
|
||||
|
||||
private static final String INSTANCED_VERTEX = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = %d) in vec4 iris_InstEntity;
|
||||
layout(location = %d) in vec4 iris_CubeTex;
|
||||
void main() { gl_Position = vec4(a_Position, 1.0) + iris_InstEntity + iris_CubeTex; }
|
||||
""".formatted(InstancedAttribs.LOC_ENTITY, CubeInstancedAttribs.LOC_CUBE_TEX);
|
||||
|
||||
private static final String IMMEDIATE_VERTEX = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = %d) in vec4 mc_Entity;
|
||||
layout(location = %d) in vec4 iris_Entity;
|
||||
void main() { gl_Position = vec4(a_Position, 1.0) + mc_Entity + iris_Entity; }
|
||||
""".formatted(ImmediateExtendedAttribHandler.LOC_MC_ENTITY, ImmediateExtendedAttribHandler.LOC_ENTITY_IDS);
|
||||
|
||||
@Test
|
||||
void instancedEntityIdsStayAtTheInstanceSlot() {
|
||||
final int program = linkWithIrisBindings(INSTANCED_VERTEX);
|
||||
try {
|
||||
assertEquals(InstancedAttribs.LOC_ENTITY, GLStateManager.glGetAttribLocation(program, "iris_InstEntity"));
|
||||
assertEquals(CubeInstancedAttribs.LOC_CUBE_TEX, GLStateManager.glGetAttribLocation(program, "iris_CubeTex"));
|
||||
} finally {
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void immediateEntityIdsDoNotMoveOntoMcEntity() {
|
||||
final int program = linkWithIrisBindings(IMMEDIATE_VERTEX);
|
||||
try {
|
||||
assertEquals(ImmediateExtendedAttribHandler.LOC_MC_ENTITY, GLStateManager.glGetAttribLocation(program, "mc_Entity"));
|
||||
assertEquals(ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, GLStateManager.glGetAttribLocation(program, "iris_Entity"));
|
||||
} finally {
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
private static int linkWithIrisBindings(String vertexSource) {
|
||||
final int program = GLStateManager.glCreateProgram();
|
||||
GLStateManager.glAttachShader(program, GlTestSupport.compile(GL20.GL_VERTEX_SHADER, vertexSource));
|
||||
GLStateManager.glAttachShader(program, GlTestSupport.compile(GL20.GL_FRAGMENT_SHADER, GlTestSupport.TRIVIAL_FS));
|
||||
GLStateManager.glBindAttribLocation(program, InstancedAttribs.LOC_ENTITY, "iris_InstEntity");
|
||||
GLStateManager.glBindAttribLocation(program, ImmediateExtendedAttribHandler.LOC_MC_ENTITY, "mc_Entity");
|
||||
GLStateManager.glLinkProgram(program);
|
||||
return program;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class GlsmSdlFfpTest {
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
CubeParityFixture.deleteResources();
|
||||
ParticleParityFixture.deleteResources();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
FfpFixture.EntityIdInstances.delete();
|
||||
}
|
||||
|
||||
private static void bindBaseTexture() {
|
||||
@@ -172,11 +172,11 @@ class GlsmSdlFfpTest {
|
||||
|
||||
@Test
|
||||
void entityAttribSelectsColorPerInstance() {
|
||||
FfpFixture.IntegerInstances.build(0.6f);
|
||||
FfpFixture.EntityIdInstances.build(0.6f);
|
||||
|
||||
GlsmSdlHeadlessRig.beginFrameAndClear();
|
||||
|
||||
FfpFixture.IntegerInstances.draw();
|
||||
FfpFixture.EntityIdInstances.draw();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.EntityIdsOnlyHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorEntityIdsFixture;
|
||||
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 static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLUE;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.GREEN;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlTessellatorEntityIdsTest {
|
||||
|
||||
private ImmediateExtendedAttribHandler previousHandler;
|
||||
private int program;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
previousHandler = EntityIdsOnlyHandler.install();
|
||||
program = GlTestSupport.program(TessellatorEntityIdsFixture.VERTEX, TessellatorEntityIdsFixture.FRAGMENT);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
GLSMHooks.immediateExtendedHandler = previousHandler;
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedMidBatchApplyToTheVerticesAddedAfterTheChange() {
|
||||
GlsmSdlHeadlessRig.beginFrameAndClear();
|
||||
GLStateManager.glUseProgram(program);
|
||||
TessellatorEntityIdsFixture.drawIdsChangedMidBatch();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 4, SIZE / 2, GREEN, "left quad was added under item 7");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 3 * SIZE / 4, SIZE / 2, BLUE, "right quad was added under item 50020");
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedAfterTheLastVertexLeaveTheBatchAlone() {
|
||||
GlsmSdlHeadlessRig.beginFrameAndClear();
|
||||
GLStateManager.glUseProgram(program);
|
||||
TessellatorEntityIdsFixture.drawIdsChangedAfterTheLastVertex();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 4, SIZE / 2, GREEN, "every vertex was added under item 7");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 3 * SIZE / 4, SIZE / 2, GREEN, "every vertex was added under item 7");
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHa
|
||||
|
||||
private static final int WANTS_MID = 1;
|
||||
private static final int WANTS_TANGENT = 2;
|
||||
private static final int WANTS_ENTITY_IDS = 4;
|
||||
|
||||
private static final int NEUTRAL_TANGENT = NormI8.pack(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
@@ -57,6 +58,7 @@ public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHa
|
||||
wants = 0;
|
||||
if (GLStateManager.glGetAttribLocation(program, "mc_midTexCoord") >= 0) wants |= WANTS_MID;
|
||||
if (GLStateManager.glGetAttribLocation(program, "at_tangent") >= 0) wants |= WANTS_TANGENT;
|
||||
if (GLStateManager.glGetAttribLocation(program, "iris_Entity") == LOC_ENTITY_IDS) wants |= WANTS_ENTITY_IDS;
|
||||
programWantsExtCache.put(program, wants);
|
||||
}
|
||||
currentProgramWants = wants;
|
||||
@@ -83,8 +85,7 @@ public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHa
|
||||
|
||||
@Override
|
||||
public boolean wantsExtended() {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
return Iris.enabled && currentProgramWants != 0 && GLStateManager.getActiveProgram() != 0;
|
||||
return programWants(WANTS_MID | WANTS_TANGENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,6 +93,17 @@ public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHa
|
||||
return (currentProgramWants & WANTS_TANGENT) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsEntityIds() {
|
||||
return programWants(WANTS_ENTITY_IDS);
|
||||
}
|
||||
|
||||
private static boolean programWants(int mask) {
|
||||
if (!Iris.enabled) return false;
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
return (currentProgramWants & mask) != 0 && GLStateManager.getActiveProgram() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsExtendedCapture() {
|
||||
return Iris.enabled && Iris.getCurrentPack().isPresent();
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.samplers.IrisSamplers;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.coderbot.iris.uniforms.SystemTimeUniforms;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -65,6 +66,9 @@ public class IrisGLSMBridge {
|
||||
|
||||
public static void installImmediateExtendedHandler() {
|
||||
GLSMHooks.immediateExtendedHandler = new ImmediateExtendedAttribs();
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.setEntityIdsListener(() -> GLStateManager.setEntityIds(
|
||||
state.getCurrentRenderedEntity(), state.getCurrentRenderedBlockEntity(), state.getCurrentRenderedItem()));
|
||||
}
|
||||
|
||||
public static void installPostTransformHook() {
|
||||
|
||||
@@ -72,8 +72,7 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
// Angelica Config
|
||||
ConfigurationManager.registerConfig(AngelicaConfig.class);
|
||||
TracyOptions.latch(AngelicaConfig.enableTracy, AngelicaConfig.tracyAllowRemote, AngelicaConfig.tracyFineZones, AngelicaConfig.tracyMaxSrcLocs);
|
||||
if (BackendStartGuard.begin(new File(Launch.minecraftHome, "angelica" + File.separator + "sdlgpu-starting"), AngelicaConfig.sdlGpuConfigured(), SystemProperties.SDL_GPU_OVERRIDE != null)) {
|
||||
LOGGER.warn("The last start with SDL GPU did not reach the main menu; resetting the render backend to OpenGL");
|
||||
if (BackendStartGuard.begin(new File(Launch.minecraftHome, "angelica"), AngelicaConfig.sdlGpuConfigured(), SystemProperties.SDL_GPU_OVERRIDE != null, SDLGPUGate.REQUIREMENTS_REVISION, BackendOptions.sdlGpuDriver(AngelicaConfig.sdlGpuDriverName()))) {
|
||||
AngelicaConfig.renderBackend = RenderBackendChoice.OPENGL;
|
||||
ConfigurationManager.save(AngelicaConfig.class);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.gtnewhorizons.angelica.debug.TPSGraph;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendStartGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
@@ -383,7 +384,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onGuiOpen(GuiOpenEvent event) {
|
||||
BackendStartGuard.finish();
|
||||
if (BackendManager.RENDER_BACKEND.isSDLGPU()) BackendStartGuard.latch();
|
||||
if (!event.isCanceled()) {
|
||||
if (event.gui instanceof GuiMainMenu && gameStartTime == -1) {
|
||||
gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ModelBoxQuads;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.PassRebindGate;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.image.GlImage;
|
||||
@@ -211,8 +210,8 @@ public final class PlayerReflectionCapture {
|
||||
final float[] xyzuv = vertexScratch;
|
||||
if (xyzuv == null || xyzuv.length < VERTEX_COUNT * FLOATS_PER_VERTEX) return;
|
||||
|
||||
PassRebindGate.rebindIfDirty();
|
||||
if (!(Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline pipeline)) return;
|
||||
pipeline.rebindCurrentPass();
|
||||
if (GLStateManager.getActiveProgram() != pipeline.getActivePassProgramId()) return;
|
||||
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_PLAYER_REFLECTION_FLUSH);
|
||||
|
||||
+22
-13
@@ -41,6 +41,12 @@ public final class GpuDrivenChunkCuller {
|
||||
|
||||
private int metaCapacityBytes;
|
||||
|
||||
private int savedProgram;
|
||||
private int savedVisibleSsbo;
|
||||
private int savedMetaSsbo;
|
||||
private int savedIndirectSsbo;
|
||||
private int savedFrustumUbo;
|
||||
|
||||
public boolean ensureReady() {
|
||||
if (ready) return true;
|
||||
if (disabled) return false;
|
||||
@@ -88,28 +94,31 @@ public final class GpuDrivenChunkCuller {
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 0L, frustumUboBytes);
|
||||
}
|
||||
|
||||
public void dispatch(int visibleSsboGlId, int indirectSsboGlId, int visibleCount) {
|
||||
if (!ready || visibleCount <= 0) return;
|
||||
final int previousProgram = GLStateManager.getActiveProgram();
|
||||
final int prevSsbo0 = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, VISIBLE_SSBO_BINDING);
|
||||
final int prevSsbo1 = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, META_SSBO_BINDING);
|
||||
final int prevSsbo2 = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, INDIRECT_SSBO_BINDING);
|
||||
final int prevUbo = RenderSystem.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, FRUSTUM_UBO_BINDING);
|
||||
void beginDispatches(int visibleSsboGlId, int indirectSsboGlId) {
|
||||
savedProgram = GLStateManager.getActiveProgram();
|
||||
savedVisibleSsbo = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, VISIBLE_SSBO_BINDING);
|
||||
savedMetaSsbo = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, META_SSBO_BINDING);
|
||||
savedIndirectSsbo = RenderSystem.getIndexedBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER, INDIRECT_SSBO_BINDING);
|
||||
savedFrustumUbo = RenderSystem.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, FRUSTUM_UBO_BINDING);
|
||||
GLStateManager.glUseProgram(program.handle());
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, VISIBLE_SSBO_BINDING, visibleSsboGlId);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, META_SSBO_BINDING, metaSsboGlId);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, INDIRECT_SSBO_BINDING, indirectSsboGlId);
|
||||
RenderSystem.bindBufferBase(GL31.GL_UNIFORM_BUFFER, FRUSTUM_UBO_BINDING, frustumUboGlId);
|
||||
}
|
||||
|
||||
void dispatch(int visibleCount) {
|
||||
final int groups = (visibleCount + LOCAL_SIZE_X - 1) / LOCAL_SIZE_X;
|
||||
GLStateManager.glDispatchCompute(groups, 1, 1);
|
||||
GLStateManager.glMemoryBarrier(GL43.GL_SHADER_STORAGE_BARRIER_BIT | GL42.GL_COMMAND_BARRIER_BIT);
|
||||
}
|
||||
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, VISIBLE_SSBO_BINDING, prevSsbo0);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, META_SSBO_BINDING, prevSsbo1);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, INDIRECT_SSBO_BINDING, prevSsbo2);
|
||||
RenderSystem.bindBufferBase(GL31.GL_UNIFORM_BUFFER, FRUSTUM_UBO_BINDING, prevUbo);
|
||||
GLStateManager.glUseProgram(previousProgram);
|
||||
void endDispatches() {
|
||||
GLStateManager.glMemoryBarrier(GL43.GL_SHADER_STORAGE_BARRIER_BIT | GL42.GL_COMMAND_BARRIER_BIT);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, VISIBLE_SSBO_BINDING, savedVisibleSsbo);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, META_SSBO_BINDING, savedMetaSsbo);
|
||||
RenderSystem.bindBufferBase(GL43.GL_SHADER_STORAGE_BUFFER, INDIRECT_SSBO_BINDING, savedIndirectSsbo);
|
||||
RenderSystem.bindBufferBase(GL31.GL_UNIFORM_BUFFER, FRUSTUM_UBO_BINDING, savedFrustumUbo);
|
||||
GLStateManager.glUseProgram(savedProgram);
|
||||
}
|
||||
|
||||
public synchronized void shutdown() {
|
||||
|
||||
@@ -261,12 +261,14 @@ public final class GpuTerrainCuller {
|
||||
GLStateManager.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, visibleSsboGlId);
|
||||
GLStateManager.glBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, 0L, visibleStagingView);
|
||||
|
||||
culler.beginDispatches(visibleSsboGlId, indirectSsboGlId);
|
||||
BackendManager.RENDER_BACKEND.beginComputeDispatchBatch();
|
||||
try {
|
||||
dispatchPass(primaryPass);
|
||||
if (secondPrepared) dispatchPass(secondPass);
|
||||
if (translucentReady) dispatchPass(sortedPass);
|
||||
} finally {
|
||||
culler.endDispatches();
|
||||
BackendManager.RENDER_BACKEND.endComputeDispatchBatch();
|
||||
}
|
||||
}
|
||||
@@ -280,7 +282,7 @@ public final class GpuTerrainCuller {
|
||||
FrustumExtractor.patchControl(r.entryCount, r.indexPointerMask, frustumUboBytes);
|
||||
FrustumExtractor.patchBatchEntryBase(r.entryBase, frustumUboBytes);
|
||||
culler.uploadFrustum(frustumUboBytes);
|
||||
culler.dispatch(visibleSsboGlId, indirectSsboGlId, r.entryCount);
|
||||
culler.dispatch(r.entryCount);
|
||||
r.dispatched = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorEntityIds;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
@@ -652,6 +653,7 @@ public final class ModelPartBatcher {
|
||||
private TessellatorRoute routeTessellatorDraw(Tessellator tess) {
|
||||
if (!active || mode != Mode.BLOCK_ENTITIES || !GLStateManager.isMainThread() || !BatchEligibility.insideRenderer()) return TessellatorRoute.DRAW_NOW;
|
||||
if (!tess.isDrawing || tess.drawMode != GL11.GL_QUADS || tess.vertexCount == 0 || (tess.vertexCount & 3) != 0) return TessellatorRoute.DRAW_NOW;
|
||||
if (shaderPipeline != null && TessellatorEntityIds.hasIdChanges(tess)) return TessellatorRoute.DRAW_NOW;
|
||||
if (DisplayListManager.isRecording() || !GLStateManager.glIsEnabled(GL11.GL_DEPTH_TEST) || !unitTexIdentity()) return TessellatorRoute.DRAW_NOW;
|
||||
if (GLStateManager.getActiveProgram() != (shaderPipeline != null ? shaderPipeline.getActivePassProgramId() : 0)) return TessellatorRoute.DRAW_NOW;
|
||||
final TesrMaterial material = EntityMaterials.fromCurrentState(true);
|
||||
|
||||
@@ -5,6 +5,7 @@ package net.coderbot.iris.gl.shader;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ProgramBinaryCache;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -24,8 +25,8 @@ public class ProgramCreator {
|
||||
public static final int AT_MIDBLOCK = ImmediateExtendedAttribHandler.LOC_MIDBLOCK;
|
||||
|
||||
// TODO: This is *really* hardcoded, we need to refactor this to support external calls to glBindAttribLocation
|
||||
private static final String[] ATTRIBUTE_NAMES = { "iris_Entity", "mc_Entity", "mc_midTexCoord", "iris_CubeTangent", "at_tangent", "at_midBlock" };
|
||||
private static final int[] ATTRIBUTE_LOCATIONS = { MC_ENTITY, MC_ENTITY, MC_MID_TEX_COORD, AT_TANGENT, AT_TANGENT, AT_MIDBLOCK };
|
||||
private static final String[] ATTRIBUTE_NAMES = { "iris_InstEntity", "mc_Entity", "mc_midTexCoord", "iris_CubeTangent", "at_tangent", "at_midBlock" };
|
||||
private static final int[] ATTRIBUTE_LOCATIONS = { InstancedAttribs.LOC_ENTITY, MC_ENTITY, MC_MID_TEX_COORD, AT_TANGENT, AT_TANGENT, AT_MIDBLOCK };
|
||||
|
||||
@Nullable
|
||||
public static ProgramBinaryCache.Key cacheKey() {
|
||||
|
||||
@@ -617,8 +617,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
() -> usesSampler(IrisSamplers.USAGE_SHADOWTEX1));
|
||||
Program shadowProgram = table.match(RenderCondition.SHADOW, SHADOW_INPUTS).getProgram();
|
||||
Program shadowWaterProgram = table.match(RenderCondition.SHADOW_TRANSLUCENT, SHADOW_INPUTS).getProgram();
|
||||
shadowRenderer.setUsesImages((shadowProgram != null && shadowProgram.getActiveImages() > 0) || (shadowWaterProgram != null && shadowWaterProgram.getActiveImages() > 0));
|
||||
shadowRenderer.setPlayerReflectionCaptureEnabled((shadowProgram != null && GLStateManager.glGetUniformLocation(shadowProgram.getProgramId(), "playerAtlas_img") != -1) || (shadowWaterProgram != null && GLStateManager.glGetUniformLocation(shadowWaterProgram.getProgramId(), "playerAtlas_img") != -1));
|
||||
shadowRenderer.setUsesImages(usesImages(shadowProgram) || usesImages(shadowWaterProgram));
|
||||
shadowRenderer.setPlayerReflectionCaptureEnabled(usesImage(shadowProgram, "playerAtlas_img") || usesImage(shadowWaterProgram, "playerAtlas_img"));
|
||||
} else {
|
||||
shadowRenderer = null;
|
||||
}
|
||||
@@ -1591,6 +1591,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
return supportsInstancing[kind.ordinal()];
|
||||
}
|
||||
|
||||
private boolean usesImages(@Nullable Program program) {
|
||||
return program != null && (program.getActiveImages() > 0 || vertexWriteReplays.writesImages(program));
|
||||
}
|
||||
|
||||
private boolean usesImage(@Nullable Program program, String image) {
|
||||
return program != null && (GLStateManager.glGetUniformLocation(program.getProgramId(), image) != -1 || vertexWriteReplays.writesImage(program, image));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInstancedVariant(Instancing kind) {
|
||||
return current != null && current.instancedVariant(kind) != null;
|
||||
|
||||
@@ -5,7 +5,9 @@ import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VertexWriteReplaySetup;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.InternalTextureFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureType;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.image.ImageHolder;
|
||||
import net.coderbot.iris.gl.program.ComputeProgram;
|
||||
@@ -33,10 +35,20 @@ import java.util.function.IntSupplier;
|
||||
final class VertexWriteReplays {
|
||||
|
||||
private final List<ComputeProgram> twins = new ArrayList<>();
|
||||
private final IntArrayList attachedPrograms = new IntArrayList();
|
||||
private final Int2ObjectMap<List<String>> writtenImagesByProgram = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
boolean writesImage(Program program, String image) {
|
||||
final List<String> images = writtenImagesByProgram.get(program.getProgramId());
|
||||
return images != null && images.contains(image);
|
||||
}
|
||||
|
||||
boolean writesImages(Program program) {
|
||||
return writtenImagesByProgram.containsKey(program.getProgramId());
|
||||
}
|
||||
|
||||
void attach(String name, Program program, Map<PatchShaderType, String> transformed, BiConsumer<SamplerHolder, ImageHolder> wireResources) {
|
||||
BackendManager.RENDER_BACKEND.setVertexWriteReplay(program.getProgramId(), null);
|
||||
writtenImagesByProgram.remove(program.getProgramId());
|
||||
final String source = transformed.get(PatchShaderType.COMPUTE);
|
||||
if (RwImageStoreExtractor.parseSentinel(source) != RwImageStoreExtractor.RwExtractMode.VERTEX_REPLAY) return;
|
||||
|
||||
@@ -76,13 +88,13 @@ final class VertexWriteReplays {
|
||||
inputNames, inputLocations, resources.images, writtenImages.size(),
|
||||
resources.samplerNames.toArray(new String[0]), resources.samplerUnits, resources.samplerTextures, resources.samplerObjects,
|
||||
RwImageStoreExtractor.VG_REPLAY_VERTEX_BUFFER_BINDING, RwImageStoreExtractor.VG_REPLAY_VERTEX_BUFFER_COUNT, RwImageStoreExtractor.VG_REPLAY_INDEX_BUFFER_BINDING));
|
||||
attachedPrograms.add(program.getProgramId());
|
||||
writtenImagesByProgram.put(program.getProgramId(), writtenImages);
|
||||
twins.add(twin);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
for (int i = 0; i < attachedPrograms.size(); i++) BackendManager.RENDER_BACKEND.setVertexWriteReplay(attachedPrograms.getInt(i), null);
|
||||
attachedPrograms.clear();
|
||||
for (IntIterator it = writtenImagesByProgram.keySet().iterator(); it.hasNext(); ) BackendManager.RENDER_BACKEND.setVertexWriteReplay(it.nextInt(), null);
|
||||
writtenImagesByProgram.clear();
|
||||
for (ComputeProgram twin : twins) twin.destroy();
|
||||
twins.clear();
|
||||
}
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
package net.coderbot.iris.pipeline.transform;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderIndex;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.taumc.glsl.Transformer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
class EntityPatcher {
|
||||
|
||||
private static final String[] ID_NAMES = { "entityId", "blockEntityId", "currentRenderedItemId" };
|
||||
private static final String[] ID_COMPONENTS = { ".x", ".y", ".z" };
|
||||
|
||||
public static void patchEntityId(Transformer transformer, AttributeParameters parameters) {
|
||||
if (!hasInstanceAttributes(parameters)) {
|
||||
injectUniformIfUndeclared(transformer, "entityId", "uniform int entityId;");
|
||||
injectUniformIfUndeclared(transformer, "blockEntityId", "uniform int blockEntityId;");
|
||||
injectUniformIfUndeclared(transformer, "currentRenderedItemId", "uniform int currentRenderedItemId;");
|
||||
return;
|
||||
}
|
||||
|
||||
removeIfDeclared(transformer, "entityId");
|
||||
removeIfDeclared(transformer, "blockEntityId");
|
||||
removeIfDeclared(transformer, "currentRenderedItemId");
|
||||
|
||||
final String geometryIn = parameters.hasTesselation ? "iris_entityInfoTES" : "iris_entityInfo";
|
||||
final String info = switch (parameters.type) {
|
||||
case GEOMETRY -> geometryIn + "[0]";
|
||||
@@ -26,17 +23,37 @@ class EntityPatcher {
|
||||
case TESSELATION_EVAL -> "iris_entityInfoTCS[0]";
|
||||
default -> "iris_entityInfo";
|
||||
};
|
||||
transformer.replaceExpression("entityId", info + ".x", GLSLParser::postfix_expression);
|
||||
transformer.replaceExpression("blockEntityId", info + ".y", GLSLParser::postfix_expression);
|
||||
transformer.replaceExpression("currentRenderedItemId", info + ".z", GLSLParser::postfix_expression);
|
||||
final StringBuilder assignments = new StringBuilder();
|
||||
for (int i = 0; i < ID_NAMES.length; i++) {
|
||||
final String name = ID_NAMES[i];
|
||||
if (!transformer.containsCall(name)) continue;
|
||||
final GLSLParser.Typeless_declarationContext global = topLevelDeclarator(transformer, name);
|
||||
if (global != null) {
|
||||
if (!isUniform(global)) continue;
|
||||
// removeVariable takes the last declaration it finds by name, which can be a pack local
|
||||
final String removed = "iris_removed_" + name;
|
||||
((CommonToken) global.IDENTIFIER().getSymbol()).setText(removed);
|
||||
transformer.removeVariable(removed);
|
||||
}
|
||||
transformer.injectVariable("int " + name + ";");
|
||||
assignments.append(name).append(" = ").append(info).append(ID_COMPONENTS[i]).append("; ");
|
||||
}
|
||||
if (parameters.type != ShaderType.VERTEX && assignments.length() > 0) {
|
||||
transformer.prependMain("{ " + assignments + "}");
|
||||
}
|
||||
|
||||
switch (parameters.type) {
|
||||
case VERTEX -> {
|
||||
final boolean instanced = hasInstanceAttributes(parameters);
|
||||
final String input = instanced ? "iris_InstEntity" : "iris_Entity";
|
||||
final int location = instanced ? InstancedAttribs.LOC_ENTITY : ImmediateExtendedAttribHandler.LOC_ENTITY_IDS;
|
||||
removeIfDeclared(transformer, "iris_Entity");
|
||||
transformer.injectVariable("layout(location = " + InstancedAttribs.LOC_ENTITY + ") in uvec4 iris_Entity;");
|
||||
removeIfDeclared(transformer, "iris_InstEntity");
|
||||
transformer.injectVariable("layout(location = " + location + ") in vec4 " + input + ";");
|
||||
transformer.injectVariable("flat out ivec3 iris_entityInfo;");
|
||||
transformer.prependMain("iris_entityInfo = ivec3(iris_Entity.x == 65535u ? -1 : int(iris_Entity.x), "
|
||||
+ "iris_Entity.y == 65535u ? -1 : int(iris_Entity.y), iris_Entity.z == 65535u ? -1 : int(iris_Entity.z));");
|
||||
transformer.prependMain("{ ivec3 iris_ids = ivec3(round(" + input + ".xyz * 65535.0)); "
|
||||
+ "iris_entityInfo = ivec3(iris_ids.x == 65535 ? -1 : iris_ids.x, iris_ids.y == 65535 ? -1 : iris_ids.y, "
|
||||
+ "iris_ids.z == 65535 ? -1 : iris_ids.z); " + assignments + "}");
|
||||
}
|
||||
case TESSELATION_CONTROL -> {
|
||||
transformer.injectVariable("flat out ivec3 iris_entityInfoTCS[];");
|
||||
@@ -113,6 +130,27 @@ class EntityPatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private static GLSLParser.Typeless_declarationContext topLevelDeclarator(Transformer transformer, String name) {
|
||||
final GLSLParser.Typeless_declarationContext[] found = new GLSLParser.Typeless_declarationContext[1];
|
||||
transformer.mutateTree(root -> {
|
||||
for (GLSLParser.External_declarationContext external : root.external_declaration()) {
|
||||
final GLSLParser.DeclarationContext declaration = external.declaration();
|
||||
if (declaration == null || declaration.init_declarator_list() == null) continue;
|
||||
for (GLSLParser.Typeless_declarationContext declarator : ShaderIndex.declarators(declaration.init_declarator_list())) {
|
||||
if (declarator.IDENTIFIER().getText().equals(name)) found[0] = declarator;
|
||||
}
|
||||
}
|
||||
});
|
||||
return found[0];
|
||||
}
|
||||
|
||||
private static boolean isUniform(GLSLParser.Typeless_declarationContext declarator) {
|
||||
final ParserRuleContext parent = declarator.getParent();
|
||||
final GLSLParser.Init_declarator_listContext list = (GLSLParser.Init_declarator_listContext)
|
||||
(parent instanceof GLSLParser.Single_declarationContext ? parent.getParent() : parent);
|
||||
return ShaderIndex.hasStorageQualifier(list.single_declaration().fully_specified_type().type_qualifier(), "uniform");
|
||||
}
|
||||
|
||||
private static boolean hasInstanceAttributes(AttributeParameters parameters) {
|
||||
return parameters.instancing.hasInstanceHead();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public class CapturedRenderingState {
|
||||
@Getter
|
||||
private int currentRenderedItem = -1;
|
||||
private Runnable itemIdListener = null;
|
||||
private Runnable entityIdsListener = null;
|
||||
private final IntArrayList itemStack = new IntArrayList();
|
||||
private final IntArrayList entityStack = new IntArrayList();
|
||||
private final IntArrayList blockEntityStack = new IntArrayList();
|
||||
@@ -88,15 +89,25 @@ public class CapturedRenderingState {
|
||||
if (this.blockEntityIdListener != null) {
|
||||
this.blockEntityIdListener.run();
|
||||
}
|
||||
notifyEntityIdsChanged();
|
||||
}
|
||||
|
||||
private void setCurrentEntity(int entity) {
|
||||
if (currentRenderedEntity == entity) return;
|
||||
this.currentRenderedEntity = entity;
|
||||
|
||||
if (this.entityIdListener != null) {
|
||||
this.entityIdListener.run();
|
||||
private void applyCurrentEntityAndItem(int entity, int item) {
|
||||
final boolean entityChanged = currentRenderedEntity != entity;
|
||||
final boolean itemChanged = currentRenderedItem != item;
|
||||
if (entityChanged) {
|
||||
this.currentRenderedEntity = entity;
|
||||
if (this.entityIdListener != null) {
|
||||
this.entityIdListener.run();
|
||||
}
|
||||
}
|
||||
if (itemChanged) {
|
||||
this.currentRenderedItem = item;
|
||||
if (this.itemIdListener != null) {
|
||||
this.itemIdListener.run();
|
||||
}
|
||||
}
|
||||
if (entityChanged || itemChanged) notifyEntityIdsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,15 +115,13 @@ public class CapturedRenderingState {
|
||||
*/
|
||||
public void setCurrentEntityAndItem(int entity, int item) {
|
||||
IrisDisplayListState.recordEntityAndItem(entity, item);
|
||||
setCurrentEntity(entity);
|
||||
applyCurrentRenderedItem(item);
|
||||
applyCurrentEntityAndItem(entity, item);
|
||||
}
|
||||
|
||||
public void setCurrentNamedEntity(NamespacedId entity) {
|
||||
IrisDisplayListState.recordNamedEntity(entity);
|
||||
final var ids = BlockRenderingSettings.INSTANCE.getEntityIds();
|
||||
setCurrentEntity(ids == null ? -1 : ids.applyAsInt(entity));
|
||||
applyCurrentRenderedItem(0);
|
||||
applyCurrentEntityAndItem(ids == null ? -1 : ids.applyAsInt(entity), 0);
|
||||
}
|
||||
|
||||
public void pushCurrentEntityAndItem() {
|
||||
@@ -123,15 +132,13 @@ public class CapturedRenderingState {
|
||||
|
||||
public void setCurrentRenderedEntity(Entity entity) {
|
||||
IrisDisplayListState.recordEntity(entity);
|
||||
setCurrentEntity(EntityIdHelper.getEntityId(entity));
|
||||
applyCurrentRenderedItem(0);
|
||||
applyCurrentEntityAndItem(EntityIdHelper.getEntityId(entity), 0);
|
||||
}
|
||||
|
||||
public void popCurrentEntityAndItem() {
|
||||
IrisDisplayListState.recordEntityScope(false);
|
||||
// Restore the playback caller, never the IDs seen while compiling the list.
|
||||
setCurrentEntity(entityStack.popInt());
|
||||
applyCurrentRenderedItem(itemStack.popInt());
|
||||
applyCurrentEntityAndItem(entityStack.popInt(), itemStack.popInt());
|
||||
}
|
||||
|
||||
public void setCurrentEntityColor(float r, float g, float b, float a) {
|
||||
@@ -209,6 +216,18 @@ public class CapturedRenderingState {
|
||||
if (this.itemIdListener != null) {
|
||||
this.itemIdListener.run();
|
||||
}
|
||||
notifyEntityIdsChanged();
|
||||
}
|
||||
|
||||
public void setEntityIdsListener(Runnable listener) {
|
||||
this.entityIdsListener = listener;
|
||||
notifyEntityIdsChanged();
|
||||
}
|
||||
|
||||
private void notifyEntityIdsChanged() {
|
||||
if (this.entityIdsListener != null) {
|
||||
this.entityIdsListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
public ValueUpdateNotifier getEntityIdNotifier() {
|
||||
|
||||
+7
@@ -1,11 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorEntityIds;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
@@ -19,4 +21,9 @@ public class MixinTessellator_CoreProfile {
|
||||
private void angelica$coreProfileDraw(CallbackInfoReturnable<Integer> cir) {
|
||||
cir.setReturnValue(ModelPartBatcher.drawTessellator((Tessellator) (Object) this));
|
||||
}
|
||||
|
||||
@Inject(method = "reset", at = @At("HEAD"))
|
||||
private void angelica$forgetEntityIds(CallbackInfo ci) {
|
||||
TessellatorEntityIds.onReset((Tessellator) (Object) this);
|
||||
}
|
||||
}
|
||||
+2
@@ -8,6 +8,7 @@ import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendStartGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
@@ -47,6 +48,7 @@ public class MixinInitGLStateManager {
|
||||
.enableDSA(AngelicaConfig.enableDSA)
|
||||
.postInitCallback(SelectionBoxRenderer::init)
|
||||
.build());
|
||||
BackendStartGuard.disarm();
|
||||
|
||||
GLSMConfig.extendedAttribsExpected = AngelicaConfig.enableIris;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.coderbot.iris.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.EntityIdsOnlyHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
@@ -7,6 +8,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
@@ -403,15 +405,19 @@ class IrisInstancingGLTest {
|
||||
""";
|
||||
final String fragmentSource = """
|
||||
#version 120
|
||||
uniform int entityId;
|
||||
uniform int blockEntityId;
|
||||
uniform int entityId, blockEntityId;
|
||||
uniform int currentRenderedItemId;
|
||||
uniform int expectedId;
|
||||
// a parameter and a local sharing the id names keep their own values
|
||||
bool isFortyTwo(int entityId) { return entityId == 42; }
|
||||
void main() {
|
||||
bool matches = entityId == expectedId && blockEntityId == expectedId && currentRenderedItemId == expectedId;
|
||||
gl_FragData[0] = matches ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
|
||||
bool shadowed = isFortyTwo(42);
|
||||
{ int blockEntityId = 7; shadowed = shadowed && blockEntityId == 7; }
|
||||
gl_FragData[0] = matches && shadowed ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
|
||||
}
|
||||
""";
|
||||
final int[] ids = { -1, 0, 32767, 32768, 45020, 50020, 50072, 65534 };
|
||||
final int vao = GLStateManager.glGenVertexArrays();
|
||||
final int buffer = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
@@ -432,7 +438,7 @@ class IrisInstancingGLTest {
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_ENTITY);
|
||||
GLStateManager.glVertexAttribDivisor(InstancedAttribs.LOC_ENTITY, 1);
|
||||
final ByteBuffer data = BufferUtils.createByteBuffer(InstancedAttribs.HEAD_SIZE);
|
||||
for (int id : new int[] { -1, 0, 32767, 32768, 45020, 50020, 50072, 65534 }) {
|
||||
for (int id : ids) {
|
||||
data.putLong(InstancedAttribs.OFFSET_ENTITY, InstancedAttribs.packEntityInfo(id, id, id));
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glUniform1i(GLStateManager.glGetUniformLocation(program, "expectedId"), id);
|
||||
@@ -449,6 +455,30 @@ class IrisInstancingGLTest {
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
final int program = linkProgram("highIdsImmediate", TransformPatcher.patchAttributes(vertexSource, null, null, null, fragmentSource, TEX_LM, false));
|
||||
final ImmediateExtendedAttribHandler previousHandler = GLSMHooks.immediateExtendedHandler;
|
||||
try {
|
||||
EntityIdsOnlyHandler.install();
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.glUniform1i(GLStateManager.glGetUniformLocation(program, "iris_currentAlphaFunc"), 7);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
for (int id : ids) {
|
||||
GLStateManager.setEntityIds(id, id, id);
|
||||
GLStateManager.glUniform1i(GLStateManager.glGetUniformLocation(program, "expectedId"), id);
|
||||
GLStateManager.glClearColor(0, 0, 0, 1);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "draw error");
|
||||
final int[] pixel = FfpFixture.readPixel(0, 0);
|
||||
assertEquals(255, pixel[1], "immediate material ID " + id + " pixel " + Arrays.toString(pixel));
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
GLSMHooks.immediateExtendedHandler = previousHandler;
|
||||
GLStateManager.setEntityIds(-1, 0, -1);
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
+14
-9
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeInstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ParticleInstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
|
||||
import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
@@ -259,9 +260,10 @@ class AttributeTransformerInstancedTest {
|
||||
assertLacks(v, "uniform int entityId;");
|
||||
assertLacks(v, "uniform int blockEntityId;");
|
||||
assertLacks(v, "uniform int currentRenderedItemId;");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_ENTITY + ") in uvec4 iris_Entity;");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_ENTITY + ") in vec4 iris_InstEntity;");
|
||||
assertLacks(v, "in vec4 iris_Entity;");
|
||||
assertHas(v, "flat out ivec3 iris_entityInfo;");
|
||||
assertHas(v, "iris_entityInfo = ivec3(iris_Entity.x == 65535u ? -1 : int(iris_Entity.x), iris_Entity.y == 65535u ? -1 : int(iris_Entity.y), iris_Entity.z == 65535u ? -1 : int(iris_Entity.z));");
|
||||
assertHas(v, "ivec3 iris_ids = ivec3(round(iris_InstEntity.xyz * 65535.0));");
|
||||
|
||||
assertLacks(f, "uniform int entityId;");
|
||||
assertHas(f, "flat in ivec3 iris_entityInfo;");
|
||||
@@ -362,6 +364,8 @@ class AttributeTransformerInstancedTest {
|
||||
assertHas(v, "layout(location = 3) in vec4 iris_CubeDelta");
|
||||
assertHas(v, "layout(location = " + CubeInstancedAttribs.LOC_CUBE_TEX + ") in vec4 iris_CubeTex");
|
||||
assertHas(v, "layout(location = " + CubeInstancedAttribs.LOC_LIGHTMAP_SCALE + ") in vec4 iris_InstLightmapScale");
|
||||
assertHas(v, "layout(location = " + InstancedAttribs.LOC_ENTITY + ") in vec4 iris_InstEntity;");
|
||||
assertLacks(v, "in vec4 iris_Entity;");
|
||||
assertLacks(v, "layout(location = 2) in vec4 iris_MultiTexCoord0");
|
||||
assertHas(v, "vec4 iris_MultiTexCoord0;");
|
||||
assertHas(v, "iris_MultiTexCoord1 = vec4(iris_InstLightmapScale.xy, 0.0, 1.0)");
|
||||
@@ -417,10 +421,11 @@ class AttributeTransformerInstancedTest {
|
||||
assertEquals(ParticleInstancedAttribs.LOC_UV, locationOf(byLocation, "iris_InstUv"));
|
||||
assertEquals(InstancedAttribs.LOC_COLOR, locationOf(byLocation, "iris_InstColor"));
|
||||
assertEquals(InstancedAttribs.LOC_LIGHTMAP, locationOf(byLocation, "iris_InstLightmap"));
|
||||
assertEquals(ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, locationOf(byLocation, "iris_Entity"));
|
||||
|
||||
assertFalse(byLocation.containsKey(7), "location 7 is unused");
|
||||
for (final int loc : byLocation.keySet()) {
|
||||
assertTrue(loc < 11, "unexpected attribute location " + loc + " declared for the particle variant");
|
||||
assertTrue(loc < 11 || loc == ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, "unexpected attribute location " + loc + " declared for the particle variant");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +436,7 @@ class AttributeTransformerInstancedTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void particleEntityIdAndOverlayColorStayAsPlainUniformsLikeTheNonePath() {
|
||||
void particleEntityIdsAndOverlayColorFollowTheNonePath() {
|
||||
final Map<PatchShaderType, String> result = TransformPatcher.patchAttributesInstanced(VERTEX_ENTITY_AND_COLOR, null, null, null, FRAGMENT_ENTITY_AND_COLOR, TEX_LM, false, Instancing.PARTICLE);
|
||||
assertNotNull(result);
|
||||
final String v = result.get(PatchShaderType.VERTEX);
|
||||
@@ -441,16 +446,16 @@ class AttributeTransformerInstancedTest {
|
||||
ShaderParser.parseShader(v);
|
||||
ShaderParser.parseShader(f);
|
||||
|
||||
assertHas(v, "uniform int entityId;");
|
||||
assertLacks(v, "uniform int entityId;");
|
||||
assertHas(v, "layout(location = " + ImmediateExtendedAttribHandler.LOC_ENTITY_IDS + ") in vec4 iris_Entity;");
|
||||
assertHas(v, "uniform vec4 entityColor;");
|
||||
assertLacks(v, "iris_entityInfo");
|
||||
assertLacks(v, "iris_InstOverlay");
|
||||
assertLacks(v, "out vec4 entityColor;");
|
||||
|
||||
assertHas(f, "uniform int entityId;");
|
||||
assertLacks(f, "uniform int entityId;");
|
||||
assertHas(f, "flat in ivec3 iris_entityInfo;");
|
||||
assertHas(f, "uniform vec4 entityColor;");
|
||||
assertLacks(f, "in vec4 entityColor;");
|
||||
assertLacks(f, "flat in ivec3 iris_entityInfo;");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -465,6 +470,6 @@ class AttributeTransformerInstancedTest {
|
||||
|
||||
final Map<PatchShaderType, String> entityResult = TransformPatcher.patchAttributes(VERTEX_ENTITY, null, null, null, FRAGMENT_ENTITY, TEX_LM, false);
|
||||
assertNotNull(entityResult);
|
||||
assertHas(entityResult.get(PatchShaderType.VERTEX), "uniform int entityId;");
|
||||
assertHas(entityResult.get(PatchShaderType.VERTEX), "layout(location = " + ImmediateExtendedAttribHandler.LOC_ENTITY_IDS + ") in vec4 iris_Entity;");
|
||||
}
|
||||
}
|
||||
@@ -118,10 +118,10 @@ class VertexReplayExtractionTest {
|
||||
final RwImageStoreExtractor.Result result = replayOf(TransformPatcher.patchAttributesInstanced(CRYSTAL_VERTEX, null, null, null, FRAGMENT, TEX_LM, false, Instancing.TEMPLATE));
|
||||
final List<VertexInput> inputs = RwImageStoreExtractor.parseVertexReplayInputs(result.computeSource());
|
||||
|
||||
final VertexInput entity = input(inputs, "iris_Entity");
|
||||
final VertexInput entity = input(inputs, "iris_InstEntity");
|
||||
assertEquals(InstancedAttribs.LOC_ENTITY, entity.location());
|
||||
assertEquals("uvec4", entity.glslType());
|
||||
assertTrue(result.computeSource().contains("iris_Entity = uvec4(_vg_uint("), result.computeSource());
|
||||
assertEquals("vec4", entity.glslType());
|
||||
assertTrue(result.computeSource().contains("iris_InstEntity = vec4(_vg_float("), result.computeSource());
|
||||
assertFalse(result.computeSource().contains("location"), "a location on a former interface variable does not compile in compute");
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user