Compare commits

...
8 Commits
36 changed files with 972 additions and 162 deletions

No files matched your search

+1
View File
@@ -12,6 +12,7 @@ build/
/config/
/crash-reports/
/logs/
tmp/
options.txt
/saves/
usernamecache.json
+1 -1
View File
@@ -144,7 +144,7 @@ dependencies {
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
@@ -877,9 +877,14 @@ public class DisplayListManager {
// Empty display list - per OpenGL spec, still valid after glNewList/glEndList
compiled = CompiledDisplayList.EMPTY;
}
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
previous.delete();
final boolean locked = GLStateManager.acquireDrawLock();
try {
final CompiledDisplayList previous = displayListCache.put(glListId, compiled);
if (previous != null && previous != CompiledDisplayList.EMPTY && previous != compiled) {
previous.delete();
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
// Log compilation details if enabled (before context restoration changes glListId)
@@ -1012,33 +1017,43 @@ public class DisplayListManager {
}
private static void executeDisplayList(int list) {
final CompiledDisplayList compiled = displayListCache.get(list);
if (compiled != null) {
compiled.render(list);
return;
}
// Fall back to VBOManager or legacy display list
if (list < 0) {
// Negative IDs are VBOManager space (entity models, etc.)
final VertexBuffer vbo = VBOManager.get(list);
if (vbo != null) {
vbo.render();
final boolean locked = GLStateManager.acquireDrawLock();
try {
final CompiledDisplayList compiled = displayListCache.get(list);
if (compiled != null) {
compiled.render(list);
return;
}
// Per OpenGL spec: if list is undefined, glCallList has no effect
// Fall back to VBOManager or legacy display list
if (list < 0) {
// Negative IDs are VBOManager space (entity models, etc.)
final VertexBuffer vbo = VBOManager.get(list);
if (vbo != null) {
vbo.render();
}
// Per OpenGL spec: if list is undefined, glCallList has no effect
}
// An uncached positive-ID list = no-op (per OpenGL spec)
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
// An uncached positive-ID list = no-op (per OpenGL spec)
}
/**
* Delete display lists and free their VBO resources.
*/
public static void glDeleteLists(int list, int range) {
for (int i = list; i < list + range; i++) {
CompiledDisplayList compiled = displayListCache.remove(i);
if (compiled != null) {
compiled.delete();
final boolean locked = GLStateManager.acquireDrawLock();
try {
for (int i = list; i < list + range; i++) {
CompiledDisplayList compiled = displayListCache.remove(i);
if (compiled != null) {
compiled.delete();
}
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
}
@@ -89,6 +89,7 @@ import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -96,6 +97,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
@@ -251,6 +253,18 @@ public class GLStateManager {
// Reference to DrawableGL (main display context) - works with both FML and BLS splash
@Setter private static Drawable drawableGL = null;
private static final ReentrantLock DRAW_LOCK = new ReentrantLock();
public static boolean acquireDrawLock() {
if (splashComplete) return false;
DRAW_LOCK.lock();
return true;
}
public static void releaseDrawLock() {
DRAW_LOCK.unlock();
}
public static boolean isCachingEnabled() {
if (splashComplete) return true;
return Thread.currentThread() == drawableGLHolder;
@@ -2243,15 +2257,25 @@ public class GLStateManager {
}
public static void preDraw(int drawMode) {
prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
final boolean locked = acquireDrawLock();
try {
prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
} finally {
if (locked) releaseDrawLock();
}
}
public static void preDraw() {
disableWideLineEmulation();
ShaderManager.getInstance().preDraw();
prepareClientArrays();
final boolean locked = acquireDrawLock();
try {
disableWideLineEmulation();
ShaderManager.getInstance().preDraw();
prepareClientArrays();
} finally {
if (locked) releaseDrawLock();
}
}
public static void glDrawElements(int mode, ByteBuffer indices) {
@@ -3245,6 +3269,7 @@ public class GLStateManager {
splashComplete = true;
drawableGLHolder = null;
drawableGL = null;
ImmediateModeRecorder.cleanupOrphanTessellators();
}
public static void glNewList(int list, int mode) {
@@ -5174,6 +5199,12 @@ public class GLStateManager {
RENDER_BACKEND.deleteBuffers(buffers);
}
public static void glDeleteBuffers(int[] buffers) {
for (int buffer : buffers) {
glDeleteBuffers(buffer);
}
}
private static void invalidateDeletedBuffer(int buffer) {
if (buffer == 0) return;
if (boundVBO == buffer) boundVBO = 0;
@@ -5185,6 +5216,7 @@ public class GLStateManager {
for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
if (data.ebo == buffer) data.ebo = 0;
}
}
public static void glBindBuffer(int target, int buffer) {
@@ -5237,34 +5269,279 @@ public class GLStateManager {
return RENDER_BACKEND.genBuffers();
}
public static void glGenBuffers(IntBuffer buffers) {
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.genBuffers());
}
public static void glGenBuffers(int[] buffers) {
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.genBuffers();
}
public static int glCreateBuffers() {
return RENDER_BACKEND.createBuffers();
}
public static void glCreateBuffers(IntBuffer buffers) {
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.createBuffers());
}
public static void glCreateBuffers(int[] buffers) {
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
}
public static void glBufferData(int target, long size, int usage) { RENDER_BACKEND.bufferData(target, size, usage); }
public static void glBufferData(int target, ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, LongBuffer data, int usage) { glBufferData(target, MemoryUtilities.memByteBuffer(data), usage); }
public static void glBufferData(int target, int[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, float[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, java.nio.ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, java.nio.ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, java.nio.IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, java.nio.FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferData(int target, java.nio.DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
public static void glBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, java.nio.ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, java.nio.IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, java.nio.FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, java.nio.DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferData(int target, short[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glBufferData(target, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glBufferData(int target, long[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glBufferData(target, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glBufferData(int target, double[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glBufferData(target, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void nglBufferData(int target, long size, long data, int usage) {
if (data == 0L) {
glBufferData(target, size, usage);
return;
}
glBufferData(target, MemoryUtilities.memByteBuffer(data, checkedSize(size)), usage);
}
public static void glBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
public static void glBufferSubData(int target, long offset, LongBuffer data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glBufferSubData(int target, long offset, short[] data) {
final ByteBuffer copy = copyOf(data);
glBufferSubData(target, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glBufferSubData(int target, long offset, int[] data) {
final ByteBuffer copy = copyOf(data);
glBufferSubData(target, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glBufferSubData(int target, long offset, long[] data) {
final ByteBuffer copy = copyOf(data);
glBufferSubData(target, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glBufferSubData(int target, long offset, float[] data) {
final ByteBuffer copy = copyOf(data);
glBufferSubData(target, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glBufferSubData(int target, long offset, double[] data) {
final ByteBuffer copy = copyOf(data);
glBufferSubData(target, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
public static ByteBuffer glMapBuffer(int target, int access) { return RENDER_BACKEND.mapBuffer(target, access); }
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
public static boolean glUnmapBuffer(int target) { return RENDER_BACKEND.unmapBuffer(target); }
public static void glGetBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, java.nio.ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, java.nio.IntBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, java.nio.FloatBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, java.nio.DoubleBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
public static void glGetBufferSubData(int target, long offset, LongBuffer data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glGetBufferSubData(int target, long offset, short[] data) {
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 1);
glGetBufferSubData(target, offset, copy);
copy.asShortBuffer().get(data);
MemoryUtilities.memFree(copy);
}
public static void glGetBufferSubData(int target, long offset, int[] data) {
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
glGetBufferSubData(target, offset, copy);
copy.asIntBuffer().get(data);
MemoryUtilities.memFree(copy);
}
public static void glGetBufferSubData(int target, long offset, long[] data) {
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
glGetBufferSubData(target, offset, copy);
copy.asLongBuffer().get(data);
MemoryUtilities.memFree(copy);
}
public static void glGetBufferSubData(int target, long offset, float[] data) {
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 2);
glGetBufferSubData(target, offset, copy);
copy.asFloatBuffer().get(data);
MemoryUtilities.memFree(copy);
}
public static void glGetBufferSubData(int target, long offset, double[] data) {
final ByteBuffer copy = MemoryUtilities.memAlloc(data.length << 3);
glGetBufferSubData(target, offset, copy);
copy.asDoubleBuffer().get(data);
MemoryUtilities.memFree(copy);
}
public static void nglGetBufferSubData(int target, long offset, long size, long data) { glGetBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
public static int glGetBufferParameteri(int target, int pname) { return RENDER_BACKEND.getBufferParameteri(target, pname); }
public static void glBufferStorage(int target, java.nio.ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
public static int glGetBufferParameter(int target, int pname) { return glGetBufferParameteri(target, pname); }
public static void glGetBufferParameter(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
public static void glGetBufferParameteriv(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
public static void glGetBufferParameteriv(int target, int pname, int[] params) { params[0] = glGetBufferParameteri(target, pname); }
public static void glBufferStorage(int target, long size, int flags) { RENDER_BACKEND.bufferStorage(target, size, flags); }
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, java.nio.ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
public static void glBufferStorage(int target, ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
public static void glBufferStorage(int target, ShortBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
public static void glBufferStorage(int target, IntBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
public static void glBufferStorage(int target, FloatBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
public static void glBufferStorage(int target, DoubleBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
public static void glBufferStorage(int target, short[] data, int flags) {
final ByteBuffer copy = copyOf(data);
glBufferStorage(target, copy, flags);
MemoryUtilities.memFree(copy);
}
public static void glBufferStorage(int target, int[] data, int flags) {
final ByteBuffer copy = copyOf(data);
glBufferStorage(target, copy, flags);
MemoryUtilities.memFree(copy);
}
public static void glBufferStorage(int target, float[] data, int flags) {
final ByteBuffer copy = copyOf(data);
glBufferStorage(target, copy, flags);
MemoryUtilities.memFree(copy);
}
public static void glBufferStorage(int target, double[] data, int flags) {
final ByteBuffer copy = copyOf(data);
glBufferStorage(target, copy, flags);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferData(int buffer, long size, int usage) { RENDER_BACKEND.namedBufferData(buffer, size, usage); }
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
public static void glNamedBufferData(int buffer, ShortBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
public static void glNamedBufferData(int buffer, IntBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
public static void glNamedBufferData(int buffer, LongBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
public static void glNamedBufferData(int buffer, DoubleBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
public static void glNamedBufferData(int buffer, short[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glNamedBufferData(buffer, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferData(int buffer, int[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glNamedBufferData(buffer, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferData(int buffer, long[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glNamedBufferData(buffer, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferData(int buffer, float[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glNamedBufferData(buffer, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferData(int buffer, double[] data, int usage) {
final ByteBuffer copy = copyOf(data);
glNamedBufferData(buffer, copy, usage);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
public static void glNamedBufferSubData(int buffer, long offset, ShortBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glNamedBufferSubData(int buffer, long offset, IntBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glNamedBufferSubData(int buffer, long offset, LongBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glNamedBufferSubData(int buffer, long offset, FloatBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glNamedBufferSubData(int buffer, long offset, DoubleBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
public static void glNamedBufferSubData(int buffer, long offset, short[] data) {
final ByteBuffer copy = copyOf(data);
glNamedBufferSubData(buffer, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferSubData(int buffer, long offset, int[] data) {
final ByteBuffer copy = copyOf(data);
glNamedBufferSubData(buffer, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferSubData(int buffer, long offset, long[] data) {
final ByteBuffer copy = copyOf(data);
glNamedBufferSubData(buffer, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferSubData(int buffer, long offset, float[] data) {
final ByteBuffer copy = copyOf(data);
glNamedBufferSubData(buffer, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glNamedBufferSubData(int buffer, long offset, double[] data) {
final ByteBuffer copy = copyOf(data);
glNamedBufferSubData(buffer, offset, copy);
MemoryUtilities.memFree(copy);
}
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
public static boolean glIsBuffer(int buffer) { return RENDER_BACKEND.isBuffer(buffer); }
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
private static int checkedSize(long size) {
if (size < 0 || size > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Buffer range does not fit a ByteBuffer wrapper: " + size);
}
return (int) size;
}
private static ByteBuffer copyOf(short[] data) {
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 1);
bb.asShortBuffer().put(data);
return bb;
}
private static ByteBuffer copyOf(int[] data) {
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
bb.asIntBuffer().put(data);
return bb;
}
private static ByteBuffer copyOf(long[] data) {
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
bb.asLongBuffer().put(data);
return bb;
}
private static ByteBuffer copyOf(float[] data) {
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 2);
bb.asFloatBuffer().put(data);
return bb;
}
private static ByteBuffer copyOf(double[] data) {
final ByteBuffer bb = MemoryUtilities.memAlloc(data.length << 3);
bb.asDoubleBuffer().put(data);
return bb;
}
public static void glBindVertexArray(int array) {
if (DisplayListManager.isRecording()) {
DisplayListManager.recordBindVAO(array);
@@ -6165,7 +6442,7 @@ public class GLStateManager {
RENDER_BACKEND.getUniformiv(program, location, params);
}
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
GL11.glGetTexImage(target, level, format, type, pixels);
RENDER_BACKEND.getTexImage(target, level, format, type, pixels);
}
/**
@@ -91,21 +91,26 @@ public final class QuadConverter {
}
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
GLStateManager.preDraw();
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
final int quadCount = vertexCount / 4;
final int prevEbo = GLStateManager.getBoundEBO();
ensureCapacity(first / 4 + quadCount);
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
final long indexOffset = (long) (first / 4) * 6 * 4;
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
final boolean locked = GLStateManager.acquireDrawLock();
try {
GLStateManager.preDraw();
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4";
assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
final int quadCount = vertexCount / 4;
final int prevEbo = GLStateManager.getBoundEBO();
ensureCapacity(first / 4 + quadCount);
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
final long indexOffset = (long) (first / 4) * 6 * 4;
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
}
/**
@@ -123,32 +128,37 @@ public final class QuadConverter {
}
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
final boolean locked = GLStateManager.acquireDrawLock();
try {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
if (scratchEboId == 0) {
scratchEboId = RENDER_BACKEND.genBuffers();
if (scratchEboId == 0) {
scratchEboId = RENDER_BACKEND.genBuffers();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
if (needed > scratchEboCapacity) {
// Power-of-2 growth -- allocate full capacity, upload actual data
int newCap = Math.max(4096, scratchEboCapacity);
while (newCap < needed) newCap *= 2;
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
scratchEboCapacity = newCap;
}
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
if (needed > scratchEboCapacity) {
// Power-of-2 growth — allocate full capacity, upload actual data
int newCap = Math.max(4096, scratchEboCapacity);
while (newCap < needed) newCap *= 2;
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
scratchEboCapacity = newCap;
}
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
if (instanced) {
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
} else {
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
}
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
memFree(dst);
}
@@ -146,6 +146,9 @@ public class RenderSystem {
coreProfile = true;
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
ShaderManager.getInstance().enable();
} else {
throw new IllegalStateException("Non-core GL context (profile mask 0x" + Integer.toHexString(profileMask)
+ "); FFP emulation requires a core profile and nothing would render. Context creation should have rejected this.");
}
}
@@ -301,7 +304,7 @@ public class RenderSystem {
}
public static void bufferData(int target, FloatBuffer data, int usage) {
RENDER_BACKEND.bufferData(target, data, usage);
GLStateManager.glBufferData(target, data, usage);
}
public static int bufferStorage(int target, FloatBuffer data, int usage) {
@@ -447,7 +450,7 @@ public class RenderSystem {
}
public static int createBuffers() {
return RENDER_BACKEND.genBuffers();
return GLStateManager.glGenBuffers();
}
public static void bindBufferBase(int target, int index, int buffer) {
@@ -455,13 +458,13 @@ public class RenderSystem {
}
public static void bufferStorage(int target, long size, int flags) {
RENDER_BACKEND.bufferStorage(target, size, flags);
GLStateManager.glBufferStorage(target, size, flags);
}
public static void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, int[] data) {
final ByteBuffer buf = BufferUtils.createByteBuffer(data.length * 4);
buf.asIntBuffer().put(data);
RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, buf);
GLStateManager.glClearBufferSubData(target, internalFormat, offset, size, format, type, buf);
}
public static void deleteBuffers(int buffer) {
@@ -549,7 +552,7 @@ public class RenderSystem {
}
}
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460". Inlined from StandardMacros.getGlVersion(). */
/** Parse a GL version string (e.g. "4.6.0 NVIDIA ...") into concatenated digits "460" */
static String parseGlVersionString(String info) {
final Matcher matcher = SEMVER_PATTERN.matcher(Objects.requireNonNull(info));
if (!matcher.matches()) {
@@ -561,4 +564,18 @@ public class RenderSystem {
if (bugfix == null || bugfix.isEmpty()) bugfix = "0";
return major + minor + bugfix;
}
/** Parse a GL version string into packed major*10+minor (e.g. "4.3.0 - Build ..." -> 43), or -1 if unparseable. */
public static int parseMajorMinor(String glVersion) {
if (glVersion == null) return -1;
final Matcher matcher = SEMVER_PATTERN.matcher(glVersion.trim());
if (!matcher.matches()) return -1;
return Integer.parseInt(matcher.group("major")) * 10 + Integer.parseInt(matcher.group("minor"));
}
public static boolean isContextValid(int requestedMajor, int requestedMinor, int actualVersion, boolean coreBitSet) {
if (!coreBitSet) return false;
if (actualVersion < 0) return true;
return actualVersion >= requestedMajor * 10 + requestedMinor;
}
}
@@ -327,7 +327,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
@Override
public void copyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) {
throw new UnsupportedOperationException("GLRenderBackend.copyBufferSubData should not be called — LWJGL service handles this");
GL31.glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
}
@Override
@@ -622,6 +622,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glGetTexImage(target, level, format, type, pixels);
}
@Override
public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
GL11.glGetTexImage(target, level, format, type, pixelBufferOffset);
}
@Override
public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
return GL30.glGetFramebufferAttachmentParameteri(target, attachment, pname);
@@ -1058,6 +1063,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return GL30.glMapBufferRange(target, offset, length, access, null);
}
@Override
public void flushMappedBufferRange(int target, long offset, long length) {
GL30.glFlushMappedBufferRange(target, offset, length);
}
@Override
public int genVertexArrays() {
return GL30.glGenVertexArrays();
@@ -1249,6 +1259,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
return GL45.glCreateBuffers();
}
@Override
public void namedBufferData(int buffer, long size, int usage) {
GL45.glNamedBufferData(buffer, size, usage);
}
@Override
public void namedBufferData(int buffer, ByteBuffer data, int usage) {
GL45.glNamedBufferData(buffer, data, usage);
@@ -151,6 +151,7 @@ public abstract class RenderBackend {
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
public abstract int getFramebufferAttachmentParameteri(int target, int attachment, int pname);
public abstract int createShader(int type);
@@ -249,6 +250,7 @@ public abstract class RenderBackend {
public abstract int getBufferParameteri(int target, int pname);
public abstract boolean isBuffer(int buffer);
public abstract ByteBuffer mapBufferRange(int target, long offset, long length, int access);
public abstract void flushMappedBufferRange(int target, long offset, long length);
public abstract int genVertexArrays();
public abstract void deleteVertexArrays(int array);
@@ -292,6 +294,7 @@ public abstract class RenderBackend {
public abstract void namedFramebufferDrawBuffers(int framebuffer, IntBuffer bufs);
public abstract void blitNamedFramebuffer(int readFramebuffer, int drawFramebuffer, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
public abstract int createBuffers();
public abstract void namedBufferData(int buffer, long size, int usage);
public abstract void namedBufferData(int buffer, ByteBuffer data, int usage);
public abstract void namedBufferData(int buffer, FloatBuffer data, int usage);
public abstract void namedBufferSubData(int buffer, long offset, ByteBuffer data);
@@ -104,8 +104,8 @@ public class DSAARB extends DSAUnsupported {
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = RENDER_BACKEND.createBuffers();
RENDER_BACKEND.namedBufferData(buffer, data, usage);
final int buffer = GLStateManager.glCreateBuffers();
GLStateManager.glNamedBufferData(buffer, data, usage);
return buffer;
}
@@ -121,9 +121,9 @@ public class DSAUnsupported implements DSAAccess {
@Override
public int bufferStorage(int target, FloatBuffer data, int usage) {
final int buffer = RENDER_BACKEND.genBuffers();
final int buffer = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(target, buffer);
RENDER_BACKEND.bufferData(target, data, usage);
GLStateManager.glBufferData(target, data, usage);
GLStateManager.glBindBuffer(target, 0);
return buffer;
@@ -6,8 +6,10 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.stacks.Vec3fStack;
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import lombok.Getter;
import org.joml.Vector3f;
import org.joml.Vector4f;
@@ -66,11 +68,33 @@ public final class ShaderManager {
}
public void enable() {
warmUp();
enabled = true;
GLStateManager.LOGGER.info("FFP shader emulation enabled");
}
// Force loading of classes before the SplashThread kicks off
// Ensure it's GL free
private static void warmUp() {
try {
final long[] fkScratch = new long[FragmentKey.MAX_UNITS];
final int fkLen = FragmentKey.packFromState(fkScratch);
final int fragMask = FragmentKey.unitMaskFromPacked(fkScratch, fkLen);
final long vkPacked = VertexKey.packFromState(true, true, true, true, fragMask);
final VertexKey vk = VertexKey.fromPacked(vkPacked);
VertexShaderGenerator.generate(vk);
FragmentShaderGenerator.generate(FragmentKey.fromPacked(fkScratch, fkLen));
GeometryShaderGenerator.generate(vk);
final Class<?>[] touched = { Program.class, ShaderCache.class, TessellatorStreamingDrawer.class, QuadConverter.class };
for (Class<?> c : touched) {
c.getName();
}
} catch (Throwable t) {
GLStateManager.LOGGER.warn("FFP warmup failed; draw-path classes will resolve lazily", t);
}
}
public void disable() {
enabled = false;
}
@@ -10,6 +10,8 @@ import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Records immediate mode GL calls (glBegin/glEnd/glVertex) via {@link DirectTessellator}.
@@ -18,28 +20,37 @@ import java.util.Arrays;
public final class ImmediateModeRecorder {
private static final CallbackTessellator tessellator = new CallbackTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
/** Separate tessellator for the splash thread. Nulled after splash completes. */
private static DirectTessellator splashTessellator;
private record ThreadTessellator(Thread thread, DirectTessellator tessellator) {}
private static final Queue<ThreadTessellator> threadTessellators = new ConcurrentLinkedQueue<>();
private static final ThreadLocal<DirectTessellator> threadTessellator = ThreadLocal.withInitial(() -> {
final DirectTessellator t = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
threadTessellators.add(new ThreadTessellator(Thread.currentThread(), t));
cleanupOrphanTessellators();
return t;
});
private ImmediateModeRecorder() {
}
public static void initSplashTessellator() {
splashTessellator = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
public static int getThreadTessellatorCount() {
return threadTessellators.size();
}
public static void destroySplashTessellator() {
splashTessellator.delete();
splashTessellator = null;
public static synchronized void cleanupOrphanTessellators() {
threadTessellators.removeIf(tt -> {
if (tt.thread().isAlive()) return false;
tt.tessellator().delete();
return true;
});
}
private static DirectTessellator tessellator() {
final DirectTessellator splash = splashTessellator;
if (splash != null && Thread.currentThread() != GLStateManager.getMainThread()) {
return splash;
if (Thread.currentThread() == GLStateManager.getMainThread()) {
return tessellator;
}
return tessellator;
return threadTessellator.get();
}
public static DirectTessellator getInternalTessellator() {
@@ -48,6 +48,11 @@ public class GLSMRedirector {
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
private static final String ARBDrawInstanced = "org/lwjgl/opengl/ARBDrawInstanced";
private static final String EXTDrawInstanced = "org/lwjgl/opengl/EXTDrawInstanced";
private static final String ARBBufferObject = "org/lwjgl/opengl/ARBBufferObject";
private static final String ARBVertexBufferObject = "org/lwjgl/opengl/ARBVertexBufferObject";
private static final String ARBBufferStorage = "org/lwjgl/opengl/ARBBufferStorage";
private static final String ARBCopyBuffer = "org/lwjgl/opengl/ARBCopyBuffer";
private static final String ARBMapBufferRange = "org/lwjgl/opengl/ARBMapBufferRange";
// Redirect VAO related calls from NHLib
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
@@ -285,11 +290,16 @@ public class GLSMRedirector {
.add("glBindBuffer")
.add("glDeleteBuffers")
.add("glBufferData")
.add("nglBufferData")
.add("glBufferSubData")
.add("nglBufferSubData")
.add("glMapBuffer")
.add("glUnmapBuffer")
.add("glGetBufferSubData")
.add("nglGetBufferSubData")
.add("glGetBufferParameteri")
.add("glGetBufferParameter")
.add("glGetBufferParameteriv")
.add("glIsBuffer");
final var gl20 = RedirectMap.newMap()
.add("glBlendEquationSeparate")
@@ -356,10 +366,13 @@ public class GLSMRedirector {
.add("glVertexAttribIPointer")
.add("glGenerateMipmap")
.add("glGetFramebufferAttachmentParameteri")
.add("glBlitFramebuffer");
.add("glBlitFramebuffer")
.add("glMapBufferRange")
.add("glFlushMappedBufferRange");
final var gl31 = RedirectMap.newMap()
.add("glDrawElementsInstanced")
.add("glDrawArraysInstanced");
.add("glDrawArraysInstanced")
.add("glCopyBufferSubData");
final var gl32 = RedirectMap.newMap()
.add("glFramebufferTexture");
final var gl33 = RedirectMap.newMap()
@@ -383,6 +396,10 @@ public class GLSMRedirector {
final var gl44 = RedirectMap.newMap()
.add("glBufferStorage")
.add("glClearTexImage");
final var gl45 = RedirectMap.newMap()
.add("glCreateBuffers")
.add("glNamedBufferData")
.add("glNamedBufferSubData");
// Merge all GL version maps into the flat lookup
glMethodRedirects.putAll(gl11);
@@ -398,6 +415,7 @@ public class GLSMRedirector {
glMethodRedirects.putAll(gl42);
glMethodRedirects.putAll(gl43);
glMethodRedirects.putAll(gl44);
glMethodRedirects.putAll(gl45);
// MINECRAFT
methodRedirects.put(OpenGlHelper, RedirectMap.newMap()
@@ -470,6 +488,31 @@ public class GLSMRedirector {
.add("glDrawArraysInstancedEXT", "glDrawArraysInstanced")
.add("glDrawElementsInstancedEXT", "glDrawElementsInstanced")
);
final var arbBufferObject = RedirectMap.newMap()
.add("glBindBufferARB", "glBindBuffer")
.add("glGenBuffersARB", "glGenBuffers")
.add("glDeleteBuffersARB", "glDeleteBuffers")
.add("glIsBufferARB", "glIsBuffer")
.add("glBufferDataARB", "glBufferData")
.add("glBufferSubDataARB", "glBufferSubData")
.add("glGetBufferSubDataARB", "glGetBufferSubData")
.add("glMapBufferARB", "glMapBuffer")
.add("glUnmapBufferARB", "glUnmapBuffer")
.add("glGetBufferParameterARB", "glGetBufferParameter")
.add("glGetBufferParameteriARB", "glGetBufferParameteri")
.add("glGetBufferParameterivARB", "glGetBufferParameteriv");
methodRedirects.put(ARBBufferObject, arbBufferObject);
methodRedirects.put(ARBVertexBufferObject, arbBufferObject);
methodRedirects.put(ARBBufferStorage, RedirectMap.newMap()
.add("glBufferStorage")
);
methodRedirects.put(ARBCopyBuffer, RedirectMap.newMap()
.add("glCopyBufferSubData")
);
methodRedirects.put(ARBMapBufferRange, RedirectMap.newMap()
.add("glMapBufferRange")
.add("glFlushMappedBufferRange")
);
// APPLE
methodRedirects.put(APPLEVertexArrayObject, RedirectMap.newMap()
@@ -88,13 +88,18 @@ public class TessellatorStreamingDrawer {
final int vertexSize = format.getVertexSize();
final int requiredBytes = vertexCount * vertexSize;
ensureRepackCapacity(requiredBytes);
final boolean locked = GLStateManager.acquireDrawLock();
try {
ensureRepackCapacity(requiredBytes);
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
repackBuffer.position(0);
repackBuffer.limit((int)(writePtr - repackAddress));
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
repackBuffer.position(0);
repackBuffer.limit((int)(writePtr - repackAddress));
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
// Shrink rawBuffer if oversized
if (tess.rawBufferSize > 0x20000 && tess.rawBufferIndex < (tess.rawBufferSize << 3)) {
@@ -187,24 +192,29 @@ public class TessellatorStreamingDrawer {
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
*/
private static void uploadAndDraw(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
ensureVAO(flags, format);
final boolean locked = GLStateManager.acquireDrawLock();
try {
ensureVAO(flags, format);
int firstVertex = -1;
int firstVertex = -1;
if (persistentBuffer != null) {
firstVertex = persistentBuffer.upload(packed, vertexSize);
if (persistentBuffer != null) {
firstVertex = persistentBuffer.upload(packed, vertexSize);
}
if (firstVertex >= 0) {
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
} else {
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
orphanBuffers[flags].upload(packed);
firstVertex = 0;
}
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0);
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
if (firstVertex >= 0) {
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
} else {
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
orphanBuffers[flags].upload(packed);
firstVertex = 0;
}
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0);
}
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
@@ -0,0 +1,34 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class GLVersionParseTest {
@Test
void parseMajorMinor() {
assertEquals(43, RenderSystem.parseMajorMinor("4.3.0 - Build 20.19.15.4835"));
assertEquals(46, RenderSystem.parseMajorMinor("4.6.0 NVIDIA 551.61"));
assertEquals(41, RenderSystem.parseMajorMinor("4.1 Metal - 88"));
assertEquals(33, RenderSystem.parseMajorMinor("3.3"));
assertEquals(46, RenderSystem.parseMajorMinor("4.6 (Core Profile) Mesa 24.0.3"));
assertEquals(-1, RenderSystem.parseMajorMinor(null));
assertEquals(-1, RenderSystem.parseMajorMinor(""));
assertEquals(-1, RenderSystem.parseMajorMinor("garbage"));
assertEquals(-1, RenderSystem.parseMajorMinor("OpenGL ES 3.2"));
}
@Test
void isContextValid() {
assertTrue(RenderSystem.isContextValid(4, 4, 46, true));
assertTrue(RenderSystem.isContextValid(4, 4, 44, true));
assertFalse(RenderSystem.isContextValid(4, 4, 43, true));
assertFalse(RenderSystem.isContextValid(4, 4, 44, false));
assertTrue(RenderSystem.isContextValid(4, 4, -1, true));
assertFalse(RenderSystem.isContextValid(4, 4, -1, false));
assertTrue(RenderSystem.isContextValid(3, 3, 33, true));
}
}
@@ -0,0 +1,125 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lwjgl.opengl.GL11;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
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.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(GLSMCoreExtension.class)
class ImmediateModeThreadIsolationTest {
@Test
void backgroundThreadsGetDistinctTessellators() throws Exception {
final DirectTessellator mainTess = ImmediateModeRecorder.getInternalTessellator();
final AtomicReference<DirectTessellator> bg1 = new AtomicReference<>();
final AtomicReference<DirectTessellator> bg2 = new AtomicReference<>();
final Thread t1 = new Thread(() -> bg1.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-1");
final Thread t2 = new Thread(() -> bg2.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-2");
t1.start(); t2.start();
t1.join(); t2.join();
assertNotNull(bg1.get());
assertNotNull(bg2.get());
assertNotSame(mainTess, bg1.get(), "background thread must not share the main-thread tessellator");
assertNotSame(mainTess, bg2.get(), "background thread must not share the main-thread tessellator");
assertNotSame(bg1.get(), bg2.get(), "background threads must not share a tessellator");
}
@Test
void concurrentImmediateModeDoesNotCorruptRecording() throws Exception {
final List<Throwable> failures = new CopyOnWriteArrayList<>();
final CountDownLatch start = new CountDownLatch(1);
final AtomicBoolean stop = new AtomicBoolean(false);
final Runnable spam = () -> {
try {
start.await();
while (!stop.get()) {
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0, 0, 0);
GLStateManager.glVertex3f(1, 0, 0);
GLStateManager.glVertex3f(1, 1, 0);
GLStateManager.glVertex3f(0, 1, 0);
GLStateManager.glEnd();
}
} catch (Throwable t) {
failures.add(t);
}
};
final Thread t1 = new Thread(spam, "iso-spam-1");
final Thread t2 = new Thread(spam, "iso-spam-2");
t1.start(); t2.start();
start.countDown();
final List<Integer> lists = new ArrayList<>();
try {
for (int i = 0; i < 50; i++) {
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0, 0, 0);
GLStateManager.glVertex3f(1, 0, 0);
GLStateManager.glVertex3f(1, 1, 0);
GLStateManager.glVertex3f(0, 1, 0);
GLStateManager.glEnd();
GLStateManager.glEndList();
lists.add(list);
}
} finally {
stop.set(true);
t1.join(5000); t2.join(5000);
}
assertTrue(failures.isEmpty(), "background immediate mode threw: " + failures);
for (int list : lists) {
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(list);
assertNotNull(compiled, "list " + list + " should be compiled");
assertEquals(1, compiled.getCommandCounts().getOrDefault(GLCommand.DRAW_RANGE, 0), "list " + list + " should contain exactly the main thread's draw");
GLStateManager.glDeleteLists(list, 1);
}
}
@Test
void sweepFreesDeadThreadTessellators() throws Exception {
final Thread t = new Thread(() -> ImmediateModeRecorder.getInternalTessellator(), "iso-sweep");
t.start();
t.join();
final int before = ImmediateModeRecorder.getThreadTessellatorCount();
assertTrue(before >= 1);
ImmediateModeRecorder.cleanupOrphanTessellators();
assertTrue(ImmediateModeRecorder.getThreadTessellatorCount() < before, "dead thread's tessellator should be swept");
}
@Test
void drawLockGatesOnSplashComplete() throws Exception {
assertFalse(GLStateManager.acquireDrawLock(), "post-splash acquire must be a no-op");
final Field f = GLStateManager.class.getDeclaredField("splashComplete");
f.setAccessible(true);
f.set(null, false);
try {
assertTrue(GLStateManager.acquireDrawLock(), "during splash the lock must engage");
GLStateManager.releaseDrawLock();
} finally {
f.set(null, true);
}
}
}
@@ -659,6 +659,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
GL11C.glGetTexImage(target, level, format, type, pixels);
}
@Override
public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
GL11C.glGetTexImage(target, level, format, type, pixelBufferOffset);
}
@Override
public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
return GL30C.glGetFramebufferAttachmentParameteri(target, attachment, pname);
@@ -1096,6 +1101,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
return GL30C.glMapBufferRange(target, offset, length, access);
}
@Override
public void flushMappedBufferRange(int target, long offset, long length) {
GL30C.glFlushMappedBufferRange(target, offset, length);
}
@Override
public int genVertexArrays() {
return GL30C.glGenVertexArrays();
@@ -1281,6 +1291,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
return GL45C.glCreateBuffers();
}
@Override
public void namedBufferData(int buffer, long size, int usage) {
GL45C.glNamedBufferData(buffer, size, usage);
}
@Override
public void namedBufferData(int buffer, ByteBuffer data, int usage) {
GL45C.glNamedBufferData(buffer, data, usage);
@@ -327,6 +327,15 @@ public class BatchingFontRenderer {
private int fontAAStrengthLast = -1;
private void flushBatch() {
final boolean locked = GLStateManager.acquireDrawLock();
try {
flushBatchInner();
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
}
private void flushBatchInner() {
if (vertexDataPos == 0) {
clearBatch();
return;
@@ -2,17 +2,25 @@ package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.FontConfig;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.util.ResourceLocation;
import java.io.IOException;
import java.io.InputStream;
public final class FontProviderUnicode implements FontProvider {
public final class FontProviderUnicode implements FontProvider, IResourceManagerReloadListener {
private FontProviderUnicode() {
onResourceManagerReload(Minecraft.getMinecraft().getResourceManager());
((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener(this);
}
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
try {
InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
InputStream inputstream = resourceManager.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
//noinspection ResultOfMethodCallIgnored
inputstream.read(this.glyphWidth);
}
@@ -0,0 +1,30 @@
package com.gtnewhorizons.angelica.compat.etfuturum;
import cpw.mods.fml.common.Loader;
import ganymedes01.etfuturum.api.client.EndFlashAPI;
/**
* Use EFR to power End flash uniforms.
*/
public final class EndFlashCompat {
private static final boolean AVAILABLE = Loader.isModLoaded("etfuturum") && EndFlashCompat.class.getResource("/ganymedes01/etfuturum/api/client/EndFlashAPI.class") != null;
private EndFlashCompat() {}
public static boolean isAvailable() {
return AVAILABLE;
}
public static float getIntensity(float partialTicks) {
return AVAILABLE ? EndFlashAPI.getIntensity(partialTicks) : 0.0F;
}
public static float getXAngle() {
return AVAILABLE ? EndFlashAPI.getXAngle() : 0.0F;
}
public static float getYAngle() {
return AVAILABLE ? EndFlashAPI.getYAngle() : 0.0F;
}
}
@@ -170,6 +170,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
private final CloudSetting cloudSetting;
private final boolean shouldRenderUnderwaterOverlay;
private final boolean shouldRenderVignette;
private final boolean supportsEndFlash;
private final boolean shouldRenderSun;
private final boolean shouldRenderMoon;
private final boolean shouldRenderStars;
@@ -231,6 +232,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.cloudSetting = programs.getPackDirectives().getCloudSetting();
this.shouldRenderUnderwaterOverlay = programs.getPackDirectives().underwaterOverlay();
this.shouldRenderVignette = programs.getPackDirectives().vignette();
this.supportsEndFlash = programs.getPackDirectives().isSupportsEndFlash();
this.shouldRenderSun = programs.getPackDirectives().shouldRenderSun();
this.shouldRenderMoon = programs.getPackDirectives().shouldRenderMoon();
this.shouldRenderStars = programs.getPackDirectives().shouldRenderStars();
@@ -651,6 +653,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
return shouldRenderVignette;
}
@Override
public boolean supportsEndFlash() {
return supportsEndFlash;
}
@Override
public boolean shouldRenderSun() {
return shouldRenderSun;
@@ -152,6 +152,11 @@ public class FixedFunctionWorldRenderingPipeline implements WorldRenderingPipeli
return false;
}
@Override
public boolean supportsEndFlash() {
return false;
}
@Override
public CloudSetting getCloudSetting() {
return CloudSetting.DEFAULT;
@@ -49,6 +49,7 @@ public interface WorldRenderingPipeline {
boolean shouldDisableVanillaEntityShadows();
boolean shouldDisableDirectionalShading();
boolean supportsEndFlash();
CloudSetting getCloudSetting();
boolean shouldRenderUnderwaterOverlay();
boolean shouldRenderVignette();
@@ -48,6 +48,7 @@ public class PackDirectives {
@Getter private boolean oldHandLight;
@Getter private boolean prepareBeforeShadow;
@Getter private boolean supportsColorCorrection;
@Getter private boolean supportsEndFlash;
@Getter private int fallbackTex;
private Object2ObjectMap<String, Object2BooleanMap<String>> explicitFlips = new Object2ObjectOpenHashMap<>();
private Object2ObjectMap<String, TextureScaleOverride> scaleOverrides = new Object2ObjectOpenHashMap<>();
@@ -93,6 +94,7 @@ public class PackDirectives {
oldLighting = properties.getOldLighting().orElse(false);
fallbackTex = properties.getFallbackTex();
supportsColorCorrection = properties.getSupportsColorCorrection().orElse(false);
supportsEndFlash = properties.getSupportsEndFlash().orElse(false);
concurrentCompute = properties.getConcurrentCompute().orElse(false);
oldHandLight = properties.getOldHandLight().orElse(true);
explicitFlips = properties.getExplicitFlips();
@@ -112,6 +114,7 @@ public class PackDirectives {
frustumCulling = directives.frustumCulling;
occlusionCulling = directives.occlusionCulling;
oldLighting = directives.oldLighting;
supportsEndFlash = directives.supportsEndFlash;
concurrentCompute = directives.concurrentCompute;
explicitFlips = directives.explicitFlips;
scaleOverrides = directives.scaleOverrides;
@@ -70,6 +70,7 @@ public class ShaderProperties {
@Getter private OptionalBoolean shadowPlayer = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowBlockEntities = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean shadowLightBlockEntities = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean supportsEndFlash = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean underwaterOverlay = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean sun = OptionalBoolean.DEFAULT;
@Getter private OptionalBoolean moon = OptionalBoolean.DEFAULT;
@@ -205,6 +206,7 @@ public class ShaderProperties {
handleBooleanDirective(key, value, "shadowPlayer", bool -> shadowPlayer = bool);
handleBooleanDirective(key, value, "shadowBlockEntities", bool -> shadowBlockEntities = bool);
handleBooleanDirective(key, value, "shadowLightBlockEntities", bool -> shadowLightBlockEntities = bool);
handleBooleanDirective(key, value, "endFlashShadows", bool -> supportsEndFlash = bool);
handleBooleanDirective(key, value, "underwaterOverlay", bool -> underwaterOverlay = bool);
handleBooleanDirective(key, value, "sun", bool -> sun = bool);
handleBooleanDirective(key, value, "moon", bool -> moon = bool);
@@ -1,6 +1,12 @@
package net.coderbot.iris.shadow;
import com.gtnewhorizons.angelica.compat.etfuturum.EndFlashCompat;
import com.gtnewhorizons.angelica.compat.toremove.MatrixStack;
import net.coderbot.iris.Iris;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.WorldProviderEnd;
import org.joml.Matrix4f;
import static com.gtnewhorizons.angelica.compat.mojang.Constants.DEGREES_TO_RADIANS;
@@ -40,23 +46,41 @@ public class ShadowMatrices {
}
public static void createBaselineModelViewMatrix(MatrixStack target, float shadowAngle, float sunPathRotation) {
final float skyAngle;
if (shadowAngle < 0.25f) {
skyAngle = shadowAngle + 0.75f;
} else {
skyAngle = shadowAngle - 0.25f;
}
target.peek().getNormal().identity();
target.peek().getModel().identity();
target.peek().getModel().translate(0.0f, 0.0f, -100.0f);
target.rotateX(90F * DEGREES_TO_RADIANS);
target.rotateZ(skyAngle * -360.0f * DEGREES_TO_RADIANS);
target.rotateX(sunPathRotation * DEGREES_TO_RADIANS);
if (isEndFlashShadow()) {
// Project shadows from the End flash direction instead of the sun/moon.
target.rotateX((0.0f - EndFlashCompat.getXAngle()) * DEGREES_TO_RADIANS);
target.rotateY(EndFlashCompat.getYAngle() * DEGREES_TO_RADIANS);
} else {
final float skyAngle;
if (shadowAngle < 0.25f) {
skyAngle = shadowAngle + 0.75f;
} else {
skyAngle = shadowAngle - 0.25f;
}
target.rotateX(90F * DEGREES_TO_RADIANS);
target.rotateZ(skyAngle * -360.0f * DEGREES_TO_RADIANS);
target.rotateX(sunPathRotation * DEGREES_TO_RADIANS);
}
}
private static boolean isEndFlashShadow() {
final Minecraft mc = Minecraft.getMinecraft();
if (mc == null) {
return false;
}
final WorldClient world = mc.theWorld;
return world != null && world.provider instanceof WorldProviderEnd
&& EndFlashCompat.isAvailable()
&& Iris.getPipelineManager().getPipeline().map(WorldRenderingPipeline::supportsEndFlash).orElse(false);
}
public static void snapModelViewToGrid(MatrixStack target, float shadowIntervalSize, double cameraX, double cameraY, double cameraZ) {
if (Math.abs(shadowIntervalSize) == 0.0F) {
// Avoid a division by zero - semantically, this just means that the snapping does not take place,
@@ -1,10 +1,14 @@
package net.coderbot.iris.uniforms;
import com.gtnewhorizons.angelica.compat.etfuturum.EndFlashCompat;
import com.gtnewhorizons.angelica.compat.mojang.Constants;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.uniform.UniformHolder;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.WorldProviderEnd;
import org.joml.Matrix4f;
import org.joml.Vector4f;
@@ -18,9 +22,12 @@ import static net.coderbot.iris.gl.uniform.UniformUpdateFrequency.PER_FRAME;
public final class CelestialUniforms {
private static final Vector4f upPositionCache = new Vector4f();
private static final Matrix4f upMatrixCache = new Matrix4f();
private static final Vector4f ZERO_VECTOR_4f = new Vector4f(0.0F, 0.0F, 0.0F, 0.0F);
private final Vector4f positionCache = new Vector4f();
private final Matrix4f celestialMatrixCache = new Matrix4f();
private final Vector4f endFlashPositionCache = new Vector4f();
private final Matrix4f endFlashMatrixCache = new Matrix4f();
private final float sunPathRotation;
public CelestialUniforms(float sunPathRotation) {
@@ -34,9 +41,46 @@ public final class CelestialUniforms {
.uniformTruncated3f(PER_FRAME, "moonPosition", this::getMoonPosition)
.uniform1f(PER_FRAME, "shadowAngle", CelestialUniforms::getShadowAngle)
.uniformTruncated3f(PER_FRAME, "shadowLightPosition", this::getShadowLightPosition)
.uniformTruncated3f(PER_FRAME, "endFlashPosition", this::getEndFlashPosition)
.uniformTruncated3f(PER_FRAME, "upPosition", CelestialUniforms::getUpPosition);
}
private Vector4f getEndFlashPosition() {
final WorldClient world = Minecraft.getMinecraft().theWorld;
if (world == null || !(world.provider instanceof WorldProviderEnd) || !EndFlashCompat.isAvailable()) {
return ZERO_VECTOR_4f;
}
final float yAngle = EndFlashCompat.getYAngle();
final float xAngle = EndFlashCompat.getXAngle();
endFlashPositionCache.set(0.0F, 100.0F, 0.0F, 0.0F);
endFlashMatrixCache.set(RenderingState.INSTANCE.getModelViewMatrix());
endFlashMatrixCache.rotateY((180.0F - yAngle) * Constants.DEGREES_TO_RADIANS);
endFlashMatrixCache.rotateX((-90.0F - xAngle) * Constants.DEGREES_TO_RADIANS);
endFlashMatrixCache.transform(endFlashPositionCache);
return endFlashPositionCache;
}
private Vector4f getEndFlashPositionInWorldSpace() {
final float yAngle = EndFlashCompat.getYAngle();
final float xAngle = EndFlashCompat.getXAngle();
endFlashPositionCache.set(0.0F, 100.0F, 0.0F, 0.0F);
endFlashMatrixCache.identity();
endFlashMatrixCache.rotateY((180.0F - yAngle) * Constants.DEGREES_TO_RADIANS);
endFlashMatrixCache.rotateX((-90.0F - xAngle) * Constants.DEGREES_TO_RADIANS);
endFlashMatrixCache.transform(endFlashPositionCache);
return endFlashPositionCache;
}
private static boolean isEndFlashShadowActive() {
final WorldClient world = Minecraft.getMinecraft().theWorld;
return world != null && world.provider instanceof WorldProviderEnd
&& EndFlashCompat.isAvailable()
&& Iris.getPipelineManager().getPipeline().map(WorldRenderingPipeline::supportsEndFlash).orElse(false);
}
public static float getSunAngle() {
final float skyAngle = getSkyAngle();
@@ -66,10 +110,16 @@ public final class CelestialUniforms {
}
public Vector4f getShadowLightPosition() {
if (isEndFlashShadowActive()) {
return getEndFlashPosition();
}
return isDay() ? getSunPosition() : getMoonPosition();
}
public Vector4f getShadowLightPositionInWorldSpace() {
if (isEndFlashShadowActive()) {
return getEndFlashPositionInWorldSpace();
}
return isDay() ? getCelestialPositionInWorldSpace(100.0F) : getCelestialPositionInWorldSpace(-100.0F);
}
@@ -58,7 +58,7 @@ public final class CommonUniforms {
SystemTimeUniforms.addSystemTimeUniforms(uniforms);
BiomeUniforms.addBiomeUniforms(uniforms);
new CelestialUniforms(directives.getSunPathRotation()).addCelestialUniforms(uniforms);
IrisExclusiveUniforms.addIrisExclusiveUniforms(uniforms);
IrisExclusiveUniforms.addIrisExclusiveUniforms(uniforms, updateNotifier);
IdMapUniforms.addIdMapUniforms(updateNotifier, uniforms, idMap, directives.isOldHandLight());
MatrixUniforms.addMatrixUniforms(uniforms, directives);
@@ -0,0 +1,19 @@
package net.coderbot.iris.uniforms;
import com.gtnewhorizons.angelica.compat.etfuturum.EndFlashCompat;
import lombok.Getter;
/**
* Holds the current and previous-frame End flash intensity.
*/
@Getter
public class EndFlashStorage {
private float lastEndFlash;
private float currentEndFlash;
public void tick() {
lastEndFlash = currentEndFlash;
currentEndFlash = EndFlashCompat.getIntensity(CapturedRenderingState.INSTANCE.getTickDelta());
}
}
@@ -1,5 +1,6 @@
package net.coderbot.iris.uniforms;
import com.gtnewhorizons.angelica.compat.etfuturum.EndFlashCompat;
import com.gtnewhorizons.angelica.compat.etfuturum.EtFuturumCompat;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
@@ -41,9 +42,15 @@ public class IrisExclusiveUniforms {
private static int cachedSelectedFrame = -1;
private static int cachedSelectedBlockId;
public static void addIrisExclusiveUniforms(UniformHolder uniforms) {
public static void addIrisExclusiveUniforms(UniformHolder uniforms, FrameUpdateNotifier updateNotifier) {
WorldInfoUniforms.addWorldInfoUniforms(uniforms);
// Et Futurum Requiem backports the End Flash
final EndFlashStorage endFlashStorage = new EndFlashStorage();
if (EndFlashCompat.isAvailable()) {
updateNotifier.addListener(endFlashStorage::tick);
}
//All Iris-exclusive uniforms (uniforms which do not exist in either OptiFine or ShadersMod) should be registered here.
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "thunderStrength", IrisExclusiveUniforms::getThunderStrength);
uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "currentPlayerHealth", IrisExclusiveUniforms::getCurrentHealth);
@@ -59,6 +66,8 @@ public class IrisExclusiveUniforms {
uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "relativeEyePosition", IrisExclusiveUniforms::getRelativeEyePosition);
uniforms.uniform4f(UniformUpdateFrequency.PER_TICK, "lightningBoltPosition", IrisExclusiveUniforms::getLightningBoltPosition);
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "cloudTime", IrisExclusiveUniforms::getCloudTime);
uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "endFlashIntensity", endFlashStorage::getCurrentEndFlash);
uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "previousEndFlashIntensity", endFlashStorage::getLastEndFlash);
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "feetInWater", IrisExclusiveUniforms::isFeetInWater);
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "isRiding", IrisExclusiveUniforms::isRiding);
uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "vehicleInWater", IrisExclusiveUniforms::isVehicleInWater);
@@ -12,9 +12,9 @@ public final class ExtendedDataHelper {
public static int computeMidBlock(float x, float y, float z, int localPosX, int localPosY, int localPosZ) {
return packMidBlock(
localPosX + 0.5f - x,
localPosY + 0.5f - y,
localPosZ + 0.5f - z
(localPosX & 0xFFFF) + 0.5f - x,
(localPosY & 0xFFFF) + 0.5f - y,
(localPosZ & 0xFFFF) + 0.5f - z
);
}
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import net.minecraftforge.client.ForgeHooksClient;
import org.apache.logging.log4j.LogManager;
@@ -11,6 +12,7 @@ import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.LWJGLUtil;
import org.spongepowered.asm.mixin.Mixin;
@@ -63,15 +65,22 @@ public abstract class MixinForgeHooksClient_CoreProfile {
final int platformMaxMinor = (LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_MACOSX) ? 1 : 6;
final int platformMax = platformMaxMajor * 10 + platformMaxMinor;
int maxProbe = platformMax;
// Try pinned version first if configured
final int pinned = angelica$clampPinned(AngelicaConfig.pinnedGLVersion, platformMax);
if (pinned >= 33) {
final Exception e = angelica$tryCreate(attribs, format, setMajor, setMinor, pinned / 10, pinned % 10);
if (e == null) {
LOGGER.info("Created GL {}.{} core profile context (pinned)", pinned / 10, pinned % 10);
return;
final int cap = angelica$validateContext(pinned / 10, pinned % 10);
if (cap == 0) {
LOGGER.info("Created GL {}.{} core profile context (pinned)", pinned / 10, pinned % 10);
return;
}
maxProbe = Math.min(maxProbe, cap);
} else {
LOGGER.warn("Pinned GL version {}.{} failed, probing from highest", pinned / 10, pinned % 10);
}
LOGGER.warn("Pinned GL version {}.{} failed, probing from highest", pinned / 10, pinned % 10);
}
// Probe from highest to lowest
@@ -80,8 +89,14 @@ public abstract class MixinForgeHooksClient_CoreProfile {
final int maxMinor = (major == 4) ? platformMaxMinor : 3;
final int minMinor = (major == 3) ? 3 : 0;
for (int minor = maxMinor; minor >= minMinor; --minor) {
if (major * 10 + minor > maxProbe) continue;
final Exception e = angelica$tryCreate(attribs, format, setMajor, setMinor, major, minor);
if (e == null) {
final int cap = angelica$validateContext(major, minor);
if (cap != 0) {
maxProbe = Math.min(maxProbe, cap);
continue;
}
final int version = major * 10 + minor;
LOGGER.info("Created GL {}.{} core profile context", major, minor);
// Auto-pin if below platform max, or update a stale pin
@@ -127,6 +142,20 @@ public abstract class MixinForgeHooksClient_CoreProfile {
}
}
@Unique
private static int angelica$validateContext(int major, int minor) {
final String glVersion = GLStateManager.glGetString(GL11.GL_VERSION);
final int actual = RenderSystem.parseMajorMinor(glVersion);
final boolean core = (GLStateManager.glGetInteger(GL32.GL_CONTEXT_PROFILE_MASK) & GL32.GL_CONTEXT_CORE_PROFILE_BIT) != 0;
if (RenderSystem.isContextValid(major, minor, actual, core)) {
return 0;
}
LOGGER.warn("Requested GL {}.{} core context but driver returned \"{}\" (core profile: {}, GPU: {}); falling back to a lower version", major, minor, glVersion, core, GLStateManager.glGetString(GL11.GL_RENDERER));
try { Display.destroy(); } catch (Exception ignored) {}
final int requested = major * 10 + minor;
return (actual >= 0 && actual < requested) ? actual : requested - 1;
}
@Unique
private static void angelica$savePin(int version) {
AngelicaConfig.pinnedGLVersion = version;
@@ -1,7 +1,6 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
@@ -27,14 +26,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinSplashProgressCaching {
private static final Logger LOGGER = LogManager.getLogger("Angelica");
/**
* Before splash starts, create a separate DirectTessellator for the splash thread.
*/
@Inject(method = "start", at = @At("HEAD"))
private static void angelica$initSplashTessellator(CallbackInfo ci) {
ImmediateModeRecorder.initSplashTessellator();
}
// VAOs aren't shared across GL contexts; the SharedDrawable the client thread just swapped
// onto has none. Core profile (macOS) rejects glValidateProgram without one.
@Inject(method = "start", at = @At("RETURN"))
@@ -42,12 +33,8 @@ public class MixinSplashProgressCaching {
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
}
/**
* On return from finish() - destroy splash tessellator and mark splash complete
*/
@Inject(method = "finish", at = @At("RETURN"))
private static void angelica$enableCachingOnFinish(CallbackInfo ci) {
ImmediateModeRecorder.destroySplashTessellator();
GLStateManager.markSplashComplete();
LOGGER.info("Splash Complete");
}
@@ -27,6 +27,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.itemrenderer;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.items.ItemRenderListManager;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
@@ -43,6 +44,10 @@ public abstract class MixinItemRenderer {
@WrapMethod(method = "renderItemIn2D")
private static void angelica$cacheItem(Tessellator tessellator, float minU, float minV, float maxU, float maxV, int widthSubdivisions, int heightSubdivisions, float thickness, Operation<Void> original) {
if (GLStateManager.isRecordingDisplayList() || TessellatorManager.isCurrentlyCapturing() || TessellatorManager.shouldInterceptDraw(Tessellator.instance)) {
original.call(tessellator, minU, minV, maxU, maxV, widthSubdivisions, heightSubdivisions, thickness);
return;
}
final ItemRenderListManager.CachedVBO vbo = ItemRenderListManager.pre(minU, minV, maxU, maxV, widthSubdivisions, heightSubdivisions, thickness);
if (vbo != null) {
final DirectTessellator tess = TessellatorManager.startCapturingDirect();
@@ -1,12 +1,15 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.itemrenderer;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.items.BlockRenderListManager;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
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;
@@ -23,6 +26,15 @@ public abstract class MixinRenderBlocks {
@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;
@WrapMethod(method = "renderBlockAsItem")
private void angelica$cacheBlockItemRenderer(Block block, int meta, float brightness, Operation<Void> original) {
if (BlockRenderListManager.isISBRH(block.getRenderType())
@@ -30,6 +42,11 @@ public abstract class MixinRenderBlocks {
|| this.overrideBlockTexture != null
|| brightness != 1.0F
|| !this.useInventoryTint
|| this.blockAccess != null
|| (uvRotateEast | uvRotateWest | uvRotateSouth | uvRotateNorth | uvRotateTop | uvRotateBottom) != 0
|| GLStateManager.isRecordingDisplayList()
|| TessellatorManager.isCurrentlyCapturing()
|| TessellatorManager.shouldInterceptDraw(Tessellator.instance)
) {
// Do not cache those
original.call(block, meta, brightness);
@@ -248,4 +248,9 @@ public abstract class MixinRenderBlocks implements ExtCeleritasRenderBlocks {
private void compactCtm_resetAfterBlock(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
CTMUtils.clearCurrentCompact();
}
@Inject(method = "renderBlockAsItem", at = @At("HEAD"))
private void compactCtm_resetBeforeItem(Block block, int meta, float brightness, CallbackInfo ci) {
CTMUtils.clearCurrentCompact();
}
}