mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32f8eef15b | ||
|
|
2d9d1a45b7 | ||
|
|
fa8842a64b | ||
|
|
69019b9a62 | ||
|
|
482dece0f7 | ||
|
|
ab3bf17696 | ||
|
|
cfc6e12e00 | ||
|
|
8537835d0c | ||
|
|
8e1809622c | ||
|
|
f81ee4b03a |
No files matched your search
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@05e7cd7ce0ea015acda8c10c1c39fb3a20a49e60
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||
with:
|
||||
disable-spotless-pr: true
|
||||
java-distribution: temurin
|
||||
|
||||
@@ -170,7 +170,13 @@ val archScanAsm: Configuration by configurations.creating {
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmRedirectAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||
archScanAsm(libs.asm.tree.test)
|
||||
archScanAsm(libs.asm.commons.test)
|
||||
}
|
||||
@@ -212,6 +218,8 @@ val glCoreTest by tasks.registering(Test::class) {
|
||||
.plus(downgraded(downgradeRootMainClasses))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
|
||||
|
||||
javaAgent(glsmRedirectAgent)
|
||||
|
||||
configureAngelicaJava8()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
|
||||
fun Test.javaAgent(jar: FileCollection) {
|
||||
inputs.files(jar).withPropertyName("javaAgent")
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${jar.singleFile.path}") })
|
||||
}
|
||||
@@ -267,6 +267,22 @@ tasks.test {
|
||||
configureGlsmJava8()
|
||||
}
|
||||
|
||||
val redirectAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "redirect-agent"
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent")
|
||||
}
|
||||
}
|
||||
|
||||
val redirectAgent by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(redirectAgent.name, redirectAgentJar)
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
tasks.register<Test>(taskName) {
|
||||
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
|
||||
@@ -275,6 +291,7 @@ val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
includeTags = setOf(tag)
|
||||
}
|
||||
configureGlsmJava8()
|
||||
javaAgent(files(redirectAgentJar))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -5622,6 +5644,9 @@ public class GLStateManager {
|
||||
return true;
|
||||
}
|
||||
switch (pname) {
|
||||
case GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_TEXTURE_WRAP_S, GL11.GL_TEXTURE_WRAP_T -> {
|
||||
return updateTexParameteriCache(info, pname, (int) param);
|
||||
}
|
||||
case EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT -> {
|
||||
if (info.getMaxAnisotropy() == param && isCachingEnabled()) return false;
|
||||
info.setMaxAnisotropy(param);
|
||||
@@ -5869,7 +5894,10 @@ public class GLStateManager {
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexStorage2D(target, internalFormat, width, height);
|
||||
}
|
||||
public static void glClearTexImage(int texture, int level, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearTexImage(texture, level, format, type); }
|
||||
public static void glClearTexImage(int texture, int level, int format, int type, ByteBuffer data) {
|
||||
RENDER_BACKEND.clearTexImage(texture, level, format, type);
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static int glGenSamplers() {
|
||||
requireContext("glGenSamplers");
|
||||
@@ -6732,6 +6760,7 @@ public class GLStateManager {
|
||||
throw DisplayListManager.unsupportedInList("glCopyTexImage2D");
|
||||
}
|
||||
RENDER_BACKEND.copyTexImage2D(target, level, internalFormat, x, y, width, height, border);
|
||||
TextureInfoCache.INSTANCE.onCopyTexImage2D(target, level, internalFormat, width, height);
|
||||
maybeGenerateMipmap(target, level);
|
||||
}
|
||||
|
||||
@@ -7267,6 +7296,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);
|
||||
@@ -7849,10 +7882,18 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
RENDER_BACKEND.framebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
public static void glFramebufferTexture(int target, int attachment, int texture, int level) { RENDER_BACKEND.framebufferTexture(target, attachment, texture, level); }
|
||||
public static void glFramebufferTexture(int target, int attachment, int texture, int level) {
|
||||
TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
RENDER_BACKEND.framebufferTexture(target, attachment, texture, level);
|
||||
}
|
||||
|
||||
public static void attachTextureForReading(int attachment, int texture) {
|
||||
RENDER_BACKEND.framebufferTexture2D(GL30.GL_READ_FRAMEBUFFER, attachment, GL11.GL_TEXTURE_2D, texture, 0);
|
||||
}
|
||||
|
||||
public static int glGenRenderbuffers() {
|
||||
requireContext("glGenRenderbuffers");
|
||||
@@ -7881,7 +7922,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 +7934,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); }
|
||||
@@ -8649,6 +8714,7 @@ public class GLStateManager {
|
||||
binding.setBinding(texture, level, layered, layer, access, format);
|
||||
if (texture != 0 && unit > glCtx.maxBoundImageUnit) glCtx.maxBoundImageUnit = unit;
|
||||
}
|
||||
if (access != GL15.GL_READ_ONLY) TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
RENDER_BACKEND.bindImageTexture(unit, texture, level, layered, layer, access, format);
|
||||
}
|
||||
|
||||
@@ -8656,7 +8722,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 +8739,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 +8751,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) {
|
||||
@@ -8700,6 +8768,7 @@ public class GLStateManager {
|
||||
int srcWidth, int srcHeight, int srcDepth) {
|
||||
RENDER_BACKEND.copyImageSubData(srcName, srcTarget, srcLevel, srcX, srcY, srcZ,
|
||||
dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);
|
||||
if (dstTarget != GL30.GL_RENDERBUFFER && dstLevel == 0) TextureInfoCache.INSTANCE.onContentsChanged(dstName);
|
||||
}
|
||||
public static void glDispatchCompute(int x, int y, int z) { RENDER_BACKEND.dispatchCompute(x, y, z); }
|
||||
|
||||
|
||||
@@ -267,6 +267,7 @@ public class RenderSystem {
|
||||
|
||||
public static void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
dsaState.copyTexSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
if (i == 0) TextureInfoCache.INSTANCE.onContentsChanged(destTexture);
|
||||
}
|
||||
|
||||
public static void texParameteri(int texture, int target, int pname, int param) {
|
||||
@@ -281,24 +282,28 @@ public class RenderSystem {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
@@ -358,6 +363,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
@@ -556,7 +562,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);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
public record CubeParams(float minX, float minY, float minZ, float spanX, float spanY, float spanZ, float u0, float v0, float uScaleX, float vScaleY, float uScaleZ, float vScaleZ) {
|
||||
|
||||
private static final float MIN_SPAN = 1.0e-4f;
|
||||
private static final float UV_EPSILON = 1.0e-5f;
|
||||
|
||||
public CubeParams {
|
||||
if (!(spanX > MIN_SPAN && spanY > MIN_SPAN && spanZ > MIN_SPAN)) throw new IllegalArgumentException("degenerate cube span");
|
||||
@@ -43,12 +44,25 @@ public record CubeParams(float minX, float minY, float minZ, float spanX, float
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, u0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, v0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY);
|
||||
public boolean uvInsideTexture() {
|
||||
final float u1 = u0 + 2.0f * (Math.abs(uScaleX) + uScaleZ);
|
||||
final float v1 = v0 + vScaleY + vScaleZ;
|
||||
return u0 >= 0.0f && v0 >= 0.0f && u1 <= 1.0f + UV_EPSILON && v1 <= 1.0f + UV_EPSILON;
|
||||
}
|
||||
|
||||
public static boolean allInsideTexture(CubeParams[] cubes) {
|
||||
for (CubeParams cube : cubes) {
|
||||
if (!cube.uvInsideTexture()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr, float regionU, float regionV, float regionW, float regionH) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ * regionH);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, regionU + u0 * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, regionV + v0 * regionH);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY * regionH);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -22,8 +22,8 @@ public class TextureInfo {
|
||||
protected int width = -1;
|
||||
protected int height = -1;
|
||||
|
||||
@Setter protected int minFilter = UNSET_INT;
|
||||
@Setter protected int magFilter = UNSET_INT;
|
||||
protected int minFilter = UNSET_INT;
|
||||
protected int magFilter = UNSET_INT;
|
||||
@Setter protected int wrapS = UNSET_INT;
|
||||
@Setter protected int wrapT = UNSET_INT;
|
||||
@Setter protected int maxLevel = UNSET_INT;
|
||||
@@ -37,9 +37,12 @@ public class TextureInfo {
|
||||
@Getter @Setter protected boolean generateMipmap = false;
|
||||
|
||||
@Getter protected int uploadGeneration;
|
||||
@Getter protected boolean writtenOutsideUploads;
|
||||
@Getter protected int changeStamp;
|
||||
|
||||
void bumpUploadGeneration() {
|
||||
uploadGeneration++;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
protected TextureInfo(int id) {
|
||||
@@ -78,6 +81,16 @@ public class TextureInfo {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setMinFilter(int minFilter) {
|
||||
this.minFilter = minFilter;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
public void setMagFilter(int magFilter) {
|
||||
this.magFilter = magFilter;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
public int getMinFilter() {
|
||||
if (minFilter == UNSET_INT) minFilter = queryParameteri(GL11.GL_TEXTURE_MIN_FILTER);
|
||||
return minFilter;
|
||||
|
||||
@@ -14,7 +14,10 @@ import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class TextureInfoCache {
|
||||
/**
|
||||
@@ -28,10 +31,45 @@ public class TextureInfoCache {
|
||||
|
||||
private final Int2ObjectMap<TextureInfo> cache = new Int2ObjectOpenHashMap<>();
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final List<IntConsumer> deleteListeners = new ArrayList<>();
|
||||
private int deleteCount;
|
||||
|
||||
private TextureInfoCache() {
|
||||
}
|
||||
|
||||
public int deleteCount() {
|
||||
return deleteCount;
|
||||
}
|
||||
|
||||
public void addDeleteListener(IntConsumer listener) {
|
||||
deleteListeners.add(listener);
|
||||
}
|
||||
|
||||
public void onContentsChanged(int texture) {
|
||||
if (texture <= 0) return;
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info != null) info.bumpUploadGeneration();
|
||||
}
|
||||
|
||||
public void onWrittenOutsideUploads(int texture) {
|
||||
if (texture <= 0) return;
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info == null || info.writtenOutsideUploads) return;
|
||||
info.writtenOutsideUploads = true;
|
||||
info.changeStamp++;
|
||||
}
|
||||
|
||||
private void defineLevelZero(int texture, int target, int internalformat, int width, int height) {
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
|
||||
public TextureInfo getInfo(int id) {
|
||||
if (id < 0) return null;
|
||||
|
||||
@@ -48,48 +86,27 @@ public class TextureInfoCache {
|
||||
}
|
||||
|
||||
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable Buffer pixels) {
|
||||
if (isTrackedTarget(target) && level == 0) {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, long pixels_buffer_offset) {
|
||||
if (isTrackedTarget(target) && level == 0) {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onCopyTexImage2D(int target, int level, int internalformat, int width, int height) {
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTextureImage2D(int texture, int target, int level, int internalformat, int width, int height) {
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(texture, target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexStorage2D(int target, int internalformat, int width, int height) {
|
||||
if (!isTrackedTarget(target)) return;
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
if (isTrackedTarget(target)) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexSubImage2D(int target, int level) {
|
||||
if (!isTrackedTarget(target) || level != 0) return;
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.bumpUploadGeneration();
|
||||
if (isTrackedTarget(target) && level == 0) onContentsChanged(GLStateManager.getBoundTextureForServerState());
|
||||
}
|
||||
|
||||
public static boolean isGenericCompressedInternalFormat(int internalFormat) {
|
||||
@@ -131,9 +148,11 @@ public class TextureInfoCache {
|
||||
|
||||
public void onDeleteTexture(int id) {
|
||||
if (id < 0) return;
|
||||
deleteCount++;
|
||||
|
||||
if (GLStateManager.isSplashComplete()) {
|
||||
cache.remove(id);
|
||||
for (IntConsumer listener : deleteListeners) listener.accept(id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+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);
|
||||
|
||||
@@ -18,7 +18,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
@AfterEach
|
||||
void resetClientArrayState() {
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class GLSM_VAO_UnitTest {
|
||||
int vao = UniversalVAO.genVertexArrays();
|
||||
Assertions.assertTrue(vao > 0, "GL_VERTEX_ARRAY_BINDING - VAO Created");
|
||||
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, vao, "GL_VERTEX_ARRAY_BINDING - Bound");
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
@@ -91,7 +91,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Check if everything got reset to default
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING - Unbound");
|
||||
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, 0, "GL_ARRAY_BUFFER_BINDING - Unbound");
|
||||
|
||||
@@ -103,18 +103,18 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Reset");
|
||||
|
||||
// Check if the VAO states are still valid after re-binding it
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_STRIDE, vaoVertexStride, "GL_VERTEX_ARRAY_STRIDE - Preserved");
|
||||
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
@@ -123,7 +123,7 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, vbo, "GL_ARRAY_BUFFER_BINDING - Not affected by glPushAttrib");
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
@@ -132,7 +132,7 @@ public class GLSM_VAO_UnitTest {
|
||||
@Test
|
||||
void testGenericAttributeSupersedesFfpConventional() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
@@ -145,7 +145,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public class GLSM_VAO_UnitTest {
|
||||
@Test
|
||||
void testGenericAttributeWinsRegardlessOfOrder() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
@@ -166,7 +166,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public class GLSM_VAO_UnitTest {
|
||||
@Test
|
||||
void testFfpConventionalAppliesWithoutGenericAttribute() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
@@ -186,7 +186,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
@@ -198,12 +198,4 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL11.GL_COLOR_ARRAY, false, "GL_COLOR_ARRAY Default State");
|
||||
verifyState(GL11.GL_INDEX_ARRAY, false, "GL_INDEX_ARRAY Default State");
|
||||
}
|
||||
|
||||
private static void glBindVertexArray(int array) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (array == 0) array = glCtx.defaultVAO;
|
||||
glCtx.boundVAO = array;
|
||||
glCtx.vaos.onBindVertexArrayPre(array);
|
||||
UniversalVAO.bindVertexArray(array);
|
||||
}
|
||||
}
|
||||
+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");
|
||||
|
||||
@@ -35,7 +35,6 @@ class WideLineClipGLTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FfpFixture.routeVertexFormats(true);
|
||||
savedEmulation = GLStateManager.wideLineEmulationEnabled;
|
||||
GLStateManager.wideLineEmulationEnabled = true;
|
||||
|
||||
@@ -54,7 +53,6 @@ class WideLineClipGLTest {
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
FfpFixture.routeVertexFormats(false);
|
||||
GLStateManager.wideLineEmulationEnabled = savedEmulation;
|
||||
GLStateManager.glLineWidth(1.0f);
|
||||
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
|
||||
|
||||
+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));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_ERR">
|
||||
<Console name="Console" target="SYSTEM_ERR" follow="true">
|
||||
<PatternLayout pattern="[%-5level] %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
GlsmRedirectAgent.requireInstalled();
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMCoreExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
@@ -81,6 +82,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
public void afterEach(ExtensionContext context) {
|
||||
final int error = GL11.glGetError();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
GlsmRedirectAgent.requireInstalled();
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
@@ -179,6 +180,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
fail("Test leaked an open display-list recorder! Missing glEndList.");
|
||||
}
|
||||
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
final int error = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, error,
|
||||
() -> "GL Error: 0x" + Integer.toHexString(error));
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.ClassDump;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.ModRedirector;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public final class GlsmRedirectAgent {
|
||||
|
||||
private static final String GTNHLIB = "com/gtnewhorizon/gtnhlib/";
|
||||
private static final List<Throwable> FAILURES = new CopyOnWriteArrayList<>();
|
||||
private static final AtomicInteger TRANSFORMED = new AtomicInteger();
|
||||
private static boolean installed;
|
||||
|
||||
private GlsmRedirectAgent() {}
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) {
|
||||
install(instrumentation);
|
||||
}
|
||||
|
||||
public static void install(Instrumentation instrumentation) {
|
||||
final ModRedirector redirector = new ModRedirector(ClassDump.DISABLED);
|
||||
addTransformer(instrumentation, (className, classfile) -> {
|
||||
if (!className.startsWith(GTNHLIB)) return null;
|
||||
final byte[] out = redirector.transform(className.replace('/', '.'), classfile, null);
|
||||
if (out == classfile) return null;
|
||||
TRANSFORMED.incrementAndGet();
|
||||
return out;
|
||||
});
|
||||
installed = true;
|
||||
}
|
||||
|
||||
public static void addTransformer(Instrumentation instrumentation, BiFunction<String, byte[], byte[]> transform) {
|
||||
instrumentation.addTransformer(new Guarded(transform));
|
||||
}
|
||||
|
||||
public static void requireInstalled() {
|
||||
if (!installed) throw new IllegalStateException("GL tests need the GLSM redirect agent");
|
||||
}
|
||||
|
||||
public static void assertNoFailures() {
|
||||
if (!FAILURES.isEmpty()) fail("agent transform failures: " + FAILURES, FAILURES.get(0));
|
||||
}
|
||||
|
||||
public static int transformedCount() {
|
||||
return TRANSFORMED.get();
|
||||
}
|
||||
|
||||
private static final class Guarded implements ClassFileTransformer {
|
||||
|
||||
private final BiFunction<String, byte[], byte[]> transform;
|
||||
|
||||
private Guarded(BiFunction<String, byte[], byte[]> transform) {
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> redefined, ProtectionDomain domain, byte[] classfile) {
|
||||
if (className == null) return null;
|
||||
try {
|
||||
return transform.apply(className, classfile);
|
||||
} catch (Throwable t) {
|
||||
FAILURES.add(new IllegalStateException("transforming " + className, t));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -260,7 +260,7 @@ public final class CubeParityFixture {
|
||||
InstancedAttribs.writeTail(ptr, 0xFFFFFFFF, 0, 0L);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE, BRIGHTNESS);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 4, BRIGHTNESS);
|
||||
cube.writeTexture(ptr);
|
||||
cube.writeTexture(ptr, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
records.position(0).limit(specs.length * CubeInstancedAttribs.STRIDE);
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import org.lwjgl.BufferUtils;
|
||||
@@ -21,7 +18,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 +28,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 +57,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();
|
||||
@@ -135,27 +133,6 @@ public final class FfpFixture {
|
||||
GLStateManager.glVertexAttribPointer(loc, size, type, normalized, stride, offset);
|
||||
}
|
||||
|
||||
private static boolean formatRoutingRegistered;
|
||||
private static boolean routeFormats;
|
||||
|
||||
public static void routeVertexFormats(boolean enabled) {
|
||||
routeFormats = enabled;
|
||||
if (formatRoutingRegistered) return;
|
||||
formatRoutingRegistered = true;
|
||||
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
|
||||
if (!routeFormats) return false;
|
||||
final int stride = format.getVertexSize();
|
||||
int elementOffset = (int) offset;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
final Usage usage = element.getUsage();
|
||||
final boolean normalized = usage == Usage.COLOR || usage == Usage.NORMAL;
|
||||
attrib(usage.getAttributeLocation(), element.getCount(), element.getType().getGlType(), normalized, stride, elementOffset);
|
||||
elementOffset += element.getByteSize();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
+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++;
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,7 @@ val glsmSdlAgent = sourceSets.create("glsmSdlAgent") {
|
||||
|
||||
dependencies {
|
||||
"glsmSdlAgentCompileOnly"(project(":glsm"))
|
||||
"glsmSdlAgentCompileOnly"(testFixtures(project(":glsm")))
|
||||
"glsmSdlAgentCompileOnly"(libs.asm.tree.test)
|
||||
testImplementation(glsmSdlAgent.output)
|
||||
}
|
||||
@@ -155,9 +156,8 @@ val glsmSdlTest by tasks.registering(Test::class) {
|
||||
useJUnitPlatform {
|
||||
includeTags(glsmSdlTag)
|
||||
}
|
||||
dependsOn(tasks.named("glsmSdlStubsClasses"), glsmSdlRedirectAgentJar)
|
||||
val agentJarPath = glsmSdlRedirectAgentJar.flatMap { it.archiveFile }.map { it.asFile.path }
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${agentJarPath.get()}") })
|
||||
dependsOn(tasks.named("glsmSdlStubsClasses"))
|
||||
javaAgent(files(glsmSdlRedirectAgentJar))
|
||||
jvmArgs(
|
||||
"-Dangelica.sdlgpu.enable=true",
|
||||
"-Dangelica.sdlgpu.disablePresenterThread=true",
|
||||
|
||||
+3
-48
@@ -1,60 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class GlsmSdlRedirectAgent {
|
||||
|
||||
private static final String GTNHLIB = "com/gtnewhorizon/gtnhlib/";
|
||||
private static final List<String> FAILURES = Collections.synchronizedList(new ArrayList<>());
|
||||
private static final AtomicInteger TRANSFORMED_COUNT = new AtomicInteger();
|
||||
|
||||
private GlsmSdlRedirectAgent() {}
|
||||
|
||||
public static void premain(String args, Instrumentation instrumentation) {
|
||||
instrumentation.addTransformer(new Redirector());
|
||||
}
|
||||
|
||||
public static List<String> failures() {
|
||||
return List.copyOf(FAILURES);
|
||||
}
|
||||
|
||||
public static int transformedCount() {
|
||||
return TRANSFORMED_COUNT.get();
|
||||
}
|
||||
|
||||
private static final class Redirector implements ClassFileTransformer {
|
||||
|
||||
private final GLSMRedirector redirector = new GLSMRedirector();
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> redefined, ProtectionDomain domain, byte[] classfile) {
|
||||
if (className == null) return null;
|
||||
try {
|
||||
final byte[] observed = MovedSamplerProbe.instrument(className, classfile);
|
||||
if (observed != null) return observed;
|
||||
if (!className.startsWith(GTNHLIB) || !redirector.shouldTransform(classfile)) return null;
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(classfile).accept(node, 0);
|
||||
if (!redirector.transformClassNode(className.replace('/', '.'), node)) return null;
|
||||
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
node.accept(writer);
|
||||
TRANSFORMED_COUNT.incrementAndGet();
|
||||
return writer.toByteArray();
|
||||
} catch (Throwable t) {
|
||||
FAILURES.add(className + ": " + t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
GlsmRedirectAgent.install(instrumentation);
|
||||
GlsmRedirectAgent.addTransformer(instrumentation, MovedSamplerProbe::instrument);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -1880,6 +1881,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
if (cs.boundFboId == framebuffer) return;
|
||||
final FboState drawing = framebuffer != 0 ? resourceManager.getFbo(framebuffer) : null;
|
||||
if (drawing != null && drawing.readOnlyColor) makeColorTargets(drawing);
|
||||
final int oldFboId = cs.boundFboId;
|
||||
final boolean structurallyIdentical = oldFboId != 0 && framebuffer != 0 && fboClearTracker.fbosHaveSameAttachments(cs, oldFboId, framebuffer);
|
||||
cs.boundFboId = framebuffer;
|
||||
@@ -1892,36 +1895,52 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
cs.pipeline.setColorTargetFormats(cachedSwapchainFormatArray);
|
||||
cs.pipeline.setDrawBuffers(null);
|
||||
cs.pipeline.setDepthTargetFormat(resourceManager.getSwapchainDepthStencilFormat());
|
||||
} else {
|
||||
final FboState fbo = resourceManager.getFbo(framebuffer);
|
||||
if (fbo != null && fbo.getColorTexture() != 0) {
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, fbo);
|
||||
cs.pipeline.setDepthTargetFormat(fbo.depthTexture != 0 ? fbo.depthFormat : 0);
|
||||
}
|
||||
} else if (drawing != null && drawing.getColorTexture() != 0) {
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, drawing);
|
||||
cs.pipeline.setDepthTargetFormat(drawing.depthTexture != 0 ? drawing.depthFormat : 0);
|
||||
}
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
|
||||
private void makeColorTargets(FboState fbo) {
|
||||
for (int i = 0; i < fbo.colorAttachmentCount; i++) {
|
||||
final int glId = fbo.colorGlIds[i];
|
||||
if (glId == 0) continue;
|
||||
fbo.colorTextures[i] = resourceManager.ensureTextureUsage(glId, SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET);
|
||||
resourceManager.markFboAttachment(glId);
|
||||
}
|
||||
fbo.readOnlyColor = false;
|
||||
fbo.targetsDirty = true;
|
||||
}
|
||||
|
||||
@Override public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
final ContextState cs = s();
|
||||
if (cs.boundFboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(cs.boundFboId);
|
||||
final int fboId = target == GL30.GL_READ_FRAMEBUFFER ? cs.boundReadFboId : cs.boundFboId;
|
||||
if (fboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(fboId);
|
||||
if (fbo == null) return;
|
||||
final boolean drawTarget = fboId == cs.boundFboId;
|
||||
|
||||
final int colorIdx = attachment - GL30.GL_COLOR_ATTACHMENT0;
|
||||
if (colorIdx >= 0 && colorIdx < ContextState.MAX_COLOR_ATTACHMENTS) {
|
||||
if (texture == 0) {
|
||||
fbo.detachColor(colorIdx);
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, fbo);
|
||||
cs.pipeline.markOutputDirty();
|
||||
if (drawTarget) {
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, fbo);
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
return;
|
||||
}
|
||||
final long handle = resourceManager.ensureTextureUsage(texture, SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET);
|
||||
final long handle = resourceManager.ensureTextureUsage(texture, drawTarget ? SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET : SDL_GPU_TEXTUREUSAGE_SAMPLER);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
||||
fbo.colorTextures[colorIdx] = handle;
|
||||
fbo.colorGlIds[colorIdx] = texture;
|
||||
fbo.colorLevels[colorIdx] = level;
|
||||
resourceManager.markFboAttachment(texture);
|
||||
if (drawTarget) {
|
||||
resourceManager.markFboAttachment(texture);
|
||||
} else {
|
||||
fbo.readOnlyColor = true;
|
||||
}
|
||||
fbo.targetsDirty = true;
|
||||
if (meta != null) {
|
||||
fbo.colorFormats[colorIdx] = meta.sdlFormat();
|
||||
@@ -1932,16 +1951,20 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
fbo.colorAttachmentCount = Math.max(fbo.colorAttachmentCount, colorIdx + 1);
|
||||
fbo.cachedFormatsDirty = true;
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, fbo);
|
||||
cs.pipeline.markOutputDirty();
|
||||
if (drawTarget) {
|
||||
fboClearTracker.updatePipelineCacheColorFormats(cs, fbo);
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
} else if (attachment == GL30.GL_DEPTH_ATTACHMENT || attachment == GL30.GL_DEPTH_STENCIL_ATTACHMENT || attachment == GL30.GL_STENCIL_ATTACHMENT) {
|
||||
final boolean stencilOnly = attachment == GL30.GL_STENCIL_ATTACHMENT;
|
||||
if (texture == 0) {
|
||||
if (stencilOnly && fbo.depthGlId != 0) return;
|
||||
fbo.detachDepth();
|
||||
cs.pipeline.hasDepthTarget = false;
|
||||
cs.pipeline.depthTargetFormat = 0;
|
||||
cs.pipeline.markOutputDirty();
|
||||
if (drawTarget) {
|
||||
cs.pipeline.hasDepthTarget = false;
|
||||
cs.pipeline.depthTargetFormat = 0;
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stencilOnly && fbo.depthGlId != 0 && fbo.depthGlId != texture) {
|
||||
@@ -1961,9 +1984,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (meta != null) {
|
||||
fbo.depthFormat = meta.sdlFormat();
|
||||
}
|
||||
cs.pipeline.hasDepthTarget = true;
|
||||
cs.pipeline.depthTargetFormat = fbo.depthFormat;
|
||||
cs.pipeline.markOutputDirty();
|
||||
if (drawTarget) {
|
||||
cs.pipeline.hasDepthTarget = true;
|
||||
cs.pipeline.depthTargetFormat = fbo.depthFormat;
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
@@ -13,6 +13,7 @@ public final class FboState {
|
||||
public final int[] colorFormats = new int[MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] colorLevels = new int[MAX_COLOR_ATTACHMENTS];
|
||||
public int colorAttachmentCount;
|
||||
public boolean readOnlyColor;
|
||||
|
||||
public int[] drawBuffers = {0};
|
||||
public int readBufferIndex;
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,33 @@ class GlsmSdlCopyBlitTest {
|
||||
GLStateManager.glDeleteTextures(destination);
|
||||
}
|
||||
|
||||
@Test
|
||||
void attachingToTheReadFramebufferLeavesTheDrawFramebufferAlone() {
|
||||
final int source = GlsmSdlHeadlessRig.createSolidTexture(GREEN);
|
||||
final int drawColor = GlsmSdlHeadlessRig.createSolidTexture(BLUE);
|
||||
final int readFbo = GLStateManager.glGenFramebuffers();
|
||||
final int drawFbo = GlsmSdlHeadlessRig.fboWithColor(drawColor, 0);
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, readFbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_READ_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, source, 0);
|
||||
GLStateManager.glBlitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, GL11.GL_COLOR_BUFFER_BIT, GL11.GL_NEAREST);
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTextureLevel(drawColor, 0), GREEN, "the blit reads the read framebuffer's attachment");
|
||||
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, drawFbo);
|
||||
GLStateManager.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTextureLevel(drawColor, 0), RED, "the draw framebuffer kept its own attachment");
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTextureLevel(source, 0), GREEN, "the read attachment never became a draw target");
|
||||
} finally {
|
||||
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(readFbo);
|
||||
GLStateManager.glDeleteFramebuffers(drawFbo);
|
||||
GLStateManager.glDeleteTextures(source);
|
||||
GLStateManager.glDeleteTextures(drawColor);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> levelBlits() {
|
||||
return Stream.of(
|
||||
Arguments.of("1:1 into level 0: reads the source attachment level", SIZE / 2, 0),
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+2
-4
@@ -1,12 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
|
||||
|
||||
@Override
|
||||
@@ -22,7 +21,6 @@ public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEac
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) {
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(),
|
||||
() -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures());
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
@@ -71,9 +72,8 @@ public final class GlsmSdlHeadlessRig {
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete("glsmSdlHeadlessRig");
|
||||
|
||||
assertTrue(GlsmSdlRedirectAgent.transformedCount() > 0, "redirect agent transformed no classes");
|
||||
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(),
|
||||
() -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures());
|
||||
assertTrue(GlsmRedirectAgent.transformedCount() > 0, "redirect agent transformed no classes");
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
|
||||
createTarget();
|
||||
booted = true;
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
|
||||
|
||||
@@ -24,6 +24,6 @@ public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, Befo
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) {
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
assertTrue(GlsmSdlRedirectAgent.failures().isEmpty(), () -> "redirect agent recorded failures: " + GlsmSdlRedirectAgent.failures());
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
}
|
||||
}
|
||||
+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");
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,7 @@ public class FontStrategist {
|
||||
|
||||
// The splash font renderer can only bind its own ascii texture.
|
||||
private static FontProvider findModernGlyph(BatchingFontRenderer me, char chr, boolean forceUnicode) {
|
||||
if (!FontConfig.enableModernFont || forceUnicode || me.isSplash) { return null; }
|
||||
if (!FontConfig.enableModernFont || forceUnicode || me.isSplash || !GLStateManager.isMainThread()) { return null; }
|
||||
return FontProviderBitmap.find(chr);
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,10 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableCubeInstancing;
|
||||
|
||||
@Config.Comment("Stitch a texture atlas between entity and block entity models so different textures draw together (requires cube instancing)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableEntityTextureAtlas;
|
||||
|
||||
@Config.Comment("Skip the end-of-frame shader buffer copy by ping-ponging buffers. Disable if a shader pack misrenders.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ public enum Mixins implements IMixins {
|
||||
"angelica.entity.MixinRendererLivingEntity_GlintClock",
|
||||
"angelica.entity.MixinRendererLivingEntity_ModelPassDraws",
|
||||
"angelica.entity.MixinRender_BatchEligibility",
|
||||
"angelica.entity.MixinRender_EntityFireBatch",
|
||||
"angelica.entity.MixinRender_NameTagDraws",
|
||||
"angelica.entity.MixinRender_ShadowBatch",
|
||||
"angelica.entity.MixinTextureManager",
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiVideoSettings;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.MathHelper;
|
||||
@@ -49,6 +50,7 @@ import com.gtnewhorizons.angelica.compat.mojang.CompatMathHelper;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.AngelicaTesrMeshCache;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityTextureAtlas;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
@@ -61,9 +63,11 @@ 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;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
|
||||
import com.gtnewhorizons.angelica.iris.ShaderWarmup;
|
||||
@@ -207,6 +211,8 @@ public final class ClientProxy extends CommonProxy {
|
||||
ItemRenderListManager.registerReloadListener();
|
||||
BlockRenderListManager.registerReloadListener();
|
||||
}
|
||||
((IReloadableResourceManager) mc.getResourceManager()).registerReloadListener(manager -> EntityTextureAtlas.INSTANCE.clear());
|
||||
TextureInfoCache.INSTANCE.addDeleteListener(EntityTextureAtlas.INSTANCE::onTextureDeleted);
|
||||
FontProviderBitmap.registerReloadListener();
|
||||
|
||||
// Debug tooling
|
||||
@@ -383,7 +389,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);
|
||||
|
||||
+1
-1
@@ -488,7 +488,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
if (te != null) {
|
||||
final NBTTagCompound teNbt = new NBTTagCompound();
|
||||
te.writeToNBT(teNbt);
|
||||
teBlockId = teMap.resolve(block, teNbt);
|
||||
teBlockId = teMap.resolve(block, teNbt, metadata);
|
||||
} else {
|
||||
teBlockId = -1;
|
||||
}
|
||||
|
||||
+16
-3
@@ -71,9 +71,18 @@ public final class BiomeVertexBlender {
|
||||
return blender != null && blender.isActive();
|
||||
}
|
||||
|
||||
public int deferBlockTint() {
|
||||
public void arm() {
|
||||
cache.armedBlender = this;
|
||||
}
|
||||
|
||||
public void disarm() {
|
||||
cache.armedBlender = null;
|
||||
}
|
||||
|
||||
private boolean tryDefer(SmoothBiomeColorCache.ColorType type, int x, int y, int z) {
|
||||
if (this.type != type || blockX != x || blockY != y || blockZ != z) return false;
|
||||
replacedBlockTint = true;
|
||||
return 0xFFFFFF;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean beginFace(RenderBlocks renderer, int face, double x, double y, double z, IIcon icon) {
|
||||
@@ -129,7 +138,11 @@ public final class BiomeVertexBlender {
|
||||
|
||||
public static long smoothColor(IBlockAccess access, SmoothBiomeColorCache.ColorType type, int x, int y, int z) {
|
||||
final SmoothBiomeColorCache cache = SmoothBiomeColorCache.getActiveCache();
|
||||
if (cache != null) return cache.getColor(type, x, y, z) & 0xFFFFFFFFL;
|
||||
if (cache != null) {
|
||||
final BiomeVertexBlender armed = cache.armedBlender;
|
||||
if (armed != null && armed.tryDefer(type, x, y, z)) return 0xFFFFFFL;
|
||||
return cache.getColor(type, x, y, z) & 0xFFFFFFFFL;
|
||||
}
|
||||
if (access instanceof WorldClientExtension ext) return ext.celeritas$getSmoothBiomeColorCache().getColor(type, x, y, z) & 0xFFFFFFFFL;
|
||||
return -1L;
|
||||
}
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ public class SmoothBiomeColorCache extends BiomeColorCache<BiomeGenBase, SmoothB
|
||||
private final double[] temperatureNoise;
|
||||
private NoiseGeneratorPerlin noiseSource;
|
||||
private int minX, minZ, waterY;
|
||||
BiomeVertexBlender armedBlender;
|
||||
private static final ThreadLocal<SmoothBiomeColorCache> activeCache = new ThreadLocal<>();
|
||||
|
||||
private static final class Slice extends BoxBlur.ColorBuffer {
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -41,6 +42,11 @@ public class BlockRenderListManager {
|
||||
return itemFrameDisplayLists[variant];
|
||||
}
|
||||
|
||||
public static boolean rendersPlainItem(RenderBlocks rb, Block block, float brightness) {
|
||||
return !isISBRH(block.getRenderType()) && !rb.enableAO && rb.overrideBlockTexture == null && brightness == 1.0F && rb.useInventoryTint
|
||||
&& rb.blockAccess == null && (rb.uvRotateEast | rb.uvRotateWest | rb.uvRotateSouth | rb.uvRotateNorth | rb.uvRotateTop | rb.uvRotateBottom) == 0;
|
||||
}
|
||||
|
||||
public static int callOrStartCompiling(int cached) {
|
||||
if (cached != 0) {
|
||||
GLStateManager.glCallList(cached);
|
||||
|
||||
@@ -35,6 +35,7 @@ public final class DroppedItemInstancer {
|
||||
private static final AngelicaTesrMeshCache.GtnhMeshBackend iconBackend = new AngelicaTesrMeshCache.GtnhMeshBackend();
|
||||
private static BakedTransformCapture blockBackend;
|
||||
private static boolean basePart;
|
||||
private static int itemsDrawingBlocks;
|
||||
private static final GlintCapture glintCapture = new GlintCapture();
|
||||
private static boolean glintSeen;
|
||||
private static final ItemPropCache.ItemProp glintProperties = new ItemPropCache.ItemProp();
|
||||
@@ -199,6 +200,24 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
|
||||
public static long block(ItemStack stack, RenderBlocks rb, Block block, int meta, float brightness, boolean unfilteredAtlas) {
|
||||
final long part = itemBlock(stack, rb, block, meta, brightness, unfilteredAtlas);
|
||||
if (part != SKIP) itemsDrawingBlocks++;
|
||||
return part;
|
||||
}
|
||||
|
||||
public static void endBlock(long part) {
|
||||
if (part != SKIP) itemsDrawingBlocks--;
|
||||
endPart(part);
|
||||
}
|
||||
|
||||
public static long directBlock(RenderBlocks rb, Block block, int meta, float brightness) {
|
||||
if (itemsDrawingBlocks != 0 || !ModelPartBatcher.INSTANCE.entitiesActive()) return RUN;
|
||||
if (!BatchEligibility.batchingAllowed()) return GLStateManager.drawCalls;
|
||||
final TesrMaterial material = EntityMaterials.itemFromCurrentState(false);
|
||||
return material != null ? batchBlock(material, rb, block, meta, brightness) : GLStateManager.drawCalls;
|
||||
}
|
||||
|
||||
private static long itemBlock(ItemStack stack, RenderBlocks rb, Block block, int meta, float brightness, boolean unfilteredAtlas) {
|
||||
final TesrMaterial material = material(stack, unfilteredAtlas);
|
||||
if (material == null) {
|
||||
return RUN;
|
||||
@@ -246,6 +265,10 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
}
|
||||
|
||||
public static void forgetItemDraws() {
|
||||
itemsDrawingBlocks = 0;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
icons.clear();
|
||||
blocks.clear();
|
||||
@@ -307,10 +330,7 @@ public final class DroppedItemInstancer {
|
||||
|
||||
private static BailReason blockStateBail(RenderBlocks renderBlocks, Block block, float brightness) {
|
||||
if (BlockRenderListManager.isISBRH(block.getRenderType())) return BailReason.ISBRH;
|
||||
if (renderBlocks.enableAO || renderBlocks.overrideBlockTexture != null || brightness != 1.0F || !renderBlocks.useInventoryTint
|
||||
|| renderBlocks.blockAccess != null || (renderBlocks.uvRotateEast | renderBlocks.uvRotateWest | renderBlocks.uvRotateSouth
|
||||
| renderBlocks.uvRotateNorth | renderBlocks.uvRotateTop | renderBlocks.uvRotateBottom) != 0) return BailReason.BLOCK_STATE;
|
||||
return null;
|
||||
return BlockRenderListManager.rendersPlainItem(renderBlocks, block, brightness) ? null : BailReason.BLOCK_STATE;
|
||||
}
|
||||
|
||||
private static final class IconMesh {
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Copies entity and block entity textures into a shared atlas, so model boxes with different textures land in one instanced draw.
|
||||
*/
|
||||
public final class EntityTextureAtlas {
|
||||
|
||||
public static final EntityTextureAtlas INSTANCE = new EntityTextureAtlas();
|
||||
|
||||
static final int PAGE_SIZE = 2048;
|
||||
private static final int MAX_PAGES = 4;
|
||||
private static final int MAX_EDGE = 512;
|
||||
private static final int MAX_QUICK_REFRESHES = 8;
|
||||
private static final long QUICK_REFRESH_MS = 1000L;
|
||||
|
||||
public static final class Slot {
|
||||
static final Slot WHOLE_TEXTURE = new Slot();
|
||||
|
||||
final Page page;
|
||||
final int width, height;
|
||||
final int x, y;
|
||||
final float u, v, w, h;
|
||||
final Matrix4f uvMatrix;
|
||||
|
||||
Slot(Page page, int x, int y, int width, int height) {
|
||||
this.page = page;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.u = (x + 1) / (float) PAGE_SIZE;
|
||||
this.v = (y + 1) / (float) PAGE_SIZE;
|
||||
this.w = width / (float) PAGE_SIZE;
|
||||
this.h = height / (float) PAGE_SIZE;
|
||||
this.uvMatrix = new Matrix4f().translation(u, v, 0.0f).scale(w, h, 1.0f);
|
||||
}
|
||||
|
||||
private Slot() {
|
||||
this.page = null;
|
||||
this.x = this.y = this.width = this.height = 0;
|
||||
this.u = this.v = 0.0f;
|
||||
this.w = this.h = 1.0f;
|
||||
this.uvMatrix = new Matrix4f();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Shelf {
|
||||
final int y, height;
|
||||
int x;
|
||||
|
||||
Shelf(int y, int height) {
|
||||
this.y = y;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Page {
|
||||
final ResourceLocation location;
|
||||
final int texture;
|
||||
final ObjectArrayList<Shelf> shelves = new ObjectArrayList<>();
|
||||
int nextShelfY;
|
||||
|
||||
Page(ResourceLocation location, int texture) {
|
||||
this.location = location;
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
long place(int width, int height) {
|
||||
Shelf best = null;
|
||||
for (Shelf shelf : shelves) {
|
||||
if (shelf.height >= height && PAGE_SIZE - shelf.x >= width && (best == null || shelf.height < best.height)) best = shelf;
|
||||
}
|
||||
if (best != null && best.height > 2 * height && PAGE_SIZE - nextShelfY >= height) best = null;
|
||||
if (best == null) {
|
||||
if (PAGE_SIZE - nextShelfY < height || width > PAGE_SIZE) return -1L;
|
||||
best = new Shelf(nextShelfY, height);
|
||||
shelves.add(best);
|
||||
nextShelfY += height;
|
||||
}
|
||||
final long at = ((long) best.x << 32) | best.y;
|
||||
best.x += width;
|
||||
return at;
|
||||
}
|
||||
|
||||
void empty() {
|
||||
shelves.clear();
|
||||
nextShelfY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PageTexture extends AbstractTexture {
|
||||
PageTexture(int texture) {
|
||||
this.glTextureId = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTexture(IResourceManager resourceManager) {}
|
||||
}
|
||||
|
||||
private static final class Entry {
|
||||
final TextureInfo info;
|
||||
int uploadGeneration;
|
||||
int quickRefreshes;
|
||||
long lastRefreshMs;
|
||||
Slot slot;
|
||||
int noRoomWidth = -1, noRoomHeight = -1;
|
||||
|
||||
Entry(TextureInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
|
||||
private final Int2ObjectOpenHashMap<Entry> entries = new Int2ObjectOpenHashMap<>();
|
||||
private final ObjectArrayList<Page> pages = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<Slot> freeSlots = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<Slot> releasedThisPass = new ObjectArrayList<>();
|
||||
private int readFbo, drawFbo;
|
||||
private int drawFboPage;
|
||||
private int lastTexture = -1;
|
||||
private boolean lastPbrPipeline;
|
||||
private TextureInfo lastInfo;
|
||||
private int lastChangeStamp, lastDeleteCount, lastPbrHolderChanges;
|
||||
private Slot lastSlot;
|
||||
|
||||
private EntityTextureAtlas() {}
|
||||
|
||||
Slot slotFor(int texture, boolean pbrPipeline) {
|
||||
if (texture == lastTexture && pbrPipeline == lastPbrPipeline && lastInfo.getChangeStamp() == lastChangeStamp
|
||||
&& TextureInfoCache.INSTANCE.deleteCount() == lastDeleteCount && PBRTextureManager.INSTANCE.holderChanges() == lastPbrHolderChanges) {
|
||||
return lastSlot;
|
||||
}
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texture);
|
||||
if (info == null) return null;
|
||||
lastSlot = lookUp(texture, info, pbrPipeline);
|
||||
lastTexture = texture;
|
||||
lastPbrPipeline = pbrPipeline;
|
||||
lastInfo = info;
|
||||
lastChangeStamp = info.getChangeStamp();
|
||||
lastDeleteCount = TextureInfoCache.INSTANCE.deleteCount();
|
||||
lastPbrHolderChanges = PBRTextureManager.INSTANCE.holderChanges();
|
||||
return lastSlot;
|
||||
}
|
||||
|
||||
private Slot lookUp(int texture, TextureInfo info, boolean pbrPipeline) {
|
||||
Entry entry = entries.get(texture);
|
||||
if (entry == null || entry.info != info) {
|
||||
if (entry != null) release(entry);
|
||||
entry = new Entry(info);
|
||||
entries.put(texture, entry);
|
||||
}
|
||||
if (!eligible(texture, entry, pbrPipeline)) {
|
||||
if (info.isWrittenOutsideUploads()) release(entry);
|
||||
return null;
|
||||
}
|
||||
final int width = info.getWidth(), height = info.getHeight();
|
||||
if (entry.slot != null && (entry.slot.width != width || entry.slot.height != height)) release(entry);
|
||||
if (entry.slot == null) {
|
||||
entry.slot = takeFreeSlot(width, height);
|
||||
if (entry.slot == null && (width != entry.noRoomWidth || height != entry.noRoomHeight)) entry.slot = placeOnPage(width, height);
|
||||
if (entry.slot == null) {
|
||||
entry.noRoomWidth = width;
|
||||
entry.noRoomHeight = height;
|
||||
return null;
|
||||
}
|
||||
} else if (entry.uploadGeneration == info.getUploadGeneration()) {
|
||||
return entry.slot;
|
||||
} else if (recordRefresh(entry)) {
|
||||
release(entry);
|
||||
return null;
|
||||
}
|
||||
copy(texture, entry.slot);
|
||||
entry.uploadGeneration = info.getUploadGeneration();
|
||||
return entry.slot;
|
||||
}
|
||||
|
||||
private static boolean recordRefresh(Entry entry) {
|
||||
final long now = System.currentTimeMillis();
|
||||
entry.quickRefreshes = now - entry.lastRefreshMs < QUICK_REFRESH_MS ? entry.quickRefreshes + 1 : 1;
|
||||
entry.lastRefreshMs = now;
|
||||
return entry.quickRefreshes > MAX_QUICK_REFRESHES;
|
||||
}
|
||||
|
||||
private static boolean eligible(int texture, Entry entry, boolean pbrPipeline) {
|
||||
final TextureInfo info = entry.info;
|
||||
final int width = info.getWidth();
|
||||
final int height = info.getHeight();
|
||||
if (width <= 0 || height <= 0 || width > MAX_EDGE || height > MAX_EDGE) return false;
|
||||
if (info.getTarget() != GL11.GL_TEXTURE_2D || info.isWrittenOutsideUploads() || entry.quickRefreshes > MAX_QUICK_REFRESHES) return false;
|
||||
final int format = info.getInternalFormat();
|
||||
if (format != GL11.GL_RGBA && format != GL11.GL_RGBA8) return false;
|
||||
if (info.getMinFilter() != GL11.GL_NEAREST || info.getMagFilter() != GL11.GL_NEAREST) return false;
|
||||
return !pbrPipeline || !PBRTextureManager.INSTANCE.hasLoadedPbrTextures(texture);
|
||||
}
|
||||
|
||||
private void release(Entry entry) {
|
||||
if (entry.slot != null) releasedThisPass.add(entry.slot);
|
||||
entry.slot = null;
|
||||
}
|
||||
|
||||
void recycleReleasedRegions() {
|
||||
if (releasedThisPass.isEmpty()) return;
|
||||
freeSlots.addAll(releasedThisPass);
|
||||
releasedThisPass.clear();
|
||||
}
|
||||
|
||||
public void onTextureDeleted(int texture) {
|
||||
final Entry entry = entries.remove(texture);
|
||||
if (entry != null) release(entry);
|
||||
}
|
||||
|
||||
private Slot takeFreeSlot(int width, int height) {
|
||||
for (int i = 0, n = freeSlots.size(); i < n; i++) {
|
||||
final Slot free = freeSlots.get(i);
|
||||
if (free.width == width && free.height == height) return freeSlots.remove(i);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Slot placeOnPage(int width, int height) {
|
||||
for (int i = 0, n = pages.size(); i <= n; i++) {
|
||||
final Page page = i < n ? pages.get(i) : newPage();
|
||||
if (page == null) return null;
|
||||
final long at = page.place(width + 2, height + 2);
|
||||
if (at >= 0) return new Slot(page, (int) (at >>> 32), (int) at, width, height);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Page newPage() {
|
||||
if (pages.size() == MAX_PAGES) return null;
|
||||
final int previous = GLStateManager.getBoundTextureForServerState();
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, PAGE_SIZE, PAGE_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
final ResourceLocation location = new ResourceLocation("angelica", "entity_atlas_" + pages.size());
|
||||
Minecraft.getMinecraft().getTextureManager().loadTexture(location, new PageTexture(texture));
|
||||
final Page page = new Page(location, texture);
|
||||
pages.add(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
private void copy(int texture, Slot slot) {
|
||||
if (readFbo == 0) {
|
||||
readFbo = GLStateManager.glGenFramebuffers();
|
||||
drawFbo = GLStateManager.glGenFramebuffers();
|
||||
}
|
||||
final int previousRead = GLStateManager.getReadFramebuffer();
|
||||
final int previousDraw = GLStateManager.getDrawFramebuffer();
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, readFbo);
|
||||
GLStateManager.attachTextureForReading(GL30.GL_COLOR_ATTACHMENT0, texture);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, drawFbo);
|
||||
assert slot.page != null;
|
||||
if (drawFboPage != slot.page.texture) {
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_DRAW_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, slot.page.texture, 0);
|
||||
drawFboPage = slot.page.texture;
|
||||
}
|
||||
final int w = slot.width, h = slot.height, x = slot.x + 1, y = slot.y + 1;
|
||||
final int[] fromX = {0, 0, w - 1}, widths = {w, 1, 1}, toX = {x, x - 1, x + w};
|
||||
final int[] fromY = {0, 0, h - 1}, heights = {h, 1, 1}, toY = {y, y - 1, y + h};
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 3; col++) blit(fromX[col], fromY[row], widths[col], heights[row], toX[col], toY[row]);
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.attachTextureForReading(GL30.GL_COLOR_ATTACHMENT0, 0);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, previousRead);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, previousDraw);
|
||||
if (scissor) GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
private static void blit(int srcX, int srcY, int width, int height, int dstX, int dstY) {
|
||||
GLStateManager.glBlitFramebuffer(srcX, srcY, srcX + width, srcY + height, dstX, dstY, dstX + width, dstY + height, GL11.GL_COLOR_BUFFER_BIT, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (Page page : pages) page.empty();
|
||||
entries.clear();
|
||||
freeSlots.clear();
|
||||
releasedThisPass.clear();
|
||||
lastTexture = -1;
|
||||
lastInfo = null;
|
||||
lastSlot = null;
|
||||
}
|
||||
|
||||
void freePages() {
|
||||
if (pages.isEmpty() && readFbo == 0) return;
|
||||
clear();
|
||||
for (Page page : pages) GLStateManager.glDeleteTextures(page.texture);
|
||||
pages.clear();
|
||||
if (readFbo != 0) {
|
||||
GLStateManager.glDeleteFramebuffers(readFbo);
|
||||
GLStateManager.glDeleteFramebuffers(drawFbo);
|
||||
readFbo = 0;
|
||||
drawFbo = 0;
|
||||
}
|
||||
drawFboPage = 0;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -178,12 +178,13 @@ final class InstancedTemplateRenderer {
|
||||
final int light = data.lights.getInt(i);
|
||||
final int overlay = data.overlays.getInt(i);
|
||||
final long info = data.infos.getLong(i);
|
||||
for (int p = 0, n = partCubes.length; p < n; p++) {
|
||||
final CubeParams cube = partCubes[p];
|
||||
final EntityTextureAtlas.Slot slot = data.atlasSlot(run, i);
|
||||
final EntityTextureAtlas.Slot region = slot != null ? slot : EntityTextureAtlas.Slot.WHOLE_TEXTURE;
|
||||
for (final CubeParams cube : partCubes) {
|
||||
cube.writeRows(ptr, data.matrices, off, scale);
|
||||
InstancedAttribs.writeTail(ptr, color, overlay, info);
|
||||
InstancedAttribs.writeLightmap(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE, light);
|
||||
cube.writeTexture(ptr);
|
||||
cube.writeTexture(ptr, region.u, region.v, region.w, region.h);
|
||||
ptr += CubeInstancedAttribs.STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.UnitCubeMesh;
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.client.model.PositionTextureVertex;
|
||||
import net.minecraft.client.model.TexturedQuad;
|
||||
|
||||
@@ -11,6 +13,10 @@ public final class ModelBoxCapture {
|
||||
|
||||
private ModelBoxCapture() {}
|
||||
|
||||
public static CubeParams capture(ModelBox box, TexturedQuad[] quads, ModelRenderer part, int texU, int texV, int sizeX, int sizeY, int sizeZ, float inflate) {
|
||||
return capture(quads, part.textureWidth, part.textureHeight, part.mirror, texU, texV, box.posX1, box.posY1, box.posZ1, sizeX, sizeY, sizeZ, inflate);
|
||||
}
|
||||
|
||||
public static CubeParams capture(TexturedQuad[] quads, float texWidth, float texHeight, boolean mirror, int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate) {
|
||||
if (quads == null || quads.length != UnitCubeMesh.FACE_COUNT) return null;
|
||||
final CubeParams params = CubeParams.of(texWidth, texHeight, mirror, texU, texV, x, y, z, sizeX, sizeY, sizeZ, inflate);
|
||||
|
||||
@@ -14,8 +14,10 @@ import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
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;
|
||||
@@ -102,6 +104,7 @@ public final class ModelPartBatcher {
|
||||
private boolean texCaptured;
|
||||
private boolean texIdentity;
|
||||
private boolean active;
|
||||
private int entityFireDepth;
|
||||
private Mode mode = Mode.BLOCK_ENTITIES;
|
||||
private boolean instancedThisCycle;
|
||||
private DeferredWorldRenderingPipeline shaderPipeline;
|
||||
@@ -140,6 +143,7 @@ public final class ModelPartBatcher {
|
||||
private static final class PartTemplate {
|
||||
TemplateBuffer template;
|
||||
CubeParams[] cubes;
|
||||
boolean cubesInsideTexture;
|
||||
float scale;
|
||||
long lastUsedMs;
|
||||
boolean emptyVanillaPart;
|
||||
@@ -247,6 +251,8 @@ public final class ModelPartBatcher {
|
||||
if (entityPass && entityTranslucentsHeld) {
|
||||
discardHeldEntities();
|
||||
}
|
||||
// Held entity translucents draw after deferred and can still sample regions released before them
|
||||
if (!entityTranslucentsHeld) recycleAtlas();
|
||||
active = true;
|
||||
final long nowMs = System.currentTimeMillis();
|
||||
mvCaptured = false;
|
||||
@@ -349,6 +355,7 @@ public final class ModelPartBatcher {
|
||||
if (instancedThisCycle) {
|
||||
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
|
||||
}
|
||||
recycleAtlas();
|
||||
AngelicaBufferSource.rebindPass();
|
||||
} finally {
|
||||
if (wrap) GbufferPrograms.endEntities();
|
||||
@@ -366,6 +373,15 @@ public final class ModelPartBatcher {
|
||||
private void discardHeldEntities() {
|
||||
entityTranslucentsHeld = false;
|
||||
entitySource.discard();
|
||||
recycleAtlas();
|
||||
}
|
||||
|
||||
private static void recycleAtlas() {
|
||||
if (AngelicaConfig.enableEntityTextureAtlas) {
|
||||
EntityTextureAtlas.INSTANCE.recycleReleasedRegions();
|
||||
} else {
|
||||
EntityTextureAtlas.INSTANCE.freePages();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
@@ -513,7 +529,11 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public boolean entityPassActive() {
|
||||
return active && mode == Mode.ENTITIES && !shadow;
|
||||
return entitiesActive() && !shadow;
|
||||
}
|
||||
|
||||
public boolean entitiesActive() {
|
||||
return active && mode == Mode.ENTITIES;
|
||||
}
|
||||
|
||||
public boolean isShadowPass() {
|
||||
@@ -554,18 +574,18 @@ public final class ModelPartBatcher {
|
||||
if (shadow && isDepthEqualDecalState()) {
|
||||
return true;
|
||||
}
|
||||
return queueTemplate(entry.template, entry.cubes, scale, null, false);
|
||||
return queueTemplate(entry.template, entry.cubes, entry.cubesInsideTexture, scale, null, false);
|
||||
}
|
||||
|
||||
public boolean queueTemplate(TemplateBuffer template, TesrMaterial material) {
|
||||
return queueTemplate(template, null, 1.0f, material, false);
|
||||
return queueTemplate(template, null, false, 1.0f, material, false);
|
||||
}
|
||||
|
||||
public boolean queueTemplate(TemplateBuffer template, TesrMaterial material, boolean afterGlint) {
|
||||
return queueTemplate(template, null, 1.0f, material, afterGlint);
|
||||
return queueTemplate(template, null, false, 1.0f, material, afterGlint);
|
||||
}
|
||||
|
||||
private boolean queueTemplate(TemplateBuffer template, CubeParams[] cubes, float scale, TesrMaterial explicitMaterial, boolean afterGlint) {
|
||||
private boolean queueTemplate(TemplateBuffer template, CubeParams[] cubes, boolean cubesInsideTexture, float scale, TesrMaterial explicitMaterial, boolean afterGlint) {
|
||||
if (!active) {
|
||||
bail(BailReason.INACTIVE);
|
||||
return false;
|
||||
@@ -612,7 +632,8 @@ public final class ModelPartBatcher {
|
||||
final int glintSlot = material.special() == TesrMaterial.SpecialRender.GLINT ? lastBoundGlintSlot : ShaderGlint.NO_TINT;
|
||||
final int cullCode = material.isNoCull() ? DrawState.DISABLED : DrawState.liveCull();
|
||||
final boolean lit = DrawState.liveLit(material);
|
||||
final RenderLayer layer = layerFor(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit, DrawState.liveLightmap(), afterGlint);
|
||||
final EntityTextureAtlas.Slot atlasSlot = texMatrix == null && cubesInsideTexture ? atlasSlotFor(material) : null;
|
||||
final RenderLayer layer = layerFor(atlasSlot != null ? Objects.requireNonNull(atlasSlot.page).location : texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit, DrawState.liveLightmap(), afterGlint);
|
||||
// Entities nested inside a TESR (mob spawner, OpenBlocks trophy) run in the block entity pass but carry an entity id
|
||||
final int entityId = groupId(shaderPipeline != null, mode == Mode.ENTITIES || pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
@@ -620,11 +641,20 @@ public final class ModelPartBatcher {
|
||||
final int overlayABGR = currentOverlay();
|
||||
final long entityInfo = currentEntityInfo(CapturedRenderingState.INSTANCE.getCurrentRenderedItem());
|
||||
final int packedLight = GLSMConfig.packedLastBrightness();
|
||||
activeGroups.queue(template, cubes, scale, layer, material, currentModelView(), packedLight, colorABGR, overlayABGR, entityInfo, entityId, texMatrix);
|
||||
activeGroups.queue(template, cubes, scale, layer, material, currentModelView(), packedLight, colorABGR, overlayABGR, entityInfo, entityId, texMatrix, atlasSlot);
|
||||
parts++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private EntityTextureAtlas.Slot atlasSlotFor(TesrMaterial material) {
|
||||
if (!AngelicaConfig.enableEntityTextureAtlas) return null;
|
||||
// Glint, overlay and custom shaders sample their own textures
|
||||
if (material.special() != TesrMaterial.SpecialRender.NONE || material.shader() != null || material.isUnfilteredAtlas() || material == EntityMaterials.OVERLAY) return null;
|
||||
if (!activeGroups.instancesCubes(material)) return null;
|
||||
if (shaderPipeline != null && !shaderPipeline.hasInstancedVariant(Instancing.CUBE)) return null;
|
||||
return EntityTextureAtlas.INSTANCE.slotFor(lastBoundGlId, shaderPipeline != null);
|
||||
}
|
||||
|
||||
private enum TessellatorRoute { DRAW_NOW, DRAW_NOW_EXPECTED, QUEUE }
|
||||
|
||||
public static int drawTessellator(Tessellator tess) {
|
||||
@@ -649,15 +679,21 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
}
|
||||
|
||||
public void beginEntityFire() { entityFireDepth++; }
|
||||
public void endEntityFire() { entityFireDepth--; }
|
||||
|
||||
private TessellatorRoute routeTessellatorDraw(Tessellator tess) {
|
||||
if (!active || mode != Mode.BLOCK_ENTITIES || !GLStateManager.isMainThread() || !BatchEligibility.insideRenderer()) return TessellatorRoute.DRAW_NOW;
|
||||
if (!active || !GLStateManager.isMainThread()) return TessellatorRoute.DRAW_NOW;
|
||||
final boolean entityFire = entityFireDepth != 0 && mode == Mode.ENTITIES;
|
||||
if (!entityFire && (mode != Mode.BLOCK_ENTITIES || !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);
|
||||
if (material == null || material == EntityMaterials.OVERLAY) return TessellatorRoute.DRAW_NOW;
|
||||
if (lastBoundLocation == null || GLStateManager.getBoundTextureForServerState() != lastBoundGlId) return TessellatorRoute.DRAW_NOW;
|
||||
if (!BatchEligibility.batchingAllowed()) return TessellatorRoute.DRAW_NOW_EXPECTED;
|
||||
if (!entityFire && !BatchEligibility.batchingAllowed()) return TessellatorRoute.DRAW_NOW_EXPECTED;
|
||||
|
||||
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
@@ -665,7 +701,8 @@ public final class ModelPartBatcher {
|
||||
final int cullCode = material.isNoCull() ? DrawState.DISABLED : DrawState.liveCull();
|
||||
final RenderLayer layer = layerFor(lastBoundLocation, material, pass, offset ? polygon.getOffsetFactor() : 0.0f, offset ? polygon.getOffsetUnits() : 0.0f,
|
||||
ShaderGlint.NO_TINT, cullCode, DrawState.liveLit(material), DrawState.liveLightmap());
|
||||
final int groupId = groupId(shaderPipeline != null, pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
// Flames carry entity_flame, which some packs may voxelize as a light source in the shadow pass
|
||||
final int groupId = groupId(shaderPipeline != null, mode == Mode.ENTITIES || pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
TessellatorStreamingDrawer.fillMissingAttributes(tess);
|
||||
activeGroups.streamVertices(layer, groupId, tess.rawBuffer, tess.vertexCount, currentModelView());
|
||||
return TessellatorRoute.QUEUE;
|
||||
@@ -722,6 +759,7 @@ public final class ModelPartBatcher {
|
||||
entry.scale = scale;
|
||||
entry.template = null;
|
||||
entry.cubes = null;
|
||||
entry.cubesInsideTexture = false;
|
||||
entry.emptyVanillaPart = false;
|
||||
final List<ModelBox> boxes = part.cubeList;
|
||||
if (builder != null) {
|
||||
@@ -756,6 +794,7 @@ public final class ModelPartBatcher {
|
||||
cubes[i] = params;
|
||||
}
|
||||
entry.cubes = cubes;
|
||||
entry.cubesInsideTexture = CubeParams.allInsideTexture(cubes);
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cullCode, boolean lit, boolean lightmap) {
|
||||
@@ -792,6 +831,7 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entityFireDepth = 0;
|
||||
shadows.reset();
|
||||
groups.clear();
|
||||
entityGroups.clear();
|
||||
|
||||
@@ -94,6 +94,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
static final class InstanceColumns {
|
||||
final ObjectArrayList<TemplateBuffer> templates = new ObjectArrayList<>();
|
||||
final ObjectArrayList<CubeParams[]> cubes = new ObjectArrayList<>();
|
||||
final ObjectArrayList<EntityTextureAtlas.Slot> atlasSlots = new ObjectArrayList<>();
|
||||
boolean anyAtlasSlot;
|
||||
final FloatArrayList scales = new FloatArrayList();
|
||||
final IntArrayList lights = new IntArrayList();
|
||||
final IntArrayList colors = new IntArrayList();
|
||||
@@ -122,8 +124,18 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
size++;
|
||||
}
|
||||
|
||||
void addCubes(TemplateBuffer templateOrNull, CubeParams[] cubeParams, float scale, Matrix4fc mv, int light, int color, int overlay, long info) {
|
||||
EntityTextureAtlas.Slot atlasSlot(int i) {
|
||||
return anyAtlasSlot ? atlasSlots.get(i) : null;
|
||||
}
|
||||
|
||||
EntityTextureAtlas.Slot atlasSlot(TexRun run, int i) {
|
||||
return run.source != null ? null : atlasSlot(i);
|
||||
}
|
||||
|
||||
void addCubes(TemplateBuffer templateOrNull, CubeParams[] cubeParams, float scale, Matrix4fc mv, int light, int color, int overlay, long info, EntityTextureAtlas.Slot atlasSlot) {
|
||||
cubes.add(cubeParams);
|
||||
atlasSlots.add(atlasSlot);
|
||||
anyAtlasSlot |= atlasSlot != null;
|
||||
scales.add(scale);
|
||||
add(templateOrNull, mv, light, color, overlay, info);
|
||||
}
|
||||
@@ -140,6 +152,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
if (src.cubes.size() == src.size) {
|
||||
for (int i = start; i < end; i++) cubes.add(src.cubes.get(i));
|
||||
atlasSlots.size(atlasSlots.size() + n);
|
||||
scales.addElements(scales.size(), src.scales.elements(), start, n);
|
||||
}
|
||||
fill(lights, n, light);
|
||||
@@ -158,6 +171,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
void clear() {
|
||||
templates.clear();
|
||||
cubes.clear();
|
||||
atlasSlots.clear();
|
||||
anyAtlasSlot = false;
|
||||
scales.clear();
|
||||
lights.clear();
|
||||
colors.clear();
|
||||
@@ -477,10 +492,14 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void queue(TemplateBuffer template, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix) {
|
||||
queue(template, null, 1.0f, layer, material, currentMV, packedLight, colorABGR, overlayABGR, entityInfo, id, texMatrix);
|
||||
queue(template, null, 1.0f, layer, material, currentMV, packedLight, colorABGR, overlayABGR, entityInfo, id, texMatrix, null);
|
||||
}
|
||||
|
||||
void queue(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix) {
|
||||
boolean instancesCubes(TesrMaterial material) {
|
||||
return instanceableMaterial(material) && glintCapture == null;
|
||||
}
|
||||
|
||||
void queue(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix, EntityTextureAtlas.Slot atlasSlot) {
|
||||
final Group group = groupFor(layer, material, id);
|
||||
touch(group, layer);
|
||||
if (group.stream) {
|
||||
@@ -488,7 +507,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final boolean capturingBase = glintCapture != null && glintCapture.capturesBase();
|
||||
if (instanceable && cubes != null) {
|
||||
final TexRun run = group.openOrExtendRun(group.cubeColumns, texMatrix, false, capturingBase && group.opaque);
|
||||
group.cubeColumns.addCubes(deferred != null ? template : null, cubes, scale, currentMV, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
group.cubeColumns.addCubes(deferred != null ? template : null, cubes, scale, currentMV, packedLight, colorABGR, overlayABGR, entityInfo, atlasSlot);
|
||||
run.last.end = group.cubeColumns.size;
|
||||
run.parts++;
|
||||
run.instances += cubes.length;
|
||||
@@ -706,7 +725,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
if (group.cubeColumns.hasInstances()) {
|
||||
if (cubeAvailable) {
|
||||
need |= NEED_CUBE;
|
||||
} else if (matrixAvailable) {
|
||||
} else if (matrixAvailable && !group.cubeColumns.anyAtlasSlot) {
|
||||
need |= NEED_MATRIX;
|
||||
} else {
|
||||
drawInstancesCpu(group, group.cubeColumns);
|
||||
@@ -724,7 +743,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final boolean fixedFunction = deferred == null && GLStateManager.getActiveProgram() == 0;
|
||||
drawTemplateRuns(group.templateColumns, group.material.special() == TesrMaterial.SpecialRender.GLINT && fixedFunction, group.material == EntityMaterials.ITEM_GLINT);
|
||||
}
|
||||
if (!cubeAvailable && group.cubeColumns.hasInstances()) {
|
||||
if (!cubeAvailable && group.cubeColumns.hasInstances() && !group.cubeColumns.anyAtlasSlot) {
|
||||
drawTemplateRuns(group.cubeColumns, group.material == EntityMaterials.GLINT && deferred == null, false);
|
||||
}
|
||||
}
|
||||
@@ -904,7 +923,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
System.arraycopy(data.matrices, i * 16, matScratch, 0, 16);
|
||||
scratchMat.set(matScratch);
|
||||
final int color = head.source != null ? head.color : data.colors.getInt(i);
|
||||
ptr = VertexTransform.writeInstance(ptr, format, templates.get(i), scratchMat, scratchVec, color, data.lights.getInt(i), texMatrix);
|
||||
final EntityTextureAtlas.Slot slot = data.atlasSlot(head, i);
|
||||
ptr = VertexTransform.writeInstance(ptr, format, templates.get(i), scratchMat, scratchVec, color, data.lights.getInt(i), slot != null ? slot.uvMatrix : texMatrix);
|
||||
}
|
||||
fallbackScratch.position((int) (ptr - base));
|
||||
fallbackScratch.flip();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.rendering.items.DroppedItemInstancer;
|
||||
import com.gtnewhorizons.angelica.rendering.particles.ParticleInstancer;
|
||||
|
||||
public final class TesrLifecycle {
|
||||
@@ -10,6 +11,8 @@ public final class TesrLifecycle {
|
||||
public static void reset() {
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
ModelPartBatcher.INSTANCE.clear();
|
||||
EntityTextureAtlas.INSTANCE.clear();
|
||||
DroppedItemInstancer.forgetItemDraws();
|
||||
ParticleInstancer.clear();
|
||||
|
||||
RenderLayer.clearInterningAndHooks();
|
||||
|
||||
@@ -103,7 +103,7 @@ public class BlockMaterialMapping {
|
||||
nbtEntries.forEach((intId, entries) -> {
|
||||
for (BlockEntry entry : entries) {
|
||||
if (entry.hasNbtProperties()) {
|
||||
map.addCondition(entry.id(), entry.nbtProperties(), intId);
|
||||
map.addCondition(entry.id(), entry.nbtProperties(), intId, entry.metas());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -121,7 +121,7 @@ public class BlockMaterialMapping {
|
||||
return;
|
||||
}
|
||||
|
||||
teMap.addCondition(block, entry.nbtProperties(), intId);
|
||||
teMap.addCondition(block, entry.nbtProperties(), intId, entry.metas());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Maps objects with specific NBT properties to shader IDs. Generic over the
|
||||
@@ -34,7 +35,9 @@ public class NbtConditionalIdMap<K> {
|
||||
boolean isExistenceOnly() { return expectedValue == null; }
|
||||
}
|
||||
|
||||
record NbtCondition(PropertyMatcher[] matchers, int shaderId) {}
|
||||
record NbtCondition(PropertyMatcher[] matchers, Set<Integer> metas, int shaderId) {
|
||||
boolean isMetaMatch() { return metas != null && !metas.isEmpty(); }
|
||||
}
|
||||
|
||||
private final Map<K, List<NbtCondition>> conditionsByKey;
|
||||
|
||||
@@ -47,7 +50,7 @@ public class NbtConditionalIdMap<K> {
|
||||
* registries at registration time. Single-quoted values are kept as literals.
|
||||
* Dot notation paths are pre-split here so we prevent allocations.
|
||||
*/
|
||||
public void addCondition(K key, Map<String, PropertiesTokenizer.NbtValue> nbtProperties, int shaderId) {
|
||||
public void addCondition(K key, Map<String, PropertiesTokenizer.NbtValue> nbtProperties, int shaderId, Set<Integer> metas) {
|
||||
final PropertyMatcher[] matchers = new PropertyMatcher[nbtProperties.size()];
|
||||
int i = 0;
|
||||
|
||||
@@ -88,7 +91,7 @@ public class NbtConditionalIdMap<K> {
|
||||
}
|
||||
|
||||
conditionsByKey.computeIfAbsent(key, k -> new ArrayList<>())
|
||||
.add(new NbtCondition(matchers, shaderId));
|
||||
.add(new NbtCondition(matchers, metas, shaderId));
|
||||
}
|
||||
|
||||
public boolean hasConditions(K key) {
|
||||
@@ -102,13 +105,16 @@ public class NbtConditionalIdMap<K> {
|
||||
/**
|
||||
* Resolves the shader ID by matching the provided NBT data.
|
||||
*/
|
||||
public int resolve(K key, NBTTagCompound nbt) {
|
||||
public int resolve(K key, NBTTagCompound nbt, Integer meta) {
|
||||
final List<NbtCondition> conditions = conditionsByKey.get(key);
|
||||
if (conditions == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (NbtCondition condition : conditions) {
|
||||
if (meta != null && condition.isMetaMatch() && !condition.metas.contains(meta)) {
|
||||
continue;
|
||||
}
|
||||
if (matchesNbt(nbt, condition.matchers())) {
|
||||
return condition.shaderId;
|
||||
}
|
||||
@@ -117,6 +123,13 @@ public class NbtConditionalIdMap<K> {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the shader ID by matching the provided NBT data.
|
||||
*/
|
||||
public int resolve(K key, NBTTagCompound nbt) {
|
||||
return resolve(key, nbt, null);
|
||||
}
|
||||
|
||||
private static boolean matchesNbt(NBTTagCompound nbt, PropertyMatcher[] matchers) {
|
||||
for (PropertyMatcher matcher : matchers) {
|
||||
if (matcher.isPathMatch()) {
|
||||
|
||||
@@ -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;
|
||||
@@ -2602,7 +2610,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
|
||||
private static final ProgramId[] INSTANCED_PROGRAM_IDS = {
|
||||
ProgramId.Block, ProgramId.BlockTrans, ProgramId.Entities, ProgramId.EntitiesTrans,
|
||||
ProgramId.Shadow, ProgramId.ShadowWater, ProgramId.ArmorGlint
|
||||
ProgramId.Shadow, ProgramId.ShadowWater, ProgramId.ArmorGlint, ProgramId.SpiderEyes
|
||||
};
|
||||
|
||||
private static final ProgramId[] PARTICLE_PROGRAM_IDS = {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class PBRTextureManager {
|
||||
}
|
||||
|
||||
private final Int2ObjectMap<PBRTextureHolder> holders = new Int2ObjectOpenHashMap<>();
|
||||
private int holderChanges;
|
||||
private final PBRTextureConsumerImpl consumer = new PBRTextureConsumerImpl();
|
||||
|
||||
private NativeImageBackedSingleColorTexture defaultNormalTexture;
|
||||
@@ -61,15 +62,24 @@ public class PBRTextureManager {
|
||||
return holder;
|
||||
}
|
||||
|
||||
public boolean hasLoadedPbrTextures(int id) {
|
||||
return getHolder(id) != defaultHolder;
|
||||
}
|
||||
|
||||
public PBRTextureHolder getOrLoadHolder(int id) {
|
||||
PBRTextureHolder holder = holders.get(id);
|
||||
if (holder == null) {
|
||||
holder = loadHolder(id);
|
||||
holders.put(id, holder);
|
||||
holderChanges++;
|
||||
}
|
||||
return holder;
|
||||
}
|
||||
|
||||
public int holderChanges() {
|
||||
return holderChanges;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private PBRTextureHolder loadHolder(int id) {
|
||||
AbstractTexture texture = TextureTracker.INSTANCE.getTexture(id);
|
||||
@@ -96,6 +106,7 @@ public class PBRTextureManager {
|
||||
PBRTextureHolder holder = holders.remove(id);
|
||||
if (holder != null) {
|
||||
closeHolder(holder);
|
||||
holderChanges++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +117,7 @@ public class PBRTextureManager {
|
||||
}
|
||||
}
|
||||
holders.clear();
|
||||
holderChanges++;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@ public abstract class MixinItemRenderer_Instanced {
|
||||
|
||||
@Surround.Finally("block")
|
||||
private void angelica$endHeldBlock(@Surround.Carry long part) {
|
||||
DroppedItemInstancer.endPart(part);
|
||||
DroppedItemInstancer.endBlock(part);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
|
||||
+2
-1
@@ -24,8 +24,9 @@ public abstract class MixinModelBox_CubeParams implements ModelBoxData {
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void angelica$captureCubeParams(ModelRenderer part, int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate, CallbackInfo ci) {
|
||||
//noinspection ConstantValue
|
||||
if (((Object) this).getClass() != ModelBox.class) return;
|
||||
this.angelica$cubeParams = ModelBoxCapture.capture(this.quadList, part.textureWidth, part.textureHeight, part.mirror, texU, texV, x, y, z, sizeX, sizeY, sizeZ, inflate);
|
||||
this.angelica$cubeParams = ModelBoxCapture.capture((ModelBox) (Object) this, this.quadList, part, texU, texV, sizeX, sizeY, sizeZ, inflate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -116,6 +116,6 @@ public abstract class MixinRenderItem_Instanced {
|
||||
|
||||
@Surround.Finally("block")
|
||||
private void angelica$endDroppedBlock(@Surround.Carry long part) {
|
||||
DroppedItemInstancer.endPart(part);
|
||||
DroppedItemInstancer.endBlock(part);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(Render.class)
|
||||
public class MixinRender_EntityFireBatch {
|
||||
|
||||
@Surround(method = "renderEntityOnFire")
|
||||
private void angelica$batchEntityFire(Entity entity, double x, double y, double z, float partialTicks) {
|
||||
ModelPartBatcher.INSTANCE.beginEntityFire();
|
||||
}
|
||||
|
||||
@Surround.Finally
|
||||
private void angelica$endEntityFire() {
|
||||
ModelPartBatcher.INSTANCE.endEntityFire();
|
||||
}
|
||||
}
|
||||
+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);
|
||||
}
|
||||
}
|
||||
+12
-30
@@ -4,51 +4,28 @@ import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.experimental.surround.Surround;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.items.BlockRenderListManager;
|
||||
import com.gtnewhorizons.angelica.rendering.items.DroppedItemInstancer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
// Innermost: shaders.MixinRenderBlocks_ItemId must also cover a cache hit
|
||||
@Mixin(value = RenderBlocks.class, priority = 900)
|
||||
public abstract class MixinRenderBlocks {
|
||||
|
||||
|
||||
@Shadow
|
||||
public boolean useInventoryTint;
|
||||
|
||||
@Shadow
|
||||
public boolean enableAO;
|
||||
|
||||
@Shadow
|
||||
public IIcon overrideBlockTexture;
|
||||
|
||||
@Shadow public IBlockAccess blockAccess;
|
||||
|
||||
@Shadow public int uvRotateEast;
|
||||
@Shadow public int uvRotateWest;
|
||||
@Shadow public int uvRotateSouth;
|
||||
@Shadow public int uvRotateNorth;
|
||||
@Shadow public int uvRotateTop;
|
||||
@Shadow public int uvRotateBottom;
|
||||
|
||||
@Surround(method = "renderBlockAsItem")
|
||||
private void angelica$cacheBlockItemRenderer(Block block, int meta, float brightness) {
|
||||
final boolean uncacheable = BlockRenderListManager.isISBRH(block.getRenderType())
|
||||
|| enableAO
|
||||
|| this.overrideBlockTexture != null
|
||||
|| brightness != 1.0F
|
||||
|| !this.useInventoryTint
|
||||
|| this.blockAccess != null
|
||||
|| (uvRotateEast | uvRotateWest | uvRotateSouth | uvRotateNorth | uvRotateTop | uvRotateBottom) != 0
|
||||
//noinspection DataFlowIssue
|
||||
final RenderBlocks self = (RenderBlocks) (Object) this;
|
||||
final boolean uncacheable = !BlockRenderListManager.rendersPlainItem(self, block, brightness)
|
||||
|| GLStateManager.isRecordingDisplayList()
|
||||
|| TessellatorManager.isCurrentlyCapturing()
|
||||
|| TessellatorManager.shouldInterceptDraw(Tessellator.instance);
|
||||
@Surround.Carry
|
||||
final int list = uncacheable ? 0 : BlockRenderListManager.callOrStartCompiling(BlockRenderListManager.getDisplayList(block, meta));
|
||||
final long part = uncacheable ? DroppedItemInstancer.RUN : DroppedItemInstancer.directBlock(self, block, meta, brightness);
|
||||
@Surround.Carry
|
||||
final int list = uncacheable ? 0 : part == DroppedItemInstancer.SKIP ? -1 : BlockRenderListManager.callOrStartCompiling(BlockRenderListManager.getDisplayList(block, meta));
|
||||
@Surround.Skip
|
||||
final boolean cached = list < 0;
|
||||
}
|
||||
@@ -62,4 +39,9 @@ public abstract class MixinRenderBlocks {
|
||||
private void angelica$abortBlockItemRenderer(Throwable error, @Surround.Carry int list) {
|
||||
BlockRenderListManager.abortCompiling(list);
|
||||
}
|
||||
|
||||
@Surround.Finally
|
||||
private void angelica$countDirectBlock(@Surround.Carry long part) {
|
||||
DroppedItemInstancer.endPart(part);
|
||||
}
|
||||
}
|
||||
+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;
|
||||
|
||||
|
||||
+13
-7
@@ -26,6 +26,7 @@ public abstract class MixinRenderBlocks {
|
||||
@Shadow public IBlockAccess blockAccess;
|
||||
@Shadow public abstract boolean hasOverrideBlockTexture();
|
||||
@Unique private final BiomeVertexBlender angelica$vertexBlender = new BiomeVertexBlender();
|
||||
@Unique private boolean angelica$blending;
|
||||
|
||||
@Surround(method = {"renderStandardBlock", "renderBlockLiquid"}, id = "blendBlock")
|
||||
private void angelica$blendBlock(Block block, int x, int y, int z) {
|
||||
@@ -34,8 +35,10 @@ public abstract class MixinRenderBlocks {
|
||||
BiomeBlendTessellator tessellator = null;
|
||||
@Surround.Carry("previous")
|
||||
BiomeVertexBlender previous = null;
|
||||
@Surround.Carry
|
||||
@Surround.Carry("blend")
|
||||
boolean blend = false;
|
||||
@Surround.Carry("wasBlending")
|
||||
final boolean wasBlending = angelica$blending;
|
||||
if (cache != null && ClientProxy.options().quality.biomeBlendMode == BiomeBlendMode.FANCY && SmoothBiomeColorCache.configuredRadius() != 0) {
|
||||
tessellator = (BiomeBlendTessellator) TessellatorManager.get();
|
||||
previous = tessellator.angelica$getBiomeBlender();
|
||||
@@ -45,11 +48,13 @@ public abstract class MixinRenderBlocks {
|
||||
if (blend) angelica$vertexBlender.setup(cache, type, x, y, z);
|
||||
tessellator.angelica$setBiomeBlender(blend ? angelica$vertexBlender : null);
|
||||
}
|
||||
angelica$blending = blend;
|
||||
}
|
||||
|
||||
@Surround.Finally("blendBlock")
|
||||
private void angelica$endBlendBlock(@Surround.Carry("tessellator") BiomeBlendTessellator tessellator, @Surround.Carry("previous") BiomeVertexBlender previous,
|
||||
@Surround.Carry boolean blend) {
|
||||
@Surround.Carry("blend") boolean blend, @Surround.Carry("wasBlending") boolean wasBlending) {
|
||||
angelica$blending = wasBlending;
|
||||
if (tessellator == null) return;
|
||||
tessellator.angelica$setBiomeBlender(previous);
|
||||
if (blend) angelica$vertexBlender.clear();
|
||||
@@ -66,13 +71,14 @@ public abstract class MixinRenderBlocks {
|
||||
@Surround(method = {"renderStandardBlock", "renderBlockLiquid"}, id = "deferTint",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;colorMultiplier(Lnet/minecraft/world/IBlockAccess;III)I"))
|
||||
private void angelica$deferTintEnter() {
|
||||
@Surround.Skip
|
||||
final boolean defer = ((BiomeBlendTessellator) TessellatorManager.get()).angelica$getBiomeBlender() == angelica$vertexBlender;
|
||||
@Surround.Carry
|
||||
final boolean armed = angelica$blending;
|
||||
if (armed) angelica$vertexBlender.arm();
|
||||
}
|
||||
|
||||
@Surround.Skipped("deferTint")
|
||||
private int angelica$deferTint() {
|
||||
return angelica$vertexBlender.deferBlockTint();
|
||||
@Surround.Finally("deferTint")
|
||||
private void angelica$deferTintExit(@Surround.Carry boolean armed) {
|
||||
if (armed) angelica$vertexBlender.disarm();
|
||||
}
|
||||
|
||||
@Surround(method = {"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial", "renderStandardBlockWithColorMultiplier"}, id = "faceYNeg",
|
||||
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@GLCoreTest
|
||||
class EntityTextureAtlasGLTest {
|
||||
|
||||
private static final int WIDTH = 4;
|
||||
private static final int HEIGHT = 2;
|
||||
private static final int PAGE = EntityTextureAtlas.PAGE_SIZE;
|
||||
|
||||
private final List<ITextureObject> pages = new ArrayList<>();
|
||||
private Minecraft previous;
|
||||
private int source, passColor, passFbo;
|
||||
|
||||
@BeforeAll
|
||||
static void listenForDeletes() {
|
||||
TextureInfoCache.INSTANCE.addDeleteListener(EntityTextureAtlas.INSTANCE::onTextureDeleted);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
previous = Minecraft.getMinecraft();
|
||||
final Minecraft minecraft = mock(Minecraft.class);
|
||||
final TextureManager textures = mock(TextureManager.class);
|
||||
when(textures.loadTexture(any(ResourceLocation.class), any(ITextureObject.class))).thenAnswer(call -> pages.add(call.getArgument(1)));
|
||||
when(minecraft.getTextureManager()).thenReturn(textures);
|
||||
Reflect.setStatic(Minecraft.class, "theMinecraft", minecraft);
|
||||
|
||||
passColor = texture(GL11.GL_NEAREST, 0);
|
||||
passFbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, passFbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, passColor, 0);
|
||||
source = texture(GL11.GL_NEAREST, 0);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
EntityTextureAtlas.INSTANCE.freePages();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(passFbo);
|
||||
GLStateManager.glDeleteTextures(source);
|
||||
GLStateManager.glDeleteTextures(passColor);
|
||||
Reflect.setStatic(Minecraft.class, "theMinecraft", previous);
|
||||
}
|
||||
|
||||
private static int argb(int x, int y, int seed) {
|
||||
return 0xFF000000 | (x * 50 + seed) << 16 | (y * 90 + seed) << 8 | 0x20;
|
||||
}
|
||||
|
||||
private static int texture(int filter, int seed) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, WIDTH, HEIGHT, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels(seed));
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static ByteBuffer texels(int seed) {
|
||||
final ByteBuffer texels = BufferUtils.createByteBuffer(WIDTH * HEIGHT * 4);
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
final int c = argb(x, y, seed);
|
||||
texels.put((byte) (c >> 16)).put((byte) (c >> 8)).put((byte) c).put((byte) (c >>> 24));
|
||||
}
|
||||
}
|
||||
texels.flip();
|
||||
return texels;
|
||||
}
|
||||
|
||||
private void assertSlotHolds(EntityTextureAtlas.Slot slot, int seed) {
|
||||
final ByteBuffer page = BufferUtils.createByteBuffer(PAGE * PAGE * 4);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, pages.get(0).getGlTextureId());
|
||||
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, page);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
for (int y = -1; y <= HEIGHT; y++) {
|
||||
for (int x = -1; x <= WIDTH; x++) {
|
||||
final int at = ((slot.y + 1 + y) * PAGE + slot.x + 1 + x) * 4;
|
||||
final int actual = (page.get(at + 3) & 0xFF) << 24 | (page.get(at) & 0xFF) << 16 | (page.get(at + 1) & 0xFF) << 8 | page.get(at + 2) & 0xFF;
|
||||
final int expected = argb(Math.clamp(x, 0, WIDTH - 1), Math.clamp(y, 0, HEIGHT - 1), seed);
|
||||
assertEquals(Integer.toHexString(expected), Integer.toHexString(actual), "page texel for source (" + x + ", " + y + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void copiesTheTextureWithEdgeGuttersAndRestoresTheFramebuffers() {
|
||||
final EntityTextureAtlas.Slot slot = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(slot);
|
||||
assertEquals(passFbo, GLStateManager.getDrawFramebuffer(), "draw framebuffer restored");
|
||||
assertEquals(passFbo, GLStateManager.getReadFramebuffer(), "read framebuffer restored");
|
||||
assertEquals((slot.x + 1) / (float) PAGE, slot.u);
|
||||
assertEquals(WIDTH / (float) PAGE, slot.w);
|
||||
assertSlotHolds(slot, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reuploadingTheTextureRefreshesItsSlot() {
|
||||
final EntityTextureAtlas.Slot slot = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, source);
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels(7));
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
assertSame(slot, EntityTextureAtlas.INSTANCE.slotFor(source, false));
|
||||
assertSlotHolds(slot, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filteredTexturesKeepTheirOwnDraw() {
|
||||
final int filtered = texture(GL11.GL_LINEAR, 0);
|
||||
try {
|
||||
assertNull(EntityTextureAtlas.INSTANCE.slotFor(filtered, false));
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void switchingToLinearFilteringDropsTheSlot() {
|
||||
final EntityTextureAtlas.Slot slot = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(slot);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, source);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
assertNull(EntityTextureAtlas.INSTANCE.slotFor(source, false), "integer filter change");
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
assertSame(slot, EntityTextureAtlas.INSTANCE.slotFor(source, false), "the kept slot comes back");
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
assertNull(EntityTextureAtlas.INSTANCE.slotFor(source, false), "float filter change");
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawingIntoTheTextureDropsItsSlot() {
|
||||
assertNotNull(EntityTextureAtlas.INSTANCE.slotFor(source, false));
|
||||
final int target = GLStateManager.glGenFramebuffers();
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, source, 0);
|
||||
GLStateManager.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, passFbo);
|
||||
GLStateManager.glDeleteFramebuffers(target);
|
||||
}
|
||||
assertNull(EntityTextureAtlas.INSTANCE.slotFor(source, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearingKeepsThePagesAndRefillsThem() {
|
||||
final EntityTextureAtlas.Slot before = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
EntityTextureAtlas.INSTANCE.clear();
|
||||
final EntityTextureAtlas.Slot after = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(after);
|
||||
assertNotNull(before);
|
||||
assertSame(before.page, after.page, "a cleared atlas reuses its page instead of deleting and reallocating it");
|
||||
assertEquals(1, pages.size(), "the page is registered with the texture manager once");
|
||||
assertSlotHolds(after, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aDeletedTexturesRegionIsReusedOnlyFromTheNextPass() {
|
||||
final EntityTextureAtlas.Slot first = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(first);
|
||||
GLStateManager.glDeleteTextures(source);
|
||||
final int samePass = texture(GL11.GL_NEAREST, 3);
|
||||
try {
|
||||
final EntityTextureAtlas.Slot taken = EntityTextureAtlas.INSTANCE.slotFor(samePass, false);
|
||||
assertNotNull(taken);
|
||||
assertFalse(taken.x == first.x && taken.y == first.y, "instances queued earlier in the pass still sample the old region");
|
||||
EntityTextureAtlas.INSTANCE.recycleReleasedRegions();
|
||||
source = texture(GL11.GL_NEAREST, 5);
|
||||
final EntityTextureAtlas.Slot reused = EntityTextureAtlas.INSTANCE.slotFor(source, false);
|
||||
assertNotNull(reused);
|
||||
assertEquals(first.x, reused.x);
|
||||
assertEquals(first.y, reused.y);
|
||||
assertSlotHolds(reused, 5);
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(samePass);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void smallTexturesStillFitAfterALargeOneIsTurnedAway() {
|
||||
final int large = 512;
|
||||
final int[] filling = new int[4 * 9 + 1];
|
||||
final int small = texture(GL11.GL_NEAREST, 0);
|
||||
try {
|
||||
for (int i = 0; i < filling.length; i++) filling[i] = blankTexture(large);
|
||||
for (int i = 0; i < filling.length - 1; i++) assertNotNull(EntityTextureAtlas.INSTANCE.slotFor(filling[i], false), "large texture " + i);
|
||||
assertNull(EntityTextureAtlas.INSTANCE.slotFor(filling[filling.length - 1], false), "no page has room for another 512");
|
||||
assertNotNull(EntityTextureAtlas.INSTANCE.slotFor(small, false), "shelves still have room for a small texture");
|
||||
} finally {
|
||||
for (int id : filling) if (id != 0) GLStateManager.glDeleteTextures(id);
|
||||
GLStateManager.glDeleteTextures(small);
|
||||
}
|
||||
}
|
||||
|
||||
private static int blankTexture(int size) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,21 @@ class ModelBoxCaptureTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"false, 0.0",
|
||||
"true, 0.0",
|
||||
"false, 0.5",
|
||||
"true, 0.5",
|
||||
})
|
||||
void constructedBoxCapturesFromItsStoredCorner(boolean mirror, float inflate) {
|
||||
final ModelRenderer part = part(64, 32, mirror);
|
||||
final ModelBox box = new ModelBox(part, 40, 16, -3, -2, -2, 4, 12, 4, inflate);
|
||||
final CubeParams fromBox = ModelBoxCapture.capture(box, quadsOf(box), part, 40, 16, 4, 12, 4, inflate);
|
||||
assertEquals(ModelBoxCapture.capture(quadsOf(box), 64, 32, mirror, 40, 16, -3, -2, -2, 4, 12, 4, inflate), fromBox);
|
||||
assertNotNull(fromBox, "mirrored and inflated boxes must verify");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mutatedQuadListIsRejected() {
|
||||
final ModelRenderer part = part(64, 32, false);
|
||||
|
||||
+16
-24
@@ -60,8 +60,7 @@ class RetainedTesrGroupsGLTest {
|
||||
private static final float[] TRIANGLE = { -0.1f, -0.1f, 0f, 0.1f, -0.1f, 0f, 0f, 0.15f, 0f };
|
||||
private static final ResourceLocation GLINT_TEXTURE = new ResourceLocation("angelica", "test/glint");
|
||||
|
||||
private static boolean overridesRegistered;
|
||||
private static boolean skipClientArrays;
|
||||
private static boolean probeRegistered;
|
||||
private static Runnable onBufferSetup;
|
||||
|
||||
private AngelicaBufferSource source;
|
||||
@@ -82,14 +81,13 @@ class RetainedTesrGroupsGLTest {
|
||||
private CubeParams[] glintCubes;
|
||||
|
||||
@BeforeAll
|
||||
static void registerClientArrayOverrides() {
|
||||
if (overridesRegistered) return;
|
||||
overridesRegistered = true;
|
||||
static void registerBufferSetupProbe() {
|
||||
if (probeRegistered) return;
|
||||
probeRegistered = true;
|
||||
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
|
||||
if (onBufferSetup != null) onBufferSetup.run();
|
||||
return skipClientArrays;
|
||||
return false;
|
||||
});
|
||||
VertexFormat.registerClearBufferStateOverride(format -> skipClientArrays);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -119,7 +117,6 @@ class RetainedTesrGroupsGLTest {
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
onBufferSetup = null;
|
||||
skipClientArrays = false;
|
||||
source.discard();
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
@@ -194,7 +191,7 @@ class RetainedTesrGroupsGLTest {
|
||||
for (int id = 1; id <= 3; id++) {
|
||||
final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f);
|
||||
groups.queue(template, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,12 +218,8 @@ class RetainedTesrGroupsGLTest {
|
||||
state.setCurrentBlockEntity(83);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.5f);
|
||||
draws.clear();
|
||||
skipClientArrays = true;
|
||||
assertEquals(GL11.GL_QUADS, template.drawMode);
|
||||
groups.drawLayer(layer);
|
||||
// FIXME each GL_QUADS fallback draw (VertexBuffer.draw inside MeshBuffer.render) raises this with the
|
||||
// client arrays skipped; a GL_TRIANGLES template does not.
|
||||
assertEquals(GL11.GL_INVALID_ENUM, GL11.glGetError());
|
||||
assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i,
|
||||
@@ -290,7 +283,7 @@ class RetainedTesrGroupsGLTest {
|
||||
for (int id = 1; id <= 3; id++) {
|
||||
final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f);
|
||||
groups.queue(template, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null, null);
|
||||
}
|
||||
|
||||
groups.drawLayer(layer);
|
||||
@@ -330,7 +323,7 @@ class RetainedTesrGroupsGLTest {
|
||||
for (int id = 1; id <= 3; id++) {
|
||||
final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f);
|
||||
groups.queue(template, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]);
|
||||
groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1], null);
|
||||
}
|
||||
|
||||
final RetainedTesrGroups.Group group = RetainedTesrTestSupport.onlyGroup(groups);
|
||||
@@ -339,7 +332,6 @@ class RetainedTesrGroupsGLTest {
|
||||
assertEquals(2, group.cubeColumns.runs.size(), "sanity: cube runs A,B,A merge into two heads");
|
||||
assertNotNull(group.cubeColumns.runs.get(0).next, "sanity: the cube A head must carry a second segment");
|
||||
|
||||
skipClientArrays = true;
|
||||
groups.drawLayer(layer);
|
||||
|
||||
assertEquals(0, pipeline.matrixBinds);
|
||||
@@ -610,8 +602,8 @@ class RetainedTesrGroupsGLTest {
|
||||
FfpFixture.clear();
|
||||
bindBase();
|
||||
final InstanceColumns cols = new InstanceColumns();
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, cubeE1, 0, 0xFFFFFFFF, 0, 0L);
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, cubeE2, 0, 0xFFFFFFFF, 0, 0L);
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, cubeE1, 0, 0xFFFFFFFF, 0, 0L, null);
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, cubeE2, 0, 0xFFFFFFFF, 0, 0L, null);
|
||||
instanced.drawCubes(cols, run(2, 2 * glintCubes.length));
|
||||
instanced.endFrame();
|
||||
return FfpFixture.readRegion(SIZE);
|
||||
@@ -619,7 +611,7 @@ class RetainedTesrGroupsGLTest {
|
||||
|
||||
private void drawCubeInstance(Matrix4f mv) {
|
||||
final InstanceColumns cols = new InstanceColumns();
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, mv, 0, 0xFFFFFFFF, 0, 0L);
|
||||
cols.addCubes(null, glintCubes, CUBE_SCALE, mv, 0, 0xFFFFFFFF, 0, 0L, null);
|
||||
instanced.drawCubes(cols, run(1, glintCubes.length));
|
||||
instanced.endFrame();
|
||||
}
|
||||
@@ -676,10 +668,10 @@ class RetainedTesrGroupsGLTest {
|
||||
setGlintBlendState();
|
||||
bindGlint();
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE1, 0, -1, 0, 0L, 1, glintMatrixA);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE1, 0, -1, 0, 0L, 1, glintMatrixB);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE2, 0, -1, 0, 0L, 2, glintMatrixA);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE2, 0, -1, 0, 0L, 2, glintMatrixB);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE1, 0, -1, 0, 0L, 1, glintMatrixA, null);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE1, 0, -1, 0, 0L, 1, glintMatrixB, null);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE2, 0, -1, 0, 0L, 2, glintMatrixA, null);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, cubeE2, 0, -1, 0, 0L, 2, glintMatrixB, null);
|
||||
groups.drawLayer(layer);
|
||||
instanced.endFrame();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "the merged candidate draw must not raise a GL error");
|
||||
@@ -755,7 +747,7 @@ class RetainedTesrGroupsGLTest {
|
||||
for (Matrix4f pose : new Matrix4f[] {cubeE1, cubeE2}) {
|
||||
applyGlintTexMatrix(glintMatrixA);
|
||||
assertTrue(capture.begin(groups));
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, pose, 0, -1, 0, 0L, 0, glintMatrixA);
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, pose, 0, -1, 0, 0L, 0, glintMatrixA, null);
|
||||
capture.end();
|
||||
applyGlintTexMatrix(glintMatrixB);
|
||||
assertTrue(capture.replay(groups));
|
||||
|
||||
+21
-7
@@ -11,6 +11,7 @@ import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.batchedentityrendering.impl.BufferSegment;
|
||||
import net.coderbot.batchedentityrendering.impl.BufferSourceProbe;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -403,8 +404,8 @@ class RetainedTesrGroupsTest {
|
||||
final Matrix4f mv = new Matrix4f().translation(1f, 2f, 3f);
|
||||
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring));
|
||||
groups.queue(template, cubes(3), 0.0625f, layer, STREAM, mv, 7, -1, 0, 0L, 5, null);
|
||||
groups.queue(template, cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 7, -1, 0, 0L, 5, null);
|
||||
groups.queue(template, cubes(3), 0.0625f, layer, STREAM, mv, 7, -1, 0, 0L, 5, null, null);
|
||||
groups.queue(template, cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 7, -1, 0, 0L, 5, null, null);
|
||||
|
||||
assertTrue(groups.hasDraws(layer));
|
||||
assertEquals(0, groups.streamedInstances);
|
||||
@@ -416,8 +417,8 @@ class RetainedTesrGroupsTest {
|
||||
|
||||
final RetainedTesrGroups piped = new RetainedTesrGroups(source);
|
||||
piped.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new RetainedTesrTestSupport.CountingPipeline(false, false));
|
||||
piped.queue(template, cubes(3), 0.0625f, layer, STREAM, mv, 7, -1, 0, 0L, 5, null);
|
||||
piped.queue(template, cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 7, -1, 0, 0L, 5, null);
|
||||
piped.queue(template, cubes(3), 0.0625f, layer, STREAM, mv, 7, -1, 0, 0L, 5, null, null);
|
||||
piped.queue(template, cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 7, -1, 0, 0L, 5, null, null);
|
||||
|
||||
final RetainedTesrGroups.Group pipedGroup = onlyGroup(piped);
|
||||
assertEquals(2, pipedGroup.cubeColumns.templates.size(), "a pipeline can fall back to the per-part template");
|
||||
@@ -428,7 +429,7 @@ class RetainedTesrGroupsTest {
|
||||
void cubePartsFallBackToTemplatesWithoutAnInstancedRenderer() {
|
||||
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
groups.queue(template(), cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null);
|
||||
groups.queue(template(), cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null, null);
|
||||
|
||||
assertEquals(0, cubeInstances(groups));
|
||||
assertEquals(1, groups.streamedInstances);
|
||||
@@ -439,9 +440,9 @@ class RetainedTesrGroupsTest {
|
||||
final CubeParams[] params = cubes(2);
|
||||
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring));
|
||||
groups.queue(template(), params, 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null);
|
||||
groups.queue(template(), params, 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null, null);
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring));
|
||||
groups.queue(template(), params, 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null);
|
||||
groups.queue(template(), params, 0.0625f, layer, STREAM, new Matrix4f(), 0, -1, 0, 0L, 5, null, null);
|
||||
|
||||
assertEquals(2, cubeInstances(groups), "the previous frame's instances must not accumulate");
|
||||
}
|
||||
@@ -526,4 +527,17 @@ class RetainedTesrGroupsTest {
|
||||
|
||||
assertEquals(2, groups.groupCount(), "the last-group memo must compare material, not just layer and id, on every queue call");
|
||||
}
|
||||
|
||||
@Test
|
||||
void glintCopiesSampleTheirOwnTextureRatherThanTheBaseAtlasRegion() {
|
||||
final EntityTextureAtlas.Page page = new EntityTextureAtlas.Page(new ResourceLocation("angelica", "test_page"), 0);
|
||||
final EntityTextureAtlas.Slot slot = new EntityTextureAtlas.Slot(page, 0, 0, 4, 2);
|
||||
final RetainedTesrGroups.InstanceColumns base = new RetainedTesrGroups.InstanceColumns();
|
||||
base.addCubes(null, cubes(1), 0.0625f, new Matrix4f(), 0, -1, 0, 0L, slot);
|
||||
final RetainedTesrGroups.InstanceColumns glint = new RetainedTesrGroups.InstanceColumns();
|
||||
glint.appendRange(base, 0, 1, 0, -1, 0);
|
||||
|
||||
assertSame(slot, base.atlasSlot(0));
|
||||
assertNull(glint.atlasSlot(0));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class EntityIdHelperTest {
|
||||
for (int id : new int[] { 41, 42 }) {
|
||||
settings.setEntityIds(key -> name.equals(key) ? 17 : -1);
|
||||
final NbtConditionalIdMap<NamespacedId> nbt = new NbtConditionalIdMap<>();
|
||||
nbt.addCondition(name, Collections.singletonMap("Data.Variant", new NbtValue("original", true)), id);
|
||||
nbt.addCondition(name, Collections.singletonMap("Data.Variant", new NbtValue("original", true)), id, null);
|
||||
settings.setEntityNbtMap(nbt);
|
||||
assertEquals(id, identity.resolve(), "resolve the captured variant against the current pack");
|
||||
assertEquals(17, EntityIdHelper.getEntityId(entity), "the live entity has changed variants");
|
||||
|
||||
Reference in new issue
Block a user